1
0
Fork 0

Change the way items (and classes) work

This commit is contained in:
Moritz Ruth 2020-06-19 14:59:53 +02:00
parent d4b337e661
commit 87d0e6c43c
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
47 changed files with 1483 additions and 1516 deletions

View file

@ -1,190 +1,28 @@
package de.moritzruth.spigot_ttt.game package de.moritzruth.spigot_ttt.game
import com.comphenix.packetwrapper.WrapperPlayServerPlayerInfo import de.moritzruth.spigot_ttt.game.players.TTTPlayer
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.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.event.block.Action import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.inventory.InventoryCloseEvent
import org.bukkit.event.entity.EntityDamageEvent import org.bukkit.event.player.PlayerEvent
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.player.*
import java.util.*
object GameListener : Listener { abstract class GameListener: Listener {
private val BLOCKED_COMMANDS = setOf("me", "tell", "msg") protected fun handle(event: InventoryClickEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
val whoClicked = event.whoClicked
@EventHandler if (whoClicked is Player) {
fun onPlayerJoin(event: PlayerJoinEvent) = PlayerManager.onPlayerJoin(event.player) handler(TTTPlayer.of(whoClicked) ?: return)
@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 protected open fun handle(event: InventoryCloseEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) { val player = event.player
val player = event.damager
if (player is Player) { if (player is Player) {
if (player.inventory.itemInMainHand.type == Material.AIR) { handler(TTTPlayer.of(player) ?: return)
event.damage = 0.2
}
} }
} }
private val ZERO_NO_DAMAGE_TICKS_CAUSES = EnumSet.of( protected fun <T: PlayerEvent> handle(event: T, handler: (tttPlayer: TTTPlayer) -> Unit) {
EntityDamageEvent.DamageCause.ENTITY_ATTACK, handler(TTTPlayer.of(event.player) ?: return)
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()
}
}
} }
} }

View file

@ -31,13 +31,13 @@ object GameManager {
val destroyedBlocks = mutableMapOf<Location, Material>() val destroyedBlocks = mutableMapOf<Location, Material>()
private val listeners = ItemManager.listeners private val listeners = ItemManager.listeners
.plus(GameListener) .plus(GeneralGameListener)
.plus(ShopListener) .plus(ShopListener)
.plus(CorpseListener) .plus(CorpseListener)
.plus(TTTClassManager.listeners) .plus(TTTClassManager.listeners)
private val packetListeners = ItemManager.packetListeners private val packetListeners = ItemManager.packetListeners
.plus(GameListener.packetListener) .plus(GeneralGameListener.packetListener)
fun initialize() { fun initialize() {
adjustWorld() adjustWorld()

View file

@ -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()
}
}
}
}

View file

@ -1,23 +1,19 @@
package de.moritzruth.spigot_ttt.game.classes package de.moritzruth.spigot_ttt.game.classes
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.game.players.TTTPlayer import de.moritzruth.spigot_ttt.game.players.TTTPlayer
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.event.Listener
abstract class TTTClass( abstract class TTTClass {
val displayName: String, lateinit var tttPlayer: TTTPlayer
val chatColor: ChatColor,
val defaultItems: Set<TTTItem> = emptySet()
) {
val coloredDisplayName = "$chatColor$displayName"
open val listener: Listener? = null open fun init() {}
open fun reset() {}
open fun onInit(tttPlayer: TTTPlayer) {} object None: TTTClassCompanion(
object None: TTTClass(
"Keine", "Keine",
ChatColor.GRAY ChatColor.GRAY,
) Instance::class
) {
class Instance: TTTClass()
}
} }

View file

@ -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
}

View file

@ -1,21 +1,17 @@
package de.moritzruth.spigot_ttt.game.classes package de.moritzruth.spigot_ttt.game.classes
import de.moritzruth.spigot_ttt.game.classes.impl.Gambler import de.moritzruth.spigot_ttt.game.classes.impl.*
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 java.util.* import java.util.*
object TTTClassManager { object TTTClassManager {
private val TTT_CLASSES = setOf( private val TTT_CLASSES = setOf(
Warrior, Gambler, Stuntman, Ninja Warrior, Gambler, Stuntman, Ninja, Oracle
// Oracle is disabled because of the bug with the radar
) )
val listeners = TTT_CLASSES.mapNotNull { it.listener } val listeners = TTT_CLASSES.mapNotNull { it.listener }
fun createClassesIterator(count: Int): Iterator<TTTClass> { fun createClassesIterator(count: Int): Iterator<TTTClassCompanion> {
val set: MutableSet<TTTClass> = TTT_CLASSES.toMutableSet() val set: MutableSet<TTTClassCompanion> = TTT_CLASSES.toMutableSet()
val playersWithoutClass = count - TTT_CLASSES.size val playersWithoutClass = count - TTT_CLASSES.size
if (playersWithoutClass > 0) set.addAll(Collections.nCopies(playersWithoutClass, TTTClass.None)) if (playersWithoutClass > 0) set.addAll(Collections.nCopies(playersWithoutClass, TTTClass.None))

View file

@ -1,11 +1,15 @@
package de.moritzruth.spigot_ttt.game.classes.impl package de.moritzruth.spigot_ttt.game.classes.impl
import de.moritzruth.spigot_ttt.game.classes.TTTClass 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 de.moritzruth.spigot_ttt.game.items.impl.SecondChance
import org.bukkit.ChatColor import org.bukkit.ChatColor
object Gambler: TTTClass( class Gambler: TTTClass() {
"Gambler", companion object: TTTClassCompanion(
ChatColor.YELLOW, "Gambler",
setOf(SecondChance) ChatColor.YELLOW,
) Gambler::class,
setOf(SecondChance)
)
}

View file

@ -1,8 +1,11 @@
package de.moritzruth.spigot_ttt.game.classes.impl 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.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.plugin
import de.moritzruth.spigot_ttt.utils.nextTick import de.moritzruth.spigot_ttt.utils.nextTick
import org.bukkit.ChatColor import org.bukkit.ChatColor
@ -14,13 +17,11 @@ import org.bukkit.potion.PotionEffectType
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import org.bukkit.util.Vector import org.bukkit.util.Vector
object Ninja: TTTClass( class Ninja: TTTClass() {
"Ninja", var jumpsRemaining = 1
ChatColor.LIGHT_PURPLE var checkOnGroundTask: BukkitTask? = null
) {
private val isc = InversedStateContainer(State::class)
override fun onInit(tttPlayer: TTTPlayer) { override fun init() {
tttPlayer.player.allowFlight = true tttPlayer.player.allowFlight = true
tttPlayer.player.addPotionEffect(PotionEffect( tttPlayer.player.addPotionEffect(PotionEffect(
PotionEffectType.JUMP, PotionEffectType.JUMP,
@ -31,70 +32,58 @@ object Ninja: TTTClass(
)) ))
} }
override val listener = object : Listener { override fun reset() {
@EventHandler(ignoreCancelled = true) checkOnGroundTask?.cancel()
fun onPlayerToggleFlight(event: PlayerToggleFlightEvent) { checkOnGroundTask = null
val tttPlayer = TTTPlayer.of(event.player) ?: return
if (event.isFlying && tttPlayer.tttClass == Ninja) {
val state = isc.getOrCreate(tttPlayer)
val current = tttPlayer.player.velocity
tttPlayer.player.velocity = Vector(current.x * 3, 0.8, current.z * 3)
state.jumpsRemaining -= 1
if (state.jumpsRemaining == 0) {
tttPlayer.player.allowFlight = false
state.checkOnGroundTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
if (tttPlayer.player.isOnGround) {
state.jumpsRemaining = 1
tttPlayer.player.allowFlight = true
state.reset()
}
}, 1, 1)
}
event.isCancelled = true
}
}
@EventHandler(ignoreCancelled = true)
fun onTTTPlayerDamage(event: TTTPlayerDamageEvent) {
if (event.tttPlayer.tttClass == Ninja) {
if (event.deathReason == DeathReason.FALL) event.damage = 0.0
}
}
@EventHandler
fun onTTTPlayerDeath(event: TTTPlayerDeathEvent) {
isc.get(event.tttPlayer)?.reset()
isc.remove(event.tttPlayer)
}
@EventHandler
fun onTTTPlayerReviveEvent(event: TTTPlayerReviveEvent) {
if (event.tttPlayer.tttClass == Ninja) {
// This must be delayed for 2 ticks, idk why
nextTick { nextTick { event.tttPlayer.player.allowFlight = true } }
}
}
@EventHandler
fun onGameEnd(event: GameEndEvent) {
isc.forEveryState { state, _ ->
state.reset()
}
}
} }
class State: IState { companion object: TTTClassCompanion(
var jumpsRemaining = 1 "Ninja",
var checkOnGroundTask: BukkitTask? = null 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
fun reset() { if (event.isFlying) {
checkOnGroundTask?.cancel() val vel = tttPlayer.player.velocity
checkOnGroundTask = null tttPlayer.player.velocity = Vector(vel.x * 3, 0.8, vel.z * 3)
instance.jumpsRemaining -= 1
if (instance.jumpsRemaining == 0) {
tttPlayer.player.allowFlight = false
instance.checkOnGroundTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
if (tttPlayer.player.isOnGround) {
instance.jumpsRemaining = 1
tttPlayer.player.allowFlight = true
instance.reset()
}
}, 1, 1)
}
event.isCancelled = true
}
}
@EventHandler(ignoreCancelled = true)
fun onTTTPlayerDamage(event: TTTPlayerDamageEvent) {
if (event.tttPlayer.tttClass == Ninja) {
if (event.deathReason == DeathReason.FALL) event.damage = 0.0
}
}
@EventHandler
fun onTTTPlayerReviveEvent(event: TTTPlayerReviveEvent) {
if (event.tttPlayer.tttClass == Ninja) {
// This must be delayed for 2 ticks, idk why
nextTick { nextTick { event.tttPlayer.player.allowFlight = true } }
}
}
} }
} }
} }

View file

@ -1,11 +1,15 @@
package de.moritzruth.spigot_ttt.game.classes.impl package de.moritzruth.spigot_ttt.game.classes.impl
import de.moritzruth.spigot_ttt.game.classes.TTTClass 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 de.moritzruth.spigot_ttt.game.items.impl.Radar
import org.bukkit.ChatColor import org.bukkit.ChatColor
object Oracle: TTTClass( class Oracle: TTTClass() {
"Oracle", companion object: TTTClassCompanion(
ChatColor.DARK_AQUA, "Oracle",
setOf(Radar) ChatColor.DARK_AQUA,
) Oracle::class,
setOf(Radar)
)
}

View file

@ -1,23 +1,27 @@
package de.moritzruth.spigot_ttt.game.classes.impl package de.moritzruth.spigot_ttt.game.classes.impl
import de.moritzruth.spigot_ttt.game.classes.TTTClass 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.DeathReason
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDamageEvent import de.moritzruth.spigot_ttt.game.players.TTTPlayerDamageEvent
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
object Stuntman: TTTClass( class Stuntman: TTTClass() {
"Stuntman", companion object: TTTClassCompanion(
ChatColor.DARK_RED "Stuntman",
) { ChatColor.DARK_RED,
val IMMUNE_DEATH_REASONS = setOf(DeathReason.FALL, DeathReason.EXPLOSION) Stuntman::class
) {
val IMMUNE_DEATH_REASONS = setOf(DeathReason.FALL, DeathReason.EXPLOSION)
override val listener = object : Listener { override val listener = object : Listener {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
fun onEntityDamage(event: TTTPlayerDamageEvent) { fun onEntityDamage(event: TTTPlayerDamageEvent) {
if (event.tttPlayer.tttClass == Stuntman) { if (event.tttPlayer.tttClass == Stuntman) {
if (IMMUNE_DEATH_REASONS.contains(event.deathReason)) event.damage = 0.0 if (IMMUNE_DEATH_REASONS.contains(event.deathReason)) event.damage = 0.0
}
} }
} }
} }

View file

@ -1,25 +1,34 @@
package de.moritzruth.spigot_ttt.game.classes.impl package de.moritzruth.spigot_ttt.game.classes.impl
import de.moritzruth.spigot_ttt.game.classes.TTTClass 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 de.moritzruth.spigot_ttt.game.players.TTTPlayerDamageEvent
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener import org.bukkit.event.Listener
object Warrior: TTTClass( class Warrior: TTTClass() {
"Warrior", override fun init() {
ChatColor.BLUE tttPlayer.walkSpeed -= WALK_SPEED_DECREASE
) {
override fun onInit(tttPlayer: TTTPlayer) {
tttPlayer.walkSpeed -= 0.05F
} }
override val listener = object : Listener { override fun reset() {
@EventHandler(ignoreCancelled = true) tttPlayer.walkSpeed += WALK_SPEED_DECREASE
fun onEntityDamage(event: TTTPlayerDamageEvent) { }
if (event.tttPlayer.tttClass == Warrior) {
event.damage *= 0.9 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.8
}
} }
} }
} }

View file

