1
0
Fork 0

Add Second Chance item

This commit is contained in:
Moritz Ruth 2020-06-08 23:17:45 +02:00
parent 33a6a2dafd
commit 021ed57486
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
14 changed files with 214 additions and 21 deletions

View file

@ -8,13 +8,15 @@ object ResourcePack {
val deathReason = Material.GRAY_STAINED_GLASS_PANE val deathReason = Material.GRAY_STAINED_GLASS_PANE
val questionMark = Material.GRASS_BLOCK val questionMark = Material.GRASS_BLOCK
val time = Material.CLOCK val time = Material.CLOCK
val dot = Material.GRAY_STAINED_GLASS
val arrowDown = Material.WHITE_STAINED_GLASS
// Roles // Roles
val innocent = Material.GREEN_STAINED_GLASS_PANE val innocent = Material.GREEN_STAINED_GLASS_PANE
val detective = Material.YELLOW_STAINED_GLASS_PANE val detective = Material.YELLOW_STAINED_GLASS_PANE
val traitor = Material.RED_STAINED_GLASS_PANE val traitor = Material.RED_STAINED_GLASS_PANE
val jackal = Material.LIGHT_BLUE_STAINED_GLASS_PANE val jackal = Material.LIGHT_BLUE_STAINED_GLASS_PANE
val sidekick = Material.LIGHT_BLUE_STAINED_GLASS_PANE val sidekick = Material.BLUE_STAINED_GLASS_PANE
// Special Items // Special Items
val cloakingDevice = Material.COBWEB val cloakingDevice = Material.COBWEB

View file

@ -11,6 +11,7 @@ import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.player.PlayerEvent import org.bukkit.event.player.PlayerEvent
import org.bukkit.event.player.PlayerInteractEvent import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerItemConsumeEvent import org.bukkit.event.player.PlayerItemConsumeEvent
@ -73,6 +74,13 @@ open class TTTItemListener(private val tttItem: TTTItem, private val cancelDamag
} }
} }
protected fun handle(event: InventoryCloseEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
val player = event.player
if (player is Player) {
handler(PlayerManager.getTTTPlayer(player) ?: return)
}
}
protected fun <T: PlayerEvent> handle(event: T, handler: (tttPlayer: TTTPlayer) -> Unit) { protected fun <T: PlayerEvent> handle(event: T, handler: (tttPlayer: TTTPlayer) -> Unit) {
handler(PlayerManager.getTTTPlayer(event.player) ?: return) handler(PlayerManager.getTTTPlayer(event.player) ?: return)
} }

View file

