forked from SteamWar/SteamWar
Add TNTLeague Module
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
plugins {
|
||||
steamwar.kotlin
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.paperapi21)
|
||||
compileOnly(project(":SpigotCore"))
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
startDelay: 10
|
||||
gameTime: 1200
|
||||
|
||||
prices:
|
||||
TNT:
|
||||
price: 4
|
||||
amount: 4
|
||||
REDSTONE:
|
||||
price: 4
|
||||
amount: 4
|
||||
REPEATER:
|
||||
price: 4
|
||||
amount: 2
|
||||
COMPARATOR:
|
||||
price: 4
|
||||
amount: 1
|
||||
REDSTONE_BLOCK:
|
||||
price: 4
|
||||
amount: 1
|
||||
REDSTONE_TORCH:
|
||||
price: 4
|
||||
amount: 2
|
||||
END_STONE:
|
||||
price: 4
|
||||
amount: 8
|
||||
ICE:
|
||||
price: 8
|
||||
amount: 1
|
||||
LEVER:
|
||||
price: 4
|
||||
amount: 1
|
||||
OAK_BUTTON:
|
||||
price: 4
|
||||
amount: 1
|
||||
STONE_BUTTON:
|
||||
price: 4
|
||||
amount: 1
|
||||
OAK_TRAPDOOR:
|
||||
price: 4
|
||||
amount: 2
|
||||
IRON_TRAPDOOR:
|
||||
price: 4
|
||||
amount: 1
|
||||
PISTON:
|
||||
price: 4
|
||||
amount: 2
|
||||
STICKY_PISTON:
|
||||
price: 4
|
||||
amount: 1
|
||||
GLASS:
|
||||
price: 4
|
||||
amount: 2
|
||||
OAK_FENCE:
|
||||
price: 4
|
||||
amount: 4
|
||||
LADDER:
|
||||
price: 6
|
||||
amount: 2
|
||||
WHITE_GLAZED_TERRACOTTA:
|
||||
price: 4
|
||||
amount: 3
|
||||
JUKEBOX:
|
||||
price: 4
|
||||
amount: 2
|
||||
OBSERVER:
|
||||
price: 10
|
||||
amount: 4
|
||||
BREWING_STAND:
|
||||
price: 4
|
||||
amount: 1
|
||||
STRING:
|
||||
price: 4
|
||||
amount: 2
|
||||
END_STONE_BRICK_SLAB:
|
||||
price: 4
|
||||
amount: 2
|
||||
TARGET:
|
||||
price: 4
|
||||
amount: 1
|
||||
COPPER_BULB:
|
||||
price: 4
|
||||
amount: 1
|
||||
SLIME_BLOCK:
|
||||
price: 6
|
||||
amount: 2
|
||||
HONEY_BLOCK:
|
||||
price: 6
|
||||
amount: 2
|
||||
STONE_PRESSURE_PLATE:
|
||||
price: 4
|
||||
amount: 1
|
||||
NOTE_BLOCK:
|
||||
price: 3
|
||||
amount: 1
|
||||
HOPPER:
|
||||
price: 3
|
||||
amount: 1
|
||||
GRAVEL:
|
||||
price: 4
|
||||
amount: 3
|
||||
@@ -0,0 +1,48 @@
|
||||
package de.steamwar.tntleague
|
||||
|
||||
import de.steamwar.tntleague.command.AcceptCommand
|
||||
import de.steamwar.tntleague.command.InviteCommand
|
||||
import de.steamwar.tntleague.command.LeaveCommand
|
||||
import de.steamwar.tntleague.command.RemoveCommand
|
||||
import de.steamwar.tntleague.events.GlobalListener
|
||||
import de.steamwar.tntleague.events.LobbyListener
|
||||
import net.kyori.adventure.key.Key
|
||||
import net.kyori.adventure.translation.GlobalTranslator
|
||||
import net.kyori.adventure.translation.TranslationRegistry
|
||||
import net.kyori.adventure.util.UTF8ResourceBundleControl
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
import java.util.*
|
||||
|
||||
lateinit var plugin: TNTLeague
|
||||
|
||||
class TNTLeague : JavaPlugin() {
|
||||
init {
|
||||
plugin = this
|
||||
}
|
||||
|
||||
override fun onEnable() {
|
||||
saveResource("config.yml", false)
|
||||
saveDefaultConfig()
|
||||
|
||||
val registry = TranslationRegistry.create(Key.key("steamwar:tntleague"))
|
||||
|
||||
val bundleDe = ResourceBundle.getBundle("de.steamwar.tntleague.TNTLeague", Locale.GERMAN, UTF8ResourceBundleControl())
|
||||
val bundleEn = ResourceBundle.getBundle("de.steamwar.tntleague.TNTLeague", Locale.US, UTF8ResourceBundleControl())
|
||||
registry.defaultLocale(Locale.US)
|
||||
|
||||
registry.registerAll(Locale.GERMAN, bundleDe, true)
|
||||
registry.registerAll(Locale.US, bundleEn, true)
|
||||
|
||||
GlobalTranslator.translator().addSource(registry)
|
||||
|
||||
server.pluginManager.registerEvents(LobbyListener, this)
|
||||
server.pluginManager.registerEvents(GlobalListener, this)
|
||||
|
||||
logger.info("TNTLeague enabled")
|
||||
|
||||
InviteCommand.register()
|
||||
AcceptCommand.register()
|
||||
RemoveCommand.register()
|
||||
LeaveCommand.register()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
join={0} ist dem Spiel beigetreten!
|
||||
joinTeam={0} ist dem {1} team begetreten!
|
||||
quit={0} hat das Spiel verlassen!
|
||||
blue=Blau
|
||||
red=Rot
|
||||
shutdown=Der Server fährt in {0} sekunden herunter!
|
||||
teamWin=Team {0} gewinnt!
|
||||
notEnoughCoins=Du hast nicht genug Coins um dir das zu kaufen!
|
||||
gameStarting=Das Spiel beginnt in {0} Sekunden!
|
||||
gameStart=Start in {0}
|
||||
gameStarted=Das Spiel beginnt!
|
||||
gameEnded=Das Spiel ist aus!
|
||||
dealer=Händler
|
||||
dealerItem=
|
||||
dealerPrice=Kosten: {0} Coins
|
||||
scoreboardTarget=Ziel: {0}
|
||||
scoreboardTime=Zeit: {0}:{1}
|
||||
scoreboardTeam=
|
||||
@@ -0,0 +1,37 @@
|
||||
join={0} joined the game!
|
||||
joinTeam={0} joined the {1} team!
|
||||
quit={0} left the game!
|
||||
quitTeam={0} left the {1} team!
|
||||
blue=Blue
|
||||
red=Red
|
||||
shutdown=The server stops in {0} seconds!
|
||||
teamWin=Team {0} wins!
|
||||
|
||||
notEnoughCoins=You don't have enough coins to buy this item!
|
||||
|
||||
gameStarting=The game starts in {0} seconds!
|
||||
gameStart=Starting in {0}
|
||||
gameStarted=The game has started!
|
||||
|
||||
timeRemaining={0} minutes remaining!
|
||||
|
||||
gameEnded=The game has ended!
|
||||
draw=The game ended in a draw!
|
||||
chat={0}» {1}
|
||||
|
||||
dealer=Shopkeeper
|
||||
dealerItem={0} {1}
|
||||
dealerPrice=Price: {0} Coins
|
||||
|
||||
scoreboardTarget=Target: {0}
|
||||
scoreboardTime=Time: {0}:{1}
|
||||
scoreboardTeam=Team {0}: {1}
|
||||
|
||||
ready=Ready
|
||||
notReady=Not ready
|
||||
isReady=Team {0} is ready!
|
||||
isNotReady=Team {0} is not ready!
|
||||
|
||||
invited={0} invited you to join the {1} team! *Click*
|
||||
invitedHover=Click to join the {0} team!
|
||||
invitedPlayer=Invited {0} to join your team!
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.tntleague.command
|
||||
|
||||
import de.steamwar.command.SWCommand
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
object AcceptCommand: SWCommand("accept") {
|
||||
|
||||
@Register
|
||||
fun acceptInvite(sender: Player, @Validator("isLeader") target: Player) {
|
||||
if (TNTLeagueGame.state != TNTLeagueGame.GameState.LOBBY) return
|
||||
|
||||
val team = TNTLeagueGame.getTeam(target) ?: return
|
||||
if (team.leader != target) return
|
||||
if (sender !in team.invites) return
|
||||
|
||||
team.invites.remove(target)
|
||||
team.opposite.invites.remove(target)
|
||||
team.join(sender)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.tntleague.command
|
||||
|
||||
import de.steamwar.command.SWCommand
|
||||
import de.steamwar.command.TypeValidator
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import de.steamwar.tntleague.util.*
|
||||
import net.kyori.adventure.text.event.ClickEvent
|
||||
import net.kyori.adventure.text.event.HoverEvent
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
object InviteCommand: SWCommand("invite") {
|
||||
|
||||
@Register
|
||||
fun invitePlayer(@Validator("isLeader") sender: Player, target: Player) {
|
||||
if (TNTLeagueGame.state != TNTLeagueGame.GameState.LOBBY) return
|
||||
if (TNTLeagueGame.getTeam(target) != null) return
|
||||
|
||||
val team = TNTLeagueGame.getTeam(sender)!!
|
||||
team.invites.add(target)
|
||||
|
||||
target.sendMessage(translate("invited", sender.name.yellow(), translate(team.name).colorByTeam(team)).basic().clickEvent(
|
||||
ClickEvent.callback {
|
||||
if (target !in team.invites) return@callback
|
||||
|
||||
team.invites.remove(target)
|
||||
team.opposite.invites.remove(target)
|
||||
team.join(target)
|
||||
})
|
||||
.hoverEvent(HoverEvent.showText(translate("invitedHover", translate(team.name).colorByTeam(team)).green())))
|
||||
sender.sendMessage(translate("invitedPlayer", target.name.yellow()).basic())
|
||||
}
|
||||
|
||||
@Validator("isLeader", local = false)
|
||||
fun isLeader(): TypeValidator<Player> {
|
||||
return TypeValidator<Player> { _, player, _ -> TNTLeagueGame.getTeam(player)?.leader == player}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.tntleague.command
|
||||
|
||||
import de.steamwar.command.SWCommand
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
object LeaveCommand: SWCommand("leave", "l") {
|
||||
|
||||
@Register
|
||||
fun leave(player: Player) = TNTLeagueGame.getTeam(player)?.remove(player)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.tntleague.command
|
||||
|
||||
import de.steamwar.command.SWCommand
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
object RemoveCommand: SWCommand("remove") {
|
||||
|
||||
@Register
|
||||
fun removePlayer(@Validator("isLeader") sender: Player, target: Player) {
|
||||
if (TNTLeagueGame.state != TNTLeagueGame.GameState.LOBBY) return
|
||||
|
||||
if (sender == target) return
|
||||
val team = TNTLeagueGame.getTeam(sender) ?: return
|
||||
if (team.leader != sender) return
|
||||
if (target !in team.members) return
|
||||
|
||||
team.remove(target)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.steamwar.tntleague.config
|
||||
|
||||
import de.steamwar.tntleague.plugin
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import org.bukkit.configuration.file.FileConfiguration
|
||||
|
||||
data class TNTLeagueConfig(
|
||||
val startDelay: Int = 10,
|
||||
val gameTime: Int = 60 * 20,
|
||||
|
||||
val prices: Map<Material, Price>
|
||||
) {
|
||||
companion object {
|
||||
val config: TNTLeagueConfig by lazy { loadConfig(plugin.config) }
|
||||
|
||||
private fun loadConfig(config: FileConfiguration): TNTLeagueConfig {
|
||||
return TNTLeagueConfig(config.getInt("startDelay"), config.getInt("gameTime"), loadPrices(config.getConfigurationSection("prices")!!))
|
||||
}
|
||||
|
||||
private fun loadPrices(config: ConfigurationSection): Map<Material, Price> {
|
||||
return config.getKeys(false).associateWith {
|
||||
Price(
|
||||
config.getInt("$it.amount"),
|
||||
config.getInt("$it.price")
|
||||
)
|
||||
}.mapKeys { Material.getMaterial(it.key)!! }
|
||||
}
|
||||
}
|
||||
|
||||
data class Price(
|
||||
val amount: Int,
|
||||
val price: Int,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package de.steamwar.tntleague.config
|
||||
|
||||
import de.steamwar.tntleague.plugin
|
||||
import de.steamwar.tntleague.util.Area
|
||||
import de.steamwar.tntleague.util.translate
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.configuration.ConfigurationSection
|
||||
import org.bukkit.configuration.file.YamlConfiguration
|
||||
import org.bukkit.entity.Villager
|
||||
import org.bukkit.entity.WanderingTrader
|
||||
import java.io.File
|
||||
|
||||
val world by lazy { plugin.server.worlds.first()!! }
|
||||
|
||||
private val targetedBlocksRed by lazy { TNTLeagueWorldConfig.redTeam.target.blocks.count { block -> block.type == TNTLeagueWorldConfig.targetMaterial } }
|
||||
|
||||
private val targetedBlocksBlue by lazy { TNTLeagueWorldConfig.blueTeam.target.blocks.count { block -> block.type == TNTLeagueWorldConfig.targetMaterial } }
|
||||
|
||||
private val targetedBlocksAll: Int
|
||||
get() = if (targetedBlocksBlue == targetedBlocksRed) targetedBlocksBlue else error("Targeted blocks are not equal")
|
||||
|
||||
val targetedBlocks: Int
|
||||
get() = if (TNTLeagueWorldConfig.target != -1) TNTLeagueWorldConfig.target else targetedBlocksAll
|
||||
|
||||
object TNTLeagueWorldConfig {
|
||||
private val config: YamlConfiguration by lazy {
|
||||
YamlConfiguration.loadConfiguration(
|
||||
File(
|
||||
plugin.server.worlds.first().worldFolder,
|
||||
"tntleague.yml"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val blueTeam: TeamConfig = TeamConfig.fromConfig(config.getConfigurationSection("blueTeam")!!)
|
||||
val redTeam: TeamConfig = TeamConfig.fromConfig(config.getConfigurationSection("redTeam")!!)
|
||||
val lobby: Location = config.getLocation("lobby", blueTeam.spawnLocation.clone().add(redTeam.spawnLocation).multiply(0.5))!!
|
||||
val targetMaterial: Material = Material.matchMaterial(config.getString("targetMaterial", "IRON_BLOCK")!!)!!
|
||||
val minHeight: Int = config.getInt("minHeight", 0)
|
||||
val target: Int = config.getInt("target", -1)
|
||||
|
||||
@JvmRecord
|
||||
data class TeamConfig(
|
||||
val spawnLocation: Location,
|
||||
val dealerSpawn: Location,
|
||||
val itemSpawn: Location,
|
||||
val target: Area
|
||||
) {
|
||||
companion object {
|
||||
fun fromConfig(config: ConfigurationSection): TeamConfig {
|
||||
val spawnLocation = config.getLocation("spawn")!!
|
||||
val dealerSpawn = config.getLocation("dealerSpawn")!!
|
||||
val itemSpawn = config.getLocation("itemSpawn")!!
|
||||
val targetPos1 = config.getLocation("targetMin")!!
|
||||
val targetPos2 = config.getLocation("targetMax")!!
|
||||
|
||||
spawnDealer(dealerSpawn)
|
||||
|
||||
return TeamConfig(spawnLocation, dealerSpawn, itemSpawn, Area(targetPos1, targetPos2))
|
||||
}
|
||||
|
||||
private fun spawnDealer(loc: Location) = world.spawn(loc, WanderingTrader::class.java)
|
||||
.apply {
|
||||
customName(translate("dealer"))
|
||||
isCustomNameVisible = false
|
||||
isInvulnerable = true
|
||||
isSilent = true
|
||||
isCollidable = false
|
||||
isAware = false
|
||||
setAI(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.tntleague.events
|
||||
|
||||
import org.bukkit.event.Listener
|
||||
|
||||
object DummyListener: Listener {
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package de.steamwar.tntleague.events
|
||||
|
||||
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import de.steamwar.tntleague.game.TNTLeagueTeam
|
||||
import de.steamwar.tntleague.inventory.SWInventoryHolder
|
||||
import de.steamwar.tntleague.plugin
|
||||
import de.steamwar.tntleague.util.*
|
||||
import io.papermc.paper.event.player.AsyncChatEvent
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.EventPriority
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.player.PlayerMoveEvent
|
||||
import org.bukkit.event.player.PlayerQuitEvent
|
||||
import org.bukkit.event.player.PlayerRespawnEvent
|
||||
|
||||
object GlobalListener: Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
fun onPlayerJoin(e: PlayerJoinEvent) {
|
||||
e.joinMessage(null)
|
||||
with(e.player) {
|
||||
teleport(TNTLeagueWorldConfig.lobby)
|
||||
inventory.clear()
|
||||
plugin.server.broadcast(translate("join", name.bold()).basic())
|
||||
isOp = false
|
||||
gameMode = GameMode.SPECTATOR
|
||||
respawnLocation = TNTLeagueWorldConfig.lobby
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
fun onPlayerQuit(e: PlayerQuitEvent) {
|
||||
e.quitMessage(null)
|
||||
plugin.server.broadcast(translate("quit", e.player.name.bold().colorByTeam(TNTLeagueGame.getTeam(e.player))).basic())
|
||||
TNTLeagueGame.playerLeave(e.player)
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
fun onPlayerClick(e: InventoryClickEvent) {
|
||||
val holder = e.inventory.getHolder(false)
|
||||
if (holder is SWInventoryHolder && e.clickedInventory == holder._inventory) {
|
||||
e.isCancelled = true
|
||||
holder.handleInventoryClick(e)
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
fun onPlayerMove(e: PlayerMoveEvent) {
|
||||
if (e.to.blockY < TNTLeagueWorldConfig.minHeight) {
|
||||
when (val team = TNTLeagueGame.getTeam(e.player)) {
|
||||
is TNTLeagueTeam -> e.player.teleport(team.config.spawnLocation)
|
||||
null -> e.player.teleport(TNTLeagueWorldConfig.blueTeam.spawnLocation)
|
||||
}
|
||||
}
|
||||
|
||||
e.player.foodLevel = 20
|
||||
e.player.saturation = 20f
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDeath(e: PlayerDeathEvent) {
|
||||
e.deathMessage(null)
|
||||
e.drops.clear()
|
||||
|
||||
e.itemsToKeep.removeIf { it.type != Material.DIAMOND_PICKAXE }
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerRespawn(e: PlayerRespawnEvent) {
|
||||
when (val team = TNTLeagueGame.getTeam(e.player)) {
|
||||
is TNTLeagueTeam -> e.respawnLocation = team.config.spawnLocation
|
||||
null -> e.respawnLocation = TNTLeagueWorldConfig.lobby
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onChat(e: AsyncChatEvent) {
|
||||
e.renderer { source, sourceDisplayName, message, _ ->
|
||||
translate("chat", sourceDisplayName.colorByTeam(TNTLeagueGame.getTeam(source)), message).basic()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package de.steamwar.tntleague.events
|
||||
|
||||
import de.steamwar.scoreboard.SWScoreboard
|
||||
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import de.steamwar.tntleague.game.TNTLeagueTeam
|
||||
import de.steamwar.tntleague.inventory.DealerInventory
|
||||
import de.steamwar.tntleague.util.TNTLeagueScoreboard
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.entity.EntityType
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.event.entity.EntityExplodeEvent
|
||||
import org.bukkit.event.entity.PlayerDeathEvent
|
||||
import org.bukkit.event.player.PlayerDropItemEvent
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.player.PlayerMoveEvent
|
||||
import org.bukkit.event.player.PlayerQuitEvent
|
||||
|
||||
object IngameListener: Listener {
|
||||
|
||||
@EventHandler
|
||||
fun onEntityInteract(e: PlayerInteractEntityEvent) {
|
||||
if (e.player.gameMode == GameMode.SPECTATOR) return
|
||||
|
||||
if(e.rightClicked.type == EntityType.VILLAGER) {
|
||||
e.isCancelled = true
|
||||
e.player.openInventory(DealerInventory(e.player).getInventory())
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onExplode(e: EntityExplodeEvent) {
|
||||
e.blockList().filter { it.type == TNTLeagueWorldConfig.targetMaterial }
|
||||
.groupBy { getTeamByTargetLocation(it.location) }
|
||||
.filterKeysNotNull()
|
||||
.mapValues { it.value.size }
|
||||
.forEach { it.key.damagedBlocks += it.value }
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onJoin(e: PlayerJoinEvent) {
|
||||
SWScoreboard.createScoreboard(e.player, TNTLeagueScoreboard(e.player))
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onMove(e: PlayerMoveEvent) {
|
||||
if (TNTLeagueGame.getTeam(e.player) != null) {
|
||||
if (e.to.blockX >= TNTLeagueWorldConfig.lobby.blockX && e.to.blockX <= TNTLeagueWorldConfig.lobby.blockX + 1 ||
|
||||
e.to.blockZ >= TNTLeagueWorldConfig.lobby.blockZ && e.to.blockZ <= TNTLeagueWorldConfig.lobby.blockZ + 1) {
|
||||
e.isCancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onDropPickaxe(e: PlayerDropItemEvent) {
|
||||
if (e.itemDrop.itemStack.type == Material.DIAMOND_PICKAXE) {
|
||||
e.isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTeamByTargetLocation(location: Location): TNTLeagueTeam? =
|
||||
when (location) {
|
||||
in TNTLeagueWorldConfig.redTeam.target -> TNTLeagueGame.redTeam
|
||||
in TNTLeagueWorldConfig.blueTeam.target -> TNTLeagueGame.blueTeam
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun <K, T> Map<K?, T>.filterKeysNotNull(destination: MutableMap<K, T> = mutableMapOf()): Map<K, T> {
|
||||
this.forEach { (t, u) -> if(t != null) destination[t] = u }
|
||||
return destination
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.steamwar.tntleague.events
|
||||
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import de.steamwar.tntleague.plugin
|
||||
import de.steamwar.tntleague.util.basic
|
||||
import de.steamwar.tntleague.util.colorByTeam
|
||||
import de.steamwar.tntleague.util.translate
|
||||
import de.steamwar.tntleague.util.yellow
|
||||
import io.papermc.paper.util.Tick
|
||||
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.EntityDamageEvent
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.player.PlayerDropItemEvent
|
||||
import org.bukkit.event.player.PlayerInteractEvent
|
||||
import org.bukkit.event.player.PlayerJoinEvent
|
||||
import org.bukkit.event.player.PlayerQuitEvent
|
||||
|
||||
object LobbyListener: Listener {
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerJoin(e: PlayerJoinEvent) {
|
||||
TNTLeagueGame.getFreeTeam()?.run {
|
||||
join(e.player)
|
||||
TNTLeagueGame.checkStart()
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
fun onPlayerQuit(e: PlayerQuitEvent) {
|
||||
val team = TNTLeagueGame.getTeam(e.player) ?: return
|
||||
team.leave(e.player)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onPlayerDamage(e: EntityDamageEvent) {
|
||||
e.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onDropEvent(e: PlayerDropItemEvent) {
|
||||
e.isCancelled = true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun interactEvent(e: PlayerInteractEvent) {
|
||||
val team = TNTLeagueGame.getTeam(e.player)
|
||||
if (e.action.isRightClick && team != null && e.item?.isSimilar(team.readyItem()) == true && team.opposite.leader != null) {
|
||||
team.isReady = !team.isReady
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun inventoryClick(e: InventoryClickEvent) {
|
||||
if (e.clickedInventory == e.whoClicked.inventory) {
|
||||
e.isCancelled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package de.steamwar.tntleague.game
|
||||
|
||||
import de.steamwar.scoreboard.SWScoreboard
|
||||
import de.steamwar.sql.Fight
|
||||
import de.steamwar.sql.FightPlayer
|
||||
import de.steamwar.sql.SteamwarUser
|
||||
import de.steamwar.tntleague.config.TNTLeagueConfig
|
||||
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
|
||||
import de.steamwar.tntleague.config.world
|
||||
import de.steamwar.tntleague.events.DummyListener
|
||||
import de.steamwar.tntleague.events.IngameListener
|
||||
import de.steamwar.tntleague.events.LobbyListener
|
||||
import de.steamwar.tntleague.inventory.DealerInventory
|
||||
import de.steamwar.tntleague.plugin
|
||||
import de.steamwar.tntleague.util.*
|
||||
import net.kyori.adventure.bossbar.BossBar
|
||||
import net.kyori.adventure.sound.Sound
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.block.data.type.TNT
|
||||
import org.bukkit.entity.Entity
|
||||
import org.bukkit.entity.Item
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.entity.TNTPrimed
|
||||
import org.bukkit.entity.Villager
|
||||
import org.bukkit.event.HandlerList
|
||||
import org.bukkit.event.Listener
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.scheduler.BukkitTask
|
||||
import java.sql.Timestamp
|
||||
import java.time.Instant
|
||||
|
||||
object TNTLeagueGame {
|
||||
var state: GameState = GameState.LOBBY
|
||||
set(value) {
|
||||
if (field.listener != value.listener) {
|
||||
HandlerList.unregisterAll(field.listener)
|
||||
plugin.server.pluginManager.registerEvents(value.listener, plugin)
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
var gameTimeRemaining: Int = TNTLeagueConfig.config.gameTime
|
||||
|
||||
val blueTeam = TNTLeagueTeam(TNTLeagueWorldConfig.blueTeam, TNTLeagueTeam.Team.BLUE)
|
||||
val redTeam = TNTLeagueTeam(TNTLeagueWorldConfig.redTeam, TNTLeagueTeam.Team.RED)
|
||||
|
||||
private lateinit var start: Timestamp
|
||||
|
||||
private var task: Int? = null
|
||||
private lateinit var spawnerTask: BukkitTask
|
||||
private lateinit var timerTask: BukkitTask
|
||||
|
||||
private fun setup() {
|
||||
assert(state == GameState.STARTING) { "Game is already running" }
|
||||
|
||||
state = GameState.RUNNING
|
||||
|
||||
plugin.server.onlinePlayers.forEach { SWScoreboard.createScoreboard(it, TNTLeagueScoreboard(it)) }
|
||||
|
||||
blueTeam.start()
|
||||
redTeam.start()
|
||||
|
||||
plugin.server.broadcast(translate("gameStarted").success())
|
||||
|
||||
val tnt = ItemStack(Material.TNT)
|
||||
|
||||
start = Timestamp.from(Instant.now())
|
||||
|
||||
spawnerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit {
|
||||
if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.blueTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) {
|
||||
spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, tnt)
|
||||
spawnItems(TNTLeagueWorldConfig.blueTeam.itemSpawn, DealerInventory.coins)
|
||||
}
|
||||
if (world.getNearbyEntitiesByType(Item::class.java, TNTLeagueWorldConfig.redTeam.itemSpawn, 3.0).sumOf { it.itemStack.amount } <= 256) {
|
||||
spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, tnt)
|
||||
spawnItems(TNTLeagueWorldConfig.redTeam.itemSpawn, DealerInventory.coins)
|
||||
}
|
||||
}, 5, 10)
|
||||
|
||||
timerTask = plugin.server.scheduler.runTaskTimer(plugin, bukkit {
|
||||
gameTimeRemaining--
|
||||
if (gameTimeRemaining == 0) {
|
||||
draw(WinReason.TIMEOUT)
|
||||
return@bukkit
|
||||
}
|
||||
|
||||
if (gameTimeRemaining % 300 == 0) {
|
||||
plugin.server.broadcast(translate("timeRemaining", (gameTimeRemaining / 60).toString().yellow()).basic())
|
||||
plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f)) }
|
||||
}
|
||||
}, 20, 20)
|
||||
}
|
||||
|
||||
private fun bukkit(f: () -> Unit): () -> Unit = f
|
||||
|
||||
private fun end() {
|
||||
if(state != GameState.RUNNING) return
|
||||
state = GameState.END
|
||||
|
||||
plugin.server.onlinePlayers.forEach {
|
||||
it.gameMode = GameMode.SPECTATOR
|
||||
SWScoreboard.removeScoreboard(it)
|
||||
it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_ENDER_DRAGON_DEATH.key, Sound.Source.MASTER, 1f, 1f))
|
||||
}
|
||||
|
||||
plugin.server.broadcast(translate("gameEnded").success())
|
||||
|
||||
spawnerTask.cancel()
|
||||
|
||||
var shutdown = 10
|
||||
|
||||
plugin.server.scheduler.runTaskTimer(plugin, bukkit {
|
||||
if (shutdown == 0) {
|
||||
plugin.server.shutdown()
|
||||
}
|
||||
|
||||
plugin.server.broadcast(translate("shutdown", shutdown.toString().yellow()).basic())
|
||||
|
||||
shutdown--
|
||||
}, 20, 20)
|
||||
}
|
||||
|
||||
private fun spawnItems(loc: Location, item: ItemStack) = plugin.server.worlds.first().dropItem(loc, item)
|
||||
|
||||
fun getTeam(player: Player) = if (player in blueTeam.members) blueTeam else if (player in redTeam.members) redTeam else null
|
||||
|
||||
fun getFreeTeam() = if (blueTeam.leader == null) blueTeam else if (redTeam.leader == null) redTeam else null
|
||||
|
||||
fun checkStart() {
|
||||
if (blueTeam.isReady && redTeam.isReady) {
|
||||
blueTeam.leader?.inventory?.clear()
|
||||
redTeam.leader?.inventory?.clear()
|
||||
state = GameState.STARTING
|
||||
|
||||
var countdown = TNTLeagueConfig.config.startDelay
|
||||
plugin.server.broadcast(translate("gameStarting", countdown.toString().yellow()).basic())
|
||||
val bar = BossBar.bossBar(translate("gameStart", countdown.toString().yellow()).gray(), (TNTLeagueConfig.config.startDelay - countdown) / TNTLeagueConfig.config.startDelay.toFloat(), BossBar.Color.GREEN, BossBar.Overlay.NOTCHED_10)
|
||||
plugin.server.onlinePlayers.forEach { bar.addViewer(it) }
|
||||
task = plugin.server.scheduler.scheduleSyncRepeatingTask(plugin, {
|
||||
plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_EXPERIENCE_ORB_PICKUP.key, Sound.Source.MASTER, 1f, 1f)) }
|
||||
if (countdown-- == 0) {
|
||||
plugin.server.onlinePlayers.forEach { it.hideBossBar(bar) }
|
||||
task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null }
|
||||
setup()
|
||||
} else {
|
||||
bar.name(translate("gameStart", countdown.toString().yellow()).gray())
|
||||
bar.progress((TNTLeagueConfig.config.startDelay - countdown) / TNTLeagueConfig.config.startDelay.toFloat())
|
||||
plugin.server.onlinePlayers.filter { !it.activeBossBars().contains(bar) }.forEach { bar.addViewer(it) }
|
||||
}
|
||||
}, 20, 20)
|
||||
|
||||
if (task == -1) {
|
||||
error("Failed to start countdown task")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun playerLeave(player: Player) {
|
||||
blueTeam.invites.remove(player)
|
||||
redTeam.invites.remove(player)
|
||||
getTeam(player)?.apply {
|
||||
members.remove(player)
|
||||
if (leader == player) {
|
||||
win(this.opposite, WinReason.LEAVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
assert(state == GameState.LOBBY || state == GameState.STARTING) { "Game is not in lobby or starting state" }
|
||||
|
||||
if (state == GameState.STARTING) {
|
||||
task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null }
|
||||
plugin.server.onlinePlayers.forEach { p -> p.activeBossBars().forEach { it.removeViewer(p) } }
|
||||
state = GameState.LOBBY
|
||||
}
|
||||
}
|
||||
|
||||
fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) {
|
||||
if (state != GameState.RUNNING) return
|
||||
end()
|
||||
plugin.server.broadcast(translate("teamWin", translate(tntLeagueTeam.name).color(tntLeagueTeam.color)).success())
|
||||
statistic(tntLeagueTeam, reason)
|
||||
explode(tntLeagueTeam.opposite)
|
||||
}
|
||||
|
||||
fun draw(reason: WinReason) {
|
||||
if (state != GameState.RUNNING) return
|
||||
end()
|
||||
plugin.server.broadcast(translate("draw").success())
|
||||
statistic(null, reason)
|
||||
}
|
||||
|
||||
fun explode(team: TNTLeagueTeam) {
|
||||
Area(team.config.spawnLocation.clone().add(20.0, 30.0, 20.0), team.config.spawnLocation.clone().subtract(20.0, 0.0, 20.0).add(0.0, 30.0, 0.0))
|
||||
.locations
|
||||
.filterIndexed { index, _ -> index % 7 == 0 }
|
||||
.forEachIndexed { index, location ->
|
||||
world.spawn(location, TNTPrimed::class.java).apply {
|
||||
fuseTicks = index + 40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun statistic(winTeam: TNTLeagueTeam?, reason: WinReason) {
|
||||
val fightId = Fight.create(
|
||||
"TNTLeague",
|
||||
world.name,
|
||||
start,
|
||||
TNTLeagueConfig.config.gameTime - gameTimeRemaining,
|
||||
SteamwarUser.get(blueTeam.leader!!.uniqueId).id,
|
||||
SteamwarUser.get(redTeam.leader!!.uniqueId).id,
|
||||
null,
|
||||
null,
|
||||
when (winTeam) {
|
||||
blueTeam -> 1
|
||||
redTeam -> 2
|
||||
else -> 0
|
||||
},
|
||||
when (reason) {
|
||||
WinReason.TIMEOUT -> "TIMEOUT"
|
||||
WinReason.DESTROYED -> "DESTROYED"
|
||||
WinReason.LEAVE -> "LEAVE"
|
||||
}
|
||||
)
|
||||
|
||||
addTeamMember(blueTeam, fightId)
|
||||
addTeamMember(redTeam, fightId)
|
||||
}
|
||||
|
||||
private fun addTeamMember(team: TNTLeagueTeam, fightId: Int) {
|
||||
team.members.filter { team.leader != it }
|
||||
.forEach {
|
||||
FightPlayer.create(
|
||||
fightId,
|
||||
SteamwarUser.get(it.uniqueId).id,
|
||||
team == blueTeam,
|
||||
"TNTLeague",
|
||||
0,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class GameState(val listener: Listener) {
|
||||
LOBBY(LobbyListener),
|
||||
STARTING(LobbyListener),
|
||||
RUNNING(IngameListener),
|
||||
END(DummyListener);
|
||||
}
|
||||
|
||||
enum class WinReason {
|
||||
TIMEOUT,
|
||||
DESTROYED,
|
||||
LEAVE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package de.steamwar.tntleague.game
|
||||
|
||||
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
|
||||
import de.steamwar.tntleague.config.targetedBlocks
|
||||
import de.steamwar.tntleague.plugin
|
||||
import de.steamwar.tntleague.util.*
|
||||
import net.kyori.adventure.sound.Sound
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import net.kyori.adventure.text.format.TextColor
|
||||
import org.bukkit.GameMode
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.enchantments.Enchantment
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import java.awt.Color.green
|
||||
|
||||
data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private val team: Team) {
|
||||
|
||||
var leader: Player? = null
|
||||
set(player) {
|
||||
field = player
|
||||
if (player != null) {
|
||||
with(player.inventory) {
|
||||
clear()
|
||||
setItem(4, readyItem())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val members = mutableListOf<Player>()
|
||||
val invites = mutableListOf<Player>()
|
||||
|
||||
val name: String
|
||||
get() = team.name.lowercase()
|
||||
|
||||
val color: TextColor
|
||||
get() = team.color
|
||||
|
||||
var isReady: Boolean = false
|
||||
set(value) {
|
||||
field = value
|
||||
leader?.inventory?.setItem(4, readyItem())
|
||||
leader?.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f))
|
||||
|
||||
plugin.server.onlinePlayers.forEach { it.sendActionBar(translate(if (value) "isReady" else "isNotReady", translate(this.name).colorByTeam(this)).let { cmp ->
|
||||
if (value) {
|
||||
cmp.green()
|
||||
} else {
|
||||
cmp.red()
|
||||
}
|
||||
}) }
|
||||
|
||||
if (value && opposite.isReady) {
|
||||
TNTLeagueGame.checkStart()
|
||||
}
|
||||
}
|
||||
|
||||
var damagedBlocks: Int = 0
|
||||
set(value) {
|
||||
field = value
|
||||
if (value >= targetedBlocks) {
|
||||
TNTLeagueGame.win(this, TNTLeagueGame.WinReason.DESTROYED)
|
||||
}
|
||||
}
|
||||
|
||||
val opposite: TNTLeagueTeam
|
||||
get() = when (team) {
|
||||
Team.BLUE -> TNTLeagueGame.redTeam
|
||||
Team.RED -> TNTLeagueGame.blueTeam
|
||||
}
|
||||
|
||||
fun join(player: Player): Boolean {
|
||||
members.add(player)
|
||||
|
||||
with(player) {
|
||||
teleport(config.spawnLocation)
|
||||
gameMode = GameMode.ADVENTURE
|
||||
inventory.clear()
|
||||
plugin.server.broadcast(translate("joinTeam", name().colorByTeam(this@TNTLeagueTeam), translate(this@TNTLeagueTeam.name).colorByTeam(this@TNTLeagueTeam)).basic())
|
||||
}
|
||||
|
||||
if (leader == null) {
|
||||
leader = player
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun readyItem() = if (isReady) {
|
||||
ItemStack.of(Material.LIME_DYE).apply {
|
||||
itemMeta = itemMeta.apply {
|
||||
displayName(translate("ready").green().translate(leader!!))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ItemStack.of(Material.RED_DYE).apply {
|
||||
itemMeta = itemMeta.apply {
|
||||
displayName(translate("notReady").red().translate(leader!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun start() = members.forEach {
|
||||
with(it) {
|
||||
gameMode = GameMode.SURVIVAL
|
||||
inventory.addItem(ItemStack.of(Material.DIAMOND_PICKAXE).apply {
|
||||
itemMeta = itemMeta.apply {
|
||||
isUnbreakable = true
|
||||
addEnchant(Enchantment.EFFICIENCY, 1, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun leave(player: Player) {
|
||||
if (TNTLeagueGame.state == TNTLeagueGame.GameState.RUNNING) {
|
||||
TNTLeagueGame.playerLeave(player)
|
||||
} else {
|
||||
members.remove(player)
|
||||
|
||||
if (members.isEmpty()) {
|
||||
plugin.server.onlinePlayers.firstOrNull { it != player && TNTLeagueGame.getTeam(it) == null }?.run {
|
||||
members.add(this)
|
||||
}
|
||||
}
|
||||
if (leader == player) {
|
||||
leader = members.firstOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun remove(player: Player) {
|
||||
leave(player)
|
||||
with(player) {
|
||||
teleport(TNTLeagueWorldConfig.lobby)
|
||||
gameMode = GameMode.SPECTATOR
|
||||
inventory.clear()
|
||||
plugin.server.broadcast(translate("quitTeam", name().colorByTeam(this@TNTLeagueTeam), translate(this@TNTLeagueTeam.name).colorByTeam(this@TNTLeagueTeam)).basic())
|
||||
}
|
||||
}
|
||||
|
||||
enum class Team(val color: TextColor) {
|
||||
BLUE(NamedTextColor.BLUE),
|
||||
RED(NamedTextColor.RED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package de.steamwar.tntleague.inventory
|
||||
|
||||
import de.steamwar.tntleague.config.TNTLeagueConfig
|
||||
import de.steamwar.tntleague.plugin
|
||||
import de.steamwar.tntleague.util.*
|
||||
import net.kyori.adventure.sound.Sound
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.format.Style
|
||||
import net.kyori.adventure.text.format.TextDecoration
|
||||
import org.bukkit.Material
|
||||
import org.bukkit.NamespacedKey
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.ItemStack
|
||||
import org.bukkit.persistence.PersistentDataType
|
||||
import java.util.*
|
||||
import kotlin.math.ceil
|
||||
|
||||
class DealerInventory(player: Player): SWInventoryHolder() {
|
||||
|
||||
init {
|
||||
items.forEachIndexed { index, item ->
|
||||
this[index] = item.first to {
|
||||
val price = item.second.price * if (it.isShiftClick) 5 else 1
|
||||
val amount = item.second.amount * if (it.isShiftClick) 5 else 1
|
||||
|
||||
if (!player.inventory.containsAtLeast(coins, price)) {
|
||||
player.sendMessage(translate("notEnoughCoins").error())
|
||||
player.playSound(Sound.sound(org.bukkit.Sound.ENTITY_VILLAGER_HURT.key, net.kyori.adventure.sound.Sound.Source.MASTER, 1f, 1f))
|
||||
return@to
|
||||
}
|
||||
|
||||
player.inventory.removeItem(coins.asQuantity(price))
|
||||
player.inventory.addItem(ItemStack.of(item.first.type, amount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createInventory(): Inventory = plugin.server.createInventory(this, ceil(TNTLeagueConfig.config.prices.size / 9f).toInt() * 9, translate("dealer").reset())
|
||||
|
||||
companion object {
|
||||
private val priceKey = NamespacedKey(plugin, "price")
|
||||
private val amountKey = NamespacedKey(plugin, "amount")
|
||||
private val coinKey = NamespacedKey(plugin, "coin")
|
||||
|
||||
val coins = ItemStack(Material.RAW_GOLD).apply {
|
||||
itemMeta = itemMeta.apply {
|
||||
displayName(Component.text("Coins").bold().gold())
|
||||
persistentDataContainer.apply {
|
||||
set(coinKey, PersistentDataType.BOOLEAN, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val items by lazy {
|
||||
val prices = TNTLeagueConfig.config.prices
|
||||
|
||||
prices.map { (material, price) ->
|
||||
ItemStack(material).apply {
|
||||
itemMeta = itemMeta.apply {
|
||||
displayName(material.name.lowercase().replace("_", " ")
|
||||
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
|
||||
.component().gray().appendSpace().append(price.amount.toString().component().yellow()))
|
||||
amount = price.amount
|
||||
lore(listOf(price.price.toString().component().yellow().bold().appendSpace().append(Component.text("Coins").yellow())))
|
||||
persistentDataContainer.apply {
|
||||
set(priceKey, PersistentDataType.INTEGER, price.price)
|
||||
set(amountKey, PersistentDataType.INTEGER, price.amount)
|
||||
}
|
||||
}
|
||||
} to price
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package de.steamwar.tntleague.inventory
|
||||
|
||||
import org.bukkit.event.inventory.InventoryClickEvent
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||
import org.bukkit.inventory.Inventory
|
||||
import org.bukkit.inventory.InventoryHolder
|
||||
import org.bukkit.inventory.ItemStack
|
||||
|
||||
abstract class SWInventoryHolder: InventoryHolder {
|
||||
|
||||
val _inventory: Inventory by lazy { createInventory() }
|
||||
|
||||
private val callbacks = mutableMapOf<Int, (event: InventoryClickEvent) -> Unit>()
|
||||
|
||||
override fun getInventory(): Inventory = _inventory
|
||||
|
||||
abstract fun createInventory(): Inventory
|
||||
|
||||
open fun handleInventoryClick(event: InventoryClickEvent) {
|
||||
callbacks[event.slot]?.invoke(event)
|
||||
}
|
||||
|
||||
fun addItem(item: ItemStack, slot: Int, callback: (event: InventoryClickEvent) -> Unit) {
|
||||
_inventory.setItem(slot, item)
|
||||
addCallback(slot, callback)
|
||||
}
|
||||
|
||||
fun addCallback(slot: Int, callback: (event: InventoryClickEvent) -> Unit) {
|
||||
callbacks[slot] = callback
|
||||
}
|
||||
|
||||
open fun handleClose(event: InventoryCloseEvent) { }
|
||||
|
||||
operator fun set(slot: Int, item: Pair<ItemStack, (event: InventoryClickEvent) -> Unit>) {
|
||||
addItem(item.first, slot, item.second)
|
||||
}
|
||||
|
||||
operator fun set(slot: Int, item: ItemStack) {
|
||||
addItem(item, slot) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package de.steamwar.tntleague.util
|
||||
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.block.Block
|
||||
|
||||
class Area(loc1: Location, loc2: Location) {
|
||||
|
||||
val min: Location
|
||||
val max: Location
|
||||
|
||||
init {
|
||||
require(loc1.world == loc2.world) { "Locations must be in the same world" }
|
||||
this.min = loc1 min loc2
|
||||
this.max = loc1 max loc2
|
||||
}
|
||||
|
||||
operator fun contains(loc: Location): Boolean {
|
||||
return loc.world == min.world && loc.x >= min.x && loc.x <= max.x && loc.y >= min.y && loc.y <= max.y && loc.z >= min.z && loc.z <= max.z
|
||||
}
|
||||
|
||||
val blocks: Sequence<Block>
|
||||
inline get() = sequence {
|
||||
for (x in locations) {
|
||||
yield(x.block)
|
||||
}
|
||||
}
|
||||
|
||||
val locations: Sequence<Location>
|
||||
inline get() = sequence {
|
||||
for (x in min.blockX..max.blockX) {
|
||||
for (y in min.blockY..max.blockY) {
|
||||
for (z in min.blockZ..max.blockZ) {
|
||||
yield(Location(min.world, x.toDouble(), y.toDouble(), z.toDouble()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infix fun Location.max(other: Location): Location {
|
||||
return Location(world, x.coerceAtLeast(other.x), y.coerceAtLeast(other.y), z.coerceAtLeast(other.z))
|
||||
}
|
||||
|
||||
infix fun Location.min(other: Location): Location {
|
||||
return Location(world, x.coerceAtMost(other.x), y.coerceAtMost(other.y), z.coerceAtMost(other.z))
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package de.steamwar.tntleague.util
|
||||
|
||||
import de.steamwar.tntleague.game.TNTLeagueTeam
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.ComponentLike
|
||||
import net.kyori.adventure.text.TranslatableComponent
|
||||
import net.kyori.adventure.text.format.NamedTextColor
|
||||
import net.kyori.adventure.text.format.Style
|
||||
import net.kyori.adventure.text.format.TextDecoration
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
|
||||
import net.kyori.adventure.translation.GlobalTranslator
|
||||
import org.bukkit.entity.Player
|
||||
import java.util.Locale
|
||||
|
||||
val prefix = Component.text("Steam").yellow()
|
||||
.append(Component.text("War").darkGray())
|
||||
.appendSpace()
|
||||
|
||||
val tntLeaguePrefix = Component.text("TNT").color(NamedTextColor.DARK_RED)
|
||||
.append(Component.text("League").color(NamedTextColor.GOLD))
|
||||
|
||||
val tntLeagueChatPrefix: Component = tntLeaguePrefix
|
||||
.append(Component.text("»").darkGray())
|
||||
.appendSpace()
|
||||
|
||||
fun TranslatableComponent.basic(): Component = tntLeagueChatPrefix.append(this.gray())
|
||||
|
||||
fun TranslatableComponent.error(): Component = tntLeagueChatPrefix.append(this.red())
|
||||
|
||||
fun TranslatableComponent.success(): Component = tntLeagueChatPrefix.append(this.green())
|
||||
|
||||
fun String.component(): Component = Component.text(this)
|
||||
|
||||
fun Component.bold(): Component = this.decorate(TextDecoration.BOLD)
|
||||
|
||||
fun String.bold(): Component = this.component().bold()
|
||||
|
||||
fun Component.yellow(): Component = this.color(NamedTextColor.YELLOW)
|
||||
|
||||
fun String.yellow(): Component = this.component().yellow()
|
||||
|
||||
fun Component.red(): Component = this.color(NamedTextColor.RED)
|
||||
|
||||
fun String.red(): Component = this.component().red()
|
||||
|
||||
fun Component.green(): Component = this.color(NamedTextColor.GREEN)
|
||||
|
||||
fun String.green(): Component = this.component().green()
|
||||
|
||||
fun Component.gray(): Component = this.color(NamedTextColor.GRAY)
|
||||
|
||||
fun String.gray(): Component = this.component().gray()
|
||||
|
||||
fun Component.darkGray(): Component = this.color(NamedTextColor.DARK_GRAY)
|
||||
|
||||
fun String.darkGray(): Component = this.component().darkGray()
|
||||
|
||||
fun Component.gold(): Component = this.color(NamedTextColor.GOLD)
|
||||
|
||||
fun translate(key: String, vararg args: ComponentLike): TranslatableComponent = Component.translatable(key, *args).decoration(TextDecoration.ITALIC, false)
|
||||
|
||||
fun Component.reset(): Component = this.style(Style.empty())
|
||||
|
||||
fun Component.colorByTeam(team: TNTLeagueTeam?) = when (team) {
|
||||
null -> this.gray()
|
||||
else -> this.color(team.color)
|
||||
}
|
||||
|
||||
fun Component.translate(locale: Locale): Component = GlobalTranslator.render(this, locale)
|
||||
|
||||
fun Component.translate(p: Player): Component = this.translate(p.locale())
|
||||
|
||||
fun Component.toLegacy(): String = LegacyComponentSerializer.legacySection().serialize(this)
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.tntleague.util
|
||||
|
||||
import de.steamwar.scoreboard.ScoreboardCallback
|
||||
import de.steamwar.tntleague.config.targetedBlocks
|
||||
import de.steamwar.tntleague.game.TNTLeagueGame
|
||||
import net.kyori.adventure.text.Component
|
||||
import org.bukkit.entity.Player
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
private val scoreboardTitle by lazy { tntLeaguePrefix.toLegacy() }
|
||||
|
||||
data class TNTLeagueScoreboard(val p: Player): ScoreboardCallback {
|
||||
override fun getData(): HashMap<String, Int> {
|
||||
val lines = mutableListOf<Component>()
|
||||
|
||||
lines.add(Component.space().green())
|
||||
|
||||
val minutes = TNTLeagueGame.gameTimeRemaining.floorDiv(60)
|
||||
val seconds = TNTLeagueGame.gameTimeRemaining.rem(60).toString().padStart(2, '0')
|
||||
lines.add(translate("scoreboardTime", minutes.toString().yellow(), seconds.yellow()).gray())
|
||||
|
||||
lines.add(Component.space().yellow())
|
||||
|
||||
with(TNTLeagueGame.blueTeam) {
|
||||
lines.add(translate("scoreboardTeam", translate(name).colorByTeam(this), (targetedBlocks - damagedBlocks).toString().yellow()).gray())
|
||||
}
|
||||
with(TNTLeagueGame.redTeam) {
|
||||
lines.add(translate("scoreboardTeam", translate(name).colorByTeam(this), (targetedBlocks - damagedBlocks).toString().yellow()).gray())
|
||||
}
|
||||
|
||||
lines.add(Component.space().gray())
|
||||
|
||||
return lines
|
||||
.foldIndexed(HashMap()) { index, acc, component -> acc.also { it[component.translate(p).toLegacy()] = index } }
|
||||
}
|
||||
|
||||
override fun getTitle(): String = scoreboardTitle
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
name: TNTLeague
|
||||
version: '1.0.0'
|
||||
main: de.steamwar.tntleague.TNTLeague
|
||||
load: POSTWORLD
|
||||
api-version: '1.21'
|
||||
dependencies:
|
||||
- name: SpigotCore
|
||||
required: true
|
||||
- name: KotlinCore
|
||||
required: true
|
||||
@@ -216,3 +216,5 @@ include(
|
||||
"VelocityCore",
|
||||
"VelocityCore:Persistent"
|
||||
)
|
||||
|
||||
include("TNTLeague")
|
||||
|
||||
Reference in New Issue
Block a user