@ -26,7 +26,7 @@ object CorpseListener: Listener {
} }
} }
@EventHandler @EventHandler(priority = EventPriority.LOW)
fun onPlayerInteractEntity(event: PlayerInteractEntityEvent) { fun onPlayerInteractEntity(event: PlayerInteractEntityEvent) {
val tttPlayer = TTTPlayer.of(event.player) ?: return val tttPlayer = TTTPlayer.of(event.player) ?: return
val tttCorpse = CorpseManager.getTTTCorpse(event.rightClicked) ?: return val tttCorpse = CorpseManager.getTTTCorpse(event.rightClicked) ?: return

View file

@ -18,7 +18,6 @@ import org.bukkit.entity.Zombie
import org.bukkit.event.inventory.InventoryType import org.bukkit.event.inventory.InventoryType
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import org.bukkit.util.Vector
import java.time.Instant import java.time.Instant
class TTTCorpse private constructor( class TTTCorpse private constructor(
@ -26,10 +25,9 @@ class TTTCorpse private constructor(
location: Location, location: Location,
private val role: Role, private val role: Role,
private val reason: DeathReason, private val reason: DeathReason,
private var credits: Int, private var credits: Int
velocity: Vector = Vector()
) { ) {
var status = Status.UNIDENTIFIED; private set private var status = Status.UNIDENTIFIED; private set
val entity: Zombie val entity: Zombie
val inventory = tttPlayer.player.server.createInventory(null, InventoryType.HOPPER, "${role.chatColor}${tttPlayer.player.displayName}") 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() { private fun setReasonItem() {
if (status == Status.INSPECTED) { 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 { inventory.setItem(REASON_SLOT, reasonItemStack.applyMeta {
setDisplayName("${ChatColor.RESET}" + reason.displayText) setDisplayName("${ChatColor.RESET}" + reason.displayText)
lore = listOf("${ChatColor.GRAY}Grund des Todes") lore = listOf("${ChatColor.GRAY}Grund des Todes")
@ -163,8 +161,7 @@ class TTTCorpse private constructor(
tttPlayer.player.location, tttPlayer.player.location,
tttPlayer.role, tttPlayer.role,
reason, reason,
tttPlayer.credits, tttPlayer.credits
tttPlayer.player.velocity
).also { CorpseManager.add(it) } ).also { CorpseManager.add(it) }
fun spawnFake(role: Role, tttPlayer: TTTPlayer, location: Location) { fun spawnFake(role: Role, tttPlayer: TTTPlayer, location: Location) {

View file

@ -0,0 +1,5 @@
package de.moritzruth.spigot_ttt.game.items
class ClickEvent {
var isCancelled = true
}

View file

@ -1,117 +1,146 @@
package de.moritzruth.spigot_ttt.game.items 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.GameManager
import de.moritzruth.spigot_ttt.game.items.impl.* 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.BaseballBat
import de.moritzruth.spigot_ttt.game.items.impl.weapons.Knife 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.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.game.players.TTTPlayer
import de.moritzruth.spigot_ttt.plugin import de.moritzruth.spigot_ttt.game.players.TTTPlayerDeathEvent
import de.moritzruth.spigot_ttt.utils.nextTick import de.moritzruth.spigot_ttt.utils.isLeftClick
import de.moritzruth.spigot_ttt.utils.isRightClick
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
import org.bukkit.ChatColor import org.bukkit.Location
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.entity.Item import org.bukkit.entity.Item
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.EventHandler 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.EntityPickupItemEvent
import org.bukkit.event.entity.ItemDespawnEvent import org.bukkit.event.entity.ItemDespawnEvent
import org.bukkit.event.player.PlayerDropItemEvent import org.bukkit.event.player.PlayerDropItemEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerItemHeldEvent import org.bukkit.event.player.PlayerItemHeldEvent
import org.bukkit.event.player.PlayerSwapHandItemsEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
object ItemManager { object ItemManager {
val ITEMS: Set<TTTItem> = setOf( val ITEMS: Set<TTTItem<*>> = setOf(
Pistol, Deagle, Glock, Pistol, Rifle, SidekickDeagle, BaseballBat, Knife, CloakingDevice, Defibrillator,
Knife, Glock, Deagle, Shotgun, SidekickDeagle, EnderPearl, FakeCorpse, Fireball, HealingPotion, MartyrdomGrenade, Radar, SecondChance, Teleporter,
BaseballBat, Shotgun, Radar, SecondChance
CloakingDevice, Rifle,
EnderPearl, Radar, HealingPotion, Fireball,
Teleporter, MartyrdomGrenade, FakeCorpse, Defibrillator, SecondChance
) )
val droppedItemStates = mutableMapOf<Int, IState>()
val listeners get () = ITEMS.mapNotNull { it.listener }.plus(listener) val listeners get () = ITEMS.mapNotNull { it.listener }.plus(listener)
val packetListeners get () = ITEMS.mapNotNull { it.packetListener } val packetListeners get () = ITEMS.mapNotNull { it.packetListener }
private fun getItemByMaterial(material: Material) = ITEMS.find { tttItem -> material === tttItem.itemStack.type } private fun getTTTItemByMaterial(material: Material) = ITEMS.find { tttItem -> material == tttItem.material }
fun getItemByItemStack(itemStack: ItemStack) = getItemByMaterial(itemStack.type) fun getTTTItemByItemStack(itemStack: ItemStack) = getTTTItemByMaterial(itemStack.type)
fun getInstanceByItemStack(itemStack: ItemStack) = getTTTItemByItemStack(itemStack)?.getInstance(itemStack)
fun reset() { fun dropItem(location: Location, tttItem: TTTItem<*>) {
droppedItemStates.clear() val instance = tttItem.createInstance()
GameManager.world.getEntitiesByClass(Item::class.java).forEach { GameManager.world.dropItem(location, instance.createItemStack())
it.remove()
}
} }
val listener = object : Listener { fun reset() {
@EventHandler GameManager.world.getEntitiesByClass(Item::class.java).forEach(Item::remove)
fun onPlayerItemHeld(event: PlayerItemHeldEvent) { ITEMS.forEach(TTTItem<*>::reset)
val tttPlayer = TTTPlayer.of(event.player) ?: return }
val itemStack = event.player.inventory.getItem(event.newSlot)
tttPlayer.itemInHand = val listener = object : GameListener() {
if (itemStack == null || itemStack.type === Material.AIR) null @EventHandler
else getItemByItemStack(itemStack) fun onPlayerInteract(event: PlayerInteractEvent) = handle(event) {
val instance = event.item?.let { getInstanceByItemStack(it) } ?: return@handle
val clickEvent = ClickEvent()
if (event.action.isLeftClick) instance.onLeftClick(clickEvent)
else if (event.action.isRightClick) instance.onRightClick(clickEvent)
event.isCancelled = clickEvent.isCancelled
} }
@EventHandler @EventHandler(ignoreCancelled = true)
fun onPlayerDropItem(event: PlayerDropItemEvent) { fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) {
val tttPlayer = TTTPlayer.of(event.player) ?: return val damager = event.damager
val tttItem = getItemByItemStack(event.itemDrop.itemStack) ?: return if (damager is Player) {
TTTPlayer.of(damager) ?: return
if (tttItem.type != TTTItem.Type.SPECIAL) { val item = damager.inventory.itemInMainHand
if (tttItem is DropHandler) { if (item.type != Material.AIR) {
if (!tttItem.onDrop(tttPlayer, event.itemDrop)) { val tttItem = getTTTItemByItemStack(item) ?: return
event.isCancelled = true event.isCancelled = tttItem.disableDamage
return
}
} }
plugin.server.scheduler.runTask(plugin, fun() {
tttPlayer.updateItemInHand()
})
} else {
event.player.sendActionBarMessage("${ChatColor.RED}Du kannst dieses Item nicht droppen")
event.isCancelled = true
} }
} }
@EventHandler @EventHandler
fun onItemDespawn(event: ItemDespawnEvent) { fun onPlayerSwapHandItems(event: PlayerSwapHandItemsEvent) = handle(event) { _ ->
if (getItemByItemStack(event.entity.itemStack) != null) { val instance = event.offHandItem?.let { getInstanceByItemStack(it) } ?: return@handle
event.entity.ticksLived = 1 instance.onHandSwap()
event.isCancelled = true
}
@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 {
tttPlayer.player.sendActionBarMessage(notDroppableReason)
event.isCancelled = true event.isCancelled = true
} }
} }
@EventHandler @EventHandler
fun onEntityPickupItem(event: EntityPickupItemEvent) { fun onEntityPickupItem(event: EntityPickupItemEvent) {
if (event.entity !is Player) { val player = event.entity
return if (player !is Player) return
}
val player = event.entity as Player
val tttPlayer = TTTPlayer.of(player) ?: return val tttPlayer = TTTPlayer.of(player) ?: return
val instance = getInstanceByItemStack(event.item.itemStack)
val tttItem = getItemByItemStack(event.item.itemStack) if (instance != null) {
if (runCatching { tttPlayer.checkAddItemPreconditions(instance.tttItem) }.isSuccess) {
if (tttItem != null) { instance.carrier = tttPlayer
if (runCatching { tttPlayer.checkAddItemPreconditions(tttItem) }.isSuccess) {
nextTick { tttPlayer.updateItemInHand() }
if (tttItem is DropHandler) {
tttItem.onPickup(tttPlayer, event.item)
}
return return
} }
} }
event.isCancelled = true 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)
}
}
}
} }
} }

View file

@ -2,14 +2,15 @@ package de.moritzruth.spigot_ttt.game.items
import de.moritzruth.spigot_ttt.game.GameManager import de.moritzruth.spigot_ttt.game.GameManager
import de.moritzruth.spigot_ttt.utils.ConfigurationFile import de.moritzruth.spigot_ttt.utils.ConfigurationFile
import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.utils.roundToCenter import de.moritzruth.spigot_ttt.utils.roundToCenter
import org.bukkit.Location import org.bukkit.Location
import java.util.*
object ItemSpawner { object ItemSpawner {
private const val CONFIG_PATH = "spawn-locations" private const val CONFIG_PATH = "spawn-locations"
private val spawnLocationsConfig = ConfigurationFile("spawnLocations") private val spawnLocationsConfig = ConfigurationFile("spawnLocations")
private val spawningItems = ItemManager.ITEMS.filter { it is Spawning }
private fun getSpawnLocations(): Set<Location> { private fun getSpawnLocations(): Set<Location> {
return spawnLocationsConfig.getStringList(CONFIG_PATH).map { return spawnLocationsConfig.getStringList(CONFIG_PATH).map {
@ -25,14 +26,19 @@ object ItemSpawner {
} }
fun spawnWeapons() { fun spawnWeapons() {
var 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))
}
var itemsIterator = spawningItems.shuffled().iterator()
for (location in getSpawnLocations()) { for (location in getSpawnLocations()) {
if (!itemIterator.hasNext()) { if (!itemsIterator.hasNext()) {
itemIterator = spawningItems.shuffled().iterator() itemsIterator = spawningItems.shuffled().iterator()
} }
GameManager.world.dropItem(location, itemIterator.next().itemStack.clone()) ItemManager.dropItem(location, itemsIterator.next())
} }
} }

View file

@ -3,46 +3,142 @@ package de.moritzruth.spigot_ttt.game.items
import com.comphenix.protocol.events.PacketListener import com.comphenix.protocol.events.PacketListener
import de.moritzruth.spigot_ttt.game.players.Role import de.moritzruth.spigot_ttt.game.players.Role
import de.moritzruth.spigot_ttt.game.players.TTTPlayer 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.ChatColor
import org.bukkit.entity.Item import org.bukkit.NamespacedKey
import org.bukkit.event.Listener import org.bukkit.event.Listener
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
import java.util.* import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
interface Selectable { open class TTTItem<InstanceT: TTTItem.Instance>(
fun onSelect(tttPlayer: TTTPlayer) val type: Type,
fun onDeselect(tttPlayer: TTTPlayer) 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 { val material = templateItemStack.type
fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item): Boolean
fun onPickup(tttPlayer: TTTPlayer, itemEntity: Item)
}
interface Buyable { val instancesByUUID = mutableMapOf<UUID, InstanceT>()
val buyableBy: EnumSet<Role> fun getInstance(itemStack: ItemStack) =
val price: Int itemStack.itemMeta?.persistentDataContainer?.get(ID_KEY, PersistentDataType.STRING)
val buyLimit: Int? ?.let { instancesByUUID[UUID.fromString(it)] }
}
val PASSIVE = "${ChatColor.RESET}${ChatColor.RED}(Passiv)" fun getInstance(tttPlayer: TTTPlayer) = instancesByUUID.values.find { it.carrier === tttPlayer }
// Marker fun reset() {
interface Spawning instancesByUUID.values.forEach {
it.carrier?.removeItem(it.tttItem, removeInstance = false)
it.reset()
}
instancesByUUID.clear()
}
interface TTTItem { fun createInstance(): InstanceT = instanceType.primaryConstructor!!.call()
val listener: Listener? get() = null .also { instancesByUUID[it.uuid] = it }
val packetListener: PacketListener? get() = null
val itemStack: ItemStack
val type: Type
fun onOwn(tttPlayer: TTTPlayer) {}
fun onRemove(tttPlayer: TTTPlayer) {}
enum class Type(val maxItemsOfTypeInInventory: Int?) { enum class Type(val maxItemsOfTypeInInventory: Int?) {
MELEE(1), MELEE(1),
PISTOL_LIKE(1), PISTOL_LIKE(2),
HEAVY_WEAPON(1), HEAVY_WEAPON(1),
SPECIAL(null); 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() {}
}
} }

View file

@ -1,66 +1,16 @@
package de.moritzruth.spigot_ttt.game.items 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.TTTPlayer
import de.moritzruth.spigot_ttt.game.players.TTTPlayerDeathEvent import de.moritzruth.spigot_ttt.game.players.TTTPlayerReviveEvent
import de.moritzruth.spigot_ttt.utils.isLeftClick
import de.moritzruth.spigot_ttt.utils.isRightClick
import org.bukkit.entity.Player 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.entity.EntityDamageByEntityEvent
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent 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.PlayerItemConsumeEvent 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( protected fun handle(
event: EntityDamageByEntityEvent, event: EntityDamageByEntityEvent,
handler: (damagerTTTPlayer: TTTPlayer, damagedTTTPlayer: TTTPlayer) -> Unit handler: (damagerTTTPlayer: TTTPlayer, damagedTTTPlayer: TTTPlayer) -> Unit
@ -71,7 +21,7 @@ open class TTTItemListener(
if ( if (
damager is Player && damager is Player &&
damaged 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 damagerTTTPlayer = TTTPlayer.of(damager) ?: return
val damagedTTTPlayer = TTTPlayer.of(damaged) ?: 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: InventoryCloseEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
val player = event.player
if (player is Player) {
handler(TTTPlayer.of(player) ?: return)
}
}
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) { protected fun handle(event: PlayerItemConsumeEvent, handler: (tttPlayer: TTTPlayer) -> Unit) {
if (event.item.type == tttItem.itemStack.type) { if (event.item.type == tttItem.material) {
handler(TTTPlayer.of(event.player) ?: return) handler(TTTPlayer.of(event.player) ?: return)
} }
} }
data class ClickEventData( protected fun handleWithInstance(event: InventoryCloseEvent, handler: (instance: InstanceT) -> Unit) {
val tttPlayer: TTTPlayer, val player = event.player
val itemStack: ItemStack, if (player is Player) {
val event: PlayerInteractEvent val tttPlayer = TTTPlayer.of(player) ?: return
) val instance = tttItem.getInstance(tttPlayer) ?: return
handler(instance)
}
}
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)
}
}
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)
}
} }

View file

@ -1,14 +1,13 @@
package de.moritzruth.spigot_ttt.game.items.impl package de.moritzruth.spigot_ttt.game.items.impl
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.GameEndEvent import de.moritzruth.spigot_ttt.game.items.ClickEvent
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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener 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.applyMeta
import de.moritzruth.spigot_ttt.utils.startItemDamageProgress
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.SoundCategory import org.bukkit.SoundCategory
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
@ -19,90 +18,67 @@ import org.bukkit.potion.PotionEffect
import org.bukkit.potion.PotionEffectType import org.bukkit.potion.PotionEffectType
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
object CloakingDevice: TTTItem, Buyable, Selectable { object CloakingDevice: TTTItem<CloakingDevice.Instance>(
override val itemStack = ItemStack(Resourcepack.Items.cloakingDevice).applyMeta { 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}###") setDisplayName("${ChatColor.GRAY}${ChatColor.MAGIC}###${ChatColor.RESET}${ChatColor.GRAY} Cloaking Device ${ChatColor.MAGIC}###")
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Macht dich unsichtbar" "${ChatColor.GOLD}Macht dich unsichtbar"
) )
addItemFlags(ItemFlag.HIDE_ATTRIBUTES) 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 private const val COOLDOWN = 10.0
override val type = TTTItem.Type.SPECIAL class Instance: TTTItem.Instance(CloakingDevice) {
override val price = 2 var enabled = false
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL) private var cooldownTask: BukkitTask? = null
override val buyLimit = 1
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)
private fun enable(tttPlayer: TTTPlayer, itemStack: ItemStack) {
val state = isc.getOrCreate(tttPlayer)
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 override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
state.itemStack = itemStack setEnabled(oldCarrier, false)
}
private fun disable(tttPlayer: TTTPlayer) {
val state = isc.get(tttPlayer) ?: return
if (!state.enabled) return
tttPlayer.player.apply {
walkSpeed = 0.2F
removePotionEffect(PotionEffectType.INVISIBILITY)
playSound(location, Resourcepack.Sounds.Item.CloakingDevice.off, SoundCategory.PLAYERS, 1F, 1F)
} }
state.enabled = false private fun setEnabled(tttPlayer: TTTPlayer, value: Boolean) {
if (value == enabled) return
val itemStack = state.itemStack if (value) {
if (itemStack != null) { tttPlayer.walkSpeed -= WALK_SPEED_DECREASE
state.cooldownTask = startItemDamageProgress(itemStack, COOLDOWN) { state.cooldownTask = null } tttPlayer.player.apply {
} isSprinting = false
}
override val listener = object : TTTItemListener(this, true) { addPotionEffect(PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0, false, false))
@EventHandler playSound(location, Resourcepack.Sounds.Item.CloakingDevice.on, SoundCategory.PLAYERS, 1F, 1F)
fun onPlayerToggleSprint(event: PlayerToggleSprintEvent) = handle(event) { tttPlayer -> }
if (event.isSprinting && isc.getOrCreate(tttPlayer).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 { } else {
enable(data.tttPlayer, data.itemStack) tttPlayer.walkSpeed += WALK_SPEED_DECREASE
tttPlayer.player.apply {
removePotionEffect(PotionEffectType.INVISIBILITY)
playSound(location, Resourcepack.Sounds.Item.CloakingDevice.off, SoundCategory.PLAYERS, 1F, 1F)
}
// TODO: Show progress in level bar
} }
enabled = value
} }
} }
class State: IState { override val listener = object : TTTItemListener<Instance>(CloakingDevice) {
var enabled: Boolean = false @EventHandler
var cooldownTask: BukkitTask? = null fun onPlayerToggleSprint(event: PlayerToggleSprintEvent) = handleWithInstance(event) { instance ->
var itemStack: ItemStack? = null if (event.isSprinting && instance.enabled) event.isCancelled = true
}
} }
} }

View file

@ -1,13 +1,13 @@
package de.moritzruth.spigot_ttt.game.items.impl package de.moritzruth.spigot_ttt.game.items.impl
import de.moritzruth.spigot_ttt.Resourcepack 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.GameManager
import de.moritzruth.spigot_ttt.game.corpses.CorpseClickEvent 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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener 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.plugin
import de.moritzruth.spigot_ttt.utils.* import de.moritzruth.spigot_ttt.utils.*
import org.bukkit.ChatColor import org.bukkit.ChatColor
@ -20,49 +20,67 @@ import org.bukkit.scheduler.BukkitTask
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
object Defibrillator: TTTItem, Buyable { object Defibrillator: TTTItem<Defibrillator.Instance>(
private const val REVIVE_DURATION = 10.0 type = Type.SPECIAL,
instanceType = Instance::class,
override val type = TTTItem.Type.SPECIAL templateItemStack = ItemStack(Resourcepack.Items.defibrillator).applyMeta {
override val itemStack = ItemStack(Resourcepack.Items.defibrillator).applyMeta {
setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Defibrillator") setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Defibrillator")
hideInfo() hideInfo()
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Belebe einen Spieler wieder" "${ChatColor.GOLD}Belebe einen Spieler wieder"
) )
} },
override val buyableBy = roles(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL) shopInfo = ShopInfo(
override val price = 2 buyLimit = 1,
override val 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 { fun stopSound() = plugin.server.onlinePlayers.forEach {
it.stopSound(Resourcepack.Sounds.Item.Defibrillator.use, SoundCategory.PLAYERS) 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) @EventHandler(ignoreCancelled = true)
fun onCorpseClick(event: CorpseClickEvent) { 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 event.isCancelled = true
val state = isc.getOrCreate(event.tttPlayer) when(val action = instance.action) {
state.bossBar.addPlayer(event.tttPlayer.player) null -> instance.action = Action.Reviving(event.tttPlayer, instance)
when(val action = state.action) {
null -> state.action = Action.Reviving(event.tttPlayer, state)
is Action.Reviving -> { is Action.Reviving -> {
action.cancelTask.cancel() action.cancelTask.cancel()
action.cancelTask = action.createCancelTask() action.cancelTask = action.createCancelTask()
val progress = action.duration / REVIVE_DURATION val progress = action.duration / REVIVE_DURATION
if (progress >= 1) { if (progress >= 1) {
try { try {
event.tttCorpse.revive() event.tttCorpse.revive()
event.tttPlayer.player.sendActionBarMessage( event.tttPlayer.player.sendActionBarMessage(
"${ChatColor.BOLD}${event.tttCorpse.tttPlayer.player.displayName} " + "${ChatColor.BOLD}${event.tttCorpse.tttPlayer.player.displayName} " +
"${ChatColor.GREEN}wurde wiederbelebt" "${ChatColor.GREEN}wurde wiederbelebt"
@ -70,25 +88,20 @@ object Defibrillator: TTTItem, Buyable {
action.cancelTask.cancel() action.cancelTask.cancel()
event.tttPlayer.player.inventory.removeTTTItemNextTick(Defibrillator) event.tttPlayer.player.inventory.removeTTTItemNextTick(Defibrillator)
state.reset(event.tttPlayer)
isc.remove(event.tttPlayer)
} catch(e: TTTPlayer.AlreadyLivingException) { } catch(e: TTTPlayer.AlreadyLivingException) {
action.cancel() action.cancel()
} }
} else state.bossBar.progress = progress } else instance.bossBar.progress = progress
} }
is Action.Canceled -> noop() is Action.Canceled -> noop()
} }
} }
@EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, tttPlayer -> state.reset(tttPlayer) }
} }
sealed class Action(val tttPlayer: TTTPlayer) { sealed class Action(val tttPlayer: TTTPlayer) {
open fun reset() {} open fun reset() {}
class Reviving(tttPlayer: TTTPlayer, val state: State): Action(tttPlayer) { class Reviving(tttPlayer: TTTPlayer, val instance: Instance): Action(tttPlayer) {
var cancelTask = createCancelTask() var cancelTask = createCancelTask()
private val startedAt: Instant = Instant.now() private val startedAt: Instant = Instant.now()
val duration get() = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000 val duration get() = Duration.between(startedAt, Instant.now()).toMillis().toDouble() / 1000
@ -99,7 +112,7 @@ object Defibrillator: TTTItem, Buyable {
fun cancel() { fun cancel() {
cancelTask.cancel() cancelTask.cancel()
state.action = Canceled(tttPlayer) instance.action = Canceled(tttPlayer, instance)
} }
init { init {
@ -111,14 +124,14 @@ object Defibrillator: TTTItem, Buyable {
1F 1F
) )
state.bossBar.color = BarColor.GREEN instance.bossBar.color = BarColor.GREEN
state.bossBar.addPlayer(tttPlayer.player) instance.bossBar.isVisible = true
} }
override fun reset() = cancelTask.cancel() 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 var switches: Int = 0
private lateinit var task: BukkitTask private lateinit var task: BukkitTask
@ -132,16 +145,15 @@ object Defibrillator: TTTItem, Buyable {
1F 1F
) )
task = plugin.server.scheduler.runTaskTimer(plugin, fun() { plugin.server.scheduler.runTaskTimer(plugin, { task ->
val state = isc.get(tttPlayer) ?: return@runTaskTimer
if (switches == SWITCHES_COUNT) { if (switches == SWITCHES_COUNT) {
this@Canceled.task = task
task.cancel() task.cancel()
state.action = null instance.action = null
state.bossBar.removePlayer(tttPlayer.player) instance.bossBar.isVisible = false
} else { } else {
state.bossBar.progress = 1.0 instance.bossBar.progress = 1.0
state.bossBar.color = if (switches % 2 == 0) BarColor.RED else BarColor.WHITE instance.bossBar.color = if (switches % 2 == 0) BarColor.RED else BarColor.WHITE
switches += 1 switches += 1
} }
}, 0, secondsToTicks(0.2).toLong()) }, 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()
}
}
} }

View file

@ -1,27 +1,25 @@
package de.moritzruth.spigot_ttt.game.items.impl 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.Role
import de.moritzruth.spigot_ttt.game.players.roles import de.moritzruth.spigot_ttt.game.players.roles
import de.moritzruth.spigot_ttt.game.items.Buyable import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.applyMeta
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
object EnderPearl: TTTItem, Buyable { object EnderPearl : TTTItem<EnderPearl.Instance>(
override val type = TTTItem.Type.SPECIAL instanceType = Instance::class,
override val itemStack = ItemStack(Material.ENDER_PEARL).applyMeta { type = Type.SPECIAL,
templateItemStack = ItemStack(Material.ENDER_PEARL).applyMeta {
setDisplayName("${ChatColor.DARK_GREEN}Enderperle") setDisplayName("${ChatColor.DARK_GREEN}Enderperle")
} },
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE) spawnProbability = Probability.VERY_LOW,
override val price = 1 shopInfo = ShopInfo(
override val buyLimit: Int? = null buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE),
price = 1
override val listener = object : TTTItemListener(this, true) { )
override fun onRightClick(data: ClickEventData) { ) {
data.event.isCancelled = false class Instance: TTTItem.Instance(EnderPearl)
}
}
} }

