Merge pull request 'Finish TNTLeague' (#53) from TNTLeague/finish into main

Reviewed-on: https://steamwar.de/devlabs/SteamWar/SteamWar/pulls/53
Reviewed-by: Lixfel <lixfel@steamwar.de>
This commit is contained in:
2024-12-11 10:53:39 +01:00
25 changed files with 461 additions and 266 deletions

View File

@ -32,4 +32,6 @@ tasks.shadowJar {
dependencies {
compileOnly(libs.paperapi21)
compileOnly(libs.nms21)
compileOnly(project(":SpigotCore"))
}

View File

@ -32,6 +32,7 @@ class KotlinCore : JavaPlugin() {
}
override fun onEnable() {
}
override fun onDisable() {

View File

@ -1,4 +1,23 @@
package de.steamwar.tntleague.inventory
/*
* 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.kotlin.inventory
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.InventoryCloseEvent

View File

@ -1,4 +1,23 @@
package de.steamwar.tntleague.util
/*
* 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.kotlin.util
import org.bukkit.Location
import org.bukkit.block.Block

View File

@ -1,3 +1,22 @@
/*
* 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/>.
*/
plugins {
steamwar.kotlin
}

View File

@ -1,19 +1,36 @@
/*
* 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
import de.steamwar.message.Message
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 de.steamwar.tntleague.game.TNTLeagueTeam
import org.bukkit.plugin.java.JavaPlugin
import java.util.*
lateinit var plugin: TNTLeague
lateinit var message: Message
class TNTLeague : JavaPlugin() {
init {
@ -22,18 +39,8 @@ class TNTLeague : JavaPlugin() {
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)
message = Message("de.steamwar.tntleague.TNTLeague", classLoader)
server.pluginManager.registerEvents(LobbyListener, this)
server.pluginManager.registerEvents(GlobalListener, this)
@ -46,3 +53,8 @@ class TNTLeague : JavaPlugin() {
LeaveCommand.register()
}
}
fun String.colorByTeam(team: TNTLeagueTeam?) = when (team) {
null -> "§7${this}"
else -> "§${team.color}${this}"
}

View File

@ -0,0 +1,49 @@
#
# 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/>.
#
PREFIX=§eTNT§7League§8»
JOIN=§e{0} §7joined the game!
JOIN_TEAM={0} §7joined the {1} §7team!
QUIT={0} §7left the game!
QUIT_TEAM={0} §7left the {1} §7team!
BLUE=blue
RED=red
SHUTDOWN=§7The server stops in §e{0} §7seconds!
TEAM_WIN=§aTeam {0} §awins!
NOT_ENOUGH_COINS=§cYou don't have enough coins to buy this item!
GAME_STARTING=§aThe game starts in §e{0} §aseconds!
GAME_START=
GAME_STARTED=§aThe game has started!
TIME_REMAINING=§e{0} §7minutes remaining!
GAME_ENDED=§cThe game has ended!
DRAW=§cThe game ended in a draw!
DEALER=Shopkeeper
DEALER_ITEM={0} {1}
DEALER_PRICE=
SCOREBOARD_TARGET=
SCOREBOARD_TIME=§7Time§8: §e{0}§8:§e{1}
SCOREBOARD_COINS=§7Coins§8: §e{0}
SCOREBOARD_TEAM=§7Team {0}§8: §e{1}
READY=§aReady
NOT_READY=§cNot ready
IS_READY=§aTeam {0} §ais ready!
IS_NOT_READY=§cTeam {0} §cis not ready!
INVITED={0} §ainvited you to join team {1}§7! *Click*
INVITED_HOVER=§7Click to join team {0}§7!
INVITED_PLAYER=§aInvited §e{0} §ato join your team!

View File

@ -0,0 +1,44 @@
#
# 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/>.
#
JOIN=§e{0} §7ist dem Spiel beigetreten!
JOIN_TEAM={0} §7ist Team {1} §7begetreten!
QUIT={0} §7hat das Spiel verlassen!
QUIT_TEAM={0} §7hat Team {1} §7verlassen!
BLUE=Blau
RED=Rot
SHUTDOWN=§7Der Server fährt in §e{0} §7Sekunden herunter!
TEAM_WIN=§aTeam {0} §agewinnt!
NOT_ENOUGH_COINS=§cDu hast nicht genug Coins, um dir das zu kaufen!
GAME_STARTING=§aDas Spiel beginnt in §e{0} §aSekunden!
GAME_STARTED=§aDas Spiel beginnt!
TIME_REMAINING=§7noch §e{0} §7Minuten!
GAME_ENDED=§cDas Spiel ist vorbei!
DRAW=§cKeiner hat gewonnen!
DEALER=Händler
DEALER_PRICE=§7Kosten: §e{0} Coins
SCOREBOARD_TARGET=§7Ziel§8: {0}
SCOREBOARD_TIME=§7Zeit§8: §e{0}§8:§e{1}
READY=§aBereit
NOT_READY=§cNicht bereit
IS_READY=§aTeam {0} §aist bereit!
IS_NOT_READY=§cTeam {0} §cist nicht bereit!
INVITED={0} §ahat dich in Team {1} §aeingeladen! *Klick*
INVITED_HOVER=§7Team {0} §7beitreten!
INVITED_PLAYER={0} §awurde eingeladen!

View File

@ -1,18 +0,0 @@
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=

View File

@ -1,37 +0,0 @@
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}<7D> {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!

View File

@ -21,10 +21,10 @@ package de.steamwar.tntleague.command
import de.steamwar.command.SWCommand
import de.steamwar.command.TypeValidator
import de.steamwar.tntleague.colorByTeam
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 de.steamwar.tntleague.message
import net.md_5.bungee.api.chat.ClickEvent
import org.bukkit.entity.Player
object InviteCommand: SWCommand("invite") {
@ -37,16 +37,11 @@ object InviteCommand: SWCommand("invite") {
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
message
.send("INVITED", target, message.parse("INVITED_HOVER", target, team.name.colorByTeam(team)),
ClickEvent(ClickEvent.Action.RUN_COMMAND, "/accept "), sender.name, team.name.colorByTeam(team), )
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())
message.send("INVITED_PLAYER", sender, target.name)
}
@Validator("isLeader", local = false)

View File

@ -26,5 +26,5 @@ import org.bukkit.entity.Player
object LeaveCommand: SWCommand("leave", "l") {
@Register
fun leave(player: Player) = TNTLeagueGame.getTeam(player)?.remove(player)
fun leave(player: Player) = TNTLeagueGame.getTeam(player)?.remove(player) ?: Unit
}

View File

@ -1,3 +1,22 @@
/*
* 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.config
import de.steamwar.tntleague.plugin

View File

@ -1,13 +1,31 @@
/*
* 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.config
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.Area
import de.steamwar.tntleague.util.translate
import de.steamwar.kotlin.util.Area
import net.kyori.adventure.text.Component
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
@ -62,7 +80,7 @@ object TNTLeagueWorldConfig {
private fun spawnDealer(loc: Location) = world.spawn(loc, WanderingTrader::class.java)
.apply {
customName(translate("dealer"))
customName(Component.text("Shop"))
isCustomNameVisible = false
isInvulnerable = true
isSilent = true

View File

@ -21,5 +21,4 @@ package de.steamwar.tntleague.events
import org.bukkit.event.Listener
object DummyListener: Listener {
}
object DummyListener: Listener

View File

@ -1,12 +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.events
import de.steamwar.tntleague.colorByTeam
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 de.steamwar.kotlin.inventory.SWInventoryHolder
import de.steamwar.tntleague.message
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.event.EventHandler
@ -27,7 +45,7 @@ object GlobalListener: Listener {
with(e.player) {
teleport(TNTLeagueWorldConfig.lobby)
inventory.clear()
plugin.server.broadcast(translate("join", name.bold()).basic())
message.broadcast("JOIN", name)
isOp = false
gameMode = GameMode.SPECTATOR
respawnLocation = TNTLeagueWorldConfig.lobby
@ -37,7 +55,7 @@ object GlobalListener: Listener {
@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())
message.broadcast("QUIT", e.player.name.colorByTeam(TNTLeagueGame.getTeam(e.player)))
TNTLeagueGame.playerLeave(e.player)
}
@ -67,6 +85,7 @@ object GlobalListener: Listener {
fun onPlayerDeath(e: PlayerDeathEvent) {
e.deathMessage(null)
e.drops.clear()
e.keepInventory = true
e.itemsToKeep.removeIf { it.type != Material.DIAMOND_PICKAXE }
}
@ -78,11 +97,4 @@ object GlobalListener: Listener {
null -> e.respawnLocation = TNTLeagueWorldConfig.lobby
}
}
@EventHandler
fun onChat(e: AsyncChatEvent) {
e.renderer { source, sourceDisplayName, message, _ ->
translate("chat", sourceDisplayName.colorByTeam(TNTLeagueGame.getTeam(source)), message).basic()
}
}
}

View File

@ -1,3 +1,22 @@
/*
* 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 de.steamwar.scoreboard.SWScoreboard
@ -13,12 +32,11 @@ 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.PlayerAttemptPickupItemEvent
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 {
@ -26,7 +44,7 @@ object IngameListener: Listener {
fun onEntityInteract(e: PlayerInteractEntityEvent) {
if (e.player.gameMode == GameMode.SPECTATOR) return
if(e.rightClicked.type == EntityType.VILLAGER) {
if(e.rightClicked.type == EntityType.WANDERING_TRADER) {
e.isCancelled = true
e.player.openInventory(DealerInventory(e.player).getInventory())
}
@ -63,6 +81,17 @@ object IngameListener: Listener {
}
}
@EventHandler
fun onPickupCoins(e: PlayerAttemptPickupItemEvent) {
if (e.item.itemStack.isSimilar(DealerInventory.coins)) {
TNTLeagueGame.getTeam(e.player)?.coins = e.item.itemStack.amount + (TNTLeagueGame.getTeam(e.player)?.coins ?: 0)
e.item.itemStack.amount = 0
e.isCancelled = true
e.flyAtPlayer = true
}
}
private fun getTeamByTargetLocation(location: Location): TNTLeagueTeam? =
when (location) {
in TNTLeagueWorldConfig.redTeam.target -> TNTLeagueGame.redTeam

View File

@ -1,22 +1,33 @@
/*
* 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 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.GameMode
import org.bukkit.entity.EntityType
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
import org.bukkit.event.player.*
object LobbyListener: Listener {
@ -30,8 +41,7 @@ object LobbyListener: Listener {
@EventHandler(priority = EventPriority.LOWEST)
fun onPlayerQuit(e: PlayerQuitEvent) {
val team = TNTLeagueGame.getTeam(e.player) ?: return
team.leave(e.player)
TNTLeagueGame.getTeam(e.player)?.leave(e.player)
}
@EventHandler
@ -58,4 +68,13 @@ object LobbyListener: Listener {
e.isCancelled = true
}
}
@EventHandler
fun onEntityInteract(e: PlayerInteractEntityEvent) {
if (e.player.gameMode == GameMode.SPECTATOR) return
if(e.rightClicked.type == EntityType.WANDERING_TRADER) {
e.isCancelled = true
}
}
}

View File

@ -1,9 +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.game
import de.steamwar.kotlin.util.Area
import de.steamwar.scoreboard.SWScoreboard
import de.steamwar.sql.Fight
import de.steamwar.sql.FightPlayer
import de.steamwar.sql.SteamwarUser
import de.steamwar.tntleague.colorByTeam
import de.steamwar.tntleague.config.TNTLeagueConfig
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.config.world
@ -11,6 +32,7 @@ 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.message
import de.steamwar.tntleague.plugin
import de.steamwar.tntleague.util.*
import net.kyori.adventure.bossbar.BossBar
@ -18,12 +40,9 @@ 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
@ -62,7 +81,7 @@ object TNTLeagueGame {
blueTeam.start()
redTeam.start()
plugin.server.broadcast(translate("gameStarted").success())
message.broadcast("GAME_STARTED")
val tnt = ItemStack(Material.TNT)
@ -87,7 +106,7 @@ object TNTLeagueGame {
}
if (gameTimeRemaining % 300 == 0) {
plugin.server.broadcast(translate("timeRemaining", (gameTimeRemaining / 60).toString().yellow()).basic())
message.broadcast("TIME_REMAINING", (gameTimeRemaining / 60))
plugin.server.onlinePlayers.forEach { it.playSound(Sound.sound(org.bukkit.Sound.BLOCK_NOTE_BLOCK_PLING.key, Sound.Source.MASTER, 1f, 1f)) }
}
}, 20, 20)
@ -105,7 +124,7 @@ object TNTLeagueGame {
it.playSound(Sound.sound(org.bukkit.Sound.ENTITY_ENDER_DRAGON_DEATH.key, Sound.Source.MASTER, 1f, 1f))
}
plugin.server.broadcast(translate("gameEnded").success())
message.broadcast("GAME_ENDED")
spawnerTask.cancel()
@ -116,7 +135,7 @@ object TNTLeagueGame {
plugin.server.shutdown()
}
plugin.server.broadcast(translate("shutdown", shutdown.toString().yellow()).basic())
message.broadcast("SHUTDOWN", shutdown)
shutdown--
}, 20, 20)
@ -135,19 +154,15 @@ object TNTLeagueGame {
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) }
message.broadcast("GAME_STARTING", countdown.toString())
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) }
if (--countdown == 0) {
plugin.server.onlinePlayers.forEach { it.level = 0 }
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) }
plugin.server.onlinePlayers.forEach { it.level = countdown }
}
}, 20, 20)
@ -162,7 +177,7 @@ object TNTLeagueGame {
redTeam.invites.remove(player)
getTeam(player)?.apply {
members.remove(player)
if (leader == player) {
if (leader == player && members.isNotEmpty() && state == GameState.RUNNING) {
win(this.opposite, WinReason.LEAVE)
}
}
@ -173,7 +188,6 @@ object TNTLeagueGame {
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
}
}
@ -181,7 +195,7 @@ object TNTLeagueGame {
fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) {
if (state != GameState.RUNNING) return
end()
plugin.server.broadcast(translate("teamWin", translate(tntLeagueTeam.name).color(tntLeagueTeam.color)).success())
plugin.server.onlinePlayers.forEach { message.send("TEAM_WIN", it, message.parse(tntLeagueTeam.name, it).colorByTeam(tntLeagueTeam)) }
statistic(tntLeagueTeam, reason)
explode(tntLeagueTeam.opposite)
}
@ -189,7 +203,7 @@ object TNTLeagueGame {
fun draw(reason: WinReason) {
if (state != GameState.RUNNING) return
end()
plugin.server.broadcast(translate("draw").success())
message.broadcast("DRAW")
statistic(null, reason)
}

View File

@ -1,18 +1,36 @@
/*
* 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.game
import de.steamwar.tntleague.colorByTeam
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.config.targetedBlocks
import de.steamwar.tntleague.message
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 net.kyori.adventure.text.Component
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) {
@ -33,7 +51,7 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
val name: String
get() = team.name.lowercase()
val color: TextColor
val color: Char
get() = team.color
var isReady: Boolean = false
@ -42,13 +60,7 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
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()
}
}) }
message.broadcastActionbar(if (value) "IS_READY" else "IS_NOT_READY", name.colorByTeam(this))
if (value && opposite.isReady) {
TNTLeagueGame.checkStart()
@ -63,6 +75,8 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
}
}
var coins: Int = 0
val opposite: TNTLeagueTeam
get() = when (team) {
Team.BLUE -> TNTLeagueGame.redTeam
@ -76,7 +90,7 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
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())
message.broadcast("JOIN_TEAM", name.colorByTeam(this@TNTLeagueTeam), this@TNTLeagueTeam.name.colorByTeam(this@TNTLeagueTeam))
}
if (leader == null) {
@ -89,13 +103,13 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
fun readyItem() = if (isReady) {
ItemStack.of(Material.LIME_DYE).apply {
itemMeta = itemMeta.apply {
displayName(translate("ready").green().translate(leader!!))
displayName(Component.text(message.parse("READY", leader!!)))
}
}
} else {
ItemStack.of(Material.RED_DYE).apply {
itemMeta = itemMeta.apply {
displayName(translate("notReady").red().translate(leader!!))
displayName(Component.text(message.parse("NOT_READY", leader!!)))
}
}
}
@ -113,9 +127,7 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
}
fun leave(player: Player) {
if (TNTLeagueGame.state == TNTLeagueGame.GameState.RUNNING) {
TNTLeagueGame.playerLeave(player)
} else {
if (TNTLeagueGame.state != TNTLeagueGame.GameState.RUNNING) {
members.remove(player)
if (members.isEmpty()) {
@ -125,6 +137,10 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
}
if (leader == player) {
leader = members.firstOrNull()
if (leader == null && TNTLeagueGame.state == TNTLeagueGame.GameState.LOBBY) {
isReady = false
}
}
}
}
@ -135,12 +151,12 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
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())
message.broadcast("QUIT_TEAM", name.colorByTeam(this@TNTLeagueTeam), this@TNTLeagueTeam.name.colorByTeam(this@TNTLeagueTeam))
}
}
enum class Team(val color: TextColor) {
BLUE(NamedTextColor.BLUE),
RED(NamedTextColor.RED);
enum class Team(val color: Char) {
BLUE('9'),
RED('c');
}
}

View File

@ -1,11 +1,32 @@
/*
* 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.inventory
import de.steamwar.kotlin.inventory.SWInventoryHolder
import de.steamwar.tntleague.config.TNTLeagueConfig
import de.steamwar.tntleague.game.TNTLeagueGame
import de.steamwar.tntleague.message
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.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import org.bukkit.Material
import org.bukkit.NamespacedKey
@ -16,7 +37,7 @@ import org.bukkit.persistence.PersistentDataType
import java.util.*
import kotlin.math.ceil
class DealerInventory(player: Player): SWInventoryHolder() {
class DealerInventory(val player: Player): SWInventoryHolder() {
init {
items.forEachIndexed { index, item ->
@ -24,19 +45,21 @@ class DealerInventory(player: Player): SWInventoryHolder() {
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())
val team = TNTLeagueGame.getTeam(player) ?: return@to
if (team.coins < price) {
message.send("NOT_ENOUGH_COINS", player)
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))
team.coins -= 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())
override fun createInventory(): Inventory = plugin.server.createInventory(this, ceil(TNTLeagueConfig.config.prices.size / 9f).toInt() * 9, Component.text(message.parse("DEALER", player)))
companion object {
private val priceKey = NamespacedKey(plugin, "price")
@ -45,7 +68,7 @@ class DealerInventory(player: Player): SWInventoryHolder() {
val coins = ItemStack(Material.RAW_GOLD).apply {
itemMeta = itemMeta.apply {
displayName(Component.text("Coins").bold().gold())
displayName(Component.text("Coins").color(NamedTextColor.GOLD))
persistentDataContainer.apply {
set(coinKey, PersistentDataType.BOOLEAN, true)
}
@ -58,11 +81,13 @@ class DealerInventory(player: Player): SWInventoryHolder() {
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()))
displayName(Component.text(material.name.lowercase().replace("_", " ")
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() })
.color(NamedTextColor.GRAY).appendSpace().append(Component.text(price.amount).color(NamedTextColor.YELLOW)))
amount = price.amount
lore(listOf(price.price.toString().component().yellow().bold().appendSpace().append(Component.text("Coins").yellow())))
lore(listOf(Component.text(price.price).color(NamedTextColor.YELLOW).decorate(TextDecoration.BOLD).appendSpace().append(Component.text("Coins").color(NamedTextColor.YELLOW))))
persistentDataContainer.apply {
set(priceKey, PersistentDataType.INTEGER, price.price)
set(amountKey, PersistentDataType.INTEGER, price.amount)

View File

@ -1,73 +0,0 @@
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)

View File

@ -20,38 +20,46 @@
package de.steamwar.tntleague.util
import de.steamwar.scoreboard.ScoreboardCallback
import de.steamwar.tntleague.colorByTeam
import de.steamwar.tntleague.config.targetedBlocks
import de.steamwar.tntleague.game.TNTLeagueGame
import net.kyori.adventure.text.Component
import de.steamwar.tntleague.game.TNTLeagueTeam
import de.steamwar.tntleague.message
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>()
val lines = mutableListOf<String>()
lines.add(Component.space().green())
lines.add("§1")
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(message.parse("SCOREBOARD_TIME", p, minutes, seconds))
lines.add(Component.space().yellow())
lines.add("§2")
with(TNTLeagueGame.blueTeam) {
lines.add(translate("scoreboardTeam", translate(name).colorByTeam(this), (targetedBlocks - damagedBlocks).toString().yellow()).gray())
when (val team = TNTLeagueGame.getTeam(p)) {
is TNTLeagueTeam -> lines.add(message.parse("SCOREBOARD_COINS", p, team.coins))
else -> lines.add(message.parse("SCOREBOARD_COINS", p,
"§${TNTLeagueGame.blueTeam.color}${TNTLeagueGame.blueTeam.coins}§8:§${TNTLeagueGame.redTeam.color}${TNTLeagueGame.redTeam.coins}"))
}
lines.add("§3")
with(TNTLeagueGame.redTeam) {
lines.add(translate("scoreboardTeam", translate(name).colorByTeam(this), (targetedBlocks - damagedBlocks).toString().yellow()).gray())
lines.add(message.parse("SCOREBOARD_TEAM", p, message.parse(name, p).colorByTeam(this), targetedBlocks - damagedBlocks))
}
with(TNTLeagueGame.blueTeam) {
lines.add(message.parse("SCOREBOARD_TEAM", p, message.parse(name, p).colorByTeam(this), targetedBlocks - damagedBlocks))
}
lines.add(Component.space().gray())
lines.add("§4")
return lines
.foldIndexed(HashMap()) { index, acc, component -> acc.also { it[component.translate(p).toLegacy()] = index } }
.foldIndexed(HashMap()) { index, acc, component -> acc.also { it[component] = index } }
}
override fun getTitle(): String = scoreboardTitle
override fun getTitle(): String = message.parse("PREFIX", p).dropLast(1)
}

View File

@ -4,7 +4,10 @@ main: de.steamwar.tntleague.TNTLeague
load: POSTWORLD
api-version: '1.21'
dependencies:
- name: SpigotCore
required: true
- name: KotlinCore
required: true
server:
SpigotCore:
required: true
load: BEFORE
KotlinCore:
required: true
load: BEFORE

View File

@ -7,7 +7,8 @@ artifacts:
"/binarys/fightsystem.jar": "FightSystem/build/libs/FightSystem-all.jar"
"/binarys/FightSystem_Standalone.jar": "FightSystem/FightSystem_Standalone/build/libs/FightSystem_Standalone-all.jar"
# KotlinCore !!
"/binarys/kotlincore.jar": "KotlinCore/build/libs/KotlinCore-all.jar"
"/binarys/tntleague.jar": "TNTLeague/build/libs/TNTLeague.jar"
"/binarys/lobbysystem2.0.jar": "LobbySystem/build/libs/LobbySystem.jar"