diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/ResourcePack.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/ResourcePack.kt index 5d02b81..d58ff7d 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/ResourcePack.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/ResourcePack.kt @@ -6,7 +6,7 @@ object ResourcePack { object Items { val textureless = Material.WHITE_STAINED_GLASS_PANE val deathReason = Material.GRAY_STAINED_GLASS_PANE - val questionMark = Material.IRON_BLOCK + val questionMark = Material.GRASS_BLOCK val time = Material.CLOCK // Roles @@ -22,6 +22,7 @@ object ResourcePack { val teleporter = Material.SPRUCE_WOOD val martyrdomGrenade = Material.BIRCH_LOG val fakeCorpse = Material.SPRUCE_LOG + val defibrillator = Material.IRON_INGOT // Weapons val deagle = Material.IRON_HOE diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/CorpseListener.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/CorpseListener.kt index a55d331..c8953aa 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/CorpseListener.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/CorpseListener.kt @@ -5,6 +5,7 @@ import de.moritzruth.spigot_ttt.game.players.Role import de.moritzruth.spigot_ttt.plugin import org.bukkit.entity.Player import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority import org.bukkit.event.Listener import org.bukkit.event.inventory.InventoryClickEvent import org.golde.bukkit.corpsereborn.CorpseAPI.events.CorpseClickEvent @@ -25,7 +26,7 @@ object CorpseListener: Listener { } } - @EventHandler + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) fun onCorpseClick(event: CorpseClickEvent) { // bug: always ClickType.UNKNOWN // if (event.clickType !== ClickType.RIGHT) return diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/TTTCorpse.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/TTTCorpse.kt index 83838a6..a03d3d2 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/TTTCorpse.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/corpses/TTTCorpse.kt @@ -12,7 +12,6 @@ import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.secondsToTicks import org.bukkit.ChatColor import org.bukkit.Location -import org.bukkit.entity.Player import org.bukkit.event.inventory.InventoryType import org.bukkit.inventory.ItemStack import org.bukkit.scheduler.BukkitTask @@ -21,7 +20,7 @@ import org.golde.bukkit.corpsereborn.nms.Corpses import java.time.Instant class TTTCorpse private constructor( - private val player: Player, + val tttPlayer: TTTPlayer, location: Location, private val role: Role, private val reason: DeathReason, @@ -29,8 +28,8 @@ class TTTCorpse private constructor( ) { var status = Status.UNIDENTIFIED; private set - val corpse: Corpses.CorpseData? - val inventory = player.server.createInventory(null, InventoryType.HOPPER, "${role.chatColor}${player.displayName}") + val corpse: Corpses.CorpseData + val inventory = tttPlayer.player.server.createInventory(null, InventoryType.HOPPER, "${role.chatColor}${tttPlayer.player.displayName}") val timestamp: Instant = Instant.now() private var fullMinutesSinceDeath = 0 @@ -44,7 +43,7 @@ class TTTCorpse private constructor( setItems() - corpse = CorpseAPI.spawnCorpse(player, location) + corpse = CorpseAPI.spawnCorpse(tttPlayer.player, location) updateTimeListener = plugin.server.scheduler.runTaskTimer(plugin, fun() { fullMinutesSinceDeath += 1 @@ -91,8 +90,9 @@ class TTTCorpse private constructor( } fun identify(tttPlayer: TTTPlayer, inspect: Boolean) { + ensureNotDestroyed() if (status == Status.UNIDENTIFIED) { - GameMessenger.corpseIdentified(tttPlayer.player.displayName, player.displayName, role) + GameMessenger.corpseIdentified(tttPlayer.player.displayName, tttPlayer.player.displayName, role) if (!inspect) { status = Status.IDENTIFIED @@ -117,13 +117,25 @@ class TTTCorpse private constructor( } } + fun revive() { + ensureNotDestroyed() + tttPlayer.revive(corpse.trueLocation, credits) + destroy() + } + fun destroy() { - if (corpse !== null) CorpseAPI.removeCorpse(corpse) + status = Status.DESTROYED + CorpseAPI.removeCorpse(corpse) updateTimeListener.cancel() inventory.viewers.toSet().forEach { it.closeInventory() } } + private fun ensureNotDestroyed() { + if (status == Status.DESTROYED) throw IllegalStateException("This corpse was destroyed") + } + enum class Status { + DESTROYED, UNIDENTIFIED, IDENTIFIED, INSPECTED @@ -136,7 +148,7 @@ class TTTCorpse private constructor( fun spawn(tttPlayer: TTTPlayer, reason: DeathReason) { CorpseManager.add(TTTCorpse( - tttPlayer.player, + tttPlayer, tttPlayer.player.location, tttPlayer.role, reason, @@ -144,9 +156,9 @@ class TTTCorpse private constructor( )) } - fun spawnFake(role: Role, player: Player, location: Location) { + fun spawnFake(role: Role, tttPlayer: TTTPlayer, location: Location) { CorpseManager.add(TTTCorpse( - player, + tttPlayer, location, role, DeathReason.Item(Pistol), diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/TTTPlayer.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/TTTPlayer.kt index 4e51438..181ae36 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/TTTPlayer.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/game/players/TTTPlayer.kt @@ -1,5 +1,6 @@ package de.moritzruth.spigot_ttt.game.players +import de.moritzruth.spigot_ttt.TTTPlugin import de.moritzruth.spigot_ttt.game.GameManager import de.moritzruth.spigot_ttt.game.GamePhase import de.moritzruth.spigot_ttt.game.corpses.TTTCorpse @@ -13,6 +14,7 @@ import de.moritzruth.spigot_ttt.utils.secondsToTicks import de.moritzruth.spigot_ttt.utils.teleportPlayerToWorldSpawn import org.bukkit.ChatColor import org.bukkit.GameMode +import org.bukkit.Location import org.bukkit.Material import org.bukkit.entity.Player import org.bukkit.potion.PotionEffect @@ -89,11 +91,30 @@ class TTTPlayer(player: Player, role: Role) { credits = 0 Shop.clear(this) - PlayerManager.letRemainingRoleGroupWin() - +// PlayerManager.letRemainingRoleGroupWin() plugin.server.pluginManager.callEvent(TTTPlayerDeathEvent(this, player.location)) } + fun revive(location: Location, credits: Int = 0) { + if (alive) throw AlreadyLivingException() + + alive = true + this.credits = credits + + player.health = 20.0 + player.teleport(location) + + Shop.setItems(this) + + player.sendMessage(TTTPlugin.prefix + "${ChatColor.GREEN}${ChatColor.BOLD}Du wurdest wiederbelebt") + + plugin.server.scheduler.runTask(plugin, fun() { + player.gameMode = GameMode.SURVIVAL + }) + } + + class AlreadyLivingException: Exception("The player already lives") + private fun adjustPlayer() { player.scoreboard = scoreboard.scoreboard } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/items/ItemManager.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/items/ItemManager.kt index e83d5f2..dff15e9 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/items/ItemManager.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/items/ItemManager.kt @@ -29,7 +29,7 @@ object ItemManager { BaseballBat, CloakingDevice, Rifle, EnderPearl, Radar, HealingPotion, Fireball, - Teleporter, MartyrdomGrenade, FakeCorpse + Teleporter, MartyrdomGrenade, FakeCorpse, Defibrillator ) val droppedItemStates = mutableMapOf() diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/Defibrillator.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/Defibrillator.kt new file mode 100644 index 0000000..dc74cb2 --- /dev/null +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/Defibrillator.kt @@ -0,0 +1,152 @@ +package de.moritzruth.spigot_ttt.items.impl + +import com.connorlinfoot.actionbarapi.ActionBarAPI +import de.moritzruth.spigot_ttt.ResourcePack +import de.moritzruth.spigot_ttt.game.corpses.CorpseManager +import de.moritzruth.spigot_ttt.game.players.* +import de.moritzruth.spigot_ttt.items.Buyable +import de.moritzruth.spigot_ttt.items.TTTItem +import de.moritzruth.spigot_ttt.items.isRelevant +import de.moritzruth.spigot_ttt.plugin +import de.moritzruth.spigot_ttt.utils.* +import org.bukkit.ChatColor +import org.bukkit.boss.BarColor +import org.bukkit.boss.BarStyle +import org.bukkit.event.EventHandler +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.inventory.ItemStack +import org.bukkit.scheduler.BukkitTask +import org.golde.bukkit.corpsereborn.CorpseAPI.events.CorpseClickEvent +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 { + 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 + + private val isc = InversedStateContainer(State::class) + + override val listener = object : Listener { + @EventHandler + fun onPlayerInteract(event: PlayerInteractEvent) { + if (event.isRelevant(Defibrillator)) event.isCancelled = true + } + + @EventHandler + fun onCorpseClick(event: CorpseClickEvent) { + val tttPlayer = PlayerManager.getTTTPlayer(event.clicker) ?: return + if (tttPlayer.player.inventory.itemInMainHand.type != itemStack.type) return + + val tttCorpse = CorpseManager.getTTTCorpse(event.corpse) ?: return + event.isCancelled = true + + val state = isc.getOrCreate(tttPlayer) + state.bossBar.addPlayer(tttPlayer.player) + + when(val action = state.action) { + null -> state.action = Action.Reviving(tttPlayer, state) + is Action.Reviving -> { + action.cancelTask.cancel() + action.cancelTask = action.createCancelTask() + + val progress = action.duration / REVIVE_DURATION + + if (progress >= 1) { + try { + tttCorpse.revive() + + ActionBarAPI.sendActionBar( + tttPlayer.player, + "${ChatColor.BOLD}${tttCorpse.tttPlayer.player.displayName} " + + "${ChatColor.GREEN}wurde wiederbelebt" + ) + + action.cancelTask.cancel() + tttPlayer.player.inventory.removeTTTItemNextTick(Defibrillator) + state.reset(tttPlayer) + isc.remove(tttPlayer) + } catch(e: TTTPlayer.AlreadyLivingException) { + // do not cancel the cancelTask + } + } else state.bossBar.progress = progress + } + is Action.Canceled -> noop() + } + } + } + + sealed class Action(val tttPlayer: TTTPlayer) { + open fun reset() {} + + class Reviving(tttPlayer: TTTPlayer, val state: State): Action(tttPlayer) { + var cancelTask = createCancelTask() + private val startedAt: Instant = Instant.now() + val duration get() = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000 + + fun createCancelTask() = plugin.server.scheduler.runTaskLater(plugin, fun() { + state.action = Canceled(tttPlayer) + }, 10) + + init { + state.bossBar.color = BarColor.GREEN + state.bossBar.addPlayer(tttPlayer.player) + } + + override fun reset() = cancelTask.cancel() + } + + class Canceled(tttPlayer: TTTPlayer): Action(tttPlayer) { + private var switches: Int = 0 + private lateinit var task: BukkitTask + + init { + task = plugin.server.scheduler.runTaskTimer(plugin, fun() { + val state = isc.get(tttPlayer) ?: return@runTaskTimer + + if (switches == SWITCHES_COUNT) { + task.cancel() + state.action = null + state.bossBar.removePlayer(tttPlayer.player) + } else { + state.bossBar.progress = 1.0 + state.bossBar.color = if (switches % 2 == 0) BarColor.RED else BarColor.WHITE + switches += 1 + } + }, 0, secondsToTicks(0.2).toLong()) + } + + override fun reset() = task.cancel() + + companion object { + const val SWITCHES_COUNT = 6 + } + } + } + + class State: IState { + var action: Action? = null + var bossBar = plugin.server.createBossBar( + "${ChatColor.BOLD}Defibrillator", + BarColor.GREEN, + BarStyle.SOLID + ) + + override fun reset(tttPlayer: TTTPlayer) { + bossBar.removePlayer(tttPlayer.player) + action?.reset() + } + } +} diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/FakeCorpse.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/FakeCorpse.kt index a8e3573..0be5ace 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/FakeCorpse.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/FakeCorpse.kt @@ -1,5 +1,6 @@ package de.moritzruth.spigot_ttt.items.impl +import com.connorlinfoot.actionbarapi.ActionBarAPI import de.moritzruth.spigot_ttt.ResourcePack import de.moritzruth.spigot_ttt.game.corpses.TTTCorpse import de.moritzruth.spigot_ttt.game.players.* @@ -10,6 +11,7 @@ import de.moritzruth.spigot_ttt.plugin import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.hideInfo import de.moritzruth.spigot_ttt.utils.isRightClick +import de.moritzruth.spigot_ttt.utils.removeTTTItem import org.bukkit.ChatColor import org.bukkit.Material import org.bukkit.entity.Player @@ -117,11 +119,16 @@ object FakeCorpse: TTTItem, Buyable { state.choosePlayerInventory -> { tttPlayer.player.closeInventory() - val player = plugin.server.getPlayer((item.itemMeta as SkullMeta).owningPlayer!!.uniqueId)!! - TTTCorpse.spawnFake(state.chosenRole!!, player, tttPlayer.player.location) + val corpsePlayer = plugin.server.getPlayer((item.itemMeta as SkullMeta).owningPlayer!!.uniqueId)!! + val corpseTTTPlayer = PlayerManager.getTTTPlayer(corpsePlayer) - val itemIndex = tttPlayer.player.inventory.indexOfFirst { it.type == itemStack.type } - tttPlayer.player.inventory.clear(itemIndex) + if (corpseTTTPlayer == null) { + ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Das hat nicht funktioniert") + } else { + TTTCorpse.spawnFake(state.chosenRole!!, corpseTTTPlayer, tttPlayer.player.location) + + tttPlayer.player.inventory.removeTTTItem(FakeCorpse) + } } } } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/MartyrdomGrenade.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/MartyrdomGrenade.kt index 493bfff..1130656 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/MartyrdomGrenade.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/items/impl/MartyrdomGrenade.kt @@ -12,7 +12,6 @@ import de.moritzruth.spigot_ttt.utils.createKillExplosion import de.moritzruth.spigot_ttt.utils.hideInfo import de.moritzruth.spigot_ttt.utils.secondsToTicks import org.bukkit.ChatColor -import org.bukkit.Sound import org.bukkit.SoundCategory import org.bukkit.event.EventHandler import org.bukkit.event.Listener @@ -71,7 +70,7 @@ object MartyrdomGrenade: TTTItem, Buyable { state.explodeTask = plugin.server.scheduler.runTaskLater(plugin, fun() { GameManager.world.playSound( event.location, - Sound.ENTITY_GENERIC_EXPLODE, + ResourcePack.Sounds.grenadeExplode, SoundCategory.PLAYERS, 1F, 1F diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/items/weapons/impl/Knife.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/items/weapons/impl/Knife.kt index 7451669..6879c0e 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/items/weapons/impl/Knife.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/items/weapons/impl/Knife.kt @@ -6,10 +6,7 @@ import de.moritzruth.spigot_ttt.items.Buyable import de.moritzruth.spigot_ttt.items.TTTItem import de.moritzruth.spigot_ttt.items.isRelevant import de.moritzruth.spigot_ttt.items.weapons.LoreHelper -import de.moritzruth.spigot_ttt.utils.applyMeta -import de.moritzruth.spigot_ttt.utils.clearHeldItemSlot -import de.moritzruth.spigot_ttt.utils.hideInfo -import de.moritzruth.spigot_ttt.utils.isLeftClick +import de.moritzruth.spigot_ttt.utils.* import org.bukkit.ChatColor import org.bukkit.Sound import org.bukkit.SoundCategory @@ -60,6 +57,9 @@ object Knife: TTTItem, Buyable { if (distance > 1.5) event.isCancelled = true else { damagedTTTPlayer.damageInfo = DamageInfo(damagerTTTPlayer, DeathReason.Item(Knife)) event.damage = 1000.0 + + damagerTTTPlayer.player.playSound(damagerTTTPlayer.player.location, Sound.ENTITY_ITEM_BREAK, SoundCategory.PLAYERS, 1F, 1F) + damagerTTTPlayer.player.inventory.removeTTTItemNextTick(Knife) } } } diff --git a/src/main/kotlin/de/moritzruth/spigot_ttt/utils/InventoryExtensions.kt b/src/main/kotlin/de/moritzruth/spigot_ttt/utils/InventoryExtensions.kt index 53b8e5b..aec24d1 100644 --- a/src/main/kotlin/de/moritzruth/spigot_ttt/utils/InventoryExtensions.kt +++ b/src/main/kotlin/de/moritzruth/spigot_ttt/utils/InventoryExtensions.kt @@ -1,5 +1,7 @@ package de.moritzruth.spigot_ttt.utils +import de.moritzruth.spigot_ttt.items.TTTItem +import de.moritzruth.spigot_ttt.plugin import org.bukkit.inventory.Inventory import org.bukkit.inventory.ItemStack import org.bukkit.inventory.PlayerInventory @@ -8,6 +10,11 @@ fun Inventory.setAllToItem(indexes: Iterable, itemStack: ItemStack) { indexes.forEach { setItem(it, itemStack) } } +fun Inventory.removeTTTItem(tttItem: TTTItem) = clear(indexOfFirst { it.type == tttItem.itemStack.type }) +fun Inventory.removeTTTItemNextTick(tttItem: TTTItem) = plugin.server.scheduler.runTask(plugin, fun() { + removeTTTItem(tttItem) +}) + fun PlayerInventory.clearHeldItemSlot() = clear(heldItemSlot) val PlayerInventory.hotbarContents get() = this.contents.slice(0..8) as List