View file

@ -2,10 +2,13 @@ package de.moritzruth.spigot_ttt.game.items.impl
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.corpses.TTTCorpse 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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener 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.plugin
import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.hideInfo import de.moritzruth.spigot_ttt.utils.hideInfo
@ -21,24 +24,34 @@ import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.SkullMeta import org.bukkit.inventory.meta.SkullMeta
object FakeCorpse: TTTItem, Buyable { object FakeCorpse: TTTItem<FakeCorpse.Instance>(
private val DISPLAY_NAME = "${ChatColor.YELLOW}${ChatColor.BOLD}Fake-Leiche" type = Type.SPECIAL,
instanceType = Instance::class,
override val itemStack = ItemStack(Resourcepack.Items.fakeCorpse).applyMeta { shopInfo = ShopInfo(
setDisplayName(DISPLAY_NAME) buyableBy = roles(Role.TRAITOR, Role.JACKAL),
buyLimit = 3,
price = 2
),
templateItemStack = ItemStack(Resourcepack.Items.fakeCorpse).applyMeta {
setDisplayName(FakeCorpse.DISPLAY_NAME)
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Spawnt eine Fake-Leiche", "${ChatColor.GOLD}Spawnt eine Fake-Leiche",
"${ChatColor.GOLD}Rolle und Spieler auswählbar" "${ChatColor.GOLD}Rolle und Spieler auswählbar"
) )
hideInfo() hideInfo()
} }
override val type = TTTItem.Type.SPECIAL ) {
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL) private val DISPLAY_NAME = "${ChatColor.YELLOW}${ChatColor.BOLD}Fake-Leiche"
override val price = 2
override val buyLimit: Int? = 3
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( private val chooseRoleInventory = plugin.server.createInventory(
null, null,
@ -72,17 +85,17 @@ object FakeCorpse: TTTItem, Buyable {
.toTypedArray()) .toTypedArray())
} }
override val listener = object : TTTItemListener(this, true) { override val listener = object : TTTItemListener<Instance>(this) {
override fun onRightClick(data: ClickEventData) {
isc.getOrCreate(data.tttPlayer).chosenRole = null
data.tttPlayer.player.openInventory(chooseRoleInventory)
}
@EventHandler @EventHandler
fun onInventoryClick(event: InventoryClickEvent) = handle(event) { tttPlayer -> 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 event.isCancelled = true
val item = event.currentItem val item = event.currentItem
@ -90,11 +103,12 @@ object FakeCorpse: TTTItem, Buyable {
if (item != null && event.click == ClickType.LEFT) { if (item != null && event.click == ClickType.LEFT) {
when (event.clickedInventory) { when (event.clickedInventory) {
chooseRoleInventory -> { chooseRoleInventory -> {
state.chosenRole = Role.values()[event.slot] instance.chosenRole = Role.values()[event.slot]
state.choosePlayerInventory = createChoosePlayerInventory() val choosePlayerInventory = createChoosePlayerInventory()
tttPlayer.player.openInventory(state.choosePlayerInventory!!) instance.choosePlayerInventory = choosePlayerInventory
tttPlayer.player.openInventory(choosePlayerInventory)
} }
state.choosePlayerInventory -> { instance.choosePlayerInventory -> {
tttPlayer.player.closeInventory() tttPlayer.player.closeInventory()
val corpsePlayer = plugin.server.getPlayer((item.itemMeta as SkullMeta).owningPlayer!!.uniqueId)!! val corpsePlayer = plugin.server.getPlayer((item.itemMeta as SkullMeta).owningPlayer!!.uniqueId)!!
@ -103,8 +117,7 @@ object FakeCorpse: TTTItem, Buyable {
if (corpseTTTPlayer == null) { if (corpseTTTPlayer == null) {
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Das hat nicht funktioniert") tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Das hat nicht funktioniert")
} else { } else {
TTTCorpse.spawnFake(state.chosenRole!!, corpseTTTPlayer, tttPlayer.player.location) TTTCorpse.spawnFake(instance.chosenRole!!, corpseTTTPlayer, tttPlayer.player.location)
tttPlayer.player.inventory.removeTTTItem(FakeCorpse) tttPlayer.player.inventory.removeTTTItem(FakeCorpse)
} }
} }
@ -112,9 +125,4 @@ object FakeCorpse: TTTItem, Buyable {
} }
} }
} }
class State: IState {
var chosenRole: Role? = null
var choosePlayerInventory: Inventory? = null
}
} }

View file

@ -1,12 +1,12 @@
package de.moritzruth.spigot_ttt.game.items.impl 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.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.Role
import de.moritzruth.spigot_ttt.game.players.TTTPlayer import de.moritzruth.spigot_ttt.game.players.TTTPlayer
import de.moritzruth.spigot_ttt.game.players.roles 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.applyMeta
import de.moritzruth.spigot_ttt.utils.clearHeldItemSlot import de.moritzruth.spigot_ttt.utils.clearHeldItemSlot
import de.moritzruth.spigot_ttt.utils.createKillExplosion 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.EventHandler
import org.bukkit.event.entity.ExplosionPrimeEvent import org.bukkit.event.entity.ExplosionPrimeEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import java.util.*
typealias FireballEntity = org.bukkit.entity.Fireball typealias FireballEntity = org.bukkit.entity.Fireball
object Fireball: TTTItem, Buyable { object Fireball: TTTItem<Fireball.Instance>(
override val type = TTTItem.Type.SPECIAL type = Type.SPECIAL,
override val itemStack = ItemStack(Material.FIRE_CHARGE).applyMeta { instanceType = Instance::class,
templateItemStack = ItemStack(Material.FIRE_CHARGE).applyMeta {
setDisplayName("${ChatColor.DARK_RED}${ChatColor.BOLD}Feuerball") setDisplayName("${ChatColor.DARK_RED}${ChatColor.BOLD}Feuerball")
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Wirf einen Feuerball" "${ChatColor.GOLD}Wirf einen Feuerball"
) )
} },
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL) shopInfo = ShopInfo(
override val price = 1 buyableBy = roles(Role.TRAITOR, Role.JACKAL),
override val buyLimit: Int? = null 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>() val vector = carrier.player.eyeLocation.toVector()
val location = vector.add(carrier.player.eyeLocation.direction.multiply(1.2))
override val listener = object : TTTItemListener(this, true) { .toLocation(carrier.player.location.world!!)
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 fireball = GameManager.world.spawnEntity(location, EntityType.FIREBALL) as FireballEntity val fireball = GameManager.world.spawnEntity(location, EntityType.FIREBALL) as FireballEntity
fireball.direction = data.tttPlayer.player.eyeLocation.direction fireball.direction = carrier.player.eyeLocation.direction
sendersByEntity[fireball] = data.tttPlayer sendersByEntity[fireball] = carrier
} }
}
val sendersByEntity = WeakHashMap<FireballEntity, TTTPlayer>()
override val listener = object : TTTItemListener<Instance>(this) {
@EventHandler @EventHandler
fun onExplosionPrime(event: ExplosionPrimeEvent) { fun onExplosionPrime(event: ExplosionPrimeEvent) {
val sender = sendersByEntity[event.entity] val sender = sendersByEntity[event.entity]
if (sender != null) { if (sender != null) {
sendersByEntity.remove(event.entity)
event.isCancelled = true event.isCancelled = true
GameManager.world.playSound( GameManager.world.playSound(

View file

@ -1,11 +1,12 @@
package de.moritzruth.spigot_ttt.game.items.impl 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.items.TTTItemListener
import de.moritzruth.spigot_ttt.game.players.Role import de.moritzruth.spigot_ttt.game.players.Role
import de.moritzruth.spigot_ttt.game.players.roles import de.moritzruth.spigot_ttt.game.players.roles
import de.moritzruth.spigot_ttt.game.items.Buyable import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.applyTypedMeta
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.Material import org.bukkit.Material
import org.bukkit.attribute.Attribute import org.bukkit.attribute.Attribute
@ -17,35 +18,35 @@ import org.bukkit.inventory.meta.PotionMeta
import org.bukkit.potion.PotionData import org.bukkit.potion.PotionData
import org.bukkit.potion.PotionType import org.bukkit.potion.PotionType
object HealingPotion: TTTItem, Buyable { object HealingPotion: TTTItem<HealingPotion.Instance>(
override val itemStack = ItemStack(Material.POTION).apply { instanceType = Instance::class,
val potionMeta = itemMeta as PotionMeta type = Type.SPECIAL,
potionMeta.basePotionData = PotionData(PotionType.INSTANT_HEAL, false, true) spawnProbability = Probability.VERY_LOW,
itemMeta = potionMeta templateItemStack = ItemStack(Material.POTION)
}.applyMeta { .applyTypedMeta<PotionMeta> { basePotionData = PotionData(PotionType.INSTANT_HEAL, false, true) }
setDisplayName("${ChatColor.LIGHT_PURPLE}Heiltrank") .applyMeta {
lore = listOf( setDisplayName("${ChatColor.LIGHT_PURPLE}Heiltrank")
lore = listOf(
"", "",
"${ChatColor.GOLD}Heilt dich voll" "${ChatColor.GOLD}Heilt dich voll"
) )
addItemFlags(ItemFlag.HIDE_POTION_EFFECTS) addItemFlags(ItemFlag.HIDE_POTION_EFFECTS)
} },
override val type = TTTItem.Type.SPECIAL shopInfo = ShopInfo(
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE) buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE),
override val price = 1 price = 1,
override val buyLimit = 2 buyLimit = 2
)
override val listener = object : TTTItemListener(this, true) { ) {
override val listener = object : TTTItemListener<Instance>(this) {
@EventHandler @EventHandler
fun onPlayerItemConsume(event: PlayerItemConsumeEvent) = handle(event) { fun onPlayerItemConsume(event: PlayerItemConsumeEvent) = handle(event) {
event.isCancelled = true event.isCancelled = true
event.player.inventory.clear(event.player.inventory.indexOf(event.item)) event.player.inventory.clear(event.player.inventory.indexOf(event.item))
event.player.health = event.player.getAttribute(Attribute.GENERIC_MAX_HEALTH)?.value ?: 100.0 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)
} }

View file

@ -1,13 +1,12 @@
package de.moritzruth.spigot_ttt.game.items.impl package de.moritzruth.spigot_ttt.game.items.impl
import de.moritzruth.spigot_ttt.Resourcepack 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.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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener 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.plugin
import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.createKillExplosion import de.moritzruth.spigot_ttt.utils.createKillExplosion
@ -19,33 +18,39 @@ import org.bukkit.event.EventHandler
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
object MartyrdomGrenade: TTTItem, Buyable { object MartyrdomGrenade: TTTItem<MartyrdomGrenade.Instance>(
override val type = TTTItem.Type.SPECIAL type = Type.SPECIAL,
override val itemStack = ItemStack(Resourcepack.Items.martyrdomGrenade).applyMeta { instanceType = Instance::class,
templateItemStack = ItemStack(Resourcepack.Items.martyrdomGrenade).applyMeta {
hideInfo() hideInfo()
setDisplayName("${ChatColor.DARK_PURPLE}${ChatColor.BOLD}Märtyriumsgranate $PASSIVE") setDisplayName("${ChatColor.DARK_PURPLE}${ChatColor.BOLD}Märtyriumsgranate$PASSIVE_SUFFIX")
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Lasse bei deinem Tod", "${ChatColor.GOLD}Lasse bei deinem Tod",
"${ChatColor.GOLD}eine Granate fallen" "${ChatColor.GOLD}eine Granate fallen"
) )
} },
override val buyableBy = roles(Role.TRAITOR) shopInfo = ShopInfo(
override val buyLimit: Int? = null buyableBy = roles(Role.TRAITOR, Role.JACKAL),
override val price = 1 price = 1
val isc = InversedStateContainer(State::class) )
) {
class Instance: TTTItem.Instance(MartyrdomGrenade, true) {
var explodeTask: BukkitTask? = null
override fun onOwn(tttPlayer: TTTPlayer) { override fun reset() {
isc.getOrCreate(tttPlayer) explodeTask?.cancel()
explodeTask = null
}
} }
override val listener = object : TTTItemListener(this, true) { override val listener = object : TTTItemListener<Instance>(this) {
@EventHandler @EventHandler
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) { 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( GameManager.world.playSound(
event.location, event.location,
Resourcepack.Sounds.grenadeExplode, Resourcepack.Sounds.grenadeExplode,
@ -57,15 +62,5 @@ object MartyrdomGrenade: TTTItem, Buyable {
createKillExplosion(event.tttPlayer, event.location, 2.5) createKillExplosion(event.tttPlayer, event.location, 2.5)
}, secondsToTicks(3).toLong()) }, secondsToTicks(3).toLong())
} }
@EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ ->
state.explodeTask?.cancel()
state.explodeTask = null
}
}
class State: IState {
var explodeTask: BukkitTask? = null
} }
} }

View file

@ -5,35 +5,29 @@ import com.comphenix.protocol.PacketType
import com.comphenix.protocol.events.PacketAdapter import com.comphenix.protocol.events.PacketAdapter
import com.comphenix.protocol.events.PacketEvent import com.comphenix.protocol.events.PacketEvent
import de.moritzruth.spigot_ttt.Resourcepack 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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener import de.moritzruth.spigot_ttt.game.players.PlayerManager
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.plugin
import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.hideInfo import de.moritzruth.spigot_ttt.utils.hideInfo
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.boss.BarColor import org.bukkit.boss.BarColor
import org.bukkit.boss.BarStyle import org.bukkit.boss.BarStyle
import org.bukkit.boss.BossBar
import org.bukkit.event.EventHandler
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.*
import kotlin.experimental.and import kotlin.experimental.and
import kotlin.experimental.or import kotlin.experimental.or
object Radar: TTTItem, Buyable { object Radar: TTTItem<Radar.Instance>(
private val DISPLAY_NAME = "${ChatColor.DARK_AQUA}${ChatColor.BOLD}Radar" type = Type.SPECIAL,
private const val ACTIVE_DURATION = 10 instanceType = Instance::class,
private const val COOLDOWN_DURATION = 40 templateItemStack = ItemStack(Resourcepack.Items.radar).applyMeta {
setDisplayName("${ChatColor.DARK_AQUA}${ChatColor.BOLD}Radar$PASSIVE_SUFFIX")
override val itemStack = ItemStack(Resourcepack.Items.radar).applyMeta {
setDisplayName("$DISPLAY_NAME $PASSIVE")
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Zeigt dir alle 30 Sekunden", "${ChatColor.GOLD}Zeigt dir alle 30 Sekunden",
@ -42,75 +36,66 @@ object Radar: TTTItem, Buyable {
) )
hideInfo() hideInfo()
} },
override val type = TTTItem.Type.SPECIAL shopInfo = ShopInfo(
override val buyableBy: EnumSet<Role> = EnumSet.of(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL) buyableBy = roles(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL),
override val price = 2 price = 2
override val buyLimit: Int? = null )
) {
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) { private var task: BukkitTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
val state = isc.getOrCreate(tttPlayer) val duration = Duration.between(timestamp, Instant.now()).toMillis().toDouble() / 1000
state.bossBar = plugin.server.createBossBar(DISPLAY_NAME, BarColor.BLUE, BarStyle.SOLID) if (active) {
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 (duration > ACTIVE_DURATION) { 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 { } else {
state.bossBar.progress = 1.0 - duration / ACTIVE_DURATION bossBar.progress = 1.0 - duration / ACTIVE_DURATION
} }
} else { } else {
if (duration > COOLDOWN_DURATION) { 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 { } else {
state.bossBar.progress = duration / COOLDOWN_DURATION bossBar.progress = duration / COOLDOWN_DURATION
} }
} }
}, 0, 2) }, 0, 1)
}
private fun setActive(tttPlayer: TTTPlayer, value: Boolean) { override fun onCarrierSet(carrier: TTTPlayer, isFirst: Boolean) {
val state = isc.getOrCreate(tttPlayer) bossBar.addPlayer(carrier.player)
if (active) resendEntityMetadata(carrier)
}
if (state.active != value) { override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
state.active = value bossBar.removePlayer(oldCarrier.player)
state.timestamp = Instant.now() if (active) resendEntityMetadata(oldCarrier)
}
if (value) { override fun reset() {
state.bossBar.setTitle(DISPLAY_NAME + "${ChatColor.WHITE} - ${ChatColor.GREEN}Aktiv") task.cancel()
} else {
state.bossBar.setTitle(DISPLAY_NAME + "${ChatColor.WHITE} - ${ChatColor.GRAY}Cooldown")
}
// Toggle sending the entity metadata
PlayerManager.tttPlayers.forEach {
if (it !== tttPlayer) {
tttPlayer.player.hidePlayer(plugin, it.player)
tttPlayer.player.showPlayer(plugin, it.player)
}
}
} }
} }
override val listener = object : TTTItemListener(this, true) { fun resendEntityMetadata(tttPlayer: TTTPlayer) {
@EventHandler PlayerManager.tttPlayers.forEach {
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) { if (it !== tttPlayer) {
isc.get(event.tttPlayer)?.reset(event.tttPlayer) tttPlayer.player.hidePlayer(plugin, it.player)
isc.remove(event.tttPlayer) tttPlayer.player.showPlayer(plugin, it.player)
} }
@EventHandler
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, tttPlayer -> state.reset(tttPlayer) }
override fun onRightClick(data: ClickEventData) {
data.event.isCancelled = true
} }
} }
@ -119,15 +104,17 @@ object Radar: TTTItem, Buyable {
val receivingTTTPlayer = TTTPlayer.of(event.player) ?: return val receivingTTTPlayer = TTTPlayer.of(event.player) ?: return
val packet = WrapperPlayServerEntityMetadata(event.packet) val packet = WrapperPlayServerEntityMetadata(event.packet)
val playerOfPacket = plugin.server.onlinePlayers.find { it.entityId == packet.entityID } ?: return val tttPlayerOfPacket = plugin.server.onlinePlayers
val tttPlayerOfPacket = TTTPlayer.of(playerOfPacket) ?: return .find { it.entityId == packet.entityID }
?.let { TTTPlayer.of(it) } ?: return
val instance = getInstance(receivingTTTPlayer) ?: return
if (tttPlayerOfPacket.alive) { if (tttPlayerOfPacket.alive) {
// https://wiki.vg/Entity_metadata#Entity_Metadata_Format // https://wiki.vg/Entity_metadata#Entity_Metadata_Format
try { try {
val modifiers = packet.metadata[0].value as Byte val modifiers = packet.metadata[0].value as Byte
packet.metadata[0].value = 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() else modifiers and 0b10111111.toByte()
} catch (ignored: Exception) { } catch (ignored: Exception) {
// Idk why this throws exceptions, but it works anyways // 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)
}
}
} }

View file

@ -1,10 +1,7 @@
package de.moritzruth.spigot_ttt.game.items.impl package de.moritzruth.spigot_ttt.game.items.impl
import de.moritzruth.spigot_ttt.Resourcepack 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.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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener import de.moritzruth.spigot_ttt.game.items.TTTItemListener
import de.moritzruth.spigot_ttt.game.players.* import de.moritzruth.spigot_ttt.game.players.*
@ -27,26 +24,76 @@ import java.time.Duration
import java.time.Instant import java.time.Instant
import kotlin.random.Random import kotlin.random.Random
object SecondChance: TTTItem, Buyable { object SecondChance: TTTItem<SecondChance.Instance>(
private val DISPLAY_NAME = "${ChatColor.GREEN}${ChatColor.BOLD}Second Chance" type = Type.SPECIAL,
val ON_CORPSE = Resourcepack.Items.arrowDown instanceType = Instance::class,
val ON_SPAWN = Resourcepack.Items.dot templateItemStack = ItemStack(Resourcepack.Items.secondChance).applyMeta {
private const val TIMEOUT = 10.0 setDisplayName("${ChatColor.GREEN}${ChatColor.BOLD}Second Chance$PASSIVE_SUFFIX")
override val type = TTTItem.Type.SPECIAL
override val itemStack = ItemStack(Resourcepack.Items.secondChance).applyMeta {
setDisplayName("$DISPLAY_NAME $PASSIVE")
hideInfo() hideInfo()
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Du wirst mit einer 50%-Chance", "${ChatColor.GOLD}Du wirst mit einer 50%-Chance",
"${ChatColor.GOLD}wiederbelebt, wenn du stirbst" "${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( private val chooseSpawnInventory = plugin.server.createInventory(
null, null,
@ -69,97 +116,42 @@ object SecondChance: TTTItem, Buyable {
}) })
} }
override fun onOwn(tttPlayer: TTTPlayer) { override val listener = object : TTTItemListener<Instance>(this) {
isc.getOrCreate(tttPlayer)
}
override val listener = object : TTTItemListener(this, true, false) {
@EventHandler @EventHandler
fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) { fun onTTTPlayerTrueDeath(event: TTTPlayerTrueDeathEvent) {
val state = isc.get(event.tttPlayer) val instance = getInstance(event.tttPlayer) ?: return
if (state != null) { instance.possiblyTrigger()
if (true || Random.nextBoolean()) { if (instancesByUUID.values.find { it.preventRoundEnd } != null) event.winnerRoleGroup = null
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 }
} }
@EventHandler @EventHandler
fun onInventoryClose(event: InventoryCloseEvent) = handle(event) { tttPlayer -> fun onInventoryClose(event: InventoryCloseEvent) {
if (event.inventory == chooseSpawnInventory) { if (event.inventory == chooseSpawnInventory) {
if (isc.get(tttPlayer)?.timeoutAction != null) { handleWithInstance(event) { instance ->
nextTick { if (isc.get(tttPlayer) != null) tttPlayer.player.openInventory(chooseSpawnInventory) } nextTick { instance.carrier?.player?.openInventory(chooseSpawnInventory) }
} }
} }
} }
@EventHandler @EventHandler
fun onInventoryClick(event: InventoryClickEvent) = handle(event) { tttPlayer -> fun onInventoryClick(event: InventoryClickEvent) {
if (event.clickedInventory != chooseSpawnInventory) return@handle if (event.clickedInventory != chooseSpawnInventory) return
val state = isc.get(tttPlayer) ?: return@handle
val timeoutAction = state.timeoutAction ?: return@handle
val location = when (event.currentItem?.type) { handleWithInstance(event) { instance ->
ON_SPAWN -> GameManager.world.spawnLocation val timeoutAction = instance.timeoutAction!!
ON_CORPSE -> timeoutAction.deathLocation
else -> return@handle val location = when (event.currentItem?.type) {
ON_SPAWN -> GameManager.world.spawnLocation
ON_CORPSE -> timeoutAction.deathLocation
else -> return@handleWithInstance
}
timeoutAction.stop()
instance.carrier!!.revive(location)
} }
timeoutAction.stop()
tttPlayer.revive(location)
} }
@EventHandler @EventHandler
fun onTTTPlayerRevive(event: TTTPlayerReviveEvent) { fun onTTTPlayerRevive(event: TTTPlayerReviveEvent) = handle(event) { it.timeoutAction?.stop() }
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
} }
} }

