Change the way items (and classes) work
This commit is contained in:
parent
d4b337e661
commit
87d0e6c43c
47 changed files with 1483 additions and 1516 deletions
|
@ -1,190 +1,28 @@
|
|||
package de.moritzruth.spigot_ttt.game
|
||||
|
||||
import com.comphenix.packetwrapper.WrapperPlayServerPlayerInfo
|
||||
import com.comphenix.protocol.PacketType
|
||||
import com.comphenix.protocol.events.PacketAdapter
|
||||
import com.comphenix.protocol.events.PacketEvent
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers
|
||||
import com.comphenix.protocol.wrappers.PlayerInfoData
|
||||
import de.moritzruth.spigot_ttt.TTTPlugin
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.call
|
||||
import de.moritzruth.spigot_ttt.utils.nextTick
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Material
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.EventPriority
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.block.Action
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||
import org.bukkit.event.entity.EntityDamageEvent
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.bukkit.event.player.*
|
||||
import java.util.*
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
import org.bukkit.event.player.PlayerEvent
|
||||
|
||||
object GameListener : Listener {
|
||||
private val BLOCKED_COMMANDS = setOf("me", "tell", "msg")
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerJoin(event: PlayerJoinEvent) = PlayerManager.onPlayerJoin(event.player)
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerQuit(event: PlayerQuitEvent) = PlayerManager.onPlayerQuit(event.player)
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerCommandPreprocess(event: PlayerCommandPreprocessEvent) {
|
||||
if (event.message.startsWith("/rl") && GameManager.phase != null) { // /reload is not blocked
|
||||
event.player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}The server may not be reloaded while the game is running")
|
||||
event.player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}You can force reload by using ${ChatColor.WHITE}/reload")
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
|
||||
if (BLOCKED_COMMANDS.find { event.message.startsWith("/$it") } != null) {
|
||||
if (GameManager.phase != null) {
|
||||
event.player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}Dieser Befehl ist blockiert.")
|
||||
event.isCancelled = true
|
||||
}
|
||||
abstract class GameListener: Listener {
|
||||
protected fun handle(event: InventoryClickEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
val whoClicked = event.whoClicked
|
||||
if (whoClicked is Player) {
|
||||
handler(TTTPlayer.of(whoClicked) ?: return)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) {
|
||||
val player = event.damager
|
||||
protected open fun handle(event: InventoryCloseEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
val player = event.player
|
||||
if (player is Player) {
|
||||
if (player.inventory.itemInMainHand.type == Material.AIR) {
|
||||
event.damage = 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ZERO_NO_DAMAGE_TICKS_CAUSES = EnumSet.of(
|
||||
EntityDamageEvent.DamageCause.ENTITY_ATTACK,
|
||||
EntityDamageEvent.DamageCause.CUSTOM,
|
||||
EntityDamageEvent.DamageCause.ENTITY_EXPLOSION,
|
||||
EntityDamageEvent.DamageCause.BLOCK_EXPLOSION,
|
||||
EntityDamageEvent.DamageCause.FALL,
|
||||
EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK,
|
||||
EntityDamageEvent.DamageCause.FALLING_BLOCK,
|
||||
EntityDamageEvent.DamageCause.SUICIDE
|
||||
)!!
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
fun onEntityDamageLow(event: EntityDamageEvent) {
|
||||
if (ZERO_NO_DAMAGE_TICKS_CAUSES.contains(event.cause)) {
|
||||
val player = event.entity
|
||||
if (player is Player) {
|
||||
nextTick { player.noDamageTicks = 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
fun onEntityDamageHighest(event: EntityDamageEvent) {
|
||||
if (event.entity !is Player) return
|
||||
val tttPlayer = TTTPlayer.of(event.entity as Player) ?: return
|
||||
if (event.cause == EntityDamageEvent.DamageCause.CUSTOM) return
|
||||
|
||||
val reason = when (event.cause) {
|
||||
EntityDamageEvent.DamageCause.FALL -> DeathReason.FALL
|
||||
EntityDamageEvent.DamageCause.BLOCK_EXPLOSION,
|
||||
EntityDamageEvent.DamageCause.ENTITY_EXPLOSION -> DeathReason.EXPLOSION
|
||||
EntityDamageEvent.DamageCause.DROWNING -> DeathReason.DROWNED
|
||||
EntityDamageEvent.DamageCause.FIRE,
|
||||
EntityDamageEvent.DamageCause.FIRE_TICK,
|
||||
EntityDamageEvent.DamageCause.LAVA,
|
||||
EntityDamageEvent.DamageCause.HOT_FLOOR -> DeathReason.FIRE
|
||||
EntityDamageEvent.DamageCause.POISON,
|
||||
EntityDamageEvent.DamageCause.WITHER -> DeathReason.POISON
|
||||
else -> DeathReason.SUICIDE
|
||||
}
|
||||
|
||||
val e = TTTPlayerDamageEvent(tttPlayer, event.finalDamage, reason).call()
|
||||
|
||||
if (tttPlayer.player.health - e.damage <= 0) {
|
||||
tttPlayer.onDeath(reason, null)
|
||||
event.damage = 0.0
|
||||
} else {
|
||||
event.damage = e.damage
|
||||
handler(TTTPlayer.of(player) ?: return)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDeath(event: PlayerDeathEvent) {
|
||||
event.deathMessage = null
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerInteract(event: PlayerInteractEvent) {
|
||||
if (event.player.inventory.itemInMainHand.type == Material.AIR && event.action == Action.LEFT_CLICK_BLOCK) {
|
||||
GameManager.destroyBlock(event.clickedBlock!!)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
fun onPlayerSwapHandItemsLowest(event: PlayerSwapHandItemsEvent) {
|
||||
event.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onAsyncPlayerChat(event: AsyncPlayerChatEvent) {
|
||||
val senderTTTPlayer = TTTPlayer.of(event.player) ?: return
|
||||
|
||||
if (!senderTTTPlayer.alive) {
|
||||
PlayerManager.tttPlayers.filter { !it.alive }.forEach {
|
||||
it.player.sendMessage("${ChatColor.GRAY}[${ChatColor.RED}TOT${ChatColor.GRAY}] <${event.player.displayName}> ${event.message}")
|
||||
}
|
||||
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) {
|
||||
if (event.winnerRoleGroup != RoleGroup.JACKAL && event.tttPlayer.role == Role.JACKAL) {
|
||||
val sidekicks = PlayerManager.tttPlayers.filter { it.role == Role.SIDEKICK }
|
||||
|
||||
if (sidekicks.isNotEmpty()) {
|
||||
val newJackal = sidekicks.random()
|
||||
newJackal.changeRole(Role.JACKAL)
|
||||
|
||||
event.tttPlayer.changeRole(Role.SIDEKICK) // The old Jackal
|
||||
|
||||
sidekicks.forEach { sidekick ->
|
||||
if (sidekick != newJackal) {
|
||||
sidekick.player.sendMessage(TTTPlugin.prefix + "${newJackal.player.displayName} ${ChatColor.GREEN}ist der neue Jackal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val packetListener = object : PacketAdapter(plugin, PacketType.Play.Server.PLAYER_INFO) {
|
||||
override fun onPacketSending(event: PacketEvent) {
|
||||
val packet = WrapperPlayServerPlayerInfo(event.packet)
|
||||
|
||||
if (
|
||||
packet.action == EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE ||
|
||||
packet.action == EnumWrappers.PlayerInfoAction.ADD_PLAYER
|
||||
) {
|
||||
packet.data = packet.data.map { info ->
|
||||
val tttPlayer = PlayerManager.tttPlayers.find { it.player.uniqueId == info.profile.uuid }
|
||||
|
||||
if (tttPlayer == null) info
|
||||
else PlayerInfoData(
|
||||
info.profile,
|
||||
info.latency,
|
||||
if (event.player.uniqueId == info.profile.uuid) {
|
||||
if (event.player.gameMode == GameMode.SPECTATOR) EnumWrappers.NativeGameMode.SPECTATOR
|
||||
else EnumWrappers.NativeGameMode.SURVIVAL
|
||||
} else EnumWrappers.NativeGameMode.SURVIVAL,
|
||||
info.displayName
|
||||
)
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
protected fun <T: PlayerEvent> handle(event: T, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
handler(TTTPlayer.of(event.player) ?: return)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,13 +31,13 @@ object GameManager {
|
|||
val destroyedBlocks = mutableMapOf<Location, Material>()
|
||||
|
||||
private val listeners = ItemManager.listeners
|
||||
.plus(GameListener)
|
||||
.plus(GeneralGameListener)
|
||||
.plus(ShopListener)
|
||||
.plus(CorpseListener)
|
||||
.plus(TTTClassManager.listeners)
|
||||
|
||||
private val packetListeners = ItemManager.packetListeners
|
||||
.plus(GameListener.packetListener)
|
||||
.plus(GeneralGameListener.packetListener)
|
||||
|
||||
fun initialize() {
|
||||
adjustWorld()
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
package de.moritzruth.spigot_ttt.game
|
||||
|
||||
import com.comphenix.packetwrapper.WrapperPlayServerPlayerInfo
|
||||
import com.comphenix.protocol.PacketType
|
||||
import com.comphenix.protocol.events.PacketAdapter
|
||||
import com.comphenix.protocol.events.PacketEvent
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers
|
||||
import com.comphenix.protocol.wrappers.PlayerInfoData
|
||||
import de.moritzruth.spigot_ttt.TTTPlugin
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.call
|
||||
import de.moritzruth.spigot_ttt.utils.nextTick
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.EventPriority
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.block.Action
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||
import org.bukkit.event.entity.EntityDamageEvent
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.bukkit.event.player.*
|
||||
import java.util.*
|
||||
|
||||
object GeneralGameListener : Listener {
|
||||
private val BLOCKED_COMMANDS = setOf("me", "tell", "msg")
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerJoin(event: PlayerJoinEvent) = PlayerManager.onPlayerJoin(event.player)
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerQuit(event: PlayerQuitEvent) = PlayerManager.onPlayerQuit(event.player)
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerCommandPreprocess(event: PlayerCommandPreprocessEvent) {
|
||||
if (event.message.startsWith("/rl") && GameManager.phase != null) { // /reload is not blocked
|
||||
event.player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}The server may not be reloaded while the game is running")
|
||||
event.player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}You can force reload by using ${ChatColor.WHITE}/reload")
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
|
||||
if (BLOCKED_COMMANDS.find { event.message.startsWith("/$it") } != null) {
|
||||
if (GameManager.phase != null) {
|
||||
event.player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}Dieser Befehl ist blockiert.")
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) {
|
||||
val player = event.damager
|
||||
if (player is Player) {
|
||||
if (player.inventory.itemInMainHand.type == Material.AIR) {
|
||||
event.damage = 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ZERO_NO_DAMAGE_TICKS_CAUSES = EnumSet.of(
|
||||
EntityDamageEvent.DamageCause.ENTITY_ATTACK,
|
||||
EntityDamageEvent.DamageCause.CUSTOM,
|
||||
EntityDamageEvent.DamageCause.ENTITY_EXPLOSION,
|
||||
EntityDamageEvent.DamageCause.BLOCK_EXPLOSION,
|
||||
EntityDamageEvent.DamageCause.FALL,
|
||||
EntityDamageEvent.DamageCause.ENTITY_SWEEP_ATTACK,
|
||||
EntityDamageEvent.DamageCause.FALLING_BLOCK,
|
||||
EntityDamageEvent.DamageCause.SUICIDE
|
||||
)!!
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
fun onEntityDamageLow(event: EntityDamageEvent) {
|
||||
if (ZERO_NO_DAMAGE_TICKS_CAUSES.contains(event.cause)) {
|
||||
val player = event.entity
|
||||
if (player is Player) {
|
||||
nextTick { player.noDamageTicks = 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
fun onEntityDamageHighest(event: EntityDamageEvent) {
|
||||
if (event.entity !is Player) return
|
||||
val tttPlayer = TTTPlayer.of(event.entity as Player) ?: return
|
||||
if (event.cause == EntityDamageEvent.DamageCause.CUSTOM) return
|
||||
|
||||
val reason = when (event.cause) {
|
||||
EntityDamageEvent.DamageCause.FALL -> DeathReason.FALL
|
||||
EntityDamageEvent.DamageCause.BLOCK_EXPLOSION,
|
||||
EntityDamageEvent.DamageCause.ENTITY_EXPLOSION -> DeathReason.EXPLOSION
|
||||
EntityDamageEvent.DamageCause.DROWNING -> DeathReason.DROWNED
|
||||
EntityDamageEvent.DamageCause.FIRE,
|
||||
EntityDamageEvent.DamageCause.FIRE_TICK,
|
||||
EntityDamageEvent.DamageCause.LAVA,
|
||||
EntityDamageEvent.DamageCause.HOT_FLOOR -> DeathReason.FIRE
|
||||
EntityDamageEvent.DamageCause.POISON,
|
||||
EntityDamageEvent.DamageCause.WITHER -> DeathReason.POISON
|
||||
else -> DeathReason.SUICIDE
|
||||
}
|
||||
|
||||
val e = TTTPlayerDamageEvent(tttPlayer, event.finalDamage, reason).call()
|
||||
|
||||
if (tttPlayer.player.health - e.damage <= 0) {
|
||||
tttPlayer.onDeath(reason, null)
|
||||
event.damage = 0.0
|
||||
} else {
|
||||
event.damage = e.damage
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDeath(event: PlayerDeathEvent) {
|
||||
event.deathMessage = null
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerInteract(event: PlayerInteractEvent) {
|
||||
if (event.player.inventory.itemInMainHand.type == Material.AIR && event.action == Action.LEFT_CLICK_BLOCK) {
|
||||
GameManager.destroyBlock(event.clickedBlock!!)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
fun onPlayerSwapHandItemsLowest(event: PlayerSwapHandItemsEvent) {
|
||||
event.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onAsyncPlayerChat(event: AsyncPlayerChatEvent) {
|
||||
val senderTTTPlayer = TTTPlayer.of(event.player) ?: return
|
||||
|
||||
if (!senderTTTPlayer.alive) {
|
||||
PlayerManager.tttPlayers.filter { !it.alive }.forEach {
|
||||
it.player.sendMessage("${ChatColor.GRAY}[${ChatColor.RED}TOT${ChatColor.GRAY}] <${event.player.displayName}> ${event.message}")
|
||||
}
|
||||
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) {
|
||||
if (event.winnerRoleGroup != RoleGroup.JACKAL && event.tttPlayer.role == Role.JACKAL) {
|
||||
val sidekicks = PlayerManager.tttPlayers.filter { it.role == Role.SIDEKICK }
|
||||
|
||||
if (sidekicks.isNotEmpty()) {
|
||||
val newJackal = sidekicks.random()
|
||||
newJackal.changeRole(Role.JACKAL)
|
||||
|
||||
event.tttPlayer.changeRole(Role.SIDEKICK) // The old Jackal
|
||||
|
||||
sidekicks.forEach { sidekick ->
|
||||
if (sidekick != newJackal) {
|
||||
sidekick.player.sendMessage(TTTPlugin.prefix + "${newJackal.player.displayName} ${ChatColor.GREEN}ist der neue Jackal")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val packetListener = object : PacketAdapter(plugin, PacketType.Play.Server.PLAYER_INFO) {
|
||||
override fun onPacketSending(event: PacketEvent) {
|
||||
val packet = WrapperPlayServerPlayerInfo(event.packet)
|
||||
|
||||
if (
|
||||
packet.action == EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE ||
|
||||
packet.action == EnumWrappers.PlayerInfoAction.ADD_PLAYER
|
||||
) {
|
||||
packet.data = packet.data.map { info ->
|
||||
val tttPlayer = PlayerManager.tttPlayers.find { it.player.uniqueId == info.profile.uuid }
|
||||
|
||||
if (tttPlayer == null) info
|
||||
else PlayerInfoData(
|
||||
info.profile,
|
||||
info.latency,
|
||||
if (event.player.uniqueId == info.profile.uuid) {
|
||||
if (event.player.gameMode == GameMode.SPECTATOR) EnumWrappers.NativeGameMode.SPECTATOR
|
||||
else EnumWrappers.NativeGameMode.SURVIVAL
|
||||
} else EnumWrappers.NativeGameMode.SURVIVAL,
|
||||
info.displayName
|
||||
)
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,19 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.event.Listener
|
||||
|
||||
abstract class TTTClass(
|
||||
val displayName: String,
|
||||
val chatColor: ChatColor,
|
||||
val defaultItems: Set<TTTItem> = emptySet()
|
||||
) {
|
||||
val coloredDisplayName = "$chatColor$displayName"
|
||||
abstract class TTTClass {
|
||||
lateinit var tttPlayer: TTTPlayer
|
||||
|
||||
open val listener: Listener? = null
|
||||
open fun init() {}
|
||||
open fun reset() {}
|
||||
|
||||
open fun onInit(tttPlayer: TTTPlayer) {}
|
||||
|
||||
object None: TTTClass(
|
||||
object None: TTTClassCompanion(
|
||||
"Keine",
|
||||
ChatColor.GRAY
|
||||
)
|
||||
ChatColor.GRAY,
|
||||
Instance::class
|
||||
) {
|
||||
class Instance: TTTClass()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.event.Listener
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
abstract class TTTClassCompanion(
|
||||
val displayName: String,
|
||||
val chatColor: ChatColor,
|
||||
private val instanceType: KClass<out TTTClass>,
|
||||
val defaultItems: Set<TTTItem<*>> = emptySet()
|
||||
) {
|
||||
val coloredDisplayName = "$chatColor$displayName"
|
||||
fun createInstance(tttPlayer: TTTPlayer): TTTClass {
|
||||
val instance = instanceType.primaryConstructor!!.call()
|
||||
instance.tttPlayer = tttPlayer
|
||||
return instance
|
||||
}
|
||||
|
||||
open val listener: Listener? = null
|
||||
}
|
|
@ -1,21 +1,17 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.classes.impl.Gambler
|
||||
import de.moritzruth.spigot_ttt.game.classes.impl.Ninja
|
||||
import de.moritzruth.spigot_ttt.game.classes.impl.Stuntman
|
||||
import de.moritzruth.spigot_ttt.game.classes.impl.Warrior
|
||||
import de.moritzruth.spigot_ttt.game.classes.impl.*
|
||||
import java.util.*
|
||||
|
||||
object TTTClassManager {
|
||||
private val TTT_CLASSES = setOf(
|
||||
Warrior, Gambler, Stuntman, Ninja
|
||||
// Oracle is disabled because of the bug with the radar
|
||||
Warrior, Gambler, Stuntman, Ninja, Oracle
|
||||
)
|
||||
|
||||
val listeners = TTT_CLASSES.mapNotNull { it.listener }
|
||||
|
||||
fun createClassesIterator(count: Int): Iterator<TTTClass> {
|
||||
val set: MutableSet<TTTClass> = TTT_CLASSES.toMutableSet()
|
||||
fun createClassesIterator(count: Int): Iterator<TTTClassCompanion> {
|
||||
val set: MutableSet<TTTClassCompanion> = TTT_CLASSES.toMutableSet()
|
||||
|
||||
val playersWithoutClass = count - TTT_CLASSES.size
|
||||
if (playersWithoutClass > 0) set.addAll(Collections.nCopies(playersWithoutClass, TTTClass.None))
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClass
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClassCompanion
|
||||
import de.moritzruth.spigot_ttt.game.items.impl.SecondChance
|
||||
import org.bukkit.ChatColor
|
||||
|
||||
object Gambler: TTTClass(
|
||||
class Gambler: TTTClass() {
|
||||
companion object: TTTClassCompanion(
|
||||
"Gambler",
|
||||
ChatColor.YELLOW,
|
||||
Gambler::class,
|
||||
setOf(SecondChance)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClass
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClassCompanion
|
||||
import de.moritzruth.spigot_ttt.game.players.DeathReason
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDamageEvent
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerReviveEvent
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.nextTick
|
||||
import org.bukkit.ChatColor
|
||||
|
@ -14,13 +17,11 @@ import org.bukkit.potion.PotionEffectType
|
|||
import org.bukkit.scheduler.BukkitTask
|
||||
import org.bukkit.util.Vector
|
||||
|
||||
object Ninja: TTTClass(
|
||||
"Ninja",
|
||||
ChatColor.LIGHT_PURPLE
|
||||
) {
|
||||
private val isc = InversedStateContainer(State::class)
|
||||
class Ninja: TTTClass() {
|
||||
var jumpsRemaining = 1
|
||||
var checkOnGroundTask: BukkitTask? = null
|
||||
|
||||
override fun onInit(tttPlayer: TTTPlayer) {
|
||||
override fun init() {
|
||||
tttPlayer.player.allowFlight = true
|
||||
tttPlayer.player.addPotionEffect(PotionEffect(
|
||||
PotionEffectType.JUMP,
|
||||
|
@ -31,26 +32,36 @@ object Ninja: TTTClass(
|
|||
))
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
checkOnGroundTask?.cancel()
|
||||
checkOnGroundTask = null
|
||||
}
|
||||
|
||||
companion object: TTTClassCompanion(
|
||||
"Ninja",
|
||||
ChatColor.LIGHT_PURPLE,
|
||||
Ninja::class
|
||||
) {
|
||||
override val listener = object : Listener {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onPlayerToggleFlight(event: PlayerToggleFlightEvent) {
|
||||
val tttPlayer = TTTPlayer.of(event.player) ?: return
|
||||
val instance = tttPlayer.tttClassInstance
|
||||
if (instance !is Ninja) return
|
||||
|
||||
if (event.isFlying && tttPlayer.tttClass == Ninja) {
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
if (event.isFlying) {
|
||||
val vel = tttPlayer.player.velocity
|
||||
tttPlayer.player.velocity = Vector(vel.x * 3, 0.8, vel.z * 3)
|
||||
instance.jumpsRemaining -= 1
|
||||
|
||||
val current = tttPlayer.player.velocity
|
||||
tttPlayer.player.velocity = Vector(current.x * 3, 0.8, current.z * 3)
|
||||
state.jumpsRemaining -= 1
|
||||
|
||||
if (state.jumpsRemaining == 0) {
|
||||
if (instance.jumpsRemaining == 0) {
|
||||
tttPlayer.player.allowFlight = false
|
||||
|
||||
state.checkOnGroundTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
instance.checkOnGroundTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
if (tttPlayer.player.isOnGround) {
|
||||
state.jumpsRemaining = 1
|
||||
instance.jumpsRemaining = 1
|
||||
tttPlayer.player.allowFlight = true
|
||||
state.reset()
|
||||
instance.reset()
|
||||
}
|
||||
}, 1, 1)
|
||||
}
|
||||
|
@ -66,12 +77,6 @@ object Ninja: TTTClass(
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) {
|
||||
isc.get(event.tttPlayer)?.reset()
|
||||
isc.remove(event.tttPlayer)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onTTTPlayerReviveEvent(event: TTTPlayerReviveEvent) {
|
||||
if (event.tttPlayer.tttClass == Ninja) {
|
||||
|
@ -79,22 +84,6 @@ object Ninja: TTTClass(
|
|||
nextTick { nextTick { event.tttPlayer.player.allowFlight = true } }
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) {
|
||||
isc.forEveryState { state, _ ->
|
||||
state.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var jumpsRemaining = 1
|
||||
var checkOnGroundTask: BukkitTask? = null
|
||||
|
||||
fun reset() {
|
||||
checkOnGroundTask?.cancel()
|
||||
checkOnGroundTask = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClass
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClassCompanion
|
||||
import de.moritzruth.spigot_ttt.game.items.impl.Radar
|
||||
import org.bukkit.ChatColor
|
||||
|
||||
object Oracle: TTTClass(
|
||||
class Oracle: TTTClass() {
|
||||
companion object: TTTClassCompanion(
|
||||
"Oracle",
|
||||
ChatColor.DARK_AQUA,
|
||||
Oracle::class,
|
||||
setOf(Radar)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClass
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClassCompanion
|
||||
import de.moritzruth.spigot_ttt.game.players.DeathReason
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDamageEvent
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
|
||||
object Stuntman: TTTClass(
|
||||
class Stuntman: TTTClass() {
|
||||
companion object: TTTClassCompanion(
|
||||
"Stuntman",
|
||||
ChatColor.DARK_RED
|
||||
) {
|
||||
ChatColor.DARK_RED,
|
||||
Stuntman::class
|
||||
) {
|
||||
val IMMUNE_DEATH_REASONS = setOf(DeathReason.FALL, DeathReason.EXPLOSION)
|
||||
|
||||
override val listener = object : Listener {
|
||||
|
@ -21,4 +24,5 @@ object Stuntman: TTTClass(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,34 @@
|
|||
package de.moritzruth.spigot_ttt.game.classes.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClass
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClassCompanion
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDamageEvent
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
|
||||
object Warrior: TTTClass(
|
||||
"Warrior",
|
||||
ChatColor.BLUE
|
||||
) {
|
||||
override fun onInit(tttPlayer: TTTPlayer) {
|
||||
tttPlayer.walkSpeed -= 0.05F
|
||||
class Warrior: TTTClass() {
|
||||
override fun init() {
|
||||
tttPlayer.walkSpeed -= WALK_SPEED_DECREASE
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
tttPlayer.walkSpeed += WALK_SPEED_DECREASE
|
||||
}
|
||||
|
||||
companion object: TTTClassCompanion(
|
||||
"Warrior",
|
||||
ChatColor.BLUE,
|
||||
Warrior::class
|
||||
) {
|
||||
const val WALK_SPEED_DECREASE = 0.05F
|
||||
|
||||
override val listener = object : Listener {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onEntityDamage(event: TTTPlayerDamageEvent) {
|
||||
if (event.tttPlayer.tttClass == Warrior) {
|
||||
event.damage *= 0.9
|
||||
event.damage *= 0.8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ object CorpseListener: Listener {
|
|||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
fun onPlayerInteractEntity(event: PlayerInteractEntityEvent) {
|
||||
val tttPlayer = TTTPlayer.of(event.player) ?: return
|
||||
val tttCorpse = CorpseManager.getTTTCorpse(event.rightClicked) ?: return
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.bukkit.entity.Zombie
|
|||
import org.bukkit.event.inventory.InventoryType
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import org.bukkit.util.Vector
|
||||
import java.time.Instant
|
||||
|
||||
class TTTCorpse private constructor(
|
||||
|
@ -26,10 +25,9 @@ class TTTCorpse private constructor(
|
|||
location: Location,
|
||||
private val role: Role,
|
||||
private val reason: DeathReason,
|
||||
private var credits: Int,
|
||||
velocity: Vector = Vector()
|
||||
private var credits: Int
|
||||
) {
|
||||
var status = Status.UNIDENTIFIED; private set
|
||||
private var status = Status.UNIDENTIFIED; private set
|
||||
val entity: Zombie
|
||||
|
||||
val inventory = tttPlayer.player.server.createInventory(null, InventoryType.HOPPER, "${role.chatColor}${tttPlayer.player.displayName}")
|
||||
|
@ -83,7 +81,7 @@ class TTTCorpse private constructor(
|
|||
|
||||
private fun setReasonItem() {
|
||||
if (status == Status.INSPECTED) {
|
||||
val reasonItemStack = if (reason is DeathReason.Item) reason.item.itemStack.clone() else ItemStack(Resourcepack.Items.deathReason)
|
||||
val reasonItemStack = if (reason is DeathReason.Item) reason.item.templateItemStack.clone() else ItemStack(Resourcepack.Items.deathReason)
|
||||
inventory.setItem(REASON_SLOT, reasonItemStack.applyMeta {
|
||||
setDisplayName("${ChatColor.RESET}" + reason.displayText)
|
||||
lore = listOf("${ChatColor.GRAY}Grund des Todes")
|
||||
|
@ -163,8 +161,7 @@ class TTTCorpse private constructor(
|
|||
tttPlayer.player.location,
|
||||
tttPlayer.role,
|
||||
reason,
|
||||
tttPlayer.credits,
|
||||
tttPlayer.player.velocity
|
||||
tttPlayer.credits
|
||||
).also { CorpseManager.add(it) }
|
||||
|
||||
fun spawnFake(role: Role, tttPlayer: TTTPlayer, location: Location) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package de.moritzruth.spigot_ttt.game.items
|
||||
|
||||
class ClickEvent {
|
||||
var isCancelled = true
|
||||
}
|
|
@ -1,117 +1,146 @@
|
|||
package de.moritzruth.spigot_ttt.game.items
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.GameListener
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.impl.*
|
||||
import de.moritzruth.spigot_ttt.game.items.impl.weapons.BaseballBat
|
||||
import de.moritzruth.spigot_ttt.game.items.impl.weapons.Knife
|
||||
import de.moritzruth.spigot_ttt.game.items.impl.weapons.guns.*
|
||||
import de.moritzruth.spigot_ttt.game.players.IState
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.nextTick
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDeathEvent
|
||||
import de.moritzruth.spigot_ttt.utils.isLeftClick
|
||||
import de.moritzruth.spigot_ttt.utils.isRightClick
|
||||
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.Item
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.EventPriority
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent
|
||||
import org.bukkit.event.entity.ItemDespawnEvent
|
||||
import org.bukkit.event.player.PlayerDropItemEvent
|
||||
import org.bukkit.event.player.PlayerInteractEvent
|
||||
import org.bukkit.event.player.PlayerItemHeldEvent
|
||||
import org.bukkit.event.player.PlayerSwapHandItemsEvent
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
object ItemManager {
|
||||
val ITEMS: Set<TTTItem> = setOf(
|
||||
Pistol,
|
||||
Knife, Glock, Deagle, Shotgun, SidekickDeagle,
|
||||
BaseballBat,
|
||||
CloakingDevice, Rifle,
|
||||
EnderPearl, Radar, HealingPotion, Fireball,
|
||||
Teleporter, MartyrdomGrenade, FakeCorpse, Defibrillator, SecondChance
|
||||
val ITEMS: Set<TTTItem<*>> = setOf(
|
||||
Deagle, Glock, Pistol, Rifle, SidekickDeagle, BaseballBat, Knife, CloakingDevice, Defibrillator,
|
||||
EnderPearl, FakeCorpse, Fireball, HealingPotion, MartyrdomGrenade, Radar, SecondChance, Teleporter,
|
||||
Shotgun, Radar, SecondChance
|
||||
)
|
||||
|
||||
val droppedItemStates = mutableMapOf<Int, IState>()
|
||||
|
||||
val listeners get () = ITEMS.mapNotNull { it.listener }.plus(listener)
|
||||
val packetListeners get () = ITEMS.mapNotNull { it.packetListener }
|
||||
|
||||
private fun getItemByMaterial(material: Material) = ITEMS.find { tttItem -> material === tttItem.itemStack.type }
|
||||
fun getItemByItemStack(itemStack: ItemStack) = getItemByMaterial(itemStack.type)
|
||||
private fun getTTTItemByMaterial(material: Material) = ITEMS.find { tttItem -> material == tttItem.material }
|
||||
fun getTTTItemByItemStack(itemStack: ItemStack) = getTTTItemByMaterial(itemStack.type)
|
||||
fun getInstanceByItemStack(itemStack: ItemStack) = getTTTItemByItemStack(itemStack)?.getInstance(itemStack)
|
||||
|
||||
fun dropItem(location: Location, tttItem: TTTItem<*>) {
|
||||
val instance = tttItem.createInstance()
|
||||
GameManager.world.dropItem(location, instance.createItemStack())
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
droppedItemStates.clear()
|
||||
GameManager.world.getEntitiesByClass(Item::class.java).forEach {
|
||||
it.remove()
|
||||
}
|
||||
GameManager.world.getEntitiesByClass(Item::class.java).forEach(Item::remove)
|
||||
ITEMS.forEach(TTTItem<*>::reset)
|
||||
}
|
||||
|
||||
val listener = object : Listener {
|
||||
val listener = object : GameListener() {
|
||||
@EventHandler
|
||||
fun onPlayerItemHeld(event: PlayerItemHeldEvent) {
|
||||
val tttPlayer = TTTPlayer.of(event.player) ?: return
|
||||
val itemStack = event.player.inventory.getItem(event.newSlot)
|
||||
fun onPlayerInteract(event: PlayerInteractEvent) = handle(event) {
|
||||
val instance = event.item?.let { getInstanceByItemStack(it) } ?: return@handle
|
||||
|
||||
tttPlayer.itemInHand =
|
||||
if (itemStack == null || itemStack.type === Material.AIR) null
|
||||
else getItemByItemStack(itemStack)
|
||||
val clickEvent = ClickEvent()
|
||||
if (event.action.isLeftClick) instance.onLeftClick(clickEvent)
|
||||
else if (event.action.isRightClick) instance.onRightClick(clickEvent)
|
||||
event.isCancelled = clickEvent.isCancelled
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) {
|
||||
val damager = event.damager
|
||||
if (damager is Player) {
|
||||
TTTPlayer.of(damager) ?: return
|
||||
val item = damager.inventory.itemInMainHand
|
||||
if (item.type != Material.AIR) {
|
||||
val tttItem = getTTTItemByItemStack(item) ?: return
|
||||
event.isCancelled = tttItem.disableDamage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDropItem(event: PlayerDropItemEvent) {
|
||||
val tttPlayer = TTTPlayer.of(event.player) ?: return
|
||||
val tttItem = getItemByItemStack(event.itemDrop.itemStack) ?: return
|
||||
|
||||
if (tttItem.type != TTTItem.Type.SPECIAL) {
|
||||
if (tttItem is DropHandler) {
|
||||
if (!tttItem.onDrop(tttPlayer, event.itemDrop)) {
|
||||
fun onPlayerSwapHandItems(event: PlayerSwapHandItemsEvent) = handle(event) { _ ->
|
||||
val instance = event.offHandItem?.let { getInstanceByItemStack(it) } ?: return@handle
|
||||
instance.onHandSwap()
|
||||
event.isCancelled = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
plugin.server.scheduler.runTask(plugin, fun() {
|
||||
tttPlayer.updateItemInHand()
|
||||
})
|
||||
@EventHandler
|
||||
fun onPlayerItemHeld(event: PlayerItemHeldEvent) = handle(event) { tttPlayer ->
|
||||
tttPlayer.player.inventory.getItem(event.previousSlot)
|
||||
?.also { itemStack -> getInstanceByItemStack(itemStack)?.isSelected = false }
|
||||
|
||||
tttPlayer.player.inventory.getItem(event.newSlot)
|
||||
?.also { itemStack -> getInstanceByItemStack(itemStack)?.isSelected = true }
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDropItem(event: PlayerDropItemEvent) = handle(event) { tttPlayer ->
|
||||
val instance = getInstanceByItemStack(event.itemDrop.itemStack) ?: return@handle
|
||||
|
||||
val notDroppableReason = instance.notDroppableReason
|
||||
if (notDroppableReason == null) {
|
||||
instance.carrier = null
|
||||
} else {
|
||||
event.player.sendActionBarMessage("${ChatColor.RED}Du kannst dieses Item nicht droppen")
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onItemDespawn(event: ItemDespawnEvent) {
|
||||
if (getItemByItemStack(event.entity.itemStack) != null) {
|
||||
event.entity.ticksLived = 1
|
||||
tttPlayer.player.sendActionBarMessage(notDroppableReason)
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onEntityPickupItem(event: EntityPickupItemEvent) {
|
||||
if (event.entity !is Player) {
|
||||
return
|
||||
}
|
||||
val player = event.entity
|
||||
if (player !is Player) return
|
||||
|
||||
val player = event.entity as Player
|
||||
val tttPlayer = TTTPlayer.of(player) ?: return
|
||||
val instance = getInstanceByItemStack(event.item.itemStack)
|
||||
|
||||
val tttItem = getItemByItemStack(event.item.itemStack)
|
||||
|
||||
if (tttItem != null) {
|
||||
if (runCatching { tttPlayer.checkAddItemPreconditions(tttItem) }.isSuccess) {
|
||||
nextTick { tttPlayer.updateItemInHand() }
|
||||
|
||||
if (tttItem is DropHandler) {
|
||||
tttItem.onPickup(tttPlayer, event.item)
|
||||
}
|
||||
|
||||
if (instance != null) {
|
||||
if (runCatching { tttPlayer.checkAddItemPreconditions(instance.tttItem) }.isSuccess) {
|
||||
instance.carrier = tttPlayer
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
event.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onItemDespawn(event: ItemDespawnEvent) {
|
||||
if (getTTTItemByItemStack(event.entity.itemStack) != null) {
|
||||
event.entity.ticksLived = 1
|
||||
event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) {
|
||||
val itemStackInHand = event.tttPlayer.player.inventory.itemInMainHand
|
||||
if (itemStackInHand.type != Material.AIR) {
|
||||
val instance = getInstanceByItemStack(itemStackInHand)
|
||||
if (instance != null && instance.notDroppableReason == null)
|
||||
GameManager.world.dropItem(event.location, instance.createItemStack())
|
||||
|
||||
event.tttPlayer.getOwningTTTItemInstances().forEach {
|
||||
event.tttPlayer.removeItem(it.tttItem, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,15 @@ package de.moritzruth.spigot_ttt.game.items
|
|||
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.utils.ConfigurationFile
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.roundToCenter
|
||||
import org.bukkit.Location
|
||||
import java.util.*
|
||||
|
||||
object ItemSpawner {
|
||||
private const val CONFIG_PATH = "spawn-locations"
|
||||
|
||||
private val spawnLocationsConfig = ConfigurationFile("spawnLocations")
|
||||
private val spawningItems = ItemManager.ITEMS.filter { it is Spawning }
|
||||
|
||||
private fun getSpawnLocations(): Set<Location> {
|
||||
return spawnLocationsConfig.getStringList(CONFIG_PATH).map {
|
||||
|
@ -25,14 +26,19 @@ object ItemSpawner {
|
|||
}
|
||||
|
||||
fun spawnWeapons() {
|
||||
var itemIterator = spawningItems.shuffled().iterator()
|
||||
|
||||
for (location in getSpawnLocations()) {
|
||||
if (!itemIterator.hasNext()) {
|
||||
itemIterator = spawningItems.shuffled().iterator()
|
||||
val spawningItems = mutableListOf<TTTItem<*>>()
|
||||
loop@ for (tttItem in ItemManager.ITEMS) {
|
||||
val count = Probability.values().indexOf(tttItem.spawnProbability) + 1
|
||||
spawningItems.addAll(Collections.nCopies(count, tttItem))
|
||||
}
|
||||
|
||||
GameManager.world.dropItem(location, itemIterator.next().itemStack.clone())
|
||||
var itemsIterator = spawningItems.shuffled().iterator()
|
||||
for (location in getSpawnLocations()) {
|
||||
if (!itemsIterator.hasNext()) {
|
||||
itemsIterator = spawningItems.shuffled().iterator()
|
||||
}
|
||||
|
||||
ItemManager.dropItem(location, itemsIterator.next())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,46 +3,142 @@ package de.moritzruth.spigot_ttt.game.items
|
|||
import com.comphenix.protocol.events.PacketListener
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.nextTick
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.entity.Item
|
||||
import org.bukkit.NamespacedKey
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.persistence.PersistentDataType
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
interface Selectable {
|
||||
fun onSelect(tttPlayer: TTTPlayer)
|
||||
fun onDeselect(tttPlayer: TTTPlayer)
|
||||
}
|
||||
open class TTTItem<InstanceT: TTTItem.Instance>(
|
||||
val type: Type,
|
||||
val templateItemStack: ItemStack,
|
||||
val instanceType: KClass<out InstanceT>,
|
||||
val shopInfo: ShopInfo? = null,
|
||||
val spawnProbability: Probability? = null,
|
||||
val disableDamage: Boolean = true
|
||||
) {
|
||||
open val listener: Listener? = null
|
||||
open val packetListener: PacketListener? = null
|
||||
|
||||
interface DropHandler {
|
||||
fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item): Boolean
|
||||
fun onPickup(tttPlayer: TTTPlayer, itemEntity: Item)
|
||||
}
|
||||
val material = templateItemStack.type
|
||||
|
||||
interface Buyable {
|
||||
val buyableBy: EnumSet<Role>
|
||||
val price: Int
|
||||
val buyLimit: Int?
|
||||
}
|
||||
val instancesByUUID = mutableMapOf<UUID, InstanceT>()
|
||||
fun getInstance(itemStack: ItemStack) =
|
||||
itemStack.itemMeta?.persistentDataContainer?.get(ID_KEY, PersistentDataType.STRING)
|
||||
?.let { instancesByUUID[UUID.fromString(it)] }
|
||||
|
||||
val PASSIVE = "${ChatColor.RESET}${ChatColor.RED}(Passiv)"
|
||||
fun getInstance(tttPlayer: TTTPlayer) = instancesByUUID.values.find { it.carrier === tttPlayer }
|
||||
|
||||
// Marker
|
||||
interface Spawning
|
||||
fun reset() {
|
||||
instancesByUUID.values.forEach {
|
||||
it.carrier?.removeItem(it.tttItem, removeInstance = false)
|
||||
it.reset()
|
||||
}
|
||||
instancesByUUID.clear()
|
||||
}
|
||||
|
||||
interface TTTItem {
|
||||
val listener: Listener? get() = null
|
||||
val packetListener: PacketListener? get() = null
|
||||
val itemStack: ItemStack
|
||||
val type: Type
|
||||
|
||||
fun onOwn(tttPlayer: TTTPlayer) {}
|
||||
fun onRemove(tttPlayer: TTTPlayer) {}
|
||||
fun createInstance(): InstanceT = instanceType.primaryConstructor!!.call()
|
||||
.also { instancesByUUID[it.uuid] = it }
|
||||
|
||||
enum class Type(val maxItemsOfTypeInInventory: Int?) {
|
||||
MELEE(1),
|
||||
PISTOL_LIKE(1),
|
||||
PISTOL_LIKE(2),
|
||||
HEAVY_WEAPON(1),
|
||||
SPECIAL(null);
|
||||
}
|
||||
|
||||
data class ShopInfo(
|
||||
val buyableBy: EnumSet<Role>,
|
||||
val price: Int,
|
||||
val buyLimit: Int = 0
|
||||
) {
|
||||
init {
|
||||
if (buyLimit < 0) throw IllegalArgumentException("buyLimit must be positive")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PASSIVE_SUFFIX = " ${ChatColor.RESET}${ChatColor.RED}(Passiv)"
|
||||
val ID_KEY = NamespacedKey(plugin, "instance")
|
||||
}
|
||||
|
||||
abstract class Instance(val tttItem: TTTItem<*>, droppable: Boolean = true) {
|
||||
val uuid = UUID.randomUUID()!!
|
||||
|
||||
fun createItemStack() = tttItem.templateItemStack.clone().applyMeta {
|
||||
persistentDataContainer.set(ID_KEY, PersistentDataType.STRING, uuid.toString())
|
||||
}
|
||||
|
||||
private var isFirstCarrier = true
|
||||
open var carrier: TTTPlayer? = null
|
||||
set(newCarrier) {
|
||||
if (newCarrier == field) return // Do nothing it does not get changed
|
||||
|
||||
if (newCarrier == null) {
|
||||
val oldCarrier = field!!
|
||||
isSelected = false
|
||||
field = newCarrier
|
||||
onCarrierRemoved(oldCarrier)
|
||||
} else {
|
||||
field = newCarrier
|
||||
onCarrierSet(newCarrier, isFirstCarrier)
|
||||
isFirstCarrier = false
|
||||
nextTick {
|
||||
if (newCarrier.player.inventory.itemInMainHand.type == tttItem.material) isSelected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called after onDeselect
|
||||
*/
|
||||
protected open fun onCarrierRemoved(oldCarrier: TTTPlayer) {}
|
||||
|
||||
/**
|
||||
* This is called before isSelected is set to true in the next tick (only if the item is in the main hand)
|
||||
*/
|
||||
protected open fun onCarrierSet(carrier: TTTPlayer, isFirst: Boolean) {}
|
||||
|
||||
protected fun requireCarrier() =
|
||||
carrier ?: run {
|
||||
throw IllegalStateException("The item must be carried to perform this action")
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason why the item can not be dropped or null if it can be dropped.
|
||||
* Should be overridden with a dynamic getter.
|
||||
*/
|
||||
open val notDroppableReason: String? =
|
||||
if (droppable) null
|
||||
else "${ChatColor.RED}Du kannst dieses Item nicht droppen"
|
||||
|
||||
open fun onRightClick(event: ClickEvent) { event.isCancelled = false }
|
||||
open fun onLeftClick(event: ClickEvent) { event.isCancelled = false }
|
||||
open fun onHandSwap() {}
|
||||
|
||||
open fun reset() {}
|
||||
|
||||
var isSelected = false
|
||||
set(value) {
|
||||
if (value == isSelected) return
|
||||
field = value
|
||||
|
||||
if (value) onSelect()
|
||||
else onDeselect()
|
||||
}
|
||||
|
||||
protected open fun onSelect() {}
|
||||
|
||||
/**
|
||||
* If this is called because the carrier is set to null, it is called before the field is changed
|
||||
*/
|
||||
protected open fun onDeselect() {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,16 @@
|
|||
package de.moritzruth.spigot_ttt.game.items
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.GameListener
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDeathEvent
|
||||
import de.moritzruth.spigot_ttt.utils.isLeftClick
|
||||
import de.moritzruth.spigot_ttt.utils.isRightClick
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerReviveEvent
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
import org.bukkit.event.player.PlayerEvent
|
||||
import org.bukkit.event.player.PlayerInteractEvent
|
||||
import org.bukkit.event.player.PlayerItemConsumeEvent
|
||||
import org.bukkit.event.player.PlayerSwapHandItemsEvent
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
open class TTTItemListener(
|
||||
private val tttItem: TTTItem,
|
||||
private val cancelDamage: Boolean,
|
||||
private val removeOnDeath: Boolean = true
|
||||
): Listener {
|
||||
@EventHandler
|
||||
open fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { _, _ ->
|
||||
if (cancelDamage) event.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
open fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) {
|
||||
if (removeOnDeath) event.tttPlayer.removeItem(tttItem)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerInteract(event: PlayerInteractEvent) = handle(event) { tttPlayer ->
|
||||
event.isCancelled = true
|
||||
val data = ClickEventData(tttPlayer, event.item!!, event)
|
||||
if (event.action.isRightClick) onRightClick(data)
|
||||
else if (event.action.isLeftClick) onLeftClick(data)
|
||||
}
|
||||
|
||||
open fun onRightClick(data: ClickEventData) {
|
||||
data.event.isCancelled = false
|
||||
}
|
||||
|
||||
open fun onLeftClick(data: ClickEventData) {
|
||||
data.event.isCancelled = false
|
||||
}
|
||||
|
||||
protected fun handle(event: PlayerInteractEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
if (event.item?.type == tttItem.itemStack.type) {
|
||||
val tttPlayer = TTTPlayer.of(event.player) ?: return
|
||||
handler(tttPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun handle(event: PlayerSwapHandItemsEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
if (event.offHandItem?.type == tttItem.itemStack.type) {
|
||||
val tttPlayer = TTTPlayer.of(event.player) ?: return
|
||||
handler(tttPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
open class TTTItemListener<InstanceT: TTTItem.Instance>(private val tttItem: TTTItem<InstanceT>): GameListener() {
|
||||
protected fun handle(
|
||||
event: EntityDamageByEntityEvent,
|
||||
handler: (damagerTTTPlayer: TTTPlayer, damagedTTTPlayer: TTTPlayer) -> Unit
|
||||
|
@ -71,7 +21,7 @@ open class TTTItemListener(
|
|||
if (
|
||||
damager is Player &&
|
||||
damaged is Player &&
|
||||
damager.inventory.itemInMainHand.type == tttItem.itemStack.type
|
||||
damager.inventory.itemInMainHand.type == tttItem.material
|
||||
) {
|
||||
val damagerTTTPlayer = TTTPlayer.of(damager) ?: return
|
||||
val damagedTTTPlayer = TTTPlayer.of(damaged) ?: return
|
||||
|
@ -79,33 +29,38 @@ open class TTTItemListener(
|
|||
}
|
||||
}
|
||||
|
||||
protected fun handle(event: InventoryClickEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
val whoClicked = event.whoClicked
|
||||
if (whoClicked is Player) {
|
||||
handler(TTTPlayer.of(whoClicked) ?: return)
|
||||
protected fun handle(event: PlayerItemConsumeEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
if (event.item.type == tttItem.material) {
|
||||
handler(TTTPlayer.of(event.player) ?: return)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun handle(event: InventoryCloseEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
protected fun handleWithInstance(event: InventoryCloseEvent, handler: (instance: InstanceT) -> Unit) {
|
||||
val player = event.player
|
||||
if (player is Player) {
|
||||
handler(TTTPlayer.of(player) ?: return)
|
||||
val tttPlayer = TTTPlayer.of(player) ?: return
|
||||
val instance = tttItem.getInstance(tttPlayer) ?: return
|
||||
handler(instance)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun <T: PlayerEvent> handle(event: T, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
handler(TTTPlayer.of(event.player) ?: return)
|
||||
}
|
||||
|
||||
protected fun handle(event: PlayerItemConsumeEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
|
||||
if (event.item.type == tttItem.itemStack.type) {
|
||||
handler(TTTPlayer.of(event.player) ?: return)
|
||||
protected fun handleWithInstance(event: InventoryClickEvent, handler: (instance: InstanceT) -> Unit) {
|
||||
val whoClicked = event.whoClicked
|
||||
if (whoClicked is Player) {
|
||||
val tttPlayer = TTTPlayer.of(whoClicked) ?: return
|
||||
val instance = tttItem.getInstance(tttPlayer) ?: return
|
||||
handler(instance)
|
||||
}
|
||||
}
|
||||
|
||||
data class ClickEventData(
|
||||
val tttPlayer: TTTPlayer,
|
||||
val itemStack: ItemStack,
|
||||
val event: PlayerInteractEvent
|
||||
)
|
||||
protected fun handleWithInstance(event: PlayerEvent, handler: (instance: InstanceT) -> Unit) {
|
||||
val player = event.player
|
||||
val tttPlayer = TTTPlayer.of(player) ?: return
|
||||
val instance = tttItem.getInstance(tttPlayer) ?: return
|
||||
handler(instance)
|
||||
}
|
||||
|
||||
protected fun handle(event: TTTPlayerReviveEvent, handler: (instance: InstanceT) -> Unit) {
|
||||
handler(tttItem.getInstance(event.tttPlayer) ?: return)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.Selectable
|
||||
import de.moritzruth.spigot_ttt.game.items.ClickEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.startItemDamageProgress
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.SoundCategory
|
||||
import org.bukkit.event.EventHandler
|
||||
|
@ -19,90 +18,67 @@ import org.bukkit.potion.PotionEffect
|
|||
import org.bukkit.potion.PotionEffectType
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
|
||||
object CloakingDevice: TTTItem, Buyable, Selectable {
|
||||
override val itemStack = ItemStack(Resourcepack.Items.cloakingDevice).applyMeta {
|
||||
object CloakingDevice: TTTItem<CloakingDevice.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.cloakingDevice).applyMeta {
|
||||
setDisplayName("${ChatColor.GRAY}${ChatColor.MAGIC}###${ChatColor.RESET}${ChatColor.GRAY} Cloaking Device ${ChatColor.MAGIC}###")
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Macht dich unsichtbar"
|
||||
)
|
||||
addItemFlags(ItemFlag.HIDE_ATTRIBUTES)
|
||||
}
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
buyLimit = 1,
|
||||
price = 2
|
||||
)
|
||||
) {
|
||||
private const val WALK_SPEED_DECREASE = 0.1F
|
||||
private const val COOLDOWN = 10.0
|
||||
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val price = 2
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val buyLimit = 1
|
||||
class Instance: TTTItem.Instance(CloakingDevice) {
|
||||
var enabled = false
|
||||
private var cooldownTask: BukkitTask? = null
|
||||
|
||||
val isc = InversedStateContainer(State::class)
|
||||
override fun onRightClick(event: ClickEvent) {
|
||||
if (cooldownTask == null) setEnabled(carrier!!, !enabled)
|
||||
}
|
||||
|
||||
override fun onSelect(tttPlayer: TTTPlayer) {}
|
||||
override fun onDeselect(tttPlayer: TTTPlayer) = disable(tttPlayer)
|
||||
override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
|
||||
setEnabled(oldCarrier, false)
|
||||
}
|
||||
|
||||
private fun enable(tttPlayer: TTTPlayer, itemStack: ItemStack) {
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
private fun setEnabled(tttPlayer: TTTPlayer, value: Boolean) {
|
||||
if (value == enabled) return
|
||||
|
||||
if (value) {
|
||||
tttPlayer.walkSpeed -= WALK_SPEED_DECREASE
|
||||
tttPlayer.player.apply {
|
||||
isSprinting = false
|
||||
walkSpeed = 0.1F
|
||||
|
||||
addPotionEffect(PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0, false, false))
|
||||
playSound(location, Resourcepack.Sounds.Item.CloakingDevice.on, SoundCategory.PLAYERS, 1F, 1F)
|
||||
}
|
||||
|
||||
state.enabled = true
|
||||
state.itemStack = itemStack
|
||||
}
|
||||
|
||||
private fun disable(tttPlayer: TTTPlayer) {
|
||||
val state = isc.get(tttPlayer) ?: return
|
||||
if (!state.enabled) return
|
||||
|
||||
} else {
|
||||
tttPlayer.walkSpeed += WALK_SPEED_DECREASE
|
||||
tttPlayer.player.apply {
|
||||
walkSpeed = 0.2F
|
||||
removePotionEffect(PotionEffectType.INVISIBILITY)
|
||||
playSound(location, Resourcepack.Sounds.Item.CloakingDevice.off, SoundCategory.PLAYERS, 1F, 1F)
|
||||
}
|
||||
|
||||
state.enabled = false
|
||||
// TODO: Show progress in level bar
|
||||
}
|
||||
|
||||
val itemStack = state.itemStack
|
||||
if (itemStack != null) {
|
||||
state.cooldownTask = startItemDamageProgress(itemStack, COOLDOWN) { state.cooldownTask = null }
|
||||
enabled = value
|
||||
}
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override val listener = object : TTTItemListener<Instance>(CloakingDevice) {
|
||||
@EventHandler
|
||||
fun onPlayerToggleSprint(event: PlayerToggleSprintEvent) = handle(event) { tttPlayer ->
|
||||
if (event.isSprinting && isc.getOrCreate(tttPlayer).enabled) event.isCancelled = true
|
||||
fun onPlayerToggleSprint(event: PlayerToggleSprintEvent) = handleWithInstance(event) { instance ->
|
||||
if (event.isSprinting && instance.enabled) event.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
override fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) {
|
||||
super.onTTTPlayerDeath(event)
|
||||
isc.get(event.tttPlayer)?.cooldownTask?.cancel()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ -> state.cooldownTask?.cancel() }
|
||||
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
val state = isc.getOrCreate(data.tttPlayer)
|
||||
if (state.cooldownTask != null) return
|
||||
|
||||
if (state.enabled) {
|
||||
disable(data.tttPlayer)
|
||||
} else {
|
||||
enable(data.tttPlayer, data.itemStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var enabled: Boolean = false
|
||||
var cooldownTask: BukkitTask? = null
|
||||
var itemStack: ItemStack? = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.corpses.CorpseClickEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.*
|
||||
import org.bukkit.ChatColor
|
||||
|
@ -20,49 +20,67 @@ import org.bukkit.scheduler.BukkitTask
|
|||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
object Defibrillator: TTTItem, Buyable {
|
||||
private const val REVIVE_DURATION = 10.0
|
||||
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val itemStack = ItemStack(Resourcepack.Items.defibrillator).applyMeta {
|
||||
object Defibrillator: TTTItem<Defibrillator.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.defibrillator).applyMeta {
|
||||
setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Defibrillator")
|
||||
hideInfo()
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Belebe einen Spieler wieder"
|
||||
)
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL)
|
||||
override val price = 2
|
||||
override val buyLimit = 1
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyLimit = 1,
|
||||
price = 2,
|
||||
buyableBy = roles(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL)
|
||||
)
|
||||
) {
|
||||
private const val REVIVE_DURATION = 10.0
|
||||
|
||||
private val isc = InversedStateContainer(State::class)
|
||||
class Instance: TTTItem.Instance(Defibrillator) {
|
||||
var action: Action? = null
|
||||
var bossBar = plugin.server.createBossBar(
|
||||
"${ChatColor.BOLD}Defibrillator",
|
||||
BarColor.GREEN,
|
||||
BarStyle.SOLID
|
||||
)
|
||||
|
||||
override fun onCarrierSet(carrier: TTTPlayer, isFirst: Boolean) {
|
||||
bossBar.addPlayer(carrier.player)
|
||||
}
|
||||
|
||||
override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
|
||||
bossBar.removePlayer(oldCarrier.player)
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
action?.reset()
|
||||
stopSound()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopSound() = plugin.server.onlinePlayers.forEach {
|
||||
it.stopSound(Resourcepack.Sounds.Item.Defibrillator.use, SoundCategory.PLAYERS)
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onCorpseClick(event: CorpseClickEvent) {
|
||||
if (event.tttPlayer.player.inventory.itemInMainHand.type != itemStack.type) return
|
||||
val instance = getInstance(event.tttPlayer.player.inventory.itemInMainHand) ?: return
|
||||
event.isCancelled = true
|
||||
|
||||
val state = isc.getOrCreate(event.tttPlayer)
|
||||
state.bossBar.addPlayer(event.tttPlayer.player)
|
||||
|
||||
when(val action = state.action) {
|
||||
null -> state.action = Action.Reviving(event.tttPlayer, state)
|
||||
when(val action = instance.action) {
|
||||
null -> instance.action = Action.Reviving(event.tttPlayer, instance)
|
||||
is Action.Reviving -> {
|
||||
action.cancelTask.cancel()
|
||||
action.cancelTask = action.createCancelTask()
|
||||
|
||||
val progress = action.duration / REVIVE_DURATION
|
||||
|
||||
if (progress >= 1) {
|
||||
try {
|
||||
event.tttCorpse.revive()
|
||||
|
||||
event.tttPlayer.player.sendActionBarMessage(
|
||||
"${ChatColor.BOLD}${event.tttCorpse.tttPlayer.player.displayName} " +
|
||||
"${ChatColor.GREEN}wurde wiederbelebt"
|
||||
|
@ -70,25 +88,20 @@ object Defibrillator: TTTItem, Buyable {
|
|||
|
||||
action.cancelTask.cancel()
|
||||
event.tttPlayer.player.inventory.removeTTTItemNextTick(Defibrillator)
|
||||
state.reset(event.tttPlayer)
|
||||
isc.remove(event.tttPlayer)
|
||||
} catch(e: TTTPlayer.AlreadyLivingException) {
|
||||
action.cancel()
|
||||
}
|
||||
} else state.bossBar.progress = progress
|
||||
} else instance.bossBar.progress = progress
|
||||
}
|
||||
is Action.Canceled -> noop()
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, tttPlayer -> state.reset(tttPlayer) }
|
||||
}
|
||||
|
||||
sealed class Action(val tttPlayer: TTTPlayer) {
|
||||
open fun reset() {}
|
||||
|
||||
class Reviving(tttPlayer: TTTPlayer, val state: State): Action(tttPlayer) {
|
||||
class Reviving(tttPlayer: TTTPlayer, val instance: Instance): Action(tttPlayer) {
|
||||
var cancelTask = createCancelTask()
|
||||
private val startedAt: Instant = Instant.now()
|
||||
val duration get() = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000
|
||||
|
@ -99,7 +112,7 @@ object Defibrillator: TTTItem, Buyable {
|
|||
|
||||
fun cancel() {
|
||||
cancelTask.cancel()
|
||||
state.action = Canceled(tttPlayer)
|
||||
instance.action = Canceled(tttPlayer, instance)
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -111,14 +124,14 @@ object Defibrillator: TTTItem, Buyable {
|
|||
1F
|
||||
)
|
||||
|
||||
state.bossBar.color = BarColor.GREEN
|
||||
state.bossBar.addPlayer(tttPlayer.player)
|
||||
instance.bossBar.color = BarColor.GREEN
|
||||
instance.bossBar.isVisible = true
|
||||
}
|
||||
|
||||
override fun reset() = cancelTask.cancel()
|
||||
}
|
||||
|
||||
class Canceled(tttPlayer: TTTPlayer): Action(tttPlayer) {
|
||||
class Canceled(tttPlayer: TTTPlayer, val instance: Instance): Action(tttPlayer) {
|
||||
private var switches: Int = 0
|
||||
private lateinit var task: BukkitTask
|
||||
|
||||
|
@ -132,16 +145,15 @@ object Defibrillator: TTTItem, Buyable {
|
|||
1F
|
||||
)
|
||||
|
||||
task = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val state = isc.get(tttPlayer) ?: return@runTaskTimer
|
||||
|
||||
plugin.server.scheduler.runTaskTimer(plugin, { task ->
|
||||
if (switches == SWITCHES_COUNT) {
|
||||
this@Canceled.task = task
|
||||
task.cancel()
|
||||
state.action = null
|
||||
state.bossBar.removePlayer(tttPlayer.player)
|
||||
instance.action = null
|
||||
instance.bossBar.isVisible = false
|
||||
} else {
|
||||
state.bossBar.progress = 1.0
|
||||
state.bossBar.color = if (switches % 2 == 0) BarColor.RED else BarColor.WHITE
|
||||
instance.bossBar.progress = 1.0
|
||||
instance.bossBar.color = if (switches % 2 == 0) BarColor.RED else BarColor.WHITE
|
||||
switches += 1
|
||||
}
|
||||
}, 0, secondsToTicks(0.2).toLong())
|
||||
|
@ -154,19 +166,4 @@ object Defibrillator: TTTItem, Buyable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var action: Action? = null
|
||||
var bossBar = plugin.server.createBossBar(
|
||||
"${ChatColor.BOLD}Defibrillator",
|
||||
BarColor.GREEN,
|
||||
BarStyle.SOLID
|
||||
)
|
||||
|
||||
fun reset(tttPlayer: TTTPlayer) {
|
||||
bossBar.removePlayer(tttPlayer.player)
|
||||
action?.reset()
|
||||
stopSound()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
object EnderPearl: TTTItem, Buyable {
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val itemStack = ItemStack(Material.ENDER_PEARL).applyMeta {
|
||||
object EnderPearl : TTTItem<EnderPearl.Instance>(
|
||||
instanceType = Instance::class,
|
||||
type = Type.SPECIAL,
|
||||
templateItemStack = ItemStack(Material.ENDER_PEARL).applyMeta {
|
||||
setDisplayName("${ChatColor.DARK_GREEN}Enderperle")
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE)
|
||||
override val price = 1
|
||||
override val buyLimit: Int? = null
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
data.event.isCancelled = false
|
||||
}
|
||||
}
|
||||
},
|
||||
spawnProbability = Probability.VERY_LOW,
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE),
|
||||
price = 1
|
||||
)
|
||||
) {
|
||||
class Instance: TTTItem.Instance(EnderPearl)
|
||||
}
|
||||
|
|
|
@ -2,10 +2,13 @@ package de.moritzruth.spigot_ttt.game.items.impl
|
|||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.corpses.TTTCorpse
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.ClickEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.hideInfo
|
||||
|
@ -21,11 +24,16 @@ import org.bukkit.inventory.Inventory
|
|||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.SkullMeta
|
||||
|
||||
object FakeCorpse: TTTItem, Buyable {
|
||||
private val DISPLAY_NAME = "${ChatColor.YELLOW}${ChatColor.BOLD}Fake-Leiche"
|
||||
|
||||
override val itemStack = ItemStack(Resourcepack.Items.fakeCorpse).applyMeta {
|
||||
setDisplayName(DISPLAY_NAME)
|
||||
object FakeCorpse: TTTItem<FakeCorpse.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
buyLimit = 3,
|
||||
price = 2
|
||||
),
|
||||
templateItemStack = ItemStack(Resourcepack.Items.fakeCorpse).applyMeta {
|
||||
setDisplayName(FakeCorpse.DISPLAY_NAME)
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Spawnt eine Fake-Leiche",
|
||||
|
@ -33,12 +41,17 @@ object FakeCorpse: TTTItem, Buyable {
|
|||
)
|
||||
hideInfo()
|
||||
}
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val price = 2
|
||||
override val buyLimit: Int? = 3
|
||||
) {
|
||||
private val DISPLAY_NAME = "${ChatColor.YELLOW}${ChatColor.BOLD}Fake-Leiche"
|
||||
|
||||
val isc = InversedStateContainer(State::class)
|
||||
class Instance: TTTItem.Instance(FakeCorpse) {
|
||||
var chosenRole: Role? = null
|
||||
var choosePlayerInventory: Inventory? = null
|
||||
|
||||
override fun onRightClick(event: ClickEvent) {
|
||||
carrier!!.player.openInventory(chooseRoleInventory)
|
||||
}
|
||||
}
|
||||
|
||||
private val chooseRoleInventory = plugin.server.createInventory(
|
||||
null,
|
||||
|
@ -72,17 +85,17 @@ object FakeCorpse: TTTItem, Buyable {
|
|||
.toTypedArray())
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
isc.getOrCreate(data.tttPlayer).chosenRole = null
|
||||
data.tttPlayer.player.openInventory(chooseRoleInventory)
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler
|
||||
fun onInventoryClick(event: InventoryClickEvent) = handle(event) { tttPlayer ->
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
val instance = getInstance(tttPlayer) ?: return@handle
|
||||
|
||||
if (!setOf(state.choosePlayerInventory, chooseRoleInventory).contains(event.clickedInventory)) return@handle
|
||||
if (
|
||||
!setOf(
|
||||
instance.choosePlayerInventory,
|
||||
chooseRoleInventory
|
||||
).contains(event.clickedInventory)
|
||||
) return@handle
|
||||
event.isCancelled = true
|
||||
|
||||
val item = event.currentItem
|
||||
|
@ -90,11 +103,12 @@ object FakeCorpse: TTTItem, Buyable {
|
|||
if (item != null && event.click == ClickType.LEFT) {
|
||||
when (event.clickedInventory) {
|
||||
chooseRoleInventory -> {
|
||||
state.chosenRole = Role.values()[event.slot]
|
||||
state.choosePlayerInventory = createChoosePlayerInventory()
|
||||
tttPlayer.player.openInventory(state.choosePlayerInventory!!)
|
||||
instance.chosenRole = Role.values()[event.slot]
|
||||
val choosePlayerInventory = createChoosePlayerInventory()
|
||||
instance.choosePlayerInventory = choosePlayerInventory
|
||||
tttPlayer.player.openInventory(choosePlayerInventory)
|
||||
}
|
||||
state.choosePlayerInventory -> {
|
||||
instance.choosePlayerInventory -> {
|
||||
tttPlayer.player.closeInventory()
|
||||
|
||||
val corpsePlayer = plugin.server.getPlayer((item.itemMeta as SkullMeta).owningPlayer!!.uniqueId)!!
|
||||
|
@ -103,8 +117,7 @@ object FakeCorpse: TTTItem, Buyable {
|
|||
if (corpseTTTPlayer == null) {
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Das hat nicht funktioniert")
|
||||
} else {
|
||||
TTTCorpse.spawnFake(state.chosenRole!!, corpseTTTPlayer, tttPlayer.player.location)
|
||||
|
||||
TTTCorpse.spawnFake(instance.chosenRole!!, corpseTTTPlayer, tttPlayer.player.location)
|
||||
tttPlayer.player.inventory.removeTTTItem(FakeCorpse)
|
||||
}
|
||||
}
|
||||
|
@ -112,9 +125,4 @@ object FakeCorpse: TTTItem, Buyable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var chosenRole: Role? = null
|
||||
var choosePlayerInventory: Inventory? = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.ClickEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.clearHeldItemSlot
|
||||
import de.moritzruth.spigot_ttt.utils.createKillExplosion
|
||||
|
@ -18,44 +18,49 @@ import org.bukkit.entity.EntityType
|
|||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.entity.ExplosionPrimeEvent
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.util.*
|
||||
|
||||
typealias FireballEntity = org.bukkit.entity.Fireball
|
||||
|
||||
object Fireball: TTTItem, Buyable {
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val itemStack = ItemStack(Material.FIRE_CHARGE).applyMeta {
|
||||
object Fireball: TTTItem<Fireball.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Material.FIRE_CHARGE).applyMeta {
|
||||
setDisplayName("${ChatColor.DARK_RED}${ChatColor.BOLD}Feuerball")
|
||||
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Wirf einen Feuerball"
|
||||
)
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val price = 1
|
||||
override val buyLimit: Int? = null
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
buyLimit = 0,
|
||||
price = 1
|
||||
)
|
||||
) {
|
||||
class Instance: TTTItem.Instance(Fireball) {
|
||||
override fun onRightClick(event: ClickEvent) {
|
||||
val carrier = carrier!!
|
||||
carrier.player.inventory.clearHeldItemSlot()
|
||||
|
||||
val sendersByEntity = mutableMapOf<FireballEntity, TTTPlayer>()
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
data.tttPlayer.player.inventory.clearHeldItemSlot()
|
||||
|
||||
val vector = data.tttPlayer.player.eyeLocation.toVector()
|
||||
val location = vector.add(data.tttPlayer.player.eyeLocation.direction.multiply(1.2))
|
||||
.toLocation(data.tttPlayer.player.location.world!!)
|
||||
val vector = carrier.player.eyeLocation.toVector()
|
||||
val location = vector.add(carrier.player.eyeLocation.direction.multiply(1.2))
|
||||
.toLocation(carrier.player.location.world!!)
|
||||
|
||||
val fireball = GameManager.world.spawnEntity(location, EntityType.FIREBALL) as FireballEntity
|
||||
fireball.direction = data.tttPlayer.player.eyeLocation.direction
|
||||
sendersByEntity[fireball] = data.tttPlayer
|
||||
fireball.direction = carrier.player.eyeLocation.direction
|
||||
sendersByEntity[fireball] = carrier
|
||||
}
|
||||
}
|
||||
|
||||
val sendersByEntity = WeakHashMap<FireballEntity, TTTPlayer>()
|
||||
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler
|
||||
fun onExplosionPrime(event: ExplosionPrimeEvent) {
|
||||
val sender = sendersByEntity[event.entity]
|
||||
|
||||
if (sender != null) {
|
||||
sendersByEntity.remove(event.entity)
|
||||
event.isCancelled = true
|
||||
|
||||
GameManager.world.playSound(
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.applyTypedMeta
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.attribute.Attribute
|
||||
|
@ -17,12 +18,13 @@ import org.bukkit.inventory.meta.PotionMeta
|
|||
import org.bukkit.potion.PotionData
|
||||
import org.bukkit.potion.PotionType
|
||||
|
||||
object HealingPotion: TTTItem, Buyable {
|
||||
override val itemStack = ItemStack(Material.POTION).apply {
|
||||
val potionMeta = itemMeta as PotionMeta
|
||||
potionMeta.basePotionData = PotionData(PotionType.INSTANT_HEAL, false, true)
|
||||
itemMeta = potionMeta
|
||||
}.applyMeta {
|
||||
object HealingPotion: TTTItem<HealingPotion.Instance>(
|
||||
instanceType = Instance::class,
|
||||
type = Type.SPECIAL,
|
||||
spawnProbability = Probability.VERY_LOW,
|
||||
templateItemStack = ItemStack(Material.POTION)
|
||||
.applyTypedMeta<PotionMeta> { basePotionData = PotionData(PotionType.INSTANT_HEAL, false, true) }
|
||||
.applyMeta {
|
||||
setDisplayName("${ChatColor.LIGHT_PURPLE}Heiltrank")
|
||||
lore = listOf(
|
||||
"",
|
||||
|
@ -30,22 +32,21 @@ object HealingPotion: TTTItem, Buyable {
|
|||
)
|
||||
|
||||
addItemFlags(ItemFlag.HIDE_POTION_EFFECTS)
|
||||
}
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE)
|
||||
override val price = 1
|
||||
override val buyLimit = 2
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE),
|
||||
price = 1,
|
||||
buyLimit = 2
|
||||
)
|
||||
) {
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler
|
||||
fun onPlayerItemConsume(event: PlayerItemConsumeEvent) = handle(event) {
|
||||
event.isCancelled = true
|
||||
event.player.inventory.clear(event.player.inventory.indexOf(event.item))
|
||||
event.player.health = event.player.getAttribute(Attribute.GENERIC_MAX_HEALTH)?.value ?: 100.0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
data.event.isCancelled = false
|
||||
}
|
||||
}
|
||||
class Instance: TTTItem.Instance(HealingPotion)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.PASSIVE
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerTrueDeathEvent
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.createKillExplosion
|
||||
|
@ -19,33 +18,39 @@ import org.bukkit.event.EventHandler
|
|||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
|
||||
object MartyrdomGrenade: TTTItem, Buyable {
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val itemStack = ItemStack(Resourcepack.Items.martyrdomGrenade).applyMeta {
|
||||
object MartyrdomGrenade: TTTItem<MartyrdomGrenade.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.martyrdomGrenade).applyMeta {
|
||||
hideInfo()
|
||||
setDisplayName("${ChatColor.DARK_PURPLE}${ChatColor.BOLD}Märtyriumsgranate $PASSIVE")
|
||||
|
||||
setDisplayName("${ChatColor.DARK_PURPLE}${ChatColor.BOLD}Märtyriumsgranate$PASSIVE_SUFFIX")
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Lasse bei deinem Tod",
|
||||
"${ChatColor.GOLD}eine Granate fallen"
|
||||
)
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR)
|
||||
override val buyLimit: Int? = null
|
||||
override val price = 1
|
||||
val isc = InversedStateContainer(State::class)
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
price = 1
|
||||
)
|
||||
) {
|
||||
class Instance: TTTItem.Instance(MartyrdomGrenade, true) {
|
||||
var explodeTask: BukkitTask? = null
|
||||
|
||||
override fun onOwn(tttPlayer: TTTPlayer) {
|
||||
isc.getOrCreate(tttPlayer)
|
||||
override fun reset() {
|
||||
explodeTask?.cancel()
|
||||
explodeTask = null
|
||||
}
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler
|
||||
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) {
|
||||
val state = isc.get(event.tttPlayer) ?: return
|
||||
val instance = getInstance(event.tttPlayer) ?: return
|
||||
event.tttPlayer.removeItem(MartyrdomGrenade, false)
|
||||
|
||||
state.explodeTask = plugin.server.scheduler.runTaskLater(plugin, fun() {
|
||||
instance.explodeTask = plugin.server.scheduler.runTaskLater(plugin, fun() {
|
||||
GameManager.world.playSound(
|
||||
event.location,
|
||||
Resourcepack.Sounds.grenadeExplode,
|
||||
|
@ -57,15 +62,5 @@ object MartyrdomGrenade: TTTItem, Buyable {
|
|||
createKillExplosion(event.tttPlayer, event.location, 2.5)
|
||||
}, secondsToTicks(3).toLong())
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ ->
|
||||
state.explodeTask?.cancel()
|
||||
state.explodeTask = null
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var explodeTask: BukkitTask? = null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,35 +5,29 @@ import com.comphenix.protocol.PacketType
|
|||
import com.comphenix.protocol.events.PacketAdapter
|
||||
import com.comphenix.protocol.events.PacketEvent
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.PASSIVE
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.hideInfo
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.boss.BarColor
|
||||
import org.bukkit.boss.BarStyle
|
||||
import org.bukkit.boss.BossBar
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import kotlin.experimental.and
|
||||
import kotlin.experimental.or
|
||||
|
||||
object Radar: TTTItem, Buyable {
|
||||
private val DISPLAY_NAME = "${ChatColor.DARK_AQUA}${ChatColor.BOLD}Radar"
|
||||
private const val ACTIVE_DURATION = 10
|
||||
private const val COOLDOWN_DURATION = 40
|
||||
|
||||
override val itemStack = ItemStack(Resourcepack.Items.radar).applyMeta {
|
||||
setDisplayName("$DISPLAY_NAME $PASSIVE")
|
||||
object Radar: TTTItem<Radar.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.radar).applyMeta {
|
||||
setDisplayName("${ChatColor.DARK_AQUA}${ChatColor.BOLD}Radar$PASSIVE_SUFFIX")
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Zeigt dir alle 30 Sekunden",
|
||||
|
@ -42,54 +36,61 @@ object Radar: TTTItem, Buyable {
|
|||
)
|
||||
|
||||
hideInfo()
|
||||
}
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val buyableBy: EnumSet<Role> = EnumSet.of(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL)
|
||||
override val price = 2
|
||||
override val buyLimit: Int? = null
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL),
|
||||
price = 2
|
||||
)
|
||||
) {
|
||||
private const val ACTIVE_DURATION = 10
|
||||
private const val COOLDOWN_DURATION = 40
|
||||
private val BOSS_BAR_TITLE = "${ChatColor.DARK_AQUA}${ChatColor.BOLD}Radar"
|
||||
|
||||
val isc = InversedStateContainer(State::class)
|
||||
class Instance: TTTItem.Instance(Radar) {
|
||||
var active: Boolean = true
|
||||
private var timestamp = Instant.now()!!
|
||||
private val bossBar = plugin.server.createBossBar(BOSS_BAR_TITLE, BarColor.BLUE, BarStyle.SOLID)
|
||||
|
||||
override fun onOwn(tttPlayer: TTTPlayer) {
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
private var task: BukkitTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val duration = Duration.between(timestamp, Instant.now()).toMillis().toDouble() / 1000
|
||||
|
||||
state.bossBar = plugin.server.createBossBar(DISPLAY_NAME, BarColor.BLUE, BarStyle.SOLID)
|
||||
state.bossBar.addPlayer(tttPlayer.player)
|
||||
|
||||
setActive(tttPlayer, true)
|
||||
state.task = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val duration = Duration.between(state.timestamp, Instant.now()).toMillis().toDouble() / 1000
|
||||
|
||||
if (state.active) {
|
||||
if (active) {
|
||||
if (duration > ACTIVE_DURATION) {
|
||||
setActive(tttPlayer, false)
|
||||
active = false
|
||||
bossBar.setTitle(BOSS_BAR_TITLE + "${ChatColor.WHITE} - ${ChatColor.GRAY}Cooldown")
|
||||
carrier?.let { resendEntityMetadata(it) }
|
||||
timestamp = Instant.now()
|
||||
} else {
|
||||
state.bossBar.progress = 1.0 - duration / ACTIVE_DURATION
|
||||
bossBar.progress = 1.0 - duration / ACTIVE_DURATION
|
||||
}
|
||||
} else {
|
||||
if (duration > COOLDOWN_DURATION) {
|
||||
setActive(tttPlayer, true)
|
||||
active = true
|
||||
bossBar.setTitle(BOSS_BAR_TITLE + "${ChatColor.WHITE} - ${ChatColor.GREEN}Aktiv")
|
||||
carrier?.let { resendEntityMetadata(it) }
|
||||
timestamp = Instant.now()
|
||||
} else {
|
||||
state.bossBar.progress = duration / COOLDOWN_DURATION
|
||||
bossBar.progress = duration / COOLDOWN_DURATION
|
||||
}
|
||||
}
|
||||
}, 0, 2)
|
||||
}, 0, 1)
|
||||
|
||||
override fun onCarrierSet(carrier: TTTPlayer, isFirst: Boolean) {
|
||||
bossBar.addPlayer(carrier.player)
|
||||
if (active) resendEntityMetadata(carrier)
|
||||
}
|
||||
|
||||
private fun setActive(tttPlayer: TTTPlayer, value: Boolean) {
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
|
||||
if (state.active != value) {
|
||||
state.active = value
|
||||
state.timestamp = Instant.now()
|
||||
|
||||
if (value) {
|
||||
state.bossBar.setTitle(DISPLAY_NAME + "${ChatColor.WHITE} - ${ChatColor.GREEN}Aktiv")
|
||||
} else {
|
||||
state.bossBar.setTitle(DISPLAY_NAME + "${ChatColor.WHITE} - ${ChatColor.GRAY}Cooldown")
|
||||
override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
|
||||
bossBar.removePlayer(oldCarrier.player)
|
||||
if (active) resendEntityMetadata(oldCarrier)
|
||||
}
|
||||
|
||||
// Toggle sending the entity metadata
|
||||
override fun reset() {
|
||||
task.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
fun resendEntityMetadata(tttPlayer: TTTPlayer) {
|
||||
PlayerManager.tttPlayers.forEach {
|
||||
if (it !== tttPlayer) {
|
||||
tttPlayer.player.hidePlayer(plugin, it.player)
|
||||
|
@ -97,37 +98,23 @@ object Radar: TTTItem, Buyable {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
@EventHandler
|
||||
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) {
|
||||
isc.get(event.tttPlayer)?.reset(event.tttPlayer)
|
||||
isc.remove(event.tttPlayer)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, tttPlayer -> state.reset(tttPlayer) }
|
||||
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
data.event.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
override val packetListener = object : PacketAdapter(plugin, PacketType.Play.Server.ENTITY_METADATA) {
|
||||
override fun onPacketSending(event: PacketEvent) {
|
||||
val receivingTTTPlayer = TTTPlayer.of(event.player) ?: return
|
||||
|
||||
val packet = WrapperPlayServerEntityMetadata(event.packet)
|
||||
val playerOfPacket = plugin.server.onlinePlayers.find { it.entityId == packet.entityID } ?: return
|
||||
val tttPlayerOfPacket = TTTPlayer.of(playerOfPacket) ?: return
|
||||
val tttPlayerOfPacket = plugin.server.onlinePlayers
|
||||
.find { it.entityId == packet.entityID }
|
||||
?.let { TTTPlayer.of(it) } ?: return
|
||||
val instance = getInstance(receivingTTTPlayer) ?: return
|
||||
|
||||
if (tttPlayerOfPacket.alive) {
|
||||
// https://wiki.vg/Entity_metadata#Entity_Metadata_Format
|
||||
try {
|
||||
val modifiers = packet.metadata[0].value as Byte
|
||||
packet.metadata[0].value =
|
||||
if (isc.get(receivingTTTPlayer)?.active == true) modifiers or 0x40.toByte()
|
||||
if (instance.active) modifiers or 0x40.toByte()
|
||||
else modifiers and 0b10111111.toByte()
|
||||
} catch (ignored: Exception) {
|
||||
// Idk why this throws exceptions, but it works anyways
|
||||
|
@ -135,18 +122,4 @@ object Radar: TTTItem, Buyable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var task: BukkitTask? = null
|
||||
var active: Boolean = false
|
||||
lateinit var timestamp: Instant
|
||||
lateinit var bossBar: BossBar
|
||||
|
||||
fun reset(tttPlayer: TTTPlayer) {
|
||||
setActive(tttPlayer, false)
|
||||
|
||||
task?.cancel()
|
||||
bossBar.removePlayer(tttPlayer.player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.PASSIVE
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
|
@ -27,26 +24,76 @@ import java.time.Duration
|
|||
import java.time.Instant
|
||||
import kotlin.random.Random
|
||||
|
||||
object SecondChance: TTTItem, Buyable {
|
||||
private val DISPLAY_NAME = "${ChatColor.GREEN}${ChatColor.BOLD}Second Chance"
|
||||
val ON_CORPSE = Resourcepack.Items.arrowDown
|
||||
val ON_SPAWN = Resourcepack.Items.dot
|
||||
private const val TIMEOUT = 10.0
|
||||
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val itemStack = ItemStack(Resourcepack.Items.secondChance).applyMeta {
|
||||
setDisplayName("$DISPLAY_NAME $PASSIVE")
|
||||
object SecondChance: TTTItem<SecondChance.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.secondChance).applyMeta {
|
||||
setDisplayName("${ChatColor.GREEN}${ChatColor.BOLD}Second Chance$PASSIVE_SUFFIX")
|
||||
hideInfo()
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Du wirst mit einer 50%-Chance",
|
||||
"${ChatColor.GOLD}wiederbelebt, wenn du stirbst"
|
||||
)
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
buyLimit = 1,
|
||||
price = 2
|
||||
)
|
||||
) {
|
||||
val ON_CORPSE = Resourcepack.Items.arrowDown
|
||||
val ON_SPAWN = Resourcepack.Items.dot
|
||||
private const val TIMEOUT = 10.0
|
||||
|
||||
class Instance: TTTItem.Instance(SecondChance, false) {
|
||||
var preventRoundEnd = false; private set
|
||||
var timeoutAction: TimeoutAction? = null
|
||||
|
||||
fun possiblyTrigger() {
|
||||
if (Random.nextBoolean()) trigger()
|
||||
}
|
||||
|
||||
private fun trigger() {
|
||||
preventRoundEnd = true
|
||||
timeoutAction = TimeoutAction(this)
|
||||
}
|
||||
|
||||
class TimeoutAction(private val instance: Instance) {
|
||||
val deathLocation: Location = instance.requireCarrier().player.location
|
||||
private val startedAt = Instant.now()!!
|
||||
private var bossBar = plugin.server.createBossBar(
|
||||
"${ChatColor.GREEN}${ChatColor.BOLD}Second Chance",
|
||||
BarColor.GREEN,
|
||||
BarStyle.SOLID
|
||||
).also { it.addPlayer(instance.requireCarrier().player) }
|
||||
|
||||
private var task: BukkitTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val duration = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000
|
||||
val progress = duration / TIMEOUT
|
||||
|
||||
if (progress > 1) onTimeout() else bossBar.progress = 1.0 - progress
|
||||
}, 0, 1)
|
||||
|
||||
private fun onTimeout() {
|
||||
try {
|
||||
PlayerManager.letRemainingRoleGroupWin()
|
||||
} catch (e: IllegalStateException) {}
|
||||
|
||||
stop()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
val carrier = instance.requireCarrier()
|
||||
task.cancel()
|
||||
carrier.player.apply {
|
||||
closeInventory()
|
||||
bossBar.removePlayer(this)
|
||||
}
|
||||
carrier.removeItem(SecondChance)
|
||||
}
|
||||
}
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val buyLimit = 1
|
||||
override val price = 2
|
||||
val isc = InversedStateContainer(State::class)
|
||||
|
||||
private val chooseSpawnInventory = plugin.server.createInventory(
|
||||
null,
|
||||
|
@ -69,97 +116,42 @@ object SecondChance: TTTItem, Buyable {
|
|||
})
|
||||
}
|
||||
|
||||
override fun onOwn(tttPlayer: TTTPlayer) {
|
||||
isc.getOrCreate(tttPlayer)
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true, false) {
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler
|
||||
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) {
|
||||
val state = isc.get(event.tttPlayer)
|
||||
if (state != null) {
|
||||
if (true || Random.nextBoolean()) {
|
||||
event.winnerRoleGroup = null
|
||||
event.tttPlayer.player.openInventory(chooseSpawnInventory)
|
||||
state.timeoutAction = TimeoutAction(event.tttPlayer, event.tttCorpse.location)
|
||||
}
|
||||
}
|
||||
|
||||
isc.forEveryState { s, _ -> if (s.timeoutAction != null) event.winnerRoleGroup = null }
|
||||
val instance = getInstance(event.tttPlayer) ?: return
|
||||
instance.possiblyTrigger()
|
||||
if (instancesByUUID.values.find { it.preventRoundEnd } != null) event.winnerRoleGroup = null
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onInventoryClose(event: InventoryCloseEvent) = handle(event) { tttPlayer ->
|
||||
fun onInventoryClose(event: InventoryCloseEvent) {
|
||||
if (event.inventory == chooseSpawnInventory) {
|
||||
if (isc.get(tttPlayer)?.timeoutAction != null) {
|
||||
nextTick { if (isc.get(tttPlayer) != null) tttPlayer.player.openInventory(chooseSpawnInventory) }
|
||||
handleWithInstance(event) { instance ->
|
||||
nextTick { instance.carrier?.player?.openInventory(chooseSpawnInventory) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onInventoryClick(event: InventoryClickEvent) = handle(event) { tttPlayer ->
|
||||
if (event.clickedInventory != chooseSpawnInventory) return@handle
|
||||
val state = isc.get(tttPlayer) ?: return@handle
|
||||
val timeoutAction = state.timeoutAction ?: return@handle
|
||||
fun onInventoryClick(event: InventoryClickEvent) {
|
||||
if (event.clickedInventory != chooseSpawnInventory) return
|
||||
|
||||
handleWithInstance(event) { instance ->
|
||||
val timeoutAction = instance.timeoutAction!!
|
||||
|
||||
val location = when (event.currentItem?.type) {
|
||||
ON_SPAWN -> GameManager.world.spawnLocation
|
||||
ON_CORPSE -> timeoutAction.deathLocation
|
||||
else -> return@handle
|
||||
else -> return@handleWithInstance
|
||||
}
|
||||
|
||||
timeoutAction.stop()
|
||||
tttPlayer.revive(location)
|
||||
instance.carrier!!.revive(location)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onTTTPlayerRevive(event: TTTPlayerReviveEvent) {
|
||||
isc.get(event.tttPlayer)?.timeoutAction?.stop()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) {
|
||||
isc.forEveryState { state, _ -> state.timeoutAction?.stop() }
|
||||
}
|
||||
}
|
||||
|
||||
class TimeoutAction(
|
||||
private val tttPlayer: TTTPlayer,
|
||||
val deathLocation: Location
|
||||
) {
|
||||
private val startedAt = Instant.now()!!
|
||||
private var bossBar = plugin.server.createBossBar(
|
||||
"${ChatColor.GREEN}${ChatColor.BOLD}Second Chance",
|
||||
BarColor.GREEN,
|
||||
BarStyle.SOLID
|
||||
).also { it.addPlayer(tttPlayer.player) }
|
||||
|
||||
private var task: BukkitTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val duration = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000
|
||||
val progress = duration / TIMEOUT
|
||||
|
||||
if (progress > 1) onTimeout() else bossBar.progress = 1.0 - progress
|
||||
}, 0, 1)
|
||||
|
||||
private fun onTimeout() {
|
||||
try {
|
||||
PlayerManager.letRemainingRoleGroupWin()
|
||||
} catch (e: IllegalStateException) {}
|
||||
|
||||
stop()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
isc.remove(tttPlayer)
|
||||
task.cancel()
|
||||
tttPlayer.player.closeInventory()
|
||||
tttPlayer.removeItem(SecondChance)
|
||||
bossBar.removePlayer(tttPlayer.player)
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var timeoutAction: TimeoutAction? = null
|
||||
fun onTTTPlayerRevive(event: TTTPlayerReviveEvent) = handle(event) { it.timeoutAction?.stop() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,24 +2,23 @@ package de.moritzruth.spigot_ttt.game.items.impl
|
|||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.ClickEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.clearHeldItemSlot
|
||||
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.Sound
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.player.PlayerSwapHandItemsEvent
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
object Teleporter: TTTItem, Buyable {
|
||||
override val type = TTTItem.Type.SPECIAL
|
||||
override val itemStack = ItemStack(Resourcepack.Items.teleporter).applyMeta {
|
||||
object Teleporter: TTTItem<Teleporter.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.teleporter).applyMeta {
|
||||
setDisplayName("${ChatColor.LIGHT_PURPLE}${ChatColor.BOLD}Teleporter")
|
||||
|
||||
lore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Tausche die Positionen zweier Spieler",
|
||||
|
@ -28,42 +27,28 @@ object Teleporter: TTTItem, Buyable {
|
|||
"",
|
||||
"${ChatColor.RED}Kann nur einmal verwendet werden"
|
||||
)
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val price = 1
|
||||
override val buyLimit = 1
|
||||
val isc = InversedStateContainer(State::class)
|
||||
|
||||
private fun getRandomPlayerToTeleport(vararg exclude: TTTPlayer): TTTPlayer? {
|
||||
return try {
|
||||
PlayerManager.tttPlayers.filter { !exclude.contains(it) && it.alive && !it.player.isSneaking }.random()
|
||||
} catch (e: NoSuchElementException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
@EventHandler
|
||||
fun onPlayerSwapHandItems(event: PlayerSwapHandItemsEvent) = handle(event) { tttPlayer ->
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
state.teleportSelf = !state.teleportSelf
|
||||
|
||||
tttPlayer.player.sendActionBarMessage(
|
||||
if (state.teleportSelf) "${ChatColor.AQUA}Mode: Teleportiere dich selbst"
|
||||
else "${ChatColor.AQUA}Mode: Teleportiere jemand anderen"
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
price = 1,
|
||||
buyLimit = 1
|
||||
)
|
||||
}
|
||||
) {
|
||||
class Instance: TTTItem.Instance(Teleporter) {
|
||||
private var teleportSelf = false
|
||||
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
val tttPlayer = data.tttPlayer
|
||||
val state = isc.getOrCreate(tttPlayer)
|
||||
|
||||
val firstPlayer = if (state.teleportSelf) {
|
||||
override fun onRightClick(event: ClickEvent) {
|
||||
val tttPlayer = carrier!!
|
||||
val firstPlayer = if (teleportSelf) {
|
||||
if (!tttPlayer.player.isOnGround) {
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}${ChatColor.BOLD}Du musst auf dem Boden stehen")
|
||||
tttPlayer.player.sendActionBarMessage(
|
||||
"${ChatColor.RED}${ChatColor.BOLD}Du musst auf dem Boden stehen"
|
||||
)
|
||||
null
|
||||
} else if (tttPlayer.player.isSneaking) {
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}${ChatColor.BOLD}Du darfst nicht sneaken")
|
||||
tttPlayer.player.sendActionBarMessage(
|
||||
"${ChatColor.RED}${ChatColor.BOLD}Du darfst nicht sneaken"
|
||||
)
|
||||
null
|
||||
} else tttPlayer
|
||||
} else getRandomPlayerToTeleport(tttPlayer)
|
||||
|
@ -76,7 +61,7 @@ object Teleporter: TTTItem, Buyable {
|
|||
firstPlayer.player.teleport(secondPlayer.player.location)
|
||||
secondPlayer.player.teleport(firstLocation)
|
||||
|
||||
tttPlayer.player.inventory.clearHeldItemSlot()
|
||||
tttPlayer.removeItem(Teleporter)
|
||||
|
||||
GameManager.world.playSound(firstPlayer.player.location, Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F)
|
||||
GameManager.world.playSound(secondPlayer.player.location, Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F)
|
||||
|
@ -87,9 +72,22 @@ object Teleporter: TTTItem, Buyable {
|
|||
// Teleport failed
|
||||
tttPlayer.player.playSound(tttPlayer.player.location, Resourcepack.Sounds.error, 1F, 1F)
|
||||
}
|
||||
|
||||
override fun onHandSwap() {
|
||||
teleportSelf = !teleportSelf
|
||||
|
||||
carrier!!.player.sendActionBarMessage(
|
||||
if (teleportSelf) "${ChatColor.AQUA}Mode: Teleportiere dich selbst"
|
||||
else "${ChatColor.AQUA}Mode: Teleportiere jemand anderen"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class State: IState {
|
||||
var teleportSelf = false
|
||||
private fun getRandomPlayerToTeleport(vararg exclude: TTTPlayer): TTTPlayer? {
|
||||
return try {
|
||||
PlayerManager.tttPlayers.filter { !exclude.contains(it) && it.alive && !it.player.isSneaking }.random()
|
||||
} catch (e: NoSuchElementException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.Selectable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.hideInfo
|
||||
import de.moritzruth.spigot_ttt.utils.removeTTTItemNextTick
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.SoundCategory
|
||||
import org.bukkit.attribute.Attribute
|
||||
|
@ -22,9 +18,10 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent
|
|||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.util.Vector
|
||||
|
||||
object BaseballBat: TTTItem, Buyable, Selectable {
|
||||
override val type = TTTItem.Type.MELEE
|
||||
override val itemStack = ItemStack(Resourcepack.Items.baseballBat).applyMeta {
|
||||
object BaseballBat: TTTItem<BaseballBat.Instance>(
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.baseballBat).applyMeta {
|
||||
setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Baseball-Schläger")
|
||||
lore = listOf(
|
||||
"",
|
||||
|
@ -41,22 +38,28 @@ object BaseballBat: TTTItem, Buyable, Selectable {
|
|||
-0.8,
|
||||
AttributeModifier.Operation.ADD_SCALAR
|
||||
))
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val price = 1
|
||||
override val buyLimit: Int? = null
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
price = 1
|
||||
),
|
||||
disableDamage = false
|
||||
) {
|
||||
const val WALK_SPEED_INCREASE = 0.1F
|
||||
|
||||
override fun onSelect(tttPlayer: TTTPlayer) {
|
||||
tttPlayer.player.walkSpeed = 0.3F
|
||||
class Instance: TTTItem.Instance(BaseballBat) {
|
||||
override fun onSelect() {
|
||||
carrier!!.walkSpeed += WALK_SPEED_INCREASE
|
||||
}
|
||||
|
||||
override fun onDeselect(tttPlayer: TTTPlayer) {
|
||||
tttPlayer.player.walkSpeed = 0.2F
|
||||
override fun onDeselect() {
|
||||
carrier!!.walkSpeed -= WALK_SPEED_INCREASE
|
||||
}
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, false) {
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
override fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { tttPlayer, _ ->
|
||||
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { tttPlayer, _ ->
|
||||
event.isCancelled = true
|
||||
if (event.damage != 1.0) return@handle // Cooldown on weapon
|
||||
|
||||
|
@ -64,7 +67,7 @@ object BaseballBat: TTTItem, Buyable, Selectable {
|
|||
val distance = tttPlayer.player.location.distance(damagedPlayer.location)
|
||||
|
||||
if (distance < 2.5) {
|
||||
tttPlayer.player.inventory.removeTTTItemNextTick(BaseballBat)
|
||||
tttPlayer.removeItem(BaseballBat)
|
||||
|
||||
GameManager.world.playSound(
|
||||
damagedPlayer.location,
|
||||
|
@ -74,6 +77,8 @@ object BaseballBat: TTTItem, Buyable, Selectable {
|
|||
1F
|
||||
)
|
||||
|
||||
event.damage = 0.0
|
||||
|
||||
val direction = tttPlayer.player.location.direction
|
||||
damagedPlayer.velocity = Vector(direction.x * 5, 8.0, direction.z * 5)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package de.moritzruth.spigot_ttt.game.items.impl.weapons
|
|||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.LoreHelper
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItemListener
|
||||
|
@ -11,7 +10,6 @@ import de.moritzruth.spigot_ttt.game.players.Role
|
|||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.hideInfo
|
||||
import de.moritzruth.spigot_ttt.utils.removeTTTItemNextTick
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.Sound
|
||||
import org.bukkit.SoundCategory
|
||||
|
@ -21,8 +19,10 @@ import org.bukkit.event.EventHandler
|
|||
import org.bukkit.event.entity.EntityDamageByEntityEvent
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
object Knife: TTTItem, Buyable {
|
||||
override val itemStack = ItemStack(Resourcepack.Items.knife).applyMeta {
|
||||
object Knife: TTTItem<Knife.Instance>(
|
||||
type = Type.MELEE,
|
||||
instanceType = Instance::class,
|
||||
templateItemStack = ItemStack(Resourcepack.Items.knife).applyMeta {
|
||||
setDisplayName("${ChatColor.RED}${ChatColor.BOLD}Knife")
|
||||
lore = listOf(
|
||||
"",
|
||||
|
@ -31,7 +31,6 @@ object Knife: TTTItem, Buyable {
|
|||
"${ChatColor.RED}Nur einmal verwendbar",
|
||||
"${ChatColor.RED}Nur aus nächster Nähe"
|
||||
)
|
||||
|
||||
hideInfo()
|
||||
addAttributeModifier(
|
||||
Attribute.GENERIC_ATTACK_SPEED, AttributeModifier(
|
||||
|
@ -39,15 +38,19 @@ object Knife: TTTItem, Buyable {
|
|||
-0.9,
|
||||
AttributeModifier.Operation.ADD_SCALAR
|
||||
))
|
||||
}
|
||||
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
|
||||
override val price = 1
|
||||
override val type = TTTItem.Type.MELEE
|
||||
override val buyLimit = 1
|
||||
},
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.TRAITOR, Role.JACKAL),
|
||||
price = 1,
|
||||
buyLimit = 1
|
||||
),
|
||||
disableDamage = false
|
||||
) {
|
||||
class Instance: TTTItem.Instance(Knife)
|
||||
|
||||
override val listener = object : TTTItemListener(this, false) {
|
||||
override val listener = object : TTTItemListener<Instance>(this) {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
override fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { damagerTTTPlayer, damagedTTTPlayer ->
|
||||
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { damagerTTTPlayer, damagedTTTPlayer ->
|
||||
event.isCancelled = true
|
||||
|
||||
if (event.damage == 1.0) {
|
||||
|
@ -77,7 +80,7 @@ object Knife: TTTItem, Buyable {
|
|||
1F
|
||||
)
|
||||
|
||||
damagerTTTPlayer.player.inventory.removeTTTItemNextTick(Knife)
|
||||
damagerTTTPlayer.removeItem(Knife)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.items.Spawning
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
||||
import org.bukkit.ChatColor
|
||||
|
||||
object Deagle: Gun(
|
||||
stateClass = State::class,
|
||||
type = Type.PISTOL_LIKE,
|
||||
instanceType = Instance::class,
|
||||
spawnProbability = Probability.NORMAL,
|
||||
displayName = "${ChatColor.BLUE}${ChatColor.BOLD}Deagle",
|
||||
damage = heartsToHealth(3.0),
|
||||
cooldown = 1.4,
|
||||
magazineSize = 8,
|
||||
reloadTime = 3.0,
|
||||
itemMaterial = Resourcepack.Items.deagle,
|
||||
material = Resourcepack.Items.deagle,
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload
|
||||
), Spawning {
|
||||
override val type = TTTItem.Type.PISTOL_LIKE
|
||||
|
||||
class State: Gun.State(magazineSize)
|
||||
) {
|
||||
class Instance: Gun.Instance(Deagle)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.items.Spawning
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
||||
import org.bukkit.ChatColor
|
||||
|
||||
object Glock: Gun(
|
||||
stateClass = State::class,
|
||||
type = Type.PISTOL_LIKE,
|
||||
instanceType = Instance::class,
|
||||
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Glock",
|
||||
damage = heartsToHealth(1.5),
|
||||
spawnProbability = Probability.NORMAL,
|
||||
cooldown = 0.3,
|
||||
magazineSize = 20,
|
||||
reloadTime = 2.0,
|
||||
itemMaterial = Resourcepack.Items.glock,
|
||||
material = Resourcepack.Items.glock,
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Glock.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Glock.reload
|
||||
), Spawning {
|
||||
override val type = TTTItem.Type.PISTOL_LIKE
|
||||
|
||||
class State: Gun.State(magazineSize)
|
||||
) {
|
||||
class Instance: Gun.Instance(Glock)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,66 +1,77 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameEndEvent
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.GamePhase
|
||||
import de.moritzruth.spigot_ttt.game.items.*
|
||||
import de.moritzruth.spigot_ttt.game.players.*
|
||||
import de.moritzruth.spigot_ttt.game.items.ClickEvent
|
||||
import de.moritzruth.spigot_ttt.game.items.LoreHelper
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.DeathReason
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.nextTick
|
||||
import de.moritzruth.spigot_ttt.utils.startItemDamageProgress
|
||||
import de.moritzruth.spigot_ttt.utils.hideInfo
|
||||
import de.moritzruth.spigot_ttt.utils.startProgressTask
|
||||
import org.bukkit.*
|
||||
import org.bukkit.entity.Item
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.inventory.ItemFlag
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.Damageable
|
||||
import org.bukkit.inventory.meta.ItemMeta
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
typealias ClickAction = org.bukkit.event.block.Action
|
||||
|
||||
abstract class Gun(
|
||||
private val stateClass: KClass<out State>,
|
||||
type: Type,
|
||||
instanceType: KClass<out Instance>,
|
||||
spawnProbability: Probability? = null,
|
||||
shopInfo: ShopInfo? = null,
|
||||
material: Material,
|
||||
displayName: String,
|
||||
additionalLore: List<String>? = null,
|
||||
itemLore: List<String>? = null,
|
||||
appendLore: Boolean = true,
|
||||
val damage: Double,
|
||||
val cooldown: Double,
|
||||
val magazineSize: Int,
|
||||
val reloadTime: Double,
|
||||
val itemMaterial: Material,
|
||||
val shootSound: String,
|
||||
val reloadSound: String
|
||||
): TTTItem, Selectable, DropHandler {
|
||||
override val itemStack = ItemStack(itemMaterial).applyMeta {
|
||||
): TTTItem<Gun.Instance>(
|
||||
type = type,
|
||||
instanceType = instanceType,
|
||||
spawnProbability = spawnProbability,
|
||||
shopInfo = shopInfo,
|
||||
templateItemStack = ItemStack(material).applyMeta {
|
||||
setDisplayName(displayName)
|
||||
lore = listOf(
|
||||
lore =
|
||||
if (appendLore) listOf(
|
||||
"",
|
||||
"${ChatColor.GRAY}Schaden: ${LoreHelper.damage(if (damage < 0) null else (damage / 2))}",
|
||||
"${ChatColor.GRAY}Cooldown: ${LoreHelper.cooldown(cooldown)}",
|
||||
"${ChatColor.GRAY}Magazin: ${LoreHelper.uses(magazineSize)} Schuss"
|
||||
) + run {
|
||||
if (additionalLore == null) emptyList()
|
||||
else listOf("") + additionalLore
|
||||
if (itemLore == null) emptyList()
|
||||
else listOf("") + itemLore
|
||||
}
|
||||
else itemLore ?: emptyList()
|
||||
|
||||
hideInfo()
|
||||
}
|
||||
) {
|
||||
open class Instance(val gun: Gun): TTTItem.Instance(gun) {
|
||||
var currentAction: Action? = null
|
||||
var remainingShots: Int = gun.magazineSize
|
||||
set(value) {
|
||||
field = value
|
||||
setCarrierLevel()
|
||||
}
|
||||
|
||||
addItemFlags(ItemFlag.HIDE_ATTRIBUTES)
|
||||
private fun setCarrierLevel() {
|
||||
if (isSelected) carrier!!.player.level = remainingShots
|
||||
}
|
||||
|
||||
val isc = InversedStateContainer(stateClass)
|
||||
private fun shoot() {
|
||||
val tttPlayer = requireCarrier()
|
||||
if (!onBeforeShoot()) return
|
||||
|
||||
protected fun updateLevel(tttPlayer: TTTPlayer, state: State = isc.getOrCreate(tttPlayer)) {
|
||||
tttPlayer.player.level = state.remainingShots
|
||||
}
|
||||
|
||||
fun shoot(tttPlayer: TTTPlayer, itemStack: ItemStack, state: State = isc.getOrCreate(tttPlayer)) {
|
||||
if (!onBeforeShoot(tttPlayer, itemStack, state)) return
|
||||
|
||||
if (state.remainingShots == 0) {
|
||||
if (remainingShots == 0) {
|
||||
GameManager.world.playSound(
|
||||
tttPlayer.player.location,
|
||||
Resourcepack.Sounds.Item.Weapon.Generic.emptyMagazine,
|
||||
|
@ -68,15 +79,14 @@ abstract class Gun(
|
|||
1F,
|
||||
1F
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
GameManager.world.playSound(tttPlayer.player.location, shootSound, SoundCategory.PLAYERS, 1F, 1F)
|
||||
GameManager.world.playSound(tttPlayer.player.location, gun.shootSound, SoundCategory.PLAYERS, 1F, 1F)
|
||||
|
||||
state.remainingShots--
|
||||
updateLevel(tttPlayer)
|
||||
remainingShots--
|
||||
|
||||
if (GameManager.phase == GamePhase.COMBAT) {
|
||||
val rayTraceResult = GameManager.world.rayTrace(
|
||||
tttPlayer.player.eyeLocation,
|
||||
tttPlayer.player.eyeLocation.direction,
|
||||
|
@ -100,167 +110,124 @@ abstract class Gun(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentAction = Action.Cooldown(this)
|
||||
}
|
||||
|
||||
state.currentAction = Action.Cooldown(this, itemStack, state)
|
||||
open fun reload() {
|
||||
val carrier = requireCarrier()
|
||||
if (currentAction != null) throw ActionInProgressError()
|
||||
if (remainingShots == gun.magazineSize) return
|
||||
|
||||
currentAction = Action.Reloading(this)
|
||||
|
||||
GameManager.world.playSound(
|
||||
carrier.player.location,
|
||||
gun.reloadSound,
|
||||
SoundCategory.PLAYERS,
|
||||
1F,
|
||||
1F
|
||||
)
|
||||
}
|
||||
|
||||
open fun reload(tttPlayer: TTTPlayer, itemStack: ItemStack, state: State = isc.getOrCreate(tttPlayer)) {
|
||||
if (state.currentAction != null) throw ActionInProgressError()
|
||||
if (state.remainingShots == magazineSize) return
|
||||
|
||||
state.currentAction = Action.Reloading(this, itemStack, state, tttPlayer).also { it.start() }
|
||||
|
||||
GameManager.world.playSound(tttPlayer.player.location, reloadSound, SoundCategory.PLAYERS, 1F, 1F)
|
||||
open fun computeActualDamage(receiver: TTTPlayer): Double {
|
||||
requireCarrier() // Only to keep parity with possible override
|
||||
return if (gun.damage < 0 ) 1000.0 else gun.damage
|
||||
}
|
||||
|
||||
open fun computeActualDamage(tttPlayer: TTTPlayer, receiver: Player) = if (damage < 0 ) 1000.0 else damage
|
||||
|
||||
open fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: State = isc.getOrCreate(tttPlayer)): Boolean {
|
||||
if (state.currentAction !== null) throw ActionInProgressError()
|
||||
/**
|
||||
* @return Whether the gun will really shoot
|
||||
*/
|
||||
open fun onBeforeShoot(): Boolean {
|
||||
if (currentAction !== null) throw ActionInProgressError()
|
||||
return true
|
||||
}
|
||||
|
||||
open fun onHit(tttPlayer: TTTPlayer, hitTTTPlayer: TTTPlayer) {
|
||||
val actualDamage = computeActualDamage(tttPlayer, hitTTTPlayer.player)
|
||||
|
||||
hitTTTPlayer.damage(actualDamage, DeathReason.Item(this), tttPlayer, true)
|
||||
tttPlayer.player.playSound(tttPlayer.player.location, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.MASTER, 2f, 1.2f)
|
||||
val actualDamage = computeActualDamage(hitTTTPlayer)
|
||||
hitTTTPlayer.damage(actualDamage, DeathReason.Item(gun), tttPlayer, true)
|
||||
tttPlayer.player.playSound(
|
||||
tttPlayer.player.location,
|
||||
Sound.ENTITY_EXPERIENCE_ORB_PICKUP,
|
||||
SoundCategory.MASTER,
|
||||
2f,
|
||||
1.2f
|
||||
)
|
||||
hitTTTPlayer.player.velocity = tttPlayer.player.location.direction.multiply(
|
||||
(actualDamage / 20).coerceAtMost(3.0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSelect(tttPlayer: TTTPlayer) {
|
||||
updateLevel(tttPlayer)
|
||||
override fun onLeftClick(event: ClickEvent) {
|
||||
try {
|
||||
reload()
|
||||
} catch (e: ActionInProgressError) {}
|
||||
}
|
||||
|
||||
override fun onDeselect(tttPlayer: TTTPlayer) {
|
||||
override fun onRightClick(event: ClickEvent) {
|
||||
try {
|
||||
shoot()
|
||||
} catch (e: ActionInProgressError) {}
|
||||
}
|
||||
|
||||
protected open fun onMovedOutOfHand(tttPlayer: TTTPlayer) {
|
||||
tttPlayer.player.level = 0
|
||||
tttPlayer.player.exp = 0F
|
||||
|
||||
val state = isc.get(tttPlayer) ?: return
|
||||
val currentAction = state.currentAction
|
||||
|
||||
if (currentAction is Action.Reloading) {
|
||||
state.currentAction = null
|
||||
currentAction.task.cancel()
|
||||
|
||||
val meta = currentAction.itemStack.itemMeta as Damageable
|
||||
meta.damage = 0
|
||||
currentAction.itemStack.itemMeta = meta as ItemMeta
|
||||
val action = currentAction
|
||||
if (action is Action.Reloading) {
|
||||
currentAction = null
|
||||
action.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item): Boolean {
|
||||
val state = isc.get(tttPlayer) ?: return true
|
||||
|
||||
when(val currentAction = state.currentAction) {
|
||||
is Action.Reloading -> {
|
||||
state.currentAction = null
|
||||
currentAction.task.cancel()
|
||||
override fun onCarrierSet(carrier: TTTPlayer, isFirst: Boolean) {
|
||||
setCarrierLevel()
|
||||
}
|
||||
is Action.Cooldown -> {
|
||||
currentAction.pause()
|
||||
|
||||
override fun onSelect() {
|
||||
setCarrierLevel()
|
||||
}
|
||||
|
||||
override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
|
||||
onMovedOutOfHand(oldCarrier)
|
||||
}
|
||||
|
||||
override fun onDeselect() {
|
||||
onMovedOutOfHand(carrier!!)
|
||||
}
|
||||
}
|
||||
|
||||
itemEntity.setItemStack(itemStack.clone())
|
||||
class ActionInProgressError: RuntimeException("The gun has an ongoing action")
|
||||
|
||||
ItemManager.droppedItemStates[itemEntity.entityId] = state
|
||||
isc.remove(tttPlayer)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPickup(tttPlayer: TTTPlayer, itemEntity: Item) {
|
||||
val state = ItemManager.droppedItemStates[itemEntity.entityId] as State?
|
||||
|
||||
if (state != null) {
|
||||
tttPlayer.stateContainer.put(stateClass, state)
|
||||
val currentAction = state.currentAction ?: return
|
||||
|
||||
nextTick { currentAction.itemStack = tttPlayer.player.inventory.find { it.type == itemEntity.itemStack.type }!!
|
||||
|
||||
if (currentAction is Action.Cooldown) {
|
||||
currentAction.resume()
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
override val listener = object : TTTItemListener(this, true) {
|
||||
override fun onLeftClick(data: ClickEventData) {
|
||||
try {
|
||||
reload(data.tttPlayer, data.event.item!!)
|
||||
} catch (e: ActionInProgressError) {}
|
||||
}
|
||||
|
||||
override fun onRightClick(data: ClickEventData) {
|
||||
try {
|
||||
shoot(data.tttPlayer, data.event.item!!)
|
||||
} catch (e: ActionInProgressError) {}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onTTTPlayerDeath(event: TTTPlayerTrueDeathEvent) = isc.get(event.tttPlayer)?.reset()
|
||||
|
||||
@EventHandler
|
||||
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ -> state.reset() }
|
||||
}
|
||||
|
||||
class ActionInProgressError: RuntimeException("The gun has an ongoing action which may not be canceled")
|
||||
|
||||
abstract class State(magazineSize: Int): IState {
|
||||
var currentAction: Action? = null
|
||||
var remainingShots = magazineSize
|
||||
|
||||
fun reset() { currentAction?.reset() }
|
||||
}
|
||||
|
||||
sealed class Action(var itemStack: ItemStack) {
|
||||
sealed class Action(val instance: Instance) {
|
||||
val startedAt = Instant.now()!!
|
||||
abstract var task: BukkitTask; protected set
|
||||
abstract val task: BukkitTask
|
||||
|
||||
open fun reset() {
|
||||
open fun cancel() {
|
||||
task.cancel()
|
||||
}
|
||||
|
||||
open class Reloading(
|
||||
private val gun: Gun,
|
||||
itemStack: ItemStack,
|
||||
protected val state: State,
|
||||
protected val tttPlayer: TTTPlayer
|
||||
): Action(itemStack) {
|
||||
override lateinit var task: BukkitTask
|
||||
open class Reloading(instance: Instance): Action(instance) {
|
||||
override val task = createProgressTask()
|
||||
|
||||
open fun start() {
|
||||
task = startItemDamageProgress(itemStack, gun.reloadTime) {
|
||||
state.currentAction = null
|
||||
state.remainingShots = gun.magazineSize
|
||||
gun.updateLevel(tttPlayer, state)
|
||||
}
|
||||
protected open fun createProgressTask() = startProgressTask(instance.gun.reloadTime) { data ->
|
||||
val exp = if (data.isComplete) {
|
||||
instance.remainingShots = instance.gun.magazineSize
|
||||
instance.currentAction = null
|
||||
0F
|
||||
} else data.progress.toFloat()
|
||||
if (instance.isSelected) instance.carrier!!.player.exp = exp
|
||||
}
|
||||
}
|
||||
|
||||
class Cooldown(private val gun: Gun, itemStack: ItemStack, private val state: State): Action(itemStack) {
|
||||
override var task = startTask()
|
||||
private var pausedProgress: Double? = null
|
||||
|
||||
private fun startTask() = startItemDamageProgress(
|
||||
itemStack = itemStack,
|
||||
duration = gun.cooldown,
|
||||
startProgress = pausedProgress ?: 0.0
|
||||
) {
|
||||
state.currentAction = null
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
if (task.isCancelled) task = startTask()
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
if (!task.isCancelled) {
|
||||
task.cancel()
|
||||
pausedProgress = (Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000) / gun.cooldown
|
||||
}
|
||||
class Cooldown(instance: Instance): Action(instance) {
|
||||
override val task = startProgressTask(instance.gun.cooldown) { data ->
|
||||
val exp = if (data.isComplete) {
|
||||
instance.currentAction = null
|
||||
0F
|
||||
} else data.progress.toFloat()
|
||||
if (instance.isSelected) instance.carrier!!.player.exp = exp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.items.Spawning
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
||||
import org.bukkit.ChatColor
|
||||
|
||||
object Pistol: Gun(
|
||||
stateClass = State::class,
|
||||
type = Type.PISTOL_LIKE,
|
||||
instanceType = Instance::class,
|
||||
spawnProbability = Probability.NORMAL,
|
||||
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Pistol",
|
||||
damage = heartsToHealth(2.5),
|
||||
cooldown = 0.8,
|
||||
magazineSize = 10,
|
||||
reloadTime = 2.0,
|
||||
itemMaterial = Resourcepack.Items.pistol,
|
||||
material = Resourcepack.Items.pistol,
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Pistol.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Pistol.reload
|
||||
), Spawning {
|
||||
override val type = TTTItem.Type.PISTOL_LIKE
|
||||
|
||||
class State: Gun.State(magazineSize)
|
||||
) {
|
||||
class Instance: Gun.Instance(Pistol)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.items.Spawning
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
||||
import org.bukkit.ChatColor
|
||||
|
||||
object Rifle: Gun(
|
||||
stateClass = State::class,
|
||||
type = Type.HEAVY_WEAPON,
|
||||
instanceType = Instance::class,
|
||||
spawnProbability = Probability.NORMAL,
|
||||
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Rifle",
|
||||
damage = heartsToHealth(0.8),
|
||||
cooldown = 0.15,
|
||||
magazineSize = 40,
|
||||
reloadTime = 2.0,
|
||||
itemMaterial = Resourcepack.Items.rifle,
|
||||
material = Resourcepack.Items.rifle,
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Rifle.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Rifle.reload
|
||||
), Spawning {
|
||||
override val type = TTTItem.Type.HEAVY_WEAPON
|
||||
|
||||
class State: Gun.State(magazineSize)
|
||||
) {
|
||||
class Instance: Gun.Instance(Rifle)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,40 +2,36 @@ package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
|||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.GameManager
|
||||
import de.moritzruth.spigot_ttt.game.items.Spawning
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import de.moritzruth.spigot_ttt.utils.Probability
|
||||
import de.moritzruth.spigot_ttt.utils.heartsToHealth
|
||||
import de.moritzruth.spigot_ttt.utils.secondsToTicks
|
||||
import de.moritzruth.spigot_ttt.utils.startItemDamageProgress
|
||||
import de.moritzruth.spigot_ttt.utils.startProgressTask
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.SoundCategory
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.Damageable
|
||||
import org.bukkit.inventory.meta.ItemMeta
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
|
||||
private const val RELOAD_TIME_PER_BULLET = 0.5
|
||||
private const val MAGAZINE_SIZE = 8
|
||||
|
||||
object Shotgun: Gun(
|
||||
stateClass = State::class,
|
||||
type = Type.HEAVY_WEAPON,
|
||||
instanceType = Instance::class,
|
||||
spawnProbability = Probability.LOW,
|
||||
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Shotgun",
|
||||
damage = heartsToHealth(3.0),
|
||||
cooldown = 0.9,
|
||||
magazineSize = MAGAZINE_SIZE,
|
||||
reloadTime = RELOAD_TIME_PER_BULLET * MAGAZINE_SIZE,
|
||||
itemMaterial = Resourcepack.Items.shotgun,
|
||||
additionalLore = listOf("${ChatColor.RED}Weniger Schaden auf Distanz"),
|
||||
material = Resourcepack.Items.shotgun,
|
||||
itemLore = listOf("${ChatColor.RED}Weniger Schaden auf Distanz"),
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Shotgun.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Shotgun.reload
|
||||
), Spawning {
|
||||
override val type = TTTItem.Type.HEAVY_WEAPON
|
||||
|
||||
override fun computeActualDamage(tttPlayer: TTTPlayer, receiver: Player): Double {
|
||||
val distance = tttPlayer.player.location.distance(receiver.location)
|
||||
) {
|
||||
class Instance: Gun.Instance(Shotgun) {
|
||||
override fun computeActualDamage(receiver: TTTPlayer): Double {
|
||||
val distance = requireCarrier().player.location.distance(receiver.player.location)
|
||||
|
||||
return when {
|
||||
distance <= 1 -> heartsToHealth(10.0)
|
||||
|
@ -45,79 +41,60 @@ object Shotgun: Gun(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDeselect(tttPlayer: TTTPlayer) {
|
||||
override fun onMovedOutOfHand(tttPlayer: TTTPlayer) {
|
||||
tttPlayer.player.level = 0
|
||||
val state = (isc.get(tttPlayer) ?: return) as State
|
||||
tttPlayer.player.exp = 0F
|
||||
|
||||
val currentAction = state.currentAction
|
||||
|
||||
if (currentAction is ReloadingAction) {
|
||||
state.currentAction = null
|
||||
currentAction.task.cancel()
|
||||
currentAction.updateTask.cancel()
|
||||
|
||||
val meta = currentAction.itemStack.itemMeta as Damageable
|
||||
meta.damage = 0
|
||||
currentAction.itemStack.itemMeta = meta as ItemMeta
|
||||
val action = currentAction
|
||||
if (action is ReloadingAction) {
|
||||
currentAction = null
|
||||
action.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
override fun reload(tttPlayer: TTTPlayer, itemStack: ItemStack, state: Gun.State) {
|
||||
val ownState = state as State
|
||||
if (ownState.currentAction != null) throw ActionInProgressError()
|
||||
if (ownState.remainingShots == magazineSize) return
|
||||
|
||||
ownState.currentAction = ReloadingAction(itemStack, ownState, tttPlayer).also { it.start() }
|
||||
override fun reload() {
|
||||
if (currentAction != null) throw ActionInProgressError()
|
||||
if (remainingShots == magazineSize) return
|
||||
currentAction = ReloadingAction(this)
|
||||
}
|
||||
|
||||
override fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: Gun.State): Boolean {
|
||||
val ownState = state as State
|
||||
if (ownState.remainingShots == 0) return true
|
||||
override fun onBeforeShoot(): Boolean {
|
||||
if (remainingShots == 0) return true
|
||||
|
||||
when(val currentAction = ownState.currentAction) {
|
||||
when(val action = currentAction) {
|
||||
is Action.Cooldown -> throw ActionInProgressError()
|
||||
is ReloadingAction -> {
|
||||
currentAction.reset()
|
||||
ownState.currentAction = null
|
||||
|
||||
val damageMeta = item.itemMeta!! as Damageable
|
||||
damageMeta.damage = 0
|
||||
item.itemMeta = damageMeta as ItemMeta
|
||||
}
|
||||
is ReloadingAction -> action.cancel()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
class State: Gun.State(magazineSize)
|
||||
|
||||
private class ReloadingAction(itemStack: ItemStack, state: State, tttPlayer: TTTPlayer): Action.Reloading(Shotgun, itemStack, state, tttPlayer) {
|
||||
lateinit var updateTask: BukkitTask
|
||||
|
||||
override fun reset() {
|
||||
task.cancel()
|
||||
updateTask.cancel()
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
task = startItemDamageProgress(
|
||||
itemStack,
|
||||
reloadTime,
|
||||
state.remainingShots.toDouble() / magazineSize
|
||||
) { state.currentAction = null }
|
||||
private class ReloadingAction(instance: Instance): Action.Reloading(instance) {
|
||||
override fun createProgressTask(): BukkitTask = startProgressTask(
|
||||
instance.gun.reloadTime,
|
||||
startAt = instance.remainingShots.toDouble() / instance.gun.magazineSize
|
||||
) { data ->
|
||||
val exp = if (data.isComplete) {
|
||||
instance.currentAction = null
|
||||
0F
|
||||
} else data.progress.toFloat()
|
||||
if (instance.isSelected) instance.carrier!!.player.exp = exp
|
||||
}
|
||||
|
||||
private var updateTask: BukkitTask? = null
|
||||
|
||||
init {
|
||||
updateTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
state.remainingShots++
|
||||
updateLevel(tttPlayer)
|
||||
|
||||
GameManager.world.playSound(tttPlayer.player.location, reloadSound, SoundCategory.PLAYERS, 1F, 1F)
|
||||
|
||||
if (state.remainingShots == magazineSize) {
|
||||
this.updateTask.cancel()
|
||||
instance.remainingShots++
|
||||
GameManager.world.playSound(instance.carrier!!.player.location, reloadSound, SoundCategory.PLAYERS, 1F, 1F)
|
||||
if (instance.remainingShots == magazineSize) updateTask?.cancel()
|
||||
}, secondsToTicks(RELOAD_TIME_PER_BULLET).toLong(), secondsToTicks(RELOAD_TIME_PER_BULLET).toLong())
|
||||
}
|
||||
},
|
||||
secondsToTicks(RELOAD_TIME_PER_BULLET).toLong(),
|
||||
secondsToTicks(RELOAD_TIME_PER_BULLET).toLong())
|
||||
|
||||
override fun cancel() {
|
||||
task.cancel()
|
||||
updateTask?.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,74 +1,55 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
|
||||
|
||||
import de.moritzruth.spigot_ttt.Resourcepack
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.Role
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.roles
|
||||
import de.moritzruth.spigot_ttt.utils.applyMeta
|
||||
import de.moritzruth.spigot_ttt.utils.hideInfo
|
||||
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.entity.Item
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
object SidekickDeagle: Gun(
|
||||
stateClass = State::class,
|
||||
type = Type.SPECIAL,
|
||||
instanceType = Instance::class,
|
||||
shopInfo = ShopInfo(
|
||||
buyableBy = roles(Role.JACKAL),
|
||||
buyLimit = 1,
|
||||
price = 1
|
||||
),
|
||||
displayName = "${ChatColor.AQUA}${ChatColor.BOLD}Sidekick Deagle",
|
||||
damage = 0.1, // Not really
|
||||
cooldown = 1.0,
|
||||
magazineSize = 1,
|
||||
reloadTime = 0.0,
|
||||
itemMaterial = Resourcepack.Items.sidekickDeagle,
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload
|
||||
), Buyable {
|
||||
override val buyableBy = roles(Role.JACKAL)
|
||||
override val price = 1
|
||||
override val type = TTTItem.Type.PISTOL_LIKE
|
||||
override val buyLimit = 1
|
||||
|
||||
override val itemStack = ItemStack(Resourcepack.Items.sidekickDeagle).applyMeta {
|
||||
hideInfo()
|
||||
setDisplayName("${ChatColor.AQUA}${ChatColor.BOLD}Sidekick Deagle")
|
||||
lore = listOf(
|
||||
itemLore = listOf(
|
||||
"",
|
||||
"${ChatColor.GOLD}Mache einen Spieler zu deinem Sidekick",
|
||||
"",
|
||||
"${ChatColor.RED}Nur ein Schuss"
|
||||
)
|
||||
}
|
||||
|
||||
override fun reload(tttPlayer: TTTPlayer, itemStack: ItemStack, state: Gun.State) {
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Du kannst diese Waffe nicht nachladen")
|
||||
),
|
||||
appendLore = false,
|
||||
damage = 0.1, // Not really
|
||||
cooldown = 1.0,
|
||||
magazineSize = 1,
|
||||
reloadTime = 0.0,
|
||||
material = Resourcepack.Items.sidekickDeagle,
|
||||
shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire,
|
||||
reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload
|
||||
) {
|
||||
class Instance: Gun.Instance(SidekickDeagle) {
|
||||
override fun reload() {
|
||||
requireCarrier().player.sendActionBarMessage("${ChatColor.RED}Du kannst diese Waffe nicht nachladen")
|
||||
}
|
||||
|
||||
override fun onHit(tttPlayer: TTTPlayer, hitTTTPlayer: TTTPlayer) {
|
||||
hitTTTPlayer.changeRole(Role.SIDEKICK)
|
||||
}
|
||||
|
||||
override fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item): Boolean {
|
||||
val state = isc.get(tttPlayer) ?: return true
|
||||
|
||||
return if (tttPlayer.role != Role.JACKAL || state.remainingShots == 0) {
|
||||
isc.remove(tttPlayer)
|
||||
itemEntity.remove()
|
||||
state.currentAction?.task?.cancel()
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
override fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: Gun.State): Boolean {
|
||||
override fun onBeforeShoot(): Boolean {
|
||||
val tttPlayer = requireCarrier()
|
||||
if (tttPlayer.role != Role.JACKAL) {
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Diese Waffe kann nur der Jackal benutzen")
|
||||
return false
|
||||
}
|
||||
|
||||
return super.onBeforeShoot(tttPlayer, item, state)
|
||||
return super.onBeforeShoot()
|
||||
}
|
||||
}
|
||||
|
||||
class State: Gun.State(magazineSize)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.shop
|
||||
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.items.ItemManager
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
|
@ -16,7 +15,9 @@ object Shop {
|
|||
}.toList()
|
||||
private val ITEMS_PER_PAGE = SHOP_SLOTS.count()
|
||||
|
||||
private fun getBuyableItems(tttPlayer: TTTPlayer) = ItemManager.ITEMS.filter { it is Buyable && it.buyableBy.contains(tttPlayer.role) }.toSet()
|
||||
private fun getBuyableItems(tttPlayer: TTTPlayer) = ItemManager.ITEMS
|
||||
.filter { it.shopInfo?.run { buyableBy.contains(tttPlayer.role) } ?: false }
|
||||
.toSet()
|
||||
|
||||
fun setItems(tttPlayer: TTTPlayer) {
|
||||
clear(tttPlayer)
|
||||
|
@ -26,12 +27,12 @@ object Shop {
|
|||
if (!itemsIterator.hasNext()) break
|
||||
|
||||
val tttItem = itemsIterator.next()
|
||||
if (tttItem !is Buyable) throw Error("Item is not buyable")
|
||||
val shopInfo = tttItem.shopInfo!!
|
||||
|
||||
tttPlayer.player.inventory.setItem(index, tttItem.itemStack.clone().applyMeta {
|
||||
tttPlayer.player.inventory.setItem(index, tttItem.templateItemStack.clone().applyMeta {
|
||||
val displayNameSuffix =
|
||||
if (isOutOfStock(tttPlayer, tttItem)) "${ChatColor.RED}Ausverkauft"
|
||||
else "$${tttItem.price}"
|
||||
else "$${shopInfo.price}"
|
||||
|
||||
setDisplayName("$displayName${ChatColor.RESET} - ${ChatColor.BOLD}$displayNameSuffix")
|
||||
})
|
||||
|
@ -42,8 +43,8 @@ object Shop {
|
|||
for(index in 9..35) tttPlayer.player.inventory.clear(index) // All slots except the hotbar and armor
|
||||
}
|
||||
|
||||
fun isOutOfStock(tttPlayer: TTTPlayer, tttItem: TTTItem): Boolean {
|
||||
if (tttItem !is Buyable) throw Error("Item is not buyable")
|
||||
return tttItem.buyLimit != null && tttPlayer.boughtItems.filter { it == tttItem }.count() >= tttItem.buyLimit!!
|
||||
fun isOutOfStock(tttPlayer: TTTPlayer, tttItem: TTTItem<*>): Boolean {
|
||||
val shopInfo = tttItem.shopInfo ?: throw Error("Item is not buyable")
|
||||
return shopInfo.buyLimit != 0 && tttPlayer.boughtItems.filter { it == tttItem }.count() >= shopInfo.buyLimit
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,21 @@
|
|||
package de.moritzruth.spigot_ttt.game.items.shop
|
||||
|
||||
import de.moritzruth.spigot_ttt.Settings
|
||||
import de.moritzruth.spigot_ttt.game.items.Buyable
|
||||
import de.moritzruth.spigot_ttt.game.GameListener
|
||||
import de.moritzruth.spigot_ttt.game.items.ItemManager
|
||||
import de.moritzruth.spigot_ttt.game.players.PlayerManager
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
|
||||
import de.moritzruth.spigot_ttt.game.players.TTTPlayerTrueDeathEvent
|
||||
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.inventory.ClickType
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
|
||||
object ShopListener: Listener {
|
||||
object ShopListener: GameListener() {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
fun onInventoryClick(event: InventoryClickEvent) {
|
||||
if (event.whoClicked !is Player) return
|
||||
val tttPlayer = TTTPlayer.of(event.whoClicked as Player) ?: return
|
||||
|
||||
if (event.click === ClickType.CREATIVE || event.clickedInventory?.holder != event.whoClicked) return
|
||||
fun onInventoryClick(event: InventoryClickEvent) = handle(event) { tttPlayer ->
|
||||
if (event.click === ClickType.CREATIVE || event.clickedInventory?.holder != event.whoClicked) return@handle
|
||||
event.isCancelled = true
|
||||
|
||||
val itemStack = event.currentItem
|
||||
|
@ -30,21 +25,21 @@ object ShopListener: Listener {
|
|||
event.clickedInventory?.holder == tttPlayer.player &&
|
||||
Shop.SHOP_SLOTS.contains(event.slot)
|
||||
) {
|
||||
val tttItem = ItemManager.getItemByItemStack(itemStack)
|
||||
if (tttItem === null || tttItem !is Buyable || !tttItem.buyableBy.contains(tttPlayer.role)) return
|
||||
val tttItem = ItemManager.getTTTItemByItemStack(itemStack) ?: return@handle
|
||||
val shopMeta = tttItem.shopInfo
|
||||
if (shopMeta == null || !shopMeta.buyableBy.contains(tttPlayer.role)) return@handle
|
||||
|
||||
when {
|
||||
Shop.isOutOfStock(tttPlayer, tttItem) ->
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Dieses Item ist ausverkauft")
|
||||
|
||||
tttPlayer.credits < tttItem.price ->
|
||||
tttPlayer.credits < tttItem.shopInfo.price ->
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Du hast nicht genug Credits")
|
||||
|
||||
else -> try {
|
||||
tttPlayer.addItem(tttItem)
|
||||
tttPlayer.boughtItems.add(tttItem)
|
||||
tttPlayer.credits -= tttItem.price
|
||||
|
||||
tttPlayer.credits -= shopMeta.price
|
||||
Shop.setItems(tttPlayer)
|
||||
} catch (e: TTTPlayer.AlreadyHasItemException) {
|
||||
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Du hast dieses Item bereits")
|
||||
|
@ -63,8 +58,9 @@ object ShopListener: Listener {
|
|||
PlayerManager.tttPlayers
|
||||
.filter { it.role.canOwnCredits && it.role.group == killer.role.group }
|
||||
.forEach {
|
||||
it.credits += Settings.creditsPerKill
|
||||
it.player.sendActionBarMessage("${ChatColor.GREEN}Du hast ${Settings.creditsPerKill} Credit(s) erhalten")
|
||||
val creditsPerKill = Settings.creditsPerKill
|
||||
it.credits += creditsPerKill
|
||||
it.player.sendActionBarMessage("${ChatColor.GREEN}Du hast $creditsPerKill Credit(s) erhalten")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,5 @@ sealed class DeathReason(val displayText: String) {
|
|||
object DROWNED: DeathReason("Ertrunken")
|
||||
object FIRE: DeathReason("Verbrannt")
|
||||
object POISON: DeathReason("Vergiftet")
|
||||
class Item(val item: TTTItem): DeathReason("Getötet mit: ${item.itemStack.itemMeta!!.displayName}")
|
||||
class Item(val item: TTTItem<*>): DeathReason("Getötet mit: ${item.templateItemStack.itemMeta!!.displayName}")
|
||||
}
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
package de.moritzruth.spigot_ttt.game.players
|
||||
|
||||
import com.google.common.collect.MutableClassToInstanceMap
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KVisibility
|
||||
|
||||
interface IState
|
||||
|
||||
class StateContainer(private val tttPlayer: TTTPlayer) {
|
||||
private val instances = MutableClassToInstanceMap.create<IState>()
|
||||
|
||||
fun <T: IState> getOrCreate(stateClass: KClass<T>): T =
|
||||
get(stateClass) ?: run {
|
||||
val parameterlessConstructor = stateClass.constructors
|
||||
.find { it.parameters.isEmpty() && it.visibility == KVisibility.PUBLIC }
|
||||
?: throw NoSuchMethodException("The stateClass has no public parameterless constructor")
|
||||
|
||||
parameterlessConstructor.call().also { instances[stateClass.java] = it }
|
||||
}
|
||||
|
||||
fun <T: IState> get(stateClass: KClass<T>): T? = instances.getInstance(stateClass.java)
|
||||
|
||||
fun <T: IState> has(stateClass: KClass<T>): Boolean = instances.containsKey(stateClass.java)
|
||||
|
||||
fun <T: IState> put(stateClass: KClass<out T>, value: T) {
|
||||
if (instances.containsKey(stateClass.java))
|
||||
throw IllegalStateException("There is already a state instance in this container")
|
||||
|
||||
instances[stateClass.java] = value
|
||||
}
|
||||
|
||||
fun <T: IState> remove(stateClass: KClass<T>) = instances.remove(stateClass.java)
|
||||
}
|
||||
|
||||
class InversedStateContainer<T: IState>(private val stateClass: KClass<T>) {
|
||||
fun getOrCreate(tttPlayer: TTTPlayer) = tttPlayer.stateContainer.getOrCreate(stateClass)
|
||||
fun get(tttPlayer: TTTPlayer) = tttPlayer.stateContainer.get(stateClass)
|
||||
fun remove(tttPlayer: TTTPlayer) = tttPlayer.stateContainer.remove(stateClass)
|
||||
|
||||
val tttPlayers get() = PlayerManager.tttPlayers.filter { it.stateContainer.has(stateClass) }
|
||||
|
||||
fun forEveryState(fn: (T, TTTPlayer) -> Unit) {
|
||||
tttPlayers.forEach { it.stateContainer.get(stateClass)?.run { fn(this, it) } }
|
||||
}
|
||||
}
|
|
@ -7,20 +7,25 @@ import de.moritzruth.spigot_ttt.game.GameManager
|
|||
import de.moritzruth.spigot_ttt.game.GamePhase
|
||||
import de.moritzruth.spigot_ttt.game.ScoreboardHelper
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClass
|
||||
import de.moritzruth.spigot_ttt.game.classes.TTTClassCompanion
|
||||
import de.moritzruth.spigot_ttt.game.corpses.TTTCorpse
|
||||
import de.moritzruth.spigot_ttt.game.items.ItemManager
|
||||
import de.moritzruth.spigot_ttt.game.items.Selectable
|
||||
import de.moritzruth.spigot_ttt.game.items.TTTItem
|
||||
import de.moritzruth.spigot_ttt.game.items.shop.Shop
|
||||
import de.moritzruth.spigot_ttt.utils.*
|
||||
import org.bukkit.*
|
||||
import org.bukkit.ChatColor
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.SoundCategory
|
||||
import org.bukkit.entity.Player
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.None) {
|
||||
class TTTPlayer(player: Player, role: Role, val tttClass: TTTClassCompanion = TTTClass.None) {
|
||||
var alive = true
|
||||
var player by Delegates.observable(player) { _, _, _ -> adjustPlayer() }
|
||||
|
||||
val tttClassInstance = tttClass.createInstance(this)
|
||||
|
||||
var role = role
|
||||
private set(value) {
|
||||
if (value !== field) {
|
||||
|
@ -33,34 +38,20 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
|
|||
}
|
||||
val roleHistory = mutableListOf<Role>()
|
||||
|
||||
var itemInHand by Delegates.observable<TTTItem?>(null) { _, oldItem, newItem ->
|
||||
if (oldItem !== newItem) onItemInHandChanged(oldItem, newItem)
|
||||
}
|
||||
|
||||
var walkSpeed
|
||||
get() = player.walkSpeed
|
||||
set(value) { player.walkSpeed = value }
|
||||
|
||||
var credits by Delegates.observable(Settings.initialCredits) { _, _, _ -> scoreboard.updateCredits() }
|
||||
val boughtItems = mutableListOf<TTTItem>()
|
||||
val boughtItems = mutableListOf<TTTItem<*>>()
|
||||
|
||||
val scoreboard = TTTScoreboard(this)
|
||||
val stateContainer = StateContainer(this)
|
||||
|
||||
init {
|
||||
adjustPlayer()
|
||||
scoreboard.initialize()
|
||||
tttClass.onInit(this)
|
||||
}
|
||||
|
||||
private fun onItemInHandChanged(oldItem: TTTItem?, newItem: TTTItem?) {
|
||||
if (oldItem !== null && oldItem is Selectable) {
|
||||
oldItem.onDeselect(this)
|
||||
}
|
||||
|
||||
if (newItem !== null && newItem is Selectable) {
|
||||
newItem.onSelect(this)
|
||||
}
|
||||
tttClassInstance.tttPlayer = this
|
||||
tttClassInstance.init()
|
||||
}
|
||||
|
||||
fun damage(damage: Double, reason: DeathReason, damager: TTTPlayer, scream: Boolean = true) {
|
||||
|
@ -159,7 +150,9 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
|
|||
player.scoreboard = scoreboard.scoreboard
|
||||
}
|
||||
|
||||
private fun getOwningTTTItems() = player.inventory.hotbarContents.mapNotNull { it?.run { ItemManager.getItemByItemStack(this) } }
|
||||
fun getOwningTTTItemInstances() = player.inventory.hotbarContents
|
||||
.filterNotNull()
|
||||
.mapNotNull { ItemManager.getInstanceByItemStack(it) }
|
||||
|
||||
fun changeRole(newRole: Role, notify: Boolean = true) {
|
||||
roleHistory.add(role)
|
||||
|
@ -194,12 +187,6 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
|
|||
player.spigot().respawn()
|
||||
}
|
||||
|
||||
itemInHand?.let {
|
||||
if (it is Selectable) {
|
||||
it.onDeselect(this)
|
||||
}
|
||||
}
|
||||
|
||||
player.gameMode = GameMode.SURVIVAL
|
||||
player.activePotionEffects.forEach { player.removePotionEffect(it.type) }
|
||||
player.health = 20.0
|
||||
|
@ -208,44 +195,39 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
|
|||
player.exp = 0F
|
||||
player.allowFlight = player.gameMode == GameMode.CREATIVE
|
||||
player.foodLevel = 20
|
||||
|
||||
player.inventory.clear()
|
||||
|
||||
tttClassInstance.reset()
|
||||
}
|
||||
|
||||
fun updateItemInHand() {
|
||||
val itemStack = player.inventory.itemInMainHand
|
||||
this.itemInHand =
|
||||
if (itemStack.type === Material.AIR) null
|
||||
else ItemManager.getItemByItemStack(itemStack)
|
||||
fun checkAddItemPreconditions(tttItem: TTTItem<*>) {
|
||||
val owningTTTItemInstances = getOwningTTTItemInstances()
|
||||
if (owningTTTItemInstances.find { it.tttItem === tttItem } != null) throw AlreadyHasItemException()
|
||||
|
||||
val maxItemsOfTypeInInventory = tttItem.type.maxItemsOfTypeInInventory
|
||||
if (
|
||||
maxItemsOfTypeInInventory != null &&
|
||||
owningTTTItemInstances.filter { it.tttItem.type == tttItem.type }.count() >= maxItemsOfTypeInInventory
|
||||
) throw TooManyItemsOfTypeException()
|
||||
}
|
||||
|
||||
fun checkAddItemPreconditions(item: TTTItem) {
|
||||
val owningTTTItems = getOwningTTTItems()
|
||||
|
||||
if (owningTTTItems.contains(item)) {
|
||||
throw AlreadyHasItemException()
|
||||
}
|
||||
|
||||
val maxItemsOfTypeInInventory = item.type.maxItemsOfTypeInInventory
|
||||
if (maxItemsOfTypeInInventory !== null && owningTTTItems.filter { it.type === item.type }.count() >= maxItemsOfTypeInInventory) {
|
||||
throw TooManyItemsOfTypeException()
|
||||
}
|
||||
}
|
||||
class AlreadyHasItemException: Exception("The player already owns this item")
|
||||
|
||||
class TooManyItemsOfTypeException: Exception("The player already owns too much items of this type")
|
||||
|
||||
fun addItem(item: TTTItem) {
|
||||
fun addItem(item: TTTItem<*>) {
|
||||
checkAddItemPreconditions(item)
|
||||
player.inventory.addItem(item.itemStack.clone())
|
||||
item.onOwn(this)
|
||||
updateItemInHand()
|
||||
val instance = item.createInstance()
|
||||
player.inventory.addItem(instance.createItemStack())
|
||||
instance.carrier = this
|
||||
}
|
||||
|
||||
fun removeItem(item: TTTItem<*>, removeInstance: Boolean = true) {
|
||||
item.getInstance(this)?.let {
|
||||
it.carrier = null
|
||||
if (removeInstance) item.instancesByUUID.remove(it.uuid)
|
||||
}
|
||||
|
||||
fun removeItem(item: TTTItem) {
|
||||
player.inventory.removeTTTItem(item)
|
||||
item.onRemove(this)
|
||||
updateItemInHand()
|
||||
}
|
||||
|
||||
fun addDefaultClassItems() = tttClass.defaultItems.forEach { addItem(it) }
|
||||
|
|
|
@ -9,11 +9,12 @@ fun Inventory.setAllToItem(indexes: Iterable<Int>, itemStack: ItemStack) {
|
|||
indexes.forEach { setItem(it, itemStack) }
|
||||
}
|
||||
|
||||
fun Inventory.removeTTTItem(tttItem: TTTItem) {
|
||||
val index = indexOfFirst { it?.type == tttItem.itemStack.type }
|
||||
fun Inventory.removeTTTItem(tttItem: TTTItem<*>) {
|
||||
val index = indexOfFirst { it?.type == tttItem.material }
|
||||
if (index != -1) clear(index)
|
||||
}
|
||||
fun Inventory.removeTTTItemNextTick(tttItem: TTTItem) = nextTick { removeTTTItem(tttItem) }
|
||||
|
||||
fun Inventory.removeTTTItemNextTick(tttItem: TTTItem<*>) = nextTick { removeTTTItem(tttItem) }
|
||||
|
||||
fun PlayerInventory.clearHeldItemSlot() = clear(heldItemSlot)
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package de.moritzruth.spigot_ttt.utils
|
||||
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.inventory.meta.Damageable
|
||||
import org.bukkit.inventory.meta.ItemMeta
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.time.Instant
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
fun startItemDamageProgress(itemStack: ItemStack, duration: Double, startProgress: Double = 0.0, fromRight: Boolean = false, onFinish: () -> Unit): BukkitTask {
|
||||
val startedAt = Instant.now().toEpochMilli()
|
||||
|
||||
lateinit var task: BukkitTask
|
||||
|
||||
task = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val secondsElapsed = (Instant.now().toEpochMilli() - startedAt) / 1000.0
|
||||
val progress = secondsElapsed / duration + startProgress
|
||||
|
||||
val maxDurability = itemStack.type.maxDurability
|
||||
val damageMeta = itemStack.itemMeta!! as Damageable
|
||||
|
||||
if (fromRight) {
|
||||
damageMeta.damage = (maxDurability * progress).roundToInt()
|
||||
} else {
|
||||
damageMeta.damage = maxDurability - (maxDurability * progress).roundToInt()
|
||||
}
|
||||
|
||||
itemStack.itemMeta = damageMeta as ItemMeta
|
||||
|
||||
if (progress >= 1) {
|
||||
task.cancel()
|
||||
onFinish()
|
||||
}
|
||||
}, 0, 1)
|
||||
|
||||
return task
|
||||
}
|
|
@ -7,3 +7,11 @@ fun ItemStack.applyMeta(fn: ItemMeta.() -> Unit): ItemStack {
|
|||
itemMeta = itemMeta!!.apply(fn)
|
||||
return this
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T: Any> ItemStack.applyTypedMeta(fn: T.() -> Unit): ItemStack {
|
||||
val typedMeta = itemMeta as T
|
||||
typedMeta.fn()
|
||||
itemMeta = itemMeta as ItemMeta
|
||||
return this
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package de.moritzruth.spigot_ttt.utils
|
||||
|
||||
enum class Probability {
|
||||
VERY_LOW,
|
||||
LOW,
|
||||
NORMAL
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package de.moritzruth.spigot_ttt.utils
|
||||
|
||||
import de.moritzruth.spigot_ttt.plugin
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import kotlin.math.min
|
||||
|
||||
data class TickData(
|
||||
val elapsedSeconds: Double,
|
||||
val progress: Double,
|
||||
val isComplete: Boolean,
|
||||
val trueProgress: Double
|
||||
)
|
||||
|
||||
/**
|
||||
* @param duration Duration in seconds
|
||||
*/
|
||||
fun startProgressTask(duration: Double, startAt: Double = 0.0, onTick: (data: TickData) -> Unit): BukkitTask {
|
||||
val startedAt = Instant.now()
|
||||
|
||||
var task: BukkitTask? = null
|
||||
task = plugin.server.scheduler.runTaskTimer(plugin, fun() {
|
||||
val elapsedSeconds: Double = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000
|
||||
val progress: Double = (elapsedSeconds / duration) + startAt
|
||||
val data = TickData(
|
||||
elapsedSeconds = elapsedSeconds,
|
||||
progress = min(a = progress, b = 1.0),
|
||||
trueProgress = progress,
|
||||
isComplete = progress >= 1.0
|
||||
)
|
||||
|
||||
if (data.isComplete) {
|
||||
task?.cancel()
|
||||
}
|
||||
|
||||
onTick(data)
|
||||
|
||||
}, 0, 1)
|
||||
return task
|
||||
}
|
Reference in a new issue