Add Some Fixes and Improvements

This commit is contained in:
2024-12-22 21:57:54 +01:00
parent 1ea6bd61f8
commit aaa808f90f
14 changed files with 242 additions and 58 deletions
@@ -71,6 +71,13 @@ public class Message {
pattern = fromRB(resourceBundle, "PREFIX") + " ";
pattern += fromRB(resourceBundle, message);
for (int i = 0; i < params.length; i++) {
if (params[i] instanceof SubMessage) {
SubMessage smsg = (SubMessage) params[i];
params[i] = parse(smsg.getMessage(), sender, smsg.getParams());
}
}
return new MessageFormat(pattern, locale).format(params);
}
@@ -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.message;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class SubMessage {
private final String message;
private final Object[] params = new Object[0];
}
@@ -22,8 +22,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
BLUE=§3Blue
RED=§cRed
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!
@@ -46,4 +46,12 @@ 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!
INVITED_PLAYER=§aInvited §e{0} §ato join your team!
DEALER_REDSTONE=§eRedstone
DEALER_BLOCKS=§eBlocks
DEALER_TOOLS=§eTools
DEALER_ANGLES=§eAngle Blocks
PARTICIPANT_CHAT={0} {1}§8» §7{2}
SPECTATOR_CHAT=§7{0}§8» §7{1}
@@ -20,8 +20,8 @@ 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
BLUE=§3Blau
RED=§cRot
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!
@@ -41,4 +41,8 @@ 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!
INVITED_PLAYER={0} §awurde eingeladen!
DEALER_BLOCKS=§eBlöcke
DEALER_TOOLS=§eWerkzeuge
DEALER_ANGLES=§eAuflageblöcke
@@ -21,6 +21,7 @@ package de.steamwar.tntleague.command
import de.steamwar.command.SWCommand
import de.steamwar.command.TypeValidator
import de.steamwar.message.SubMessage
import de.steamwar.tntleague.colorByTeam
import de.steamwar.tntleague.game.TNTLeagueGame
import de.steamwar.tntleague.message
@@ -38,8 +39,8 @@ object InviteCommand: SWCommand("invite") {
team.invites.add(target)
message
.send("INVITED", target, message.parse("INVITED_HOVER", target, team.name.colorByTeam(team)),
ClickEvent(ClickEvent.Action.RUN_COMMAND, "/accept " + sender.name), sender.name, team.name.colorByTeam(team), )
.send("INVITED", target, message.parse("INVITED_HOVER", target, SubMessage(team.name)),
ClickEvent(ClickEvent.Action.RUN_COMMAND, "/accept " + sender.name), sender.name.colorByTeam(team), SubMessage(team.name), )
message.send("INVITED_PLAYER", sender, target.name)
}
@@ -23,12 +23,21 @@ import de.steamwar.tntleague.plugin
import org.bukkit.Material
import org.bukkit.configuration.ConfigurationSection
import org.bukkit.configuration.file.FileConfiguration
import org.bukkit.enchantments.Enchantment
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.meta.BookMeta
import org.bukkit.inventory.meta.Damageable
import org.bukkit.inventory.meta.ItemMeta
import java.util.UUID
data class TNTLeagueConfig(
val startDelay: Int = 10,
val gameTime: Int = 60 * 20,
val prices: Map<Material, Price>
val prices: Map<Material, Price>,
val blueLeader: UUID? = System.getProperty("blueLeader")?.let { UUID.fromString(it) },
val redLeader: UUID? = System.getProperty("redLeader")?.let { UUID.fromString(it) },
) {
companion object {
val config: TNTLeagueConfig by lazy { loadConfig(plugin.config) }
@@ -41,14 +50,48 @@ data class TNTLeagueConfig(
return config.getKeys(false).associateWith {
Price(
config.getInt("$it.amount"),
config.getInt("$it.price")
config.getInt("$it.price"),
config.getString("$it.category")?.let { s -> categoryFromString[s] } ?: ItemCategory.REDSTONE,
config.getInt("$it.pinned", -1),
config.getStringList("$it.extras").map { s -> extrasFromString[s]!! }.toSet()
)
}.mapKeys { Material.getMaterial(it.key)!! }
}.mapKeys { Material.getMaterial(it.key) ?: Material.BARRIER }
}
}
data class Price(
val amount: Int,
val price: Int,
val category: ItemCategory,
val pinned: Int,
val extras: Set<ItemExtra>
)
enum class ItemCategory {
REDSTONE,
BLOCKS,
TOOLS,
ANGLES;
}
enum class ItemExtra(val func: (item: ItemMeta) -> ItemMeta) {
ONESHOT({
if (it is Damageable) {
it.damage = 384
}
it
}),
FLAME({
it.addEnchant(Enchantment.FLAME, 1, false)
it
}),
UNBREAKING({
it.addEnchant(Enchantment.UNBREAKING, 1, false)
it
})
}
}
val categoryFromString = TNTLeagueConfig.ItemCategory.entries.associateBy { it.name.lowercase() }
val extrasFromString = TNTLeagueConfig.ItemExtra.entries.associateBy { it.name.lowercase() }
@@ -69,6 +69,7 @@ object TNTLeagueWorldConfig {
minHeight = config.getInt("minHeight", 0)
target = config.getInt("target", -1)
} catch (e: NullPointerException) {
e.printStackTrace()
Bukkit.shutdown()
}
}
@@ -19,14 +19,14 @@
package de.steamwar.tntleague.events
import de.steamwar.kotlin.inventory.SWInventoryHolder
import de.steamwar.message.SubMessage
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.kotlin.inventory.SWInventoryHolder
import de.steamwar.tntleague.message
import de.steamwar.tntleague.plugin
import org.bukkit.Bukkit
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.event.EventHandler
@@ -35,10 +35,8 @@ import org.bukkit.event.Listener
import org.bukkit.event.entity.PlayerDeathEvent
import org.bukkit.event.inventory.CraftItemEvent
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
import org.bukkit.event.player.*
import java.util.logging.Level
object GlobalListener: Listener {
@@ -48,7 +46,6 @@ object GlobalListener: Listener {
with(e.player) {
teleport(TNTLeagueWorldConfig.lobby)
inventory.clear()
message.broadcast("JOIN", name)
isOp = false
gameMode = GameMode.SPECTATOR
respawnLocation = TNTLeagueWorldConfig.lobby
@@ -58,7 +55,6 @@ object GlobalListener: Listener {
@EventHandler(priority = EventPriority.HIGHEST)
fun onPlayerQuit(e: PlayerQuitEvent) {
e.quitMessage(null)
message.broadcast("QUIT", e.player.name.colorByTeam(TNTLeagueGame.getTeam(e.player)))
TNTLeagueGame.playerLeave(e.player)
if (plugin.server.onlinePlayers.size == 1 && plugin.server.onlinePlayers.first() == e.player) {
@@ -109,4 +105,20 @@ object GlobalListener: Listener {
null -> e.respawnLocation = TNTLeagueWorldConfig.lobby
}
}
@EventHandler
fun handlePlayerChat(event: AsyncPlayerChatEvent) {
val player = event.player
val msg = event.message
val fightTeam = TNTLeagueGame.getTeam(player)
if (fightTeam != null) {
message.broadcastPrefixless("PARTICIPANT_CHAT", SubMessage(fightTeam.name), player.name, msg)
} else {
message.broadcastPrefixless("SPECTATOR_CHAT", player.name, msg)
}
event.isCancelled = true
}
}
@@ -33,7 +33,7 @@ object LobbyListener: Listener {
@EventHandler
fun onPlayerJoin(e: PlayerJoinEvent) {
TNTLeagueGame.getFreeTeam()?.run {
TNTLeagueGame.getFreeTeam(e.player)?.run {
join(e.player)
TNTLeagueGame.checkStart()
}
@@ -20,6 +20,9 @@
package de.steamwar.tntleague.game
import de.steamwar.kotlin.util.Area
import de.steamwar.message.SubMessage
import de.steamwar.network.NetworkSender
import de.steamwar.network.packets.common.FightInfoPacket
import de.steamwar.scoreboard.SWScoreboard
import de.steamwar.sql.Fight
import de.steamwar.sql.FightPlayer
@@ -80,6 +83,8 @@ object TNTLeagueGame {
blueTeam.start()
redTeam.start()
updateFightinfo()
message.broadcast("GAME_STARTED")
val tnt = ItemStack(Material.TNT)
@@ -143,13 +148,17 @@ object TNTLeagueGame {
shutdown--
}, 20, 20)
updateFightinfo()
}
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 getFreeTeam(player: Player) = if (blueTeam.leader == null && (TNTLeagueConfig.config.blueLeader == null || player.uniqueId == TNTLeagueConfig.config.blueLeader)) blueTeam
else if (redTeam.leader == null && TNTLeagueConfig.config.redLeader == null || player.uniqueId == TNTLeagueConfig.config.redLeader) redTeam
else null
fun checkStart() {
if (blueTeam.isReady && redTeam.isReady) {
@@ -173,6 +182,8 @@ object TNTLeagueGame {
if (task == -1) {
error("Failed to start countdown task")
}
updateFightinfo()
}
}
@@ -191,12 +202,14 @@ object TNTLeagueGame {
task = task?.also { plugin.server.scheduler.cancelTask(it) }.let { null }
state = GameState.LOBBY
}
updateFightinfo()
}
fun win(tntLeagueTeam: TNTLeagueTeam, reason: WinReason) {
if (state != GameState.RUNNING) return
end()
plugin.server.onlinePlayers.forEach { message.send("TEAM_WIN", it, message.parse(tntLeagueTeam.name, it).colorByTeam(tntLeagueTeam)) }
plugin.server.onlinePlayers.forEach { message.send("TEAM_WIN", it, SubMessage(tntLeagueTeam.name)) }
explode(tntLeagueTeam.opposite)
}
@@ -217,18 +230,23 @@ object TNTLeagueGame {
}
}
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
)
}
fun updateFightinfo() {
NetworkSender.send(FightInfoPacket(
plugin.server.worlds.first().name,
"TNTLeague",
"",
blueTeam.name.colorByTeam(blueTeam),
redTeam.name.colorByTeam(redTeam),
state.name,
TNTLeagueConfig.config.gameTime - gameTimeRemaining,
blueTeam.leader?.let { SteamwarUser.get(it.uniqueId).id } ?: 0,
redTeam.leader?.let { SteamwarUser.get(it.uniqueId).id } ?: 0,
0,
0,
blueTeam.members.map { SteamwarUser.get(it.uniqueId).id },
redTeam.members.map { SteamwarUser.get(it.uniqueId).id },
plugin.server.onlinePlayers.filter {! blueTeam.members.contains(it) && !redTeam.members.contains(it) }.map { SteamwarUser.get(it.uniqueId).id }
))
}
enum class GameState(val listener: Listener) {
@@ -19,10 +19,12 @@
package de.steamwar.tntleague.game
import de.steamwar.message.SubMessage
import de.steamwar.tntleague.colorByTeam
import de.steamwar.tntleague.config.TNTLeagueWorldConfig
import de.steamwar.tntleague.config.targetedBlocks
import de.steamwar.tntleague.game.TNTLeagueGame.WinReason
import de.steamwar.tntleague.game.TNTLeagueGame.updateFightinfo
import de.steamwar.tntleague.game.TNTLeagueGame.win
import de.steamwar.tntleague.message
import de.steamwar.tntleague.plugin
@@ -62,7 +64,7 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
leader?.inventory?.setItem(4, readyItem())
leader?.let { it.playSound(it.location, Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1f) }
message.broadcastActionbar(if (value) "IS_READY" else "IS_NOT_READY", name.colorByTeam(this))
message.broadcastActionbar(if (value) "IS_READY" else "IS_NOT_READY", SubMessage(name))
if (value && opposite.isReady) {
TNTLeagueGame.checkStart()
@@ -73,7 +75,7 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
set(value) {
field = value
if (value >= targetedBlocks) {
TNTLeagueGame.win(this, TNTLeagueGame.WinReason.DESTROYED)
win(this, WinReason.DESTROYED)
}
}
@@ -92,13 +94,15 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
teleport(config.spawnLocation)
gameMode = GameMode.ADVENTURE
inventory.clear()
message.broadcast("JOIN_TEAM", name.colorByTeam(this@TNTLeagueTeam), this@TNTLeagueTeam.name.colorByTeam(this@TNTLeagueTeam))
message.broadcast("JOIN_TEAM", name.colorByTeam(this@TNTLeagueTeam), SubMessage(name))
}
if (leader == null) {
leader = player
}
updateFightinfo()
return true
}
@@ -156,12 +160,13 @@ data class TNTLeagueTeam(val config: TNTLeagueWorldConfig.TeamConfig, private va
teleport(TNTLeagueWorldConfig.lobby)
gameMode = GameMode.SPECTATOR
inventory.clear()
message.broadcast("QUIT_TEAM", name.colorByTeam(this@TNTLeagueTeam), this@TNTLeagueTeam.name.colorByTeam(this@TNTLeagueTeam))
message.broadcast("QUIT_TEAM", name.colorByTeam(this@TNTLeagueTeam), SubMessage(name))
}
updateFightinfo()
}
enum class Team(val color: Char) {
BLUE('9'),
BLUE('3'),
RED('c');
}
}
@@ -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.inventory
import de.steamwar.kotlin.inventory.SWInventoryHolder
import de.steamwar.tntleague.config.TNTLeagueConfig
import de.steamwar.tntleague.inventory.DealerInventory.Companion.buyItem
import de.steamwar.tntleague.inventory.DealerInventory.Companion.itemsByCategory
import de.steamwar.tntleague.message
import net.kyori.adventure.text.Component
import org.bukkit.Bukkit
import org.bukkit.entity.Player
import org.bukkit.inventory.Inventory
class CategoryInventory(val player: Player, category: TNTLeagueConfig.ItemCategory): SWInventoryHolder() {
override fun createInventory(): Inventory = Bukkit.createInventory(this, 9 * 6, Component.text(message.parse("DEALER", player)))
init {
itemsByCategory[category]!!.forEachIndexed { index, item ->
this[index] = item.first to { buyItem(player, item, it) }
}
}
}
@@ -19,6 +19,7 @@
package de.steamwar.tntleague.inventory
import de.steamwar.inventory.SWItem
import de.steamwar.kotlin.inventory.SWInventoryHolder
import de.steamwar.tntleague.config.TNTLeagueConfig
import de.steamwar.tntleague.game.TNTLeagueGame
@@ -31,6 +32,7 @@ import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.Sound
import org.bukkit.entity.Player
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType
@@ -40,26 +42,19 @@ import kotlin.math.ceil
class DealerInventory(val 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
this[10] = SWItem(Material.REDSTONE_BLOCK, message.parse("DEALER_REDSTONE", player)).itemStack to openCategory(TNTLeagueConfig.ItemCategory.REDSTONE)
this[12] = SWItem(Material.END_STONE, message.parse("DEALER_BLOCKS", player)).itemStack to openCategory(TNTLeagueConfig.ItemCategory.BLOCKS)
this[14] = SWItem(Material.DIAMOND_PICKAXE, message.parse("DEALER_TOOLS", player)).itemStack to openCategory(TNTLeagueConfig.ItemCategory.TOOLS)
this[16] = SWItem(Material.BREWING_STAND, message.parse("DEALER_ANGLES", player)).itemStack to openCategory(TNTLeagueConfig.ItemCategory.ANGLES)
val team = TNTLeagueGame.getTeam(player) ?: return@to
if (team.coins < price) {
message.send("NOT_ENOUGH_COINS", player)
player.playSound(player, Sound.ENTITY_VILLAGER_HURT, 1f, 1f)
return@to
}
team.coins -= price
player.inventory.addItem(ItemStack.of(item.first.type, amount))
}
pinnedItems.forEach { item ->
this[item.key] = item.value.first to { buyItem(player, item.value, it) }
}
}
override fun createInventory(): Inventory = plugin.server.createInventory(this, ceil(TNTLeagueConfig.config.prices.size / 9f).toInt() * 9, Component.text(message.parse("DEALER", player)))
private fun openCategory(cat: TNTLeagueConfig.ItemCategory): (e: InventoryClickEvent) -> Unit = { player.openInventory(CategoryInventory(player, cat).inventory) }
override fun createInventory(): Inventory = plugin.server.createInventory(this, 6 * 9, Component.text(message.parse("DEALER", player)))
companion object {
private val priceKey = NamespacedKey(plugin, "price")
@@ -75,7 +70,23 @@ class DealerInventory(val player: Player): SWInventoryHolder() {
}
}
val items by lazy {
fun buyItem(player: Player, item: Pair<ItemStack, TNTLeagueConfig.Price>, e: InventoryClickEvent) {
val price = item.second.price * if (e.isShiftClick) 5 else 1
val amount = item.second.amount * if (e.isShiftClick) 5 else 1
val team = TNTLeagueGame.getTeam(player) ?: return
if (team.coins < price) {
message.send("NOT_ENOUGH_COINS", player)
player.playSound(player, Sound.ENTITY_VILLAGER_HURT, 1f, 1f)
return
}
team.coins -= price
player.inventory.addItem(ItemStack.of(item.first.type, amount).also { item.second.extras.forEach { extra -> it.itemMeta = extra.func(it.itemMeta) } })
}
private val items by lazy {
val prices = TNTLeagueConfig.config.prices
prices.map { (material, price) ->
@@ -96,5 +107,8 @@ class DealerInventory(val player: Player): SWInventoryHolder() {
} to price
}
}
val itemsByCategory = items.groupBy { it.second.category }
val pinnedItems = items.filter { it.second.pinned != -1 }.associateBy { it.second.pinned }
}
}
@@ -19,6 +19,7 @@
package de.steamwar.tntleague.util
import de.steamwar.message.SubMessage
import de.steamwar.scoreboard.ScoreboardCallback
import de.steamwar.tntleague.colorByTeam
import de.steamwar.tntleague.config.targetedBlocks
@@ -49,10 +50,10 @@ data class TNTLeagueScoreboard(val p: Player): ScoreboardCallback {
lines.add("§3")
with(TNTLeagueGame.redTeam) {
lines.add(message.parse("SCOREBOARD_TEAM", p, message.parse(name, p).colorByTeam(this), targetedBlocks - damagedBlocks))
lines.add(message.parse("SCOREBOARD_TEAM", p, SubMessage(name), targetedBlocks - damagedBlocks))
}
with(TNTLeagueGame.blueTeam) {
lines.add(message.parse("SCOREBOARD_TEAM", p, message.parse(name, p).colorByTeam(this), targetedBlocks - damagedBlocks))
lines.add(message.parse("SCOREBOARD_TEAM", p, SubMessage(name), targetedBlocks - damagedBlocks))
}
lines.add("§4")