View file

@ -2,24 +2,23 @@ package de.moritzruth.spigot_ttt.game.items.impl
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.GameManager 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.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener import de.moritzruth.spigot_ttt.game.players.PlayerManager
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.applyMeta
import de.moritzruth.spigot_ttt.utils.clearHeldItemSlot
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.event.EventHandler
import org.bukkit.event.player.PlayerSwapHandItemsEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
object Teleporter: TTTItem, Buyable { object Teleporter: TTTItem<Teleporter.Instance>(
override val type = TTTItem.Type.SPECIAL type = Type.SPECIAL,
override val itemStack = ItemStack(Resourcepack.Items.teleporter).applyMeta { instanceType = Instance::class,
templateItemStack = ItemStack(Resourcepack.Items.teleporter).applyMeta {
setDisplayName("${ChatColor.LIGHT_PURPLE}${ChatColor.BOLD}Teleporter") setDisplayName("${ChatColor.LIGHT_PURPLE}${ChatColor.BOLD}Teleporter")
lore = listOf( lore = listOf(
"", "",
"${ChatColor.GOLD}Tausche die Positionen zweier Spieler", "${ChatColor.GOLD}Tausche die Positionen zweier Spieler",
@ -28,42 +27,28 @@ object Teleporter: TTTItem, Buyable {
"", "",
"${ChatColor.RED}Kann nur einmal verwendet werden" "${ChatColor.RED}Kann nur einmal verwendet werden"
) )
} },
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL) shopInfo = ShopInfo(
override val price = 1 buyableBy = roles(Role.TRAITOR, Role.JACKAL),
override val buyLimit = 1 price = 1,
val isc = InversedStateContainer(State::class) buyLimit = 1
)
) {
class Instance: TTTItem.Instance(Teleporter) {
private var teleportSelf = false
private fun getRandomPlayerToTeleport(vararg exclude: TTTPlayer): TTTPlayer? { override fun onRightClick(event: ClickEvent) {
return try { val tttPlayer = carrier!!
PlayerManager.tttPlayers.filter { !exclude.contains(it) && it.alive && !it.player.isSneaking }.random() val firstPlayer = if (teleportSelf) {
} 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"
)
}
override fun onRightClick(data: ClickEventData) {
val tttPlayer = data.tttPlayer
val state = isc.getOrCreate(tttPlayer)
val firstPlayer = if (state.teleportSelf) {
if (!tttPlayer.player.isOnGround) { 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 null
} else if (tttPlayer.player.isSneaking) { } 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 null
} else tttPlayer } else tttPlayer
} else getRandomPlayerToTeleport(tttPlayer) } else getRandomPlayerToTeleport(tttPlayer)
@ -76,7 +61,7 @@ object Teleporter: TTTItem, Buyable {
firstPlayer.player.teleport(secondPlayer.player.location) firstPlayer.player.teleport(secondPlayer.player.location)
secondPlayer.player.teleport(firstLocation) 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(firstPlayer.player.location, Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 1F)
GameManager.world.playSound(secondPlayer.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 // Teleport failed
tttPlayer.player.playSound(tttPlayer.player.location, Resourcepack.Sounds.error, 1F, 1F) 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 { private fun getRandomPlayerToTeleport(vararg exclude: TTTPlayer): TTTPlayer? {
var teleportSelf = false return try {
PlayerManager.tttPlayers.filter { !exclude.contains(it) && it.alive && !it.player.isSneaking }.random()
} catch (e: NoSuchElementException) {
null
}
} }
} }

View file

@ -1,17 +1,13 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons package de.moritzruth.spigot_ttt.game.items.impl.weapons
import de.moritzruth.spigot_ttt.Resourcepack 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.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.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.applyMeta
import de.moritzruth.spigot_ttt.utils.hideInfo import de.moritzruth.spigot_ttt.utils.hideInfo
import de.moritzruth.spigot_ttt.utils.removeTTTItemNextTick
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.SoundCategory import org.bukkit.SoundCategory
import org.bukkit.attribute.Attribute import org.bukkit.attribute.Attribute
@ -22,9 +18,10 @@ import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.util.Vector import org.bukkit.util.Vector
object BaseballBat: TTTItem, Buyable, Selectable { object BaseballBat: TTTItem<BaseballBat.Instance>(
override val type = TTTItem.Type.MELEE type = Type.SPECIAL,
override val itemStack = ItemStack(Resourcepack.Items.baseballBat).applyMeta { instanceType = Instance::class,
templateItemStack = ItemStack(Resourcepack.Items.baseballBat).applyMeta {
setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Baseball-Schläger") setDisplayName("${ChatColor.RESET}${ChatColor.BOLD}Baseball-Schläger")
lore = listOf( lore = listOf(
"", "",
@ -41,22 +38,28 @@ object BaseballBat: TTTItem, Buyable, Selectable {
-0.8, -0.8,
AttributeModifier.Operation.ADD_SCALAR AttributeModifier.Operation.ADD_SCALAR
)) ))
} },
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL) shopInfo = ShopInfo(
override val price = 1 buyableBy = roles(Role.TRAITOR, Role.JACKAL),
override val buyLimit: Int? = null price = 1
),
disableDamage = false
) {
const val WALK_SPEED_INCREASE = 0.1F
override fun onSelect(tttPlayer: TTTPlayer) { class Instance: TTTItem.Instance(BaseballBat) {
tttPlayer.player.walkSpeed = 0.3F override fun onSelect() {
carrier!!.walkSpeed += WALK_SPEED_INCREASE
}
override fun onDeselect() {
carrier!!.walkSpeed -= WALK_SPEED_INCREASE
}
} }
override fun onDeselect(tttPlayer: TTTPlayer) { override val listener = object : TTTItemListener<Instance>(this) {
tttPlayer.player.walkSpeed = 0.2F
}
override val listener = object : TTTItemListener(this, false) {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
override fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { tttPlayer, _ -> fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { tttPlayer, _ ->
event.isCancelled = true event.isCancelled = true
if (event.damage != 1.0) return@handle // Cooldown on weapon 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) val distance = tttPlayer.player.location.distance(damagedPlayer.location)
if (distance < 2.5) { if (distance < 2.5) {
tttPlayer.player.inventory.removeTTTItemNextTick(BaseballBat) tttPlayer.removeItem(BaseballBat)
GameManager.world.playSound( GameManager.world.playSound(
damagedPlayer.location, damagedPlayer.location,
@ -74,6 +77,8 @@ object BaseballBat: TTTItem, Buyable, Selectable {
1F 1F
) )
event.damage = 0.0
val direction = tttPlayer.player.location.direction val direction = tttPlayer.player.location.direction
damagedPlayer.velocity = Vector(direction.x * 5, 8.0, direction.z * 5) damagedPlayer.velocity = Vector(direction.x * 5, 8.0, direction.z * 5)
} }

View file

@ -2,7 +2,6 @@ package de.moritzruth.spigot_ttt.game.items.impl.weapons
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.GameManager 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.LoreHelper
import de.moritzruth.spigot_ttt.game.items.TTTItem import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.game.items.TTTItemListener 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.game.players.roles
import de.moritzruth.spigot_ttt.utils.applyMeta import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.hideInfo import de.moritzruth.spigot_ttt.utils.hideInfo
import de.moritzruth.spigot_ttt.utils.removeTTTItemNextTick
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.Sound import org.bukkit.Sound
import org.bukkit.SoundCategory import org.bukkit.SoundCategory
@ -21,8 +19,10 @@ import org.bukkit.event.EventHandler
import org.bukkit.event.entity.EntityDamageByEntityEvent import org.bukkit.event.entity.EntityDamageByEntityEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
object Knife: TTTItem, Buyable { object Knife: TTTItem<Knife.Instance>(
override val itemStack = ItemStack(Resourcepack.Items.knife).applyMeta { type = Type.MELEE,
instanceType = Instance::class,
templateItemStack = ItemStack(Resourcepack.Items.knife).applyMeta {
setDisplayName("${ChatColor.RED}${ChatColor.BOLD}Knife") setDisplayName("${ChatColor.RED}${ChatColor.BOLD}Knife")
lore = listOf( lore = listOf(
"", "",
@ -31,23 +31,26 @@ object Knife: TTTItem, Buyable {
"${ChatColor.RED}Nur einmal verwendbar", "${ChatColor.RED}Nur einmal verwendbar",
"${ChatColor.RED}Nur aus nächster Nähe" "${ChatColor.RED}Nur aus nächster Nähe"
) )
hideInfo() hideInfo()
addAttributeModifier( addAttributeModifier(
Attribute.GENERIC_ATTACK_SPEED, AttributeModifier( Attribute.GENERIC_ATTACK_SPEED, AttributeModifier(
"_", "_",
-0.9, -0.9,
AttributeModifier.Operation.ADD_SCALAR AttributeModifier.Operation.ADD_SCALAR
)) ))
} },
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL) shopInfo = ShopInfo(
override val price = 1 buyableBy = roles(Role.TRAITOR, Role.JACKAL),
override val type = TTTItem.Type.MELEE price = 1,
override val buyLimit = 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) @EventHandler(ignoreCancelled = true)
override fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { damagerTTTPlayer, damagedTTTPlayer -> fun onEntityDamageByEntity(event: EntityDamageByEntityEvent) = handle(event) { damagerTTTPlayer, damagedTTTPlayer ->
event.isCancelled = true event.isCancelled = true
if (event.damage == 1.0) { if (event.damage == 1.0) {
@ -77,7 +80,7 @@ object Knife: TTTItem, Buyable {
1F 1F
) )
damagerTTTPlayer.player.inventory.removeTTTItemNextTick(Knife) damagerTTTPlayer.removeItem(Knife)
} }
} }
} }

View file

@ -1,25 +1,24 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.items.Spawning import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.utils.heartsToHealth import de.moritzruth.spigot_ttt.utils.heartsToHealth
import org.bukkit.ChatColor import org.bukkit.ChatColor
object Deagle: Gun( object Deagle: Gun(
stateClass = State::class, type = Type.PISTOL_LIKE,
instanceType = Instance::class,
spawnProbability = Probability.NORMAL,
displayName = "${ChatColor.BLUE}${ChatColor.BOLD}Deagle", displayName = "${ChatColor.BLUE}${ChatColor.BOLD}Deagle",
damage = heartsToHealth(3.0), damage = heartsToHealth(3.0),
cooldown = 1.4, cooldown = 1.4,
magazineSize = 8, magazineSize = 8,
reloadTime = 3.0, reloadTime = 3.0,
itemMaterial = Resourcepack.Items.deagle, material = Resourcepack.Items.deagle,
shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire, shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire,
reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload
), Spawning { ) {
override val type = TTTItem.Type.PISTOL_LIKE class Instance: Gun.Instance(Deagle)
class State: Gun.State(magazineSize)
} }

View file

@ -1,25 +1,24 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.items.Spawning import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.utils.heartsToHealth import de.moritzruth.spigot_ttt.utils.heartsToHealth
import org.bukkit.ChatColor import org.bukkit.ChatColor
object Glock: Gun( object Glock: Gun(
stateClass = State::class, type = Type.PISTOL_LIKE,
instanceType = Instance::class,
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Glock", displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Glock",
damage = heartsToHealth(1.5), damage = heartsToHealth(1.5),
spawnProbability = Probability.NORMAL,
cooldown = 0.3, cooldown = 0.3,
magazineSize = 20, magazineSize = 20,
reloadTime = 2.0, reloadTime = 2.0,
itemMaterial = Resourcepack.Items.glock, material = Resourcepack.Items.glock,
shootSound = Resourcepack.Sounds.Item.Weapon.Glock.fire, shootSound = Resourcepack.Sounds.Item.Weapon.Glock.fire,
reloadSound = Resourcepack.Sounds.Item.Weapon.Glock.reload reloadSound = Resourcepack.Sounds.Item.Weapon.Glock.reload
), Spawning { ) {
override val type = TTTItem.Type.PISTOL_LIKE class Instance: Gun.Instance(Glock)
class State: Gun.State(magazineSize)
} }

View file

@ -1,89 +1,99 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack 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.GameManager
import de.moritzruth.spigot_ttt.game.GamePhase import de.moritzruth.spigot_ttt.game.items.ClickEvent
import de.moritzruth.spigot_ttt.game.items.* import de.moritzruth.spigot_ttt.game.items.LoreHelper
import de.moritzruth.spigot_ttt.game.players.* 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.applyMeta
import de.moritzruth.spigot_ttt.utils.nextTick import de.moritzruth.spigot_ttt.utils.hideInfo
import de.moritzruth.spigot_ttt.utils.startItemDamageProgress import de.moritzruth.spigot_ttt.utils.startProgressTask
import org.bukkit.* import org.bukkit.*
import org.bukkit.entity.Item
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.inventory.ItemFlag
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.ItemMeta
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import java.time.Duration
import java.time.Instant import java.time.Instant
import kotlin.reflect.KClass import kotlin.reflect.KClass
typealias ClickAction = org.bukkit.event.block.Action
abstract class Gun( 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, displayName: String,
additionalLore: List<String>? = null, itemLore: List<String>? = null,
appendLore: Boolean = true,
val damage: Double, val damage: Double,
val cooldown: Double, val cooldown: Double,
val magazineSize: Int, val magazineSize: Int,
val reloadTime: Double, val reloadTime: Double,
val itemMaterial: Material,
val shootSound: String, val shootSound: String,
val reloadSound: String val reloadSound: String
): TTTItem, Selectable, DropHandler { ): TTTItem<Gun.Instance>(
override val itemStack = ItemStack(itemMaterial).applyMeta { type = type,
instanceType = instanceType,
spawnProbability = spawnProbability,
shopInfo = shopInfo,
templateItemStack = ItemStack(material).applyMeta {
setDisplayName(displayName) 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}Schaden: ${LoreHelper.damage(if (damage < 0) null else (damage / 2))}",
"${ChatColor.GRAY}Magazin: ${LoreHelper.uses(magazineSize)} Schuss" "${ChatColor.GRAY}Cooldown: ${LoreHelper.cooldown(cooldown)}",
) + run { "${ChatColor.GRAY}Magazin: ${LoreHelper.uses(magazineSize)} Schuss"
if (additionalLore == null) emptyList() ) + run {
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()
}
private fun setCarrierLevel() {
if (isSelected) carrier!!.player.level = remainingShots
} }
addItemFlags(ItemFlag.HIDE_ATTRIBUTES) private fun shoot() {
} val tttPlayer = requireCarrier()
if (!onBeforeShoot()) return
val isc = InversedStateContainer(stateClass) if (remainingShots == 0) {
GameManager.world.playSound(
tttPlayer.player.location,
Resourcepack.Sounds.Item.Weapon.Generic.emptyMagazine,
SoundCategory.PLAYERS,
1F,
1F
)
protected fun updateLevel(tttPlayer: TTTPlayer, state: State = isc.getOrCreate(tttPlayer)) { return
tttPlayer.player.level = state.remainingShots }
}
fun shoot(tttPlayer: TTTPlayer, itemStack: ItemStack, state: State = isc.getOrCreate(tttPlayer)) { GameManager.world.playSound(tttPlayer.player.location, gun.shootSound, SoundCategory.PLAYERS, 1F, 1F)
if (!onBeforeShoot(tttPlayer, itemStack, state)) return
if (state.remainingShots == 0) { remainingShots--
GameManager.world.playSound(
tttPlayer.player.location,
Resourcepack.Sounds.Item.Weapon.Generic.emptyMagazine,
SoundCategory.PLAYERS,
1F,
1F
)
return
}
GameManager.world.playSound(tttPlayer.player.location, shootSound, SoundCategory.PLAYERS, 1F, 1F)
state.remainingShots--
updateLevel(tttPlayer)
if (GameManager.phase == GamePhase.COMBAT) {
val rayTraceResult = GameManager.world.rayTrace( val rayTraceResult = GameManager.world.rayTrace(
tttPlayer.player.eyeLocation, tttPlayer.player.eyeLocation,
tttPlayer.player.eyeLocation.direction, tttPlayer.player.eyeLocation.direction,
200.0, 200.0,
FluidCollisionMode.ALWAYS, FluidCollisionMode.ALWAYS,
true, true,
0.01 0.01
) { it !== tttPlayer.player } ) { it !== tttPlayer.player }
if (rayTraceResult !== null) { if (rayTraceResult !== null) {
@ -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
open fun reload(tttPlayer: TTTPlayer, itemStack: ItemStack, state: State = isc.getOrCreate(tttPlayer)) { currentAction = Action.Reloading(this)
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(
carrier.player.location,
GameManager.world.playSound(tttPlayer.player.location, reloadSound, SoundCategory.PLAYERS, 1F, 1F) gun.reloadSound,
} SoundCategory.PLAYERS,
1F,
open fun computeActualDamage(tttPlayer: TTTPlayer, receiver: Player) = if (damage < 0 ) 1000.0 else damage 1F
)
open fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: State = isc.getOrCreate(tttPlayer)): Boolean {
if (state.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)
hitTTTPlayer.player.velocity = tttPlayer.player.location.direction.multiply(
(actualDamage / 20).coerceAtMost(3.0)
)
}
override fun onSelect(tttPlayer: TTTPlayer) {
updateLevel(tttPlayer)
}
override fun onDeselect(tttPlayer: TTTPlayer) {
tttPlayer.player.level = 0
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
}
}
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()
}
is Action.Cooldown -> {
currentAction.pause()
}
} }
itemEntity.setItemStack(itemStack.clone()) open fun computeActualDamage(receiver: TTTPlayer): Double {
requireCarrier() // Only to keep parity with possible override
ItemManager.droppedItemStates[itemEntity.entityId] = state return if (gun.damage < 0 ) 1000.0 else gun.damage
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) { * @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(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 onLeftClick(event: ClickEvent) {
try { try {
reload(data.tttPlayer, data.event.item!!) reload()
} catch (e: ActionInProgressError) {} } catch (e: ActionInProgressError) {}
} }
override fun onRightClick(data: ClickEventData) { override fun onRightClick(event: ClickEvent) {
try { try {
shoot(data.tttPlayer, data.event.item!!) shoot()
} catch (e: ActionInProgressError) {} } catch (e: ActionInProgressError) {}
} }
@EventHandler protected open fun onMovedOutOfHand(tttPlayer: TTTPlayer) {
fun onTTTPlayerDeath(event: TTTPlayerTrueDeathEvent) = isc.get(event.tttPlayer)?.reset() tttPlayer.player.level = 0
tttPlayer.player.exp = 0F
@EventHandler val action = currentAction
fun onGameEnd(event: GameEndEvent) = isc.forEveryState { state, _ -> state.reset() } if (action is Action.Reloading) {
currentAction = null
action.cancel()
}
}
override fun onCarrierSet(carrier: TTTPlayer, isFirst: Boolean) {
setCarrierLevel()
}
override fun onSelect() {
setCarrierLevel()
}
override fun onCarrierRemoved(oldCarrier: TTTPlayer) {
onMovedOutOfHand(oldCarrier)
}
override fun onDeselect() {
onMovedOutOfHand(carrier!!)
}
} }
class ActionInProgressError: RuntimeException("The gun has an ongoing action which may not be canceled") class ActionInProgressError: RuntimeException("The gun has an ongoing action")
abstract class State(magazineSize: Int): IState { sealed class Action(val instance: Instance) {
var currentAction: Action? = null
var remainingShots = magazineSize
fun reset() { currentAction?.reset() }
}
sealed class Action(var itemStack: ItemStack) {
val startedAt = Instant.now()!! val startedAt = Instant.now()!!
abstract var task: BukkitTask; protected set abstract val task: BukkitTask
open fun reset() { open fun cancel() {
task.cancel() task.cancel()
} }
open class Reloading( open class Reloading(instance: Instance): Action(instance) {
private val gun: Gun, override val task = createProgressTask()
itemStack: ItemStack,
protected val state: State,
protected val tttPlayer: TTTPlayer
): Action(itemStack) {
override lateinit var task: BukkitTask
open fun start() { protected open fun createProgressTask() = startProgressTask(instance.gun.reloadTime) { data ->
task = startItemDamageProgress(itemStack, gun.reloadTime) { val exp = if (data.isComplete) {
state.currentAction = null instance.remainingShots = instance.gun.magazineSize
state.remainingShots = gun.magazineSize instance.currentAction = null
gun.updateLevel(tttPlayer, state) 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) { class Cooldown(instance: Instance): Action(instance) {
override var task = startTask() override val task = startProgressTask(instance.gun.cooldown) { data ->
private var pausedProgress: Double? = null val exp = if (data.isComplete) {
instance.currentAction = null
private fun startTask() = startItemDamageProgress( 0F
itemStack = itemStack, } else data.progress.toFloat()
duration = gun.cooldown, if (instance.isSelected) instance.carrier!!.player.exp = exp
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
}
} }
} }
} }

View file

@ -1,25 +1,24 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.items.Spawning import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.utils.heartsToHealth import de.moritzruth.spigot_ttt.utils.heartsToHealth
import org.bukkit.ChatColor import org.bukkit.ChatColor
object Pistol: Gun( object Pistol: Gun(
stateClass = State::class, type = Type.PISTOL_LIKE,
instanceType = Instance::class,
spawnProbability = Probability.NORMAL,
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Pistol", displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Pistol",
damage = heartsToHealth(2.5), damage = heartsToHealth(2.5),
cooldown = 0.8, cooldown = 0.8,
magazineSize = 10, magazineSize = 10,
reloadTime = 2.0, reloadTime = 2.0,
itemMaterial = Resourcepack.Items.pistol, material = Resourcepack.Items.pistol,
shootSound = Resourcepack.Sounds.Item.Weapon.Pistol.fire, shootSound = Resourcepack.Sounds.Item.Weapon.Pistol.fire,
reloadSound = Resourcepack.Sounds.Item.Weapon.Pistol.reload reloadSound = Resourcepack.Sounds.Item.Weapon.Pistol.reload
), Spawning { ) {
override val type = TTTItem.Type.PISTOL_LIKE class Instance: Gun.Instance(Pistol)
class State: Gun.State(magazineSize)
} }

View file

@ -1,25 +1,24 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.items.Spawning import de.moritzruth.spigot_ttt.utils.Probability
import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.utils.heartsToHealth import de.moritzruth.spigot_ttt.utils.heartsToHealth
import org.bukkit.ChatColor import org.bukkit.ChatColor
object Rifle: Gun( object Rifle: Gun(
stateClass = State::class, type = Type.HEAVY_WEAPON,
instanceType = Instance::class,
spawnProbability = Probability.NORMAL,
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Rifle", displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Rifle",
damage = heartsToHealth(0.8), damage = heartsToHealth(0.8),
cooldown = 0.15, cooldown = 0.15,
magazineSize = 40, magazineSize = 40,
reloadTime = 2.0, reloadTime = 2.0,
itemMaterial = Resourcepack.Items.rifle, material = Resourcepack.Items.rifle,
shootSound = Resourcepack.Sounds.Item.Weapon.Rifle.fire, shootSound = Resourcepack.Sounds.Item.Weapon.Rifle.fire,
reloadSound = Resourcepack.Sounds.Item.Weapon.Rifle.reload reloadSound = Resourcepack.Sounds.Item.Weapon.Rifle.reload
), Spawning { ) {
override val type = TTTItem.Type.HEAVY_WEAPON class Instance: Gun.Instance(Rifle)
class State: Gun.State(magazineSize)
} }

View file

@ -2,122 +2,99 @@ package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack import de.moritzruth.spigot_ttt.Resourcepack
import de.moritzruth.spigot_ttt.game.GameManager 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.game.players.TTTPlayer
import de.moritzruth.spigot_ttt.plugin 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.heartsToHealth
import de.moritzruth.spigot_ttt.utils.secondsToTicks 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.ChatColor
import org.bukkit.SoundCategory 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 import org.bukkit.scheduler.BukkitTask
private const val RELOAD_TIME_PER_BULLET = 0.5 private const val RELOAD_TIME_PER_BULLET = 0.5
private const val MAGAZINE_SIZE = 8 private const val MAGAZINE_SIZE = 8
object Shotgun: Gun( object Shotgun: Gun(
stateClass = State::class, type = Type.HEAVY_WEAPON,
instanceType = Instance::class,
spawnProbability = Probability.LOW,
displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Shotgun", displayName = "${ChatColor.YELLOW}${ChatColor.BOLD}Shotgun",
damage = heartsToHealth(3.0), damage = heartsToHealth(3.0),
cooldown = 0.9, cooldown = 0.9,
magazineSize = MAGAZINE_SIZE, magazineSize = MAGAZINE_SIZE,
reloadTime = RELOAD_TIME_PER_BULLET * MAGAZINE_SIZE, reloadTime = RELOAD_TIME_PER_BULLET * MAGAZINE_SIZE,
itemMaterial = Resourcepack.Items.shotgun, material = Resourcepack.Items.shotgun,
additionalLore = listOf("${ChatColor.RED}Weniger Schaden auf Distanz"), itemLore = listOf("${ChatColor.RED}Weniger Schaden auf Distanz"),
shootSound = Resourcepack.Sounds.Item.Weapon.Shotgun.fire, shootSound = Resourcepack.Sounds.Item.Weapon.Shotgun.fire,
reloadSound = Resourcepack.Sounds.Item.Weapon.Shotgun.reload reloadSound = Resourcepack.Sounds.Item.Weapon.Shotgun.reload
), Spawning { ) {
override val type = TTTItem.Type.HEAVY_WEAPON class Instance: Gun.Instance(Shotgun) {
override fun computeActualDamage(receiver: TTTPlayer): Double {
val distance = requireCarrier().player.location.distance(receiver.player.location)
override fun computeActualDamage(tttPlayer: TTTPlayer, receiver: Player): Double { return when {
val distance = tttPlayer.player.location.distance(receiver.location) distance <= 1 -> heartsToHealth(10.0)
distance >= 14 -> 0.5
return when { distance > 8 -> heartsToHealth(1.5)
distance <= 1 -> heartsToHealth(10.0) else -> heartsToHealth(damage)
distance >= 14 -> 0.5
distance > 8 -> heartsToHealth(1.5)
else -> heartsToHealth(damage)
}
}
override fun onDeselect(tttPlayer: TTTPlayer) {
tttPlayer.player.level = 0
val state = (isc.get(tttPlayer) ?: return) as State
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
}
}
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 onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: Gun.State): Boolean {
val ownState = state as State
if (ownState.remainingShots == 0) return true
when(val currentAction = ownState.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
} }
} }
return true override fun onMovedOutOfHand(tttPlayer: TTTPlayer) {
} tttPlayer.player.level = 0
tttPlayer.player.exp = 0F
class State: Gun.State(magazineSize) val action = currentAction
if (action is ReloadingAction) {
private class ReloadingAction(itemStack: ItemStack, state: State, tttPlayer: TTTPlayer): Action.Reloading(Shotgun, itemStack, state, tttPlayer) { currentAction = null
lateinit var updateTask: BukkitTask action.cancel()
}
override fun reset() {
task.cancel()
updateTask.cancel()
} }
override fun start() { override fun reload() {
task = startItemDamageProgress( if (currentAction != null) throw ActionInProgressError()
itemStack, if (remainingShots == magazineSize) return
reloadTime, currentAction = ReloadingAction(this)
state.remainingShots.toDouble() / magazineSize }
) { state.currentAction = null }
override fun onBeforeShoot(): Boolean {
if (remainingShots == 0) return true
when(val action = currentAction) {
is Action.Cooldown -> throw ActionInProgressError()
is ReloadingAction -> action.cancel()
}
return true
}
}
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() { updateTask = plugin.server.scheduler.runTaskTimer(plugin, fun() {
state.remainingShots++ instance.remainingShots++
updateLevel(tttPlayer) 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())
}
GameManager.world.playSound(tttPlayer.player.location, reloadSound, SoundCategory.PLAYERS, 1F, 1F) override fun cancel() {
task.cancel()
if (state.remainingShots == magazineSize) { updateTask?.cancel()
this.updateTask.cancel()
}
},
secondsToTicks(RELOAD_TIME_PER_BULLET).toLong(),
secondsToTicks(RELOAD_TIME_PER_BULLET).toLong())
} }
} }
} }

View file

@ -1,74 +1,55 @@
package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns package de.moritzruth.spigot_ttt.game.items.impl.weapons.guns
import de.moritzruth.spigot_ttt.Resourcepack 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.Role
import de.moritzruth.spigot_ttt.game.players.TTTPlayer import de.moritzruth.spigot_ttt.game.players.TTTPlayer
import de.moritzruth.spigot_ttt.game.players.roles 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 de.moritzruth.spigot_ttt.utils.sendActionBarMessage
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.entity.Item
import org.bukkit.inventory.ItemStack
object SidekickDeagle: Gun( 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", displayName = "${ChatColor.AQUA}${ChatColor.BOLD}Sidekick Deagle",
itemLore = listOf(
"",
"${ChatColor.GOLD}Mache einen Spieler zu deinem Sidekick",
"",
"${ChatColor.RED}Nur ein Schuss"
),
appendLore = false,
damage = 0.1, // Not really damage = 0.1, // Not really
cooldown = 1.0, cooldown = 1.0,
magazineSize = 1, magazineSize = 1,
reloadTime = 0.0, reloadTime = 0.0,
itemMaterial = Resourcepack.Items.sidekickDeagle, material = Resourcepack.Items.sidekickDeagle,
shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire, shootSound = Resourcepack.Sounds.Item.Weapon.Deagle.fire,
reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload reloadSound = Resourcepack.Sounds.Item.Weapon.Deagle.reload
), Buyable { ) {
override val buyableBy = roles(Role.JACKAL) class Instance: Gun.Instance(SidekickDeagle) {
override val price = 1 override fun reload() {
override val type = TTTItem.Type.PISTOL_LIKE requireCarrier().player.sendActionBarMessage("${ChatColor.RED}Du kannst diese Waffe nicht nachladen")
override val buyLimit = 1
override val itemStack = ItemStack(Resourcepack.Items.sidekickDeagle).applyMeta {
hideInfo()
setDisplayName("${ChatColor.AQUA}${ChatColor.BOLD}Sidekick Deagle")
lore = 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")
}
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 {
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) override fun onHit(tttPlayer: TTTPlayer, hitTTTPlayer: TTTPlayer) {
} hitTTTPlayer.changeRole(Role.SIDEKICK)
}
class State: Gun.State(magazineSize) 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()
}
}
} }

View file

@ -1,6 +1,5 @@
package de.moritzruth.spigot_ttt.game.items.shop 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.ItemManager
import de.moritzruth.spigot_ttt.game.items.TTTItem import de.moritzruth.spigot_ttt.game.items.TTTItem
import de.moritzruth.spigot_ttt.game.players.TTTPlayer import de.moritzruth.spigot_ttt.game.players.TTTPlayer
@ -16,7 +15,9 @@ object Shop {
}.toList() }.toList()
private val ITEMS_PER_PAGE = SHOP_SLOTS.count() 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) { fun setItems(tttPlayer: TTTPlayer) {
clear(tttPlayer) clear(tttPlayer)
@ -26,12 +27,12 @@ object Shop {
if (!itemsIterator.hasNext()) break if (!itemsIterator.hasNext()) break
val tttItem = itemsIterator.next() 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 = val displayNameSuffix =
if (isOutOfStock(tttPlayer, tttItem)) "${ChatColor.RED}Ausverkauft" if (isOutOfStock(tttPlayer, tttItem)) "${ChatColor.RED}Ausverkauft"
else "$${tttItem.price}" else "$${shopInfo.price}"
setDisplayName("$displayName${ChatColor.RESET} - ${ChatColor.BOLD}$displayNameSuffix") 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 for(index in 9..35) tttPlayer.player.inventory.clear(index) // All slots except the hotbar and armor
} }
fun isOutOfStock(tttPlayer: TTTPlayer, tttItem: TTTItem): Boolean { fun isOutOfStock(tttPlayer: TTTPlayer, tttItem: TTTItem<*>): Boolean {
if (tttItem !is Buyable) throw Error("Item is not buyable") val shopInfo = tttItem.shopInfo ?: throw Error("Item is not buyable")
return tttItem.buyLimit != null && tttPlayer.boughtItems.filter { it == tttItem }.count() >= tttItem.buyLimit!! return shopInfo.buyLimit != 0 && tttPlayer.boughtItems.filter { it == tttItem }.count() >= shopInfo.buyLimit
} }
} }