@ -124,6 +124,7 @@ class TTTCorpse private constructor(
} }
fun destroy() { fun destroy() {
ensureNotDestroyed()
status = Status.DESTROYED status = Status.DESTROYED
CorpseAPI.removeCorpse(corpse) CorpseAPI.removeCorpse(corpse)
updateTimeListener.cancel() updateTimeListener.cancel()
@ -146,15 +147,13 @@ class TTTCorpse private constructor(
private const val REASON_SLOT = 1 private const val REASON_SLOT = 1
private const val TIME_SLOT = 2 private const val TIME_SLOT = 2
fun spawn(tttPlayer: TTTPlayer, reason: DeathReason) { fun spawn(tttPlayer: TTTPlayer, reason: DeathReason): TTTCorpse = TTTCorpse(
CorpseManager.add(TTTCorpse( tttPlayer,
tttPlayer, tttPlayer.player.location,
tttPlayer.player.location, tttPlayer.role,
tttPlayer.role, reason,
reason, tttPlayer.credits
tttPlayer.credits ).also { CorpseManager.add(it) }
))
}
fun spawnFake(role: Role, tttPlayer: TTTPlayer, location: Location) { fun spawnFake(role: Role, tttPlayer: TTTPlayer, location: Location) {
CorpseManager.add(TTTCorpse( CorpseManager.add(TTTCorpse(

View file

@ -39,7 +39,7 @@ class InversedStateContainer<T: IState>(private val stateClass: KClass<T>) {
val tttPlayers get() = PlayerManager.tttPlayers.filter { it.stateContainer.has(stateClass) } val tttPlayers get() = PlayerManager.tttPlayers.filter { it.stateContainer.has(stateClass) }
fun forEachState(fn: (T, TTTPlayer) -> Unit) { fun forEveryState(fn: (T, TTTPlayer) -> Unit) {
tttPlayers.forEach { it.stateContainer.get(stateClass)?.run { fn(this, it) } } tttPlayers.forEach { it.stateContainer.get(stateClass)?.run { fn(this, it) } }
} }
} }

View file

@ -84,15 +84,21 @@ class TTTPlayer(player: Player, role: Role) {
fun onDeath(reason: DeathReason = DeathReason.SUICIDE) { fun onDeath(reason: DeathReason = DeathReason.SUICIDE) {
GameManager.ensurePhase(GamePhase.COMBAT) GameManager.ensurePhase(GamePhase.COMBAT)
player.sendMessage(TTTPlugin.prefix + "${ChatColor.RED}${ChatColor.BOLD}Du bist gestorben")
player.gameMode = GameMode.SPECTATOR player.gameMode = GameMode.SPECTATOR
alive = false alive = false
TTTCorpse.spawn(this, reason) val tttCorpse = TTTCorpse.spawn(this, reason)
player.inventory.clear() player.inventory.clear()
credits = 0 credits = 0
// PlayerManager.letRemainingRoleGroupWin() val event = TTTPlayerDeathEvent(this, player.location, tttCorpse)
plugin.server.pluginManager.callEvent(TTTPlayerDeathEvent(this, player.location)) plugin.server.pluginManager.callEvent(event)
if (event.letRoundEnd) {
// PlayerManager.letRemainingRoleGroupWin()
}
} }
fun revive(location: Location, credits: Int = 0) { fun revive(location: Location, credits: Int = 0) {
@ -106,6 +112,7 @@ class TTTPlayer(player: Player, role: Role) {
Shop.setItems(this) Shop.setItems(this)
plugin.server.pluginManager.callEvent(TTTPlayerReviveEvent(this))
player.sendMessage(TTTPlugin.prefix + "${ChatColor.GREEN}${ChatColor.BOLD}Du wurdest wiederbelebt") player.sendMessage(TTTPlugin.prefix + "${ChatColor.GREEN}${ChatColor.BOLD}Du wurdest wiederbelebt")
plugin.server.scheduler.runTask(plugin, fun() { plugin.server.scheduler.runTask(plugin, fun() {

View file

@ -1,18 +1,22 @@
package de.moritzruth.spigot_ttt.game.players package de.moritzruth.spigot_ttt.game.players
import de.moritzruth.spigot_ttt.game.corpses.TTTCorpse
import org.bukkit.Location import org.bukkit.Location
import org.bukkit.event.Event import org.bukkit.event.Event
import org.bukkit.event.HandlerList import org.bukkit.event.HandlerList
class TTTPlayerDeathEvent( class TTTPlayerDeathEvent(
val tttPlayer: TTTPlayer, val tttPlayer: TTTPlayer,
val location: Location val location: Location,
val tttCorpse: TTTCorpse
): Event() { ): Event() {
override fun getHandlers(): HandlerList { override fun getHandlers(): HandlerList {
@Suppress("RedundantCompanionReference") // false positive @Suppress("RedundantCompanionReference") // false positive
return Companion.handlers return Companion.handlers
} }
var letRoundEnd = true
companion object { companion object {
private val handlers = HandlerList() private val handlers = HandlerList()

View file

@ -0,0 +1,18 @@
package de.moritzruth.spigot_ttt.game.players
import org.bukkit.event.Event
import org.bukkit.event.HandlerList
class TTTPlayerReviveEvent(val tttPlayer: TTTPlayer): Event() {
override fun getHandlers(): HandlerList {
@Suppress("RedundantCompanionReference") // false positive
return Companion.handlers
}
companion object {
private val handlers = HandlerList()
@JvmStatic
fun getHandlerList() = handlers
}
}

View file

@ -29,7 +29,7 @@ object ItemManager {
BaseballBat, BaseballBat,
CloakingDevice, Rifle, CloakingDevice, Rifle,
EnderPearl, Radar, HealingPotion, Fireball, EnderPearl, Radar, HealingPotion, Fireball,
Teleporter, MartyrdomGrenade, FakeCorpse, Defibrillator Teleporter, MartyrdomGrenade, FakeCorpse, Defibrillator, SecondChance
) )
val droppedItemStates = mutableMapOf<Int, IState>() val droppedItemStates = mutableMapOf<Int, IState>()

View file

@ -82,7 +82,7 @@ object Defibrillator: TTTItem, Buyable {
} }
@EventHandler @EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEachState { state, tttPlayer -> state.reset(tttPlayer) } fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, tttPlayer -> state.reset(tttPlayer) }
} }
sealed class Action(val tttPlayer: TTTPlayer) { sealed class Action(val tttPlayer: TTTPlayer) {

View file

@ -59,7 +59,7 @@ object MartyrdomGrenade: TTTItem, Buyable {
} }
@EventHandler @EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEachState { state, _ -> fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ ->
state.explodeTask?.cancel() state.explodeTask?.cancel()
state.explodeTask = null state.explodeTask = null
} }

View file

@ -107,7 +107,7 @@ object Radar: TTTItem, Buyable {
} }
@EventHandler @EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEachState { state, tttPlayer -> state.reset(tttPlayer) } fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, tttPlayer -> state.reset(tttPlayer) }
} }
override val packetListener = object : PacketAdapter(plugin, PacketType.Play.Server.ENTITY_METADATA) { override val packetListener = object : PacketAdapter(plugin, PacketType.Play.Server.ENTITY_METADATA) {

View file

@ -0,0 +1,155 @@
package de.moritzruth.spigot_ttt.items.impl
import de.moritzruth.spigot_ttt.ResourcePack
import de.moritzruth.spigot_ttt.TTTItemListener
import de.moritzruth.spigot_ttt.game.GameEndEvent
import de.moritzruth.spigot_ttt.game.GameManager
import de.moritzruth.spigot_ttt.game.players.*
import de.moritzruth.spigot_ttt.items.Buyable
import de.moritzruth.spigot_ttt.items.PASSIVE
import de.moritzruth.spigot_ttt.items.TTTItem
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.setAllToItem
import org.bukkit.ChatColor
import org.bukkit.Location
import org.bukkit.boss.BarColor
import org.bukkit.boss.BarStyle
import org.bukkit.event.EventHandler
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.inventory.InventoryType
import org.bukkit.inventory.ItemStack
import org.bukkit.scheduler.BukkitTask
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 = 20.0
override val type = TTTItem.Type.SPECIAL
override val itemStack = ItemStack(ResourcePack.Items.secondChance).applyMeta {
setDisplayName("$DISPLAY_NAME $PASSIVE")
hideInfo()
lore = listOf(
"",
"${ChatColor.GOLD}Du wirst mit einer 50%-Chance",
"${ChatColor.GOLD}wiederbelebt, wenn du stirbst"
)
}
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,
InventoryType.CHEST,
"${ChatColor.DARK_RED}${ChatColor.BOLD}Second Chance"
).apply {
setAllToItem(setOf(0, 1, 2, 9, 10, 11, 18, 19, 20), ItemStack(ON_CORPSE).applyMeta {
hideInfo()
setDisplayName("${ChatColor.GREEN}${ChatColor.BOLD}Bei der Leiche")
})
setAllToItem(setOf(3, 4, 5, 12, 13, 14, 21, 22, 23), ItemStack(ResourcePack.Items.textureless).applyMeta {
hideInfo()
setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Wo möchtest du spawnen?")
})
setAllToItem(setOf(6, 7, 8, 15, 16, 17, 24, 25, 26), ItemStack(ON_SPAWN).applyMeta {
hideInfo()
setDisplayName("${ChatColor.AQUA}${ChatColor.BOLD}Am Spawn")
})
}
override fun onBuy(tttPlayer: TTTPlayer) {
isc.getOrCreate(tttPlayer)
}
override val listener = object : TTTItemListener(this, true) {
@EventHandler
fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) {
val state = isc.get(event.tttPlayer)
if (state != null) {
if (Random.nextBoolean()) {
event.letRoundEnd = false
event.tttPlayer.player.openInventory(chooseSpawnInventory)
state.timeoutAction = TimeoutAction(event.tttPlayer, event.tttCorpse.corpse.trueLocation)
}
}
}
@EventHandler
fun onInventoryClose(event: InventoryCloseEvent) = handle(event) { tttPlayer ->
if (event.inventory == chooseSpawnInventory) {
if (isc.get(tttPlayer)?.timeoutAction != null) {
plugin.server.scheduler.runTask(plugin, fun() {
if (isc.get(tttPlayer) != null) tttPlayer.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
val location = when (event.currentItem?.type) {
ON_SPAWN -> GameManager.world.spawnLocation
ON_CORPSE -> timeoutAction.deathLocation
else -> return@handle
}
timeoutAction.stop()
tttPlayer.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) stop() else bossBar.progress = 1.0 - progress
}, 0, 1)
fun stop() {
isc.remove(tttPlayer)
task.cancel()
tttPlayer.player.closeInventory()
bossBar.removePlayer(tttPlayer.player)
}
}
class State: IState {
var timeoutAction: TimeoutAction? = null
}
}

View file

@ -201,7 +201,7 @@ abstract class Gun(
fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) = isc.get(event.tttPlayer)?.reset() fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) = isc.get(event.tttPlayer)?.reset()
@EventHandler @EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEachState { state, _ -> state.reset() } fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ -> state.reset() }
} }
class ActionInProgressError: RuntimeException("The gun has an ongoing action which may not be canceled") class ActionInProgressError: RuntimeException("The gun has an ongoing action which may not be canceled")

View file

@ -10,7 +10,7 @@ fun Inventory.setAllToItem(indexes: Iterable<Int>, itemStack: ItemStack) {
indexes.forEach { setItem(it, itemStack) } indexes.forEach { setItem(it, itemStack) }
} }
fun Inventory.removeTTTItem(tttItem: TTTItem) = clear(indexOfFirst { it.type == tttItem.itemStack.type }) fun Inventory.removeTTTItem(tttItem: TTTItem) = clear(indexOfFirst { it?.type == tttItem.itemStack.type })
fun Inventory.removeTTTItemNextTick(tttItem: TTTItem) = plugin.server.scheduler.runTask(plugin, fun() { fun Inventory.removeTTTItemNextTick(tttItem: TTTItem) = plugin.server.scheduler.runTask(plugin, fun() {
removeTTTItem(tttItem) removeTTTItem(tttItem)
}) })