1
0
Fork 0

Add sidekick deagle and sidekick functionality

This commit is contained in:
Moritz Ruth 2020-06-07 20:03:17 +02:00
parent 9d05ff74ab
commit 6d364fa3eb
No known key found for this signature in database
GPG key ID: AFD57E23E753841B
18 changed files with 197 additions and 102 deletions

View file

@ -21,7 +21,7 @@ object CustomItems {
// Weapons
val deagle = Material.IRON_HOE
val goldenDeagle = Material.GOLDEN_HOE
val sidekickDeagle = Material.GOLDEN_HOE
val glock = Material.STONE_HOE
val pistol = Material.WOODEN_HOE
val shotgun = Material.IRON_AXE

View file

@ -36,7 +36,7 @@ object GameManager {
PlayerManager.tttPlayers.forEach {
it.setMuted(false)
Shop.hide(it)
Shop.clear(it)
}
Timers.startOverPhaseTimer(plugin.config.getInt("duration.over", 10)) {
@ -96,7 +96,7 @@ object GameManager {
ensurePhase(GamePhase.PREPARING)
phase = GamePhase.COMBAT
PlayerManager.tttPlayers.forEach { Shop.show(it) }
PlayerManager.tttPlayers.forEach { Shop.setItems(it) }
ScoreboardHelper.forEveryScoreboard { it.updateTeams() }
Shop.startCreditsTimer()

View file

@ -15,7 +15,7 @@ enum class Role(
DETECTIVE(ChatColor.YELLOW, "Detective", CustomItems.detective, true),
TRAITOR(ChatColor.RED, "Traitor", CustomItems.traitor, true),
JACKAL(ChatColor.AQUA, "Jackal", CustomItems.jackal, true),
SIDEKICK(ChatColor.AQUA, "Sidekick", CustomItems.sidekick, true);
SIDEKICK(ChatColor.AQUA, "Sidekick", CustomItems.sidekick, false);
val coloredDisplayName = "$chatColor$displayName${ChatColor.RESET}"

View file

@ -13,6 +13,7 @@ import de.moritzruth.spigot_ttt.shop.Shop
import de.moritzruth.spigot_ttt.utils.hotbarContents
import de.moritzruth.spigot_ttt.utils.secondsToTicks
import de.moritzruth.spigot_ttt.utils.teleportPlayerToWorldSpawn
import org.bukkit.ChatColor
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.entity.Player
@ -23,15 +24,25 @@ import kotlin.properties.Delegates
class TTTPlayer(player: Player, role: Role) {
var alive = true
var player by Delegates.observable(player) { _, _, _ -> initializePlayer() }
var player by Delegates.observable(player) { _, _, _ -> adjustPlayer() }
var role by Delegates.observable(role) { _, _, _ -> scoreboard.updateRole() }
var role = role
private set(value) {
if (value !== field) {
field = value
scoreboard.updateRole()
scoreboard.showCorrectSidebarScoreboard()
Shop.setItems(this)
}
}
val roleHistory = mutableListOf<Role>()
var itemInHand by Delegates.observable<TTTItem?>(null) { _, oldItem, newItem ->
if (oldItem !== newItem) onItemInHandChanged(oldItem, newItem)
}
var credits by Delegates.observable(10) { _, _, _ -> scoreboard.updateCredits() }
val boughtItems = mutableListOf<TTTItem>()
var invisible by Delegates.observable(false) { _, _, value ->
if (value) {
PlayerManager.tttPlayers.forEach {
@ -58,18 +69,11 @@ class TTTPlayer(player: Player, role: Role) {
private val discordUser get() = DiscordInterface.getUserByPlayerUUID(player.uniqueId)
init {
initializePlayer()
adjustPlayer()
scoreboard.initialize()
}
private fun initializePlayer() {
player.scoreboard = scoreboard.scoreboard
}
private fun onItemInHandChanged(oldItem: TTTItem?, newItem: TTTItem?) {
println(oldItem)
println(newItem)
if (oldItem !== null && oldItem is Selectable) {
oldItem.onDeselect(this)
}
@ -79,6 +83,27 @@ class TTTPlayer(player: Player, role: Role) {
}
}
fun onDeath(reason: DeathReason = DeathReason.SUICIDE) {
GameManager.ensurePhase(GamePhase.COMBAT)
player.gameMode = GameMode.SPECTATOR
alive = false
TTTCorpse.spawn(this, reason)
credits = 0
Shop.clear(this)
setMuted(true)
PlayerManager.letRemainingRoleGroupWin()
}
private fun adjustPlayer() {
player.scoreboard = scoreboard.scoreboard
}
private fun getOwningTTTItems() = player.inventory.hotbarContents.mapNotNull { it?.run { ItemManager.getItemByItemStack(this) } }
fun activateStamina() {
if (staminaTask != null) return
@ -96,21 +121,23 @@ class TTTPlayer(player: Player, role: Role) {
}, 0, secondsToTicks(0.5).toLong())
}
fun onDeath(reason: DeathReason = DeathReason.SUICIDE) {
GameManager.ensurePhase(GamePhase.COMBAT)
fun changeRole(newRole: Role) {
roleHistory.add(role)
role = newRole
player.gameMode = GameMode.SPECTATOR
alive = false
val message = if (role == Role.SIDEKICK) {
val jackal = PlayerManager.tttPlayers.find { it.role == Role.JACKAL }
?: throw NoJackalLivingException()
TTTCorpse.spawn(this, reason)
credits = 0
Shop.hide(this)
setMuted(true)
"${ChatColor.WHITE}Du bist jetzt ${role.coloredDisplayName} von ${jackal.role.chatColor}${jackal.player.displayName}"
} else "${ChatColor.WHITE}Du bist jetzt ${role.coloredDisplayName}"
player.sendTitle("", message, secondsToTicks(0.2), secondsToTicks(3), secondsToTicks(0.5))
PlayerManager.letRemainingRoleGroupWin()
}
class NoJackalLivingException: Exception("There is no living jackal for this sidekick")
fun resetAfterGameEnd() {
if (!alive) {
teleportToSpawn()
@ -138,7 +165,6 @@ class TTTPlayer(player: Player, role: Role) {
setMuted(false)
invisible = false
alive = true
player.gameMode = GameMode.SURVIVAL
player.activePotionEffects.forEach { player.removePotionEffect(it.type) }
player.health = 20.0
@ -182,6 +208,9 @@ class TTTPlayer(player: Player, role: Role) {
throw TooManyItemsOfTypeException()
}
}
class AlreadyHasItemException: Exception("The player already owns this item")
class TooManyItemsOfTypeException: Exception("The player already owns too much items of this type")
fun addItem(item: TTTItem) {
checkAddItemPreconditions(item)
@ -189,10 +218,5 @@ class TTTPlayer(player: Player, role: Role) {
updateItemInHand()
}
class AlreadyHasItemException: Exception("The player already owns this item")
class TooManyItemsOfTypeException: Exception("The player already owns too much items of this type")
private fun getOwningTTTItems() = player.inventory.hotbarContents.mapNotNull { it?.run { ItemManager.getItemByItemStack(this) } }
override fun toString() = "TTTPlayer(${player.name} is $role)"
}

View file

@ -9,12 +9,7 @@ import de.moritzruth.spigot_ttt.items.impl.CloakingDevice
import de.moritzruth.spigot_ttt.items.impl.EnderPearl
import de.moritzruth.spigot_ttt.items.impl.HealingPotion
import de.moritzruth.spigot_ttt.items.impl.Radar
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.GoldenDeagle
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.Deagle
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.Glock
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.Pistol
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.Shotgun
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.Rifle
import de.moritzruth.spigot_ttt.items.weapons.guns.impl.*
import de.moritzruth.spigot_ttt.items.weapons.impl.BaseballBat
import de.moritzruth.spigot_ttt.items.weapons.impl.Knife
import de.moritzruth.spigot_ttt.plugin
@ -33,7 +28,7 @@ import org.bukkit.inventory.ItemStack
object ItemManager {
val ITEMS: Set<TTTItem> = setOf(
Pistol,
Knife, Glock, Deagle, Shotgun, GoldenDeagle,
Knife, Glock, Deagle, Shotgun, SidekickDeagle,
BaseballBat,
CloakingDevice, Rifle,
EnderPearl, Radar, HealingPotion
@ -78,7 +73,10 @@ object ItemManager {
if (tttItem.type != TTTItem.Type.SPECIAL) {
if (tttItem is DropHandler) {
tttItem.onDrop(tttPlayer, event.itemDrop)
if (!tttItem.onDrop(tttPlayer, event.itemDrop)) {
event.isCancelled = true
return
}
}
plugin.server.scheduler.runTask(plugin, fun() {

View file

@ -17,13 +17,14 @@ interface Selectable {
}
interface DropHandler {
fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item)
fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item): Boolean
fun onPickup(tttPlayer: TTTPlayer, itemEntity: Item)
}
interface Buyable {
val buyableBy: EnumSet<Role>
val price: Int
val buyLimit: Int?
}
// Marker

View file

@ -33,6 +33,7 @@ object CloakingDevice: TTTItem,
override val type = TTTItem.Type.SPECIAL
override val price = 2
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
override val buyLimit = 1
val isc = InversedStateContainer(State::class)

View file

@ -20,6 +20,7 @@ object EnderPearl: TTTItem, Buyable {
}
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE)
override val price = 1
override val buyLimit: Int? = null
override val listener = object : Listener {
@EventHandler

View file

@ -37,6 +37,7 @@ object HealingPotion: TTTItem, Buyable {
override val type = TTTItem.Type.SPECIAL
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE)
override val price = 1
override val buyLimit = 2
override val listener = object : Listener {
@EventHandler

View file

@ -48,6 +48,7 @@ object Radar: TTTItem, Buyable {
override val type = TTTItem.Type.SPECIAL
override val buyableBy: EnumSet<Role> = EnumSet.of(Role.TRAITOR, Role.DETECTIVE, Role.JACKAL)
override val price = 2
override val buyLimit: Int? = null
val isc = InversedStateContainer(State::class)

View file

@ -59,7 +59,7 @@ abstract class Gun(
}
fun shoot(tttPlayer: TTTPlayer, itemStack: ItemStack, state: State = isc.getOrCreate(tttPlayer)) {
onBeforeShoot(tttPlayer, itemStack, state)
if (!onBeforeShoot(tttPlayer, itemStack, state)) return
if (state.remainingShots == 0) {
GameManager.world.playSound(tttPlayer.player.location, Sound.BLOCK_ANVIL_PLACE, SoundCategory.PLAYERS, 1f, 1.3f)
@ -89,14 +89,7 @@ abstract class Gun(
val damagedTTTPlayer = PlayerManager.getTTTPlayer(entity)
if (damagedTTTPlayer != null) {
damagedTTTPlayer.damageInfo = DamageInfo(tttPlayer, DeathReason.Item(this))
val actualDamage = computeActualDamage(tttPlayer, entity)
entity.damage(actualDamage)
tttPlayer.player.playSound(tttPlayer.player.location, Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.MASTER, 2f, 1.2f)
entity.velocity = tttPlayer.player.location.direction.multiply(
(actualDamage / 20).coerceAtMost(3.0)
)
onHit(tttPlayer, damagedTTTPlayer)
}
}
}
@ -116,8 +109,20 @@ abstract class Gun(
open fun computeActualDamage(tttPlayer: TTTPlayer, receiver: Player) = if (damage < 0 ) 1000.0 else damage
open fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: State = isc.getOrCreate(tttPlayer)) {
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) {
hitTTTPlayer.damageInfo = DamageInfo(tttPlayer, DeathReason.Item(this))
val actualDamage = computeActualDamage(tttPlayer, hitTTTPlayer.player)
hitTTTPlayer.player.damage(actualDamage)
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) {
@ -140,8 +145,8 @@ abstract class Gun(
}
}
override fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item) {
val state = isc.get(tttPlayer) ?: return
override fun onDrop(tttPlayer: TTTPlayer, itemEntity: Item): Boolean {
val state = isc.get(tttPlayer) ?: return true
when(val currentAction = state.currentAction) {
is Action.Reloading -> {
@ -157,7 +162,7 @@ abstract class Gun(
ItemManager.droppedItemStates[itemEntity.entityId] = state
isc.remove(tttPlayer)
return
return true
}
override fun onPickup(tttPlayer: TTTPlayer, itemEntity: Item) {

View file

@ -1,27 +0,0 @@
package de.moritzruth.spigot_ttt.items.weapons.guns.impl
import de.moritzruth.spigot_ttt.CustomItems
import de.moritzruth.spigot_ttt.game.players.Role
import de.moritzruth.spigot_ttt.game.players.roles
import de.moritzruth.spigot_ttt.items.Buyable
import de.moritzruth.spigot_ttt.items.TTTItem
import de.moritzruth.spigot_ttt.items.weapons.guns.Gun
import org.bukkit.ChatColor
object GoldenDeagle: Gun(
stateClass = State::class,
displayName = "${ChatColor.GOLD}${ChatColor.BOLD}Golden Deagle",
damage = INFINITE_DAMAGE,
cooldown = 1.0,
magazineSize = 1,
reloadTime = 20.0,
itemMaterial = CustomItems.goldenDeagle
), Buyable {
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL, Role.DETECTIVE)
override val price = 3
override val type = TTTItem.Type.PISTOL_LIKE
class State: Gun.State(magazineSize)
}

View file

@ -66,9 +66,9 @@ object Shotgun: Gun(
ownState.currentAction = ReloadingAction(itemStack, ownState, tttPlayer).also { it.start() }
}
override fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: Gun.State) {
override fun onBeforeShoot(tttPlayer: TTTPlayer, item: ItemStack, state: Gun.State): Boolean {
val ownState = state as State
if (ownState.remainingShots == 0) return
if (ownState.remainingShots == 0) return true
when(val currentAction = ownState.currentAction) {
is Action.Cooldown -> throw ActionInProgressError()
@ -82,6 +82,8 @@ object Shotgun: Gun(
item.itemMeta = damageMeta as ItemMeta
}
}
return true
}
class State: Gun.State(magazineSize)

View file

@ -0,0 +1,73 @@
package de.moritzruth.spigot_ttt.items.weapons.guns.impl
import com.connorlinfoot.actionbarapi.ActionBarAPI
import de.moritzruth.spigot_ttt.CustomItems
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.items.Buyable
import de.moritzruth.spigot_ttt.items.TTTItem
import de.moritzruth.spigot_ttt.items.weapons.guns.Gun
import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.hideInfo
import org.bukkit.ChatColor
import org.bukkit.entity.Item
import org.bukkit.inventory.ItemStack
object SidekickDeagle: Gun(
stateClass = State::class,
displayName = "${ChatColor.AQUA}${ChatColor.BOLD}Sidekick Deagle",
damage = 0.1, // Not really
cooldown = 1.0,
magazineSize = 1,
reloadTime = 0.0,
itemMaterial = CustomItems.sidekickDeagle
), Buyable {
override val buyableBy = roles(Role.JACKAL)
override val price = 1
override val type = TTTItem.Type.PISTOL_LIKE
override val buyLimit = 1
override val itemStack = ItemStack(CustomItems.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) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${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) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Diese Waffe kann nur der Jackal benutzen")
return false
}
return super.onBeforeShoot(tttPlayer, item, state)
}
class State: Gun.State(magazineSize)
}

View file

@ -36,6 +36,7 @@ object BaseballBat: TTTItem, Buyable, Selectable {
}
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
override val price = 1
override val buyLimit: Int? = null
override fun onSelect(tttPlayer: TTTPlayer) {
tttPlayer.player.walkSpeed = 0.3F

View file

@ -34,6 +34,7 @@ object Knife: TTTItem, Buyable {
override val buyableBy = roles(Role.TRAITOR, Role.JACKAL)
override val price = 1
override val type = TTTItem.Type.MELEE
override val buyLimit = 1
override val listener = object : Listener {
@EventHandler

View file

@ -5,7 +5,9 @@ import de.moritzruth.spigot_ttt.game.players.PlayerManager
import de.moritzruth.spigot_ttt.game.players.TTTPlayer
import de.moritzruth.spigot_ttt.items.Buyable
import de.moritzruth.spigot_ttt.items.ItemManager
import de.moritzruth.spigot_ttt.items.TTTItem
import de.moritzruth.spigot_ttt.plugin
import de.moritzruth.spigot_ttt.utils.applyMeta
import de.moritzruth.spigot_ttt.utils.secondsToTicks
import org.bukkit.ChatColor
import org.bukkit.scheduler.BukkitTask
@ -21,29 +23,29 @@ object Shop {
private var creditsTimer: BukkitTask? = null
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 is Buyable && it.buyableBy.contains(tttPlayer.role) }.toSet()
fun show(tttPlayer: TTTPlayer) {
fun setItems(tttPlayer: TTTPlayer) {
clear(tttPlayer)
val itemsIterator = getBuyableItems(tttPlayer).iterator()
for(index in SHOP_SLOTS) {
if (!itemsIterator.hasNext()) break
val tttItem = itemsIterator.next()
val itemStack = tttItem.itemStack.clone()
val meta = itemStack.itemMeta!!
meta.setDisplayName(meta.displayName + "${ChatColor.RESET} - ${ChatColor.BOLD}$${(tttItem as Buyable).price}")
itemStack.itemMeta = meta
if (tttItem !is Buyable) throw Error("Item is not buyable")
tttPlayer.player.inventory.setItem(index, itemStack)
tttPlayer.player.inventory.setItem(index, tttItem.itemStack.clone().applyMeta {
val displayNameSuffix =
if (isOutOfStock(tttPlayer, tttItem)) "${ChatColor.RED}Ausverkauft"
else "$${tttItem.price}"
setDisplayName("$displayName${ChatColor.RESET} - ${ChatColor.BOLD}$displayNameSuffix")
})
}
}
fun hide(tttPlayer: TTTPlayer) {
val range = 9..19
range + (1..8)
fun clear(tttPlayer: TTTPlayer) {
for(index in 9..35) tttPlayer.player.inventory.clear(index) // All slots except the hotbar and armor
}
@ -62,4 +64,9 @@ object Shop {
fun stopCreditsTimer() {
creditsTimer?.cancel()
}
fun isOutOfStock(tttPlayer: TTTPlayer, tttItem: TTTItem): Boolean {
if (tttItem !is Buyable) throw Error("Item is not buyable")
return tttItem.buyLimit != null && tttPlayer.boughtItems.filter { it == tttItem }.count() >= tttItem.buyLimit!!
}
}

View file

@ -36,17 +36,23 @@ object ShopListener: Listener {
val tttItem = ItemManager.getItemByItemStack(itemStack)
if (tttItem === null || tttItem !is Buyable || !tttItem.buyableBy.contains(tttPlayer.role)) return
if (tttPlayer.credits < tttItem.price) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Du hast nicht genug Credits")
}
when {
Shop.isOutOfStock(tttPlayer, tttItem) ->
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Dieses Item ist ausverkauft")
try {
tttPlayer.addItem(tttItem)
tttPlayer.credits -= tttItem.price
} catch (e: TTTPlayer.AlreadyHasItemException) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Du hast dieses Item bereits")
} catch (e: TTTPlayer.TooManyItemsOfTypeException) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Du hast keinen Platz dafür")
tttPlayer.credits < tttItem.price ->
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Du hast nicht genug Credits")
else -> try {
tttPlayer.addItem(tttItem)
tttPlayer.boughtItems.add(tttItem)
tttPlayer.credits -= tttItem.price
Shop.setItems(tttPlayer)
} catch (e: TTTPlayer.AlreadyHasItemException) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Du hast dieses Item bereits")
} catch (e: TTTPlayer.TooManyItemsOfTypeException) {
ActionBarAPI.sendActionBar(tttPlayer.player, "${ChatColor.RED}Du hast keinen Platz dafür")
}
}
}
}