View file

@ -1,26 +1,21 @@
package de.moritzruth.spigot_ttt.game.items.shop package de.moritzruth.spigot_ttt.game.items.shop
import de.moritzruth.spigot_ttt.Settings 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.items.ItemManager
import de.moritzruth.spigot_ttt.game.players.PlayerManager import de.moritzruth.spigot_ttt.game.players.PlayerManager
import de.moritzruth.spigot_ttt.game.players.TTTPlayer import de.moritzruth.spigot_ttt.game.players.TTTPlayer
import de.moritzruth.spigot_ttt.game.players.TTTPlayerTrueDeathEvent import de.moritzruth.spigot_ttt.game.players.TTTPlayerTrueDeathEvent
import de.moritzruth.spigot_ttt.utils.sendActionBarMessage import de.moritzruth.spigot_ttt.utils.sendActionBarMessage
import org.bukkit.ChatColor import org.bukkit.ChatColor
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.ClickType import org.bukkit.event.inventory.ClickType
import org.bukkit.event.inventory.InventoryClickEvent import org.bukkit.event.inventory.InventoryClickEvent
object ShopListener: Listener { object ShopListener: GameListener() {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
fun onInventoryClick(event: InventoryClickEvent) { fun onInventoryClick(event: InventoryClickEvent) = handle(event) { tttPlayer ->
if (event.whoClicked !is Player) return if (event.click === ClickType.CREATIVE || event.clickedInventory?.holder != event.whoClicked) return@handle
val tttPlayer = TTTPlayer.of(event.whoClicked as Player) ?: return
if (event.click === ClickType.CREATIVE || event.clickedInventory?.holder != event.whoClicked) return
event.isCancelled = true event.isCancelled = true
val itemStack = event.currentItem val itemStack = event.currentItem
@ -30,21 +25,21 @@ object ShopListener: Listener {
event.clickedInventory?.holder == tttPlayer.player && event.clickedInventory?.holder == tttPlayer.player &&
Shop.SHOP_SLOTS.contains(event.slot) Shop.SHOP_SLOTS.contains(event.slot)
) { ) {
val tttItem = ItemManager.getItemByItemStack(itemStack) val tttItem = ItemManager.getTTTItemByItemStack(itemStack) ?: return@handle
if (tttItem === null || tttItem !is Buyable || !tttItem.buyableBy.contains(tttPlayer.role)) return val shopMeta = tttItem.shopInfo
if (shopMeta == null || !shopMeta.buyableBy.contains(tttPlayer.role)) return@handle
when { when {
Shop.isOutOfStock(tttPlayer, tttItem) -> Shop.isOutOfStock(tttPlayer, tttItem) ->
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Dieses Item ist ausverkauft") 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") tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Du hast nicht genug Credits")
else -> try { else -> try {
tttPlayer.addItem(tttItem) tttPlayer.addItem(tttItem)
tttPlayer.boughtItems.add(tttItem) tttPlayer.boughtItems.add(tttItem)
tttPlayer.credits -= tttItem.price tttPlayer.credits -= shopMeta.price
Shop.setItems(tttPlayer) Shop.setItems(tttPlayer)
} catch (e: TTTPlayer.AlreadyHasItemException) { } catch (e: TTTPlayer.AlreadyHasItemException) {
tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Du hast dieses Item bereits") tttPlayer.player.sendActionBarMessage("${ChatColor.RED}Du hast dieses Item bereits")
@ -63,8 +58,9 @@ object ShopListener: Listener {
PlayerManager.tttPlayers PlayerManager.tttPlayers
.filter { it.role.canOwnCredits && it.role.group == killer.role.group } .filter { it.role.canOwnCredits && it.role.group == killer.role.group }
.forEach { .forEach {
it.credits += Settings.creditsPerKill val creditsPerKill = Settings.creditsPerKill
it.player.sendActionBarMessage("${ChatColor.GREEN}Du hast ${Settings.creditsPerKill} Credit(s) erhalten") it.credits += creditsPerKill
it.player.sendActionBarMessage("${ChatColor.GREEN}Du hast $creditsPerKill Credit(s) erhalten")
} }
} }
} }

View file

@ -10,5 +10,5 @@ sealed class DeathReason(val displayText: String) {
object DROWNED: DeathReason("Ertrunken") object DROWNED: DeathReason("Ertrunken")
object FIRE: DeathReason("Verbrannt") object FIRE: DeathReason("Verbrannt")
object POISON: DeathReason("Vergiftet") 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}")
} }

View file

@ -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) } }
}
}

View file

@ -7,20 +7,25 @@ import de.moritzruth.spigot_ttt.game.GameManager
import de.moritzruth.spigot_ttt.game.GamePhase import de.moritzruth.spigot_ttt.game.GamePhase
import de.moritzruth.spigot_ttt.game.ScoreboardHelper import de.moritzruth.spigot_ttt.game.ScoreboardHelper
import de.moritzruth.spigot_ttt.game.classes.TTTClass 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.corpses.TTTCorpse
import de.moritzruth.spigot_ttt.game.items.ItemManager 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.TTTItem
import de.moritzruth.spigot_ttt.game.items.shop.Shop import de.moritzruth.spigot_ttt.game.items.shop.Shop
import de.moritzruth.spigot_ttt.utils.* 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 org.bukkit.entity.Player
import kotlin.properties.Delegates 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 alive = true
var player by Delegates.observable(player) { _, _, _ -> adjustPlayer() } var player by Delegates.observable(player) { _, _, _ -> adjustPlayer() }
val tttClassInstance = tttClass.createInstance(this)
var role = role var role = role
private set(value) { private set(value) {
if (value !== field) { if (value !== field) {
@ -33,34 +38,20 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
} }
val roleHistory = mutableListOf<Role>() val roleHistory = mutableListOf<Role>()
var itemInHand by Delegates.observable<TTTItem?>(null) { _, oldItem, newItem ->
if (oldItem !== newItem) onItemInHandChanged(oldItem, newItem)
}
var walkSpeed var walkSpeed
get() = player.walkSpeed get() = player.walkSpeed
set(value) { player.walkSpeed = value } set(value) { player.walkSpeed = value }
var credits by Delegates.observable(Settings.initialCredits) { _, _, _ -> scoreboard.updateCredits() } var credits by Delegates.observable(Settings.initialCredits) { _, _, _ -> scoreboard.updateCredits() }
val boughtItems = mutableListOf<TTTItem>() val boughtItems = mutableListOf<TTTItem<*>>()
val scoreboard = TTTScoreboard(this) val scoreboard = TTTScoreboard(this)
val stateContainer = StateContainer(this)
init { init {
adjustPlayer() adjustPlayer()
scoreboard.initialize() scoreboard.initialize()
tttClass.onInit(this) tttClassInstance.tttPlayer = this
} tttClassInstance.init()
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)
}
} }
fun damage(damage: Double, reason: DeathReason, damager: TTTPlayer, scream: Boolean = true) { 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 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) { fun changeRole(newRole: Role, notify: Boolean = true) {
roleHistory.add(role) roleHistory.add(role)
@ -194,12 +187,6 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
player.spigot().respawn() player.spigot().respawn()
} }
itemInHand?.let {
if (it is Selectable) {
it.onDeselect(this)
}
}
player.gameMode = GameMode.SURVIVAL player.gameMode = GameMode.SURVIVAL
player.activePotionEffects.forEach { player.removePotionEffect(it.type) } player.activePotionEffects.forEach { player.removePotionEffect(it.type) }
player.health = 20.0 player.health = 20.0
@ -208,44 +195,39 @@ class TTTPlayer(player: Player, role: Role, val tttClass: TTTClass = TTTClass.No
player.exp = 0F player.exp = 0F
player.allowFlight = player.gameMode == GameMode.CREATIVE player.allowFlight = player.gameMode == GameMode.CREATIVE
player.foodLevel = 20 player.foodLevel = 20
player.inventory.clear() player.inventory.clear()
tttClassInstance.reset()
} }
fun updateItemInHand() { fun checkAddItemPreconditions(tttItem: TTTItem<*>) {
val itemStack = player.inventory.itemInMainHand val owningTTTItemInstances = getOwningTTTItemInstances()
this.itemInHand = if (owningTTTItemInstances.find { it.tttItem === tttItem } != null) throw AlreadyHasItemException()
if (itemStack.type === Material.AIR) null
else ItemManager.getItemByItemStack(itemStack) 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 AlreadyHasItemException: Exception("The player already owns this item")
class TooManyItemsOfTypeException: Exception("The player already owns too much items of this type") class TooManyItemsOfTypeException: Exception("The player already owns too much items of this type")
fun addItem(item: TTTItem) { fun addItem(item: TTTItem<*>) {
checkAddItemPreconditions(item) checkAddItemPreconditions(item)
player.inventory.addItem(item.itemStack.clone()) val instance = item.createInstance()
item.onOwn(this) player.inventory.addItem(instance.createItemStack())
updateItemInHand() instance.carrier = this
} }
fun removeItem(item: TTTItem) { fun removeItem(item: TTTItem<*>, removeInstance: Boolean = true) {
item.getInstance(this)?.let {
it.carrier = null
if (removeInstance) item.instancesByUUID.remove(it.uuid)
}
player.inventory.removeTTTItem(item) player.inventory.removeTTTItem(item)
item.onRemove(this)
updateItemInHand()
} }
fun addDefaultClassItems() = tttClass.defaultItems.forEach { addItem(it) } fun addDefaultClassItems() = tttClass.defaultItems.forEach { addItem(it) }

View file

@ -9,11 +9,12 @@ fun Inventory.setAllToItem(indexes: Iterable<Int>, itemStack: ItemStack) {
indexes.forEach { setItem(it, itemStack) } indexes.forEach { setItem(it, itemStack) }
} }
fun Inventory.removeTTTItem(tttItem: TTTItem) { fun Inventory.removeTTTItem(tttItem: TTTItem<*>) {
val index = indexOfFirst { it?.type == tttItem.itemStack.type } val index = indexOfFirst { it?.type == tttItem.material }
if (index != -1) clear(index) 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) fun PlayerInventory.clearHeldItemSlot() = clear(heldItemSlot)

View file

@ -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
}

View file

@ -7,3 +7,11 @@ fun ItemStack.applyMeta(fn: ItemMeta.() -> Unit): ItemStack {
itemMeta = itemMeta!!.apply(fn) itemMeta = itemMeta!!.apply(fn)
return this 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
}

View file

@ -0,0 +1,7 @@
package de.moritzruth.spigot_ttt.utils
enum class Probability {
VERY_LOW,
LOW,
NORMAL
}

View file

@ -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
}