Compare commits
40
Commits
cf011ae72f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dce9d9f30 | ||
|
|
95219c17bc | ||
|
|
d412812dcb | ||
|
|
21f12904ac | ||
|
|
416c44a8f3 | ||
|
|
fc7b5c76a6 | ||
|
|
9198af6c29 | ||
|
|
d5266b659b | ||
|
|
e9e7c1cc80 | ||
|
|
40f52a4a45 | ||
|
|
3906bd747b | ||
|
|
0d757aa2ab | ||
|
|
5f35866e7a | ||
|
|
491e6a6050 | ||
|
|
244f8f6df7 | ||
|
|
fe10d57978 | ||
|
|
8d05658214 | ||
|
|
7324177250 | ||
|
|
c7161f707c | ||
|
|
5ce26782f2 | ||
|
|
a137ef4cc6 | ||
|
|
62c9661c82 | ||
|
|
936cabfd42 | ||
|
|
38f8ffd4a3 | ||
|
|
1a9f4cbd2d | ||
|
|
99bff5446d | ||
|
|
f9e00b04cc | ||
|
|
a7ce376abb | ||
|
|
897c8ab14d | ||
|
|
a3085ad4b4 | ||
|
|
29c46ba485 | ||
|
|
ca7b6f1152 | ||
|
|
4bf6ca9dc4 | ||
|
|
f0d3a20283 | ||
|
|
233cffd2df | ||
|
|
dd060bce48 | ||
|
|
10a6103f32 | ||
|
|
646b9a2a35 | ||
|
|
09b48fbafe | ||
|
|
6cd5315d28 |
@@ -2,17 +2,10 @@ package com.alttd.hunger_games;
|
||||
|
||||
import com.alttd.hunger_games.commands.BaseCommand;
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.config.LootItems;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.event_listeners.ChestListener;
|
||||
import com.alttd.hunger_games.event_listeners.PlayerDamageListener;
|
||||
import com.alttd.hunger_games.event_listeners.PlayerDisconnectListener;
|
||||
import com.alttd.hunger_games.event_listeners.PlayerJoinListener;
|
||||
import com.alttd.hunger_games.services.LootService;
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
import com.alttd.hunger_games.services.PlayerTeleporterService;
|
||||
import com.alttd.hunger_games.services.Round;
|
||||
import com.alttd.hunger_games.services.RoundService;
|
||||
import com.alttd.hunger_games.services.StatService;
|
||||
import com.alttd.hunger_games.event_listeners.*;
|
||||
import com.alttd.hunger_games.services.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
@@ -26,6 +19,8 @@ public final class Main extends JavaPlugin {
|
||||
private PlayerTeleporterService playerTeleporterService;
|
||||
private LootService lootService;
|
||||
private StatService statService;
|
||||
private BossBarService bossBarService;
|
||||
private TabListService tabListService;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
@@ -41,9 +36,15 @@ public final class Main extends JavaPlugin {
|
||||
round = Round.createSingletonInstance();
|
||||
lootService = new LootService();
|
||||
statService = new StatService(this, round);
|
||||
roundService = RoundService.createSingletonInstance(round, this, lootService, statService);
|
||||
playerTeleporterService = PlayerTeleporterService.createSingletonInstance();
|
||||
roundService = RoundService.createSingletonInstance(round, this, lootService, statService, playerTeleporterService);
|
||||
tabListService = new TabListService(round, roundService);
|
||||
roundService.setTabListService(tabListService);
|
||||
playerService = PlayerService.createSingletonInstance(round, roundService, playerTeleporterService);
|
||||
|
||||
bossBarService = BossBarService.createSingletonInstance(round, roundService);
|
||||
round.setBossBarService(bossBarService);
|
||||
roundService.setBossBarService(bossBarService);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,20 +53,23 @@ public final class Main extends JavaPlugin {
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
BaseCommand command = new BaseCommand(this, roundService, playerService, round, statService);
|
||||
BaseCommand command = new BaseCommand(this, roundService, playerService, round, statService, tabListService);
|
||||
}
|
||||
|
||||
private void registerEvents() {
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
pluginManager.registerEvents(new PlayerDisconnectListener(playerService), this);
|
||||
pluginManager.registerEvents(new PlayerJoinListener(playerService), this);
|
||||
pluginManager.registerEvents(new PlayerJoinListener(playerService, tabListService), this);
|
||||
pluginManager.registerEvents(new PlayerDamageListener(roundService, playerService, statService), this);
|
||||
pluginManager.registerEvents(new ChestListener(roundService, lootService), this);
|
||||
pluginManager.registerEvents(new ItemSpawnListener(roundService), this);
|
||||
pluginManager.registerEvents(new CommandListener(), this);
|
||||
}
|
||||
|
||||
public void reloadConfigs() {
|
||||
Config.reload();
|
||||
Messages.reload();
|
||||
LootItems.reload();
|
||||
}
|
||||
|
||||
private void registerSchedulers() {
|
||||
|
||||
@@ -3,10 +3,7 @@ package com.alttd.hunger_games.commands;
|
||||
import com.alttd.hunger_games.Main;
|
||||
import com.alttd.hunger_games.commands.subcommands.*;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
import com.alttd.hunger_games.services.Round;
|
||||
import com.alttd.hunger_games.services.RoundService;
|
||||
import com.alttd.hunger_games.services.StatService;
|
||||
import com.alttd.hunger_games.services.*;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
@@ -23,7 +20,7 @@ import java.util.stream.Collectors;
|
||||
public class BaseCommand implements CommandExecutor, TabExecutor {
|
||||
private final List<SubCommand> subCommands;
|
||||
|
||||
public BaseCommand(Main main, RoundService roundService, PlayerService playerService, Round round, StatService statService) {
|
||||
public BaseCommand(Main main, RoundService roundService, PlayerService playerService, Round round, StatService statService, TabListService tablistService) {
|
||||
PluginCommand command = main.getCommand("hungergames");
|
||||
if (command == null) {
|
||||
subCommands = null;
|
||||
@@ -37,10 +34,13 @@ public class BaseCommand implements CommandExecutor, TabExecutor {
|
||||
subCommands = new ArrayList<>(List.of(
|
||||
new Reload(main),
|
||||
new RoundState(roundService),
|
||||
new Register(playerService),
|
||||
new StartRound(round, roundService),
|
||||
new Register(playerService, tablistService),
|
||||
new StartRound(round, roundService, playerService),
|
||||
new NextPhase(round),
|
||||
new EndRound(round),
|
||||
new Stuck(main),
|
||||
new Stats(statService)
|
||||
new Stats(statService),
|
||||
new ListPlayers(roundService)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.alttd.hunger_games.commands.subcommands;
|
||||
|
||||
import com.alttd.hunger_games.commands.SubCommand;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.services.Round;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EndRound extends SubCommand {
|
||||
|
||||
private final Round round;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
round.endRound();
|
||||
commandSender.sendRichMessage("<green>Ending the round.</green>");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "end";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return Messages.HELP.END_ROUND;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.alttd.hunger_games.commands.subcommands;
|
||||
|
||||
import com.alttd.hunger_games.commands.SubCommand;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.services.RoundService;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ListPlayers extends SubCommand {
|
||||
|
||||
private final RoundService roundService;
|
||||
|
||||
public ListPlayers(RoundService roundService) {
|
||||
this.roundService = roundService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
Set<UUID> registeredUuids = roundService.getPlayers(PLAYER_STATE.REGISTERED);
|
||||
Set<UUID> spectatingUuids = roundService.getPlayers(PLAYER_STATE.SPECTATING);
|
||||
|
||||
List<Component> registeredNames = registeredUuids.stream()
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Player::name)
|
||||
.toList();
|
||||
|
||||
List<Component> spectatingNames = spectatingUuids.stream()
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.map(Player::name)
|
||||
.toList();
|
||||
|
||||
Component inGamePlayers = Component.join(JoinConfiguration.commas(true), registeredNames);
|
||||
Component spectators = Component.join(JoinConfiguration.commas(true), spectatingNames);
|
||||
|
||||
int inGameCount = registeredNames.size();
|
||||
int totalCount = inGameCount + spectatingNames.size();
|
||||
|
||||
commandSender.sendRichMessage(Messages.COMMANDS.LIST_PLAYERS,
|
||||
Placeholder.unparsed("in_game", String.valueOf(inGameCount)),
|
||||
Placeholder.unparsed("total", String.valueOf(totalCount)),
|
||||
Placeholder.component("in_game_players", inGamePlayers),
|
||||
Placeholder.component("spectators", spectators)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return Messages.HELP.LIST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.alttd.hunger_games.commands.subcommands;
|
||||
|
||||
import com.alttd.hunger_games.commands.SubCommand;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.services.Round;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class NextPhase extends SubCommand {
|
||||
|
||||
private final Round round;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
round.forceNextPhase();
|
||||
commandSender.sendRichMessage(Messages.COMMANDS.NEXT_PHASE_SUCCESS);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "nextphase";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return Messages.HELP.NEXT_PHASE;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import com.alttd.hunger_games.commands.shared_parsers.OnlinePlayerParser;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
import com.alttd.hunger_games.services.TabListService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -15,16 +17,14 @@ import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class Register extends SubCommand {
|
||||
|
||||
private final static OnlinePlayerParser ONLINE_PLAYER_PARSER = new OnlinePlayerParser();
|
||||
private final static int PLAYER_ARG = 1;
|
||||
private final PlayerService playerService;
|
||||
|
||||
public Register(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
private final TabListService tabListService;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
@@ -44,6 +44,7 @@ public class Register extends SubCommand {
|
||||
playerToRegister.sendRichMessage(Messages.REGISTER.FAILED_TO_REGISTER_YOU);
|
||||
} else {
|
||||
notifyRegisteredPlayer(playerToRegister, nullablePlayerState);
|
||||
tabListService.updatePlayerColor(playerToRegister);
|
||||
}
|
||||
|
||||
if (commandSender instanceof Player commandPlayer) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.alttd.hunger_games.commands.subcommands;
|
||||
|
||||
import com.alttd.hunger_games.commands.SubCommand;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import com.alttd.hunger_games.services.RoundService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
@@ -16,9 +17,10 @@ public class RoundState extends SubCommand {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
ROUND_STATE roundState = roundService.getRoundState();
|
||||
commandSender.sendRichMessage(Messages.ROUND_STATE.ROUND_STATE,
|
||||
Placeholder.parsed("round_state",
|
||||
roundService.getRoundState().getHumandReadableName()));
|
||||
roundState == null ? "null" : roundState.getHumanReadableName()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ package com.alttd.hunger_games.commands.subcommands;
|
||||
import com.alttd.hunger_games.commands.SubCommand;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
import com.alttd.hunger_games.services.Round;
|
||||
import com.alttd.hunger_games.services.RoundService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
@@ -16,15 +18,19 @@ public class StartRound extends SubCommand {
|
||||
|
||||
private final Round round;
|
||||
private final RoundService roundService;
|
||||
private final PlayerService playerService;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
ROUND_STATE roundState = roundService.getRoundState();
|
||||
if (roundState == ROUND_STATE.PLAYER_REGISTRATION) {
|
||||
if (roundState == ROUND_STATE.PLAYER_REGISTRATION || roundState == ROUND_STATE.ENDED) {
|
||||
Bukkit.getOnlinePlayers().stream()
|
||||
.filter(player -> !player.hasPermission("hungergames.auto-join-bypass"))
|
||||
.forEach(playerService::registerPlayer);
|
||||
round.startRound();
|
||||
} else {
|
||||
commandSender.sendRichMessage(Messages.START_ROUND.CAN_NOT_START_ROUND,
|
||||
Placeholder.parsed("round_state", roundState.getHumandReadableName()));
|
||||
Placeholder.parsed("round_state", roundState.getHumanReadableName()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,18 +155,28 @@ public class Stuck extends SubCommand implements Listener {
|
||||
|
||||
private Optional<Location> findSafeLocation(Location start) {
|
||||
int radius = 10;
|
||||
Location finaleCenter = Config.DESTINATION.FINALE_CENTER;
|
||||
boolean prioritizeFinale = finaleCenter != null && finaleCenter.getWorld().equals(start.getWorld());
|
||||
|
||||
for (int r = 0; r <= radius; r++) {
|
||||
List<Location> candidates = new ArrayList<>();
|
||||
for (int x = -r; x <= r; x++) {
|
||||
for (int z = -r; z <= r; z++) {
|
||||
if (Math.abs(x) != r && Math.abs(z) != r) {
|
||||
continue;
|
||||
}
|
||||
candidates.add(start.clone().add(x, 0, z));
|
||||
}
|
||||
}
|
||||
|
||||
Location baseLocation = start.clone().add(x, 0, z);
|
||||
Optional<Location> safeY = findSafeY(baseLocation);
|
||||
if (safeY.isPresent()) {
|
||||
return safeY;
|
||||
}
|
||||
if (prioritizeFinale) {
|
||||
candidates.sort(Comparator.comparingDouble(loc -> loc.distanceSquared(finaleCenter)));
|
||||
}
|
||||
|
||||
for (Location baseLocation : candidates) {
|
||||
Optional<Location> safeY = findSafeY(baseLocation);
|
||||
if (safeY.isPresent()) {
|
||||
return safeY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,12 @@ package com.alttd.hunger_games.config;
|
||||
|
||||
import com.alttd.hunger_games.data_objects.GameStage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
public class Config extends AbstractConfig {
|
||||
@@ -44,8 +40,13 @@ public class Config extends AbstractConfig {
|
||||
public static class ROUND {
|
||||
private static final String prefix = "round.";
|
||||
|
||||
public static String HG_WORLD = "world";
|
||||
public static Duration COUNTDOWN = Duration.ofSeconds(10);
|
||||
public static Duration SAFE_PHASE = Duration.ofSeconds(30);
|
||||
public static Duration TIME_TO_FINALE = Duration.ofMinutes(5);
|
||||
public static int INITIAL_BORDER_SIZE = 5000;
|
||||
public static int FINALE_BORDER_SIZE = 48;
|
||||
public static int DEFAULT_BORDER_TRANSITION_SECONDS = 45;
|
||||
public static List<GameStage> STAGES = List.of(
|
||||
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(1000).build(),
|
||||
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(750).build(),
|
||||
@@ -54,9 +55,16 @@ public class Config extends AbstractConfig {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
HG_WORLD = config.getString(prefix, "hg-world", HG_WORLD);
|
||||
int countdownSeconds = config.getInt(prefix, "countdown-seconds", Math.toIntExact(COUNTDOWN.toSeconds()));
|
||||
COUNTDOWN = Duration.ofSeconds(countdownSeconds);
|
||||
int safePhaseSeconds = config.getInt(prefix, "safe-phase-seconds", Math.toIntExact(SAFE_PHASE.toSeconds()));
|
||||
SAFE_PHASE = Duration.ofSeconds(safePhaseSeconds);
|
||||
int killPhaseSeconds = config.getInt(prefix, "time-to-finale-seconds", Math.toIntExact(TIME_TO_FINALE.toSeconds()));
|
||||
TIME_TO_FINALE = Duration.ofSeconds(killPhaseSeconds);
|
||||
INITIAL_BORDER_SIZE = config.getInt(prefix, "initial-border-size", INITIAL_BORDER_SIZE);
|
||||
FINALE_BORDER_SIZE = config.getInt(prefix, "finale-border-size", FINALE_BORDER_SIZE);
|
||||
DEFAULT_BORDER_TRANSITION_SECONDS = config.getInt(prefix, "default-border-transition-seconds", DEFAULT_BORDER_TRANSITION_SECONDS);
|
||||
|
||||
ConfigurationSection configurationSection = config.getConfigurationSection(prefix + "stages");
|
||||
if (configurationSection == null) {
|
||||
@@ -134,4 +142,17 @@ public class Config extends AbstractConfig {
|
||||
STUCK_WARMUP = Duration.ofSeconds(config.getInt(prefix, "stuck-warmup-seconds", (int) STUCK_WARMUP.toSeconds()));
|
||||
}
|
||||
}
|
||||
|
||||
public static class COMMAND_WHITELIST {
|
||||
private static final String prefix = "command-whitelist";
|
||||
|
||||
public static final List<String> PREFIXES = new ArrayList<>(List.of("msg", "ac", "gac", "tell", "r", "server", "lobby", "hub", "bayou", "creative", "hg", "lp"));
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
List<String> stringList = config.getStringList(prefix, "prefix-list", PREFIXES);
|
||||
PREFIXES.clear();
|
||||
PREFIXES.addAll(stringList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package com.alttd.hunger_games.config;
|
||||
|
||||
import com.alttd.hunger_games.data_objects.LootItem;
|
||||
import com.alttd.hunger_games.data_objects.RARITY;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Material;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class LootItems extends AbstractConfig {
|
||||
static LootItems config;
|
||||
|
||||
@@ -27,18 +31,40 @@ public class LootItems extends AbstractConfig {
|
||||
public static class ITEMS {
|
||||
private static final String prefix = "items.";
|
||||
|
||||
private static final HashMap<RARITY, List<Material>> ITEMS = new HashMap<>();
|
||||
private static final HashMap<RARITY, List<LootItem>> ITEMS = new HashMap<>();
|
||||
|
||||
public static List<Material> get(RARITY rarity) {
|
||||
public static List<LootItem> get(RARITY rarity) {
|
||||
return ITEMS.get(rarity);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
for (RARITY rarity : RARITY.values()) {
|
||||
List<String> materialNameList = ITEMS.get(rarity).stream().map(Material::name).toList();
|
||||
List<LootItem> currentItems = ITEMS.getOrDefault(rarity, List.of());
|
||||
List<String> materialNameList = currentItems.stream()
|
||||
.map(item -> item.material().name() + (item.maxAmount() > 1 ? ":" + item.maxAmount() : ""))
|
||||
.toList();
|
||||
List<String> materialList = config.getList(prefix, rarity.getConfigName(), materialNameList);
|
||||
ITEMS.put(rarity, materialList.stream().map(Material::getMaterial).toList());
|
||||
|
||||
List<LootItem> lootItems = new ArrayList<>();
|
||||
for (String entry : materialList) {
|
||||
String[] split = entry.split(":");
|
||||
Material material = Material.getMaterial(split[0].toUpperCase());
|
||||
if (material == null) {
|
||||
log.warn("Invalid material found in loot-items.yml: {}", split[0]);
|
||||
continue;
|
||||
}
|
||||
int maxAmount = 1;
|
||||
if (split.length > 1) {
|
||||
try {
|
||||
maxAmount = Integer.parseInt(split[1]);
|
||||
} catch (NumberFormatException ignored) {
|
||||
log.error("Invalid max amount found in loot-items.yml: {}", split[1]);
|
||||
}
|
||||
}
|
||||
lootItems.add(new LootItem(material, maxAmount));
|
||||
}
|
||||
ITEMS.put(rarity, lootItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,12 @@ public class Messages extends AbstractConfig {
|
||||
public static String ROUND_STATE = "<green>Show the current round state: <gold>/hg roundstate</gold></green>";
|
||||
public static String REGISTER = "<green>Register a player for the game: <gold>/hg register <player></gold></green>";
|
||||
public static String START_ROUND = "<green>Start the game: <gold>/hg start</gold></green>";
|
||||
public static String NEXT_PHASE = "<green>Skip to the next phase/stage: <gold>/hg nextphase</gold></green>";
|
||||
public static String RELOAD = "<green>Reload config and messages: <gold>/hg reload</gold></green>";
|
||||
public static String STUCK = "<green>Teleport to safety if stuck: <gold>/hg stuck</gold></green>";
|
||||
public static String STATS = "<green>Show player stats: <gold>/hg stats [player]</gold></green>";
|
||||
public static String LIST = "<green>List all players in the game and spectators: <gold>/hg list</gold></green>";
|
||||
public static String END_ROUND = "<green>End the game: <gold>/hg end</gold></green>";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
@@ -40,9 +43,12 @@ public class Messages extends AbstractConfig {
|
||||
ROUND_STATE = config.getString(prefix, "round-state", ROUND_STATE);
|
||||
REGISTER = config.getString(prefix, "register", REGISTER);
|
||||
START_ROUND = config.getString(prefix, "start", START_ROUND);
|
||||
NEXT_PHASE = config.getString(prefix, "next-phase", NEXT_PHASE);
|
||||
RELOAD = config.getString(prefix, "reload", RELOAD);
|
||||
STUCK = config.getString(prefix, "stuck", STUCK);
|
||||
STATS = config.getString(prefix, "stats", STATS);
|
||||
LIST = config.getString(prefix, "list", LIST);
|
||||
END_ROUND = config.getString(prefix, "end-round", END_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +59,7 @@ public class Messages extends AbstractConfig {
|
||||
public static String PLAYER_ONLY = "<red>This command can only be executed as a player</red>";
|
||||
public static String PLAYER_NOT_FOUND = "<red>Unable to find online player <player></red>";
|
||||
public static String RELOAD_SUCCESS = "<green>Config and messages have been reloaded.</green>";
|
||||
public static String COMMAND_BLOCKED = "<red>You cannot use that command here!</red>";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
@@ -60,6 +67,7 @@ public class Messages extends AbstractConfig {
|
||||
PLAYER_ONLY = config.getString(prefix, "player-only", PLAYER_ONLY);
|
||||
PLAYER_NOT_FOUND = config.getString(prefix, "player-only", PLAYER_NOT_FOUND);
|
||||
RELOAD_SUCCESS = config.getString(prefix, "reload-success", RELOAD_SUCCESS);
|
||||
COMMAND_BLOCKED = config.getString(prefix, "command-blocked", COMMAND_BLOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +90,16 @@ public class Messages extends AbstractConfig {
|
||||
public static String BORDER_SHRINK = "<red>The border is shrinking to <size> blocks!</red>";
|
||||
public static String CHEST_EMPTY = "<red>This chest is empty!</red>";
|
||||
public static String WINNER = "<gold><player> has won the Hunger Games!</gold>";
|
||||
public static String PLAYER_DEATH = "<red><player> has died! <gold><remaining></gold> players remaining.</red>";
|
||||
public static String PLAYER_KILLED = "<red><victim> was killed by <killer>! <gold><remaining></gold> players remaining.</red>";
|
||||
|
||||
public static String BOSSBAR_COUNTDOWN = "<gold>Loot phase in <time></gold>";
|
||||
public static String BOSSBAR_PVP_COUNTDOWN = "<green>PVP starts in <time></green>";
|
||||
public static String BOSSBAR_SAFE_PHASE = "<green>Border shrinking in <time></green>";
|
||||
public static String BOSSBAR_KILL_PHASE = "<red>Border shrinking... <time></red>";
|
||||
public static String BOSSBAR_FINALE = "<gold>Remaining Players: <remaining></gold>";
|
||||
|
||||
public static String NEXT_STATE = "<gold>Starting <round>!</gold>";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
@@ -90,6 +108,27 @@ public class Messages extends AbstractConfig {
|
||||
BORDER_SHRINK = config.getString(prefix, "border-shrink", BORDER_SHRINK);
|
||||
CHEST_EMPTY = config.getString(prefix, "chest-empty", CHEST_EMPTY);
|
||||
WINNER = config.getString(prefix, "winner", WINNER);
|
||||
PLAYER_DEATH = config.getString(prefix, "player-death", PLAYER_DEATH);
|
||||
PLAYER_KILLED = config.getString(prefix, "player-killed", PLAYER_KILLED);
|
||||
|
||||
BOSSBAR_COUNTDOWN = config.getString(prefix, "bossbar-countdown", BOSSBAR_COUNTDOWN);
|
||||
BOSSBAR_PVP_COUNTDOWN = config.getString(prefix, "bossbar-pvp-countdown", BOSSBAR_PVP_COUNTDOWN);
|
||||
BOSSBAR_SAFE_PHASE = config.getString(prefix, "bossbar-border-shrinking-in", BOSSBAR_SAFE_PHASE);
|
||||
BOSSBAR_KILL_PHASE = config.getString(prefix, "bossbar-border-shrinking", BOSSBAR_KILL_PHASE);
|
||||
BOSSBAR_FINALE = config.getString(prefix, "bossbar-finale", BOSSBAR_FINALE);
|
||||
|
||||
NEXT_STATE = config.getString(prefix, "next-state", NEXT_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
public static class START_ROUND {
|
||||
private static final String prefix = "start-round.";
|
||||
|
||||
public static String CAN_NOT_START_ROUND = "<red>Unable to start the round, current state is <round_state></red>";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
CAN_NOT_START_ROUND = config.getString(prefix, "can-not-start", CAN_NOT_START_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,18 +192,35 @@ public class Messages extends AbstractConfig {
|
||||
public static class STATS {
|
||||
private static final String prefix = "stats.";
|
||||
|
||||
public static String STATS_FORMAT = "<gold>Stats for <player>:</gold>\n" +
|
||||
"<green>Kills: <gold><kills></gold></green>\n" +
|
||||
"<green>Deaths: <gold><deaths></gold></green>\n" +
|
||||
"<green>Damage Dealt: <gold><damage_dealt></gold></green>\n" +
|
||||
"<green>Damage Taken: <gold><damage_taken></gold></green>\n" +
|
||||
"<green>Wins: <gold><wins></gold></green>\n" +
|
||||
"<green>Rounds Played: <gold><rounds_played></gold></green>\n" +
|
||||
"<green>K/D Ratio: <gold><kd_ratio></gold></green>";
|
||||
public static String STATS_FORMAT = """
|
||||
<gold>Stats for <player>:</gold>
|
||||
<green>Kills: <gold><kills></gold></green>
|
||||
<green>Deaths: <gold><deaths></gold></green>
|
||||
<green>Damage Dealt: <gold><damage_dealt></gold></green>
|
||||
<green>Damage Taken: <gold><damage_taken></gold></green>
|
||||
<green>Wins: <gold><wins></gold></green>
|
||||
<green>Rounds Played: <gold><rounds_played></gold></green>
|
||||
<green>K/D Ratio: <gold><kd_ratio></gold></green>""";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
STATS_FORMAT = config.getString(prefix, "format", STATS_FORMAT);
|
||||
}
|
||||
}
|
||||
|
||||
public static class COMMANDS {
|
||||
private static final String prefix = "commands.";
|
||||
|
||||
public static String NEXT_PHASE_SUCCESS = "<green>Skipping to the next phase/stage.</green>";
|
||||
public static String LIST_PLAYERS = """
|
||||
<gold>HungerGames Players (<in_game>/<total>):</gold>
|
||||
<green>In Game: <white><in_game_players></white></green>
|
||||
<yellow>Spectators: <white><spectators></white></yellow>""";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
NEXT_PHASE_SUCCESS = config.getString(prefix + "next-phase", "success", NEXT_PHASE_SUCCESS);
|
||||
LIST_PLAYERS = config.getString(prefix, "list-players", LIST_PLAYERS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.alttd.hunger_games.data_objects;
|
||||
|
||||
import org.bukkit.Material;
|
||||
|
||||
public record LootItem(Material material, int maxAmount) {
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public enum ROUND_STATE {
|
||||
return Optional.of(values()[ordinal() + 1]);
|
||||
}
|
||||
|
||||
public String getHumandReadableName() {
|
||||
public String getHumanReadableName() {
|
||||
return switch (this) {
|
||||
case PLAYER_REGISTRATION -> "Player Registration";
|
||||
case COUNTDOWN -> "Countdown";
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alttd.hunger_games.event_listeners;
|
||||
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class CommandListener implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (player.hasPermission("hg.bypass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove leading slash
|
||||
String message = event.getMessage().substring(1).toLowerCase();
|
||||
String command = message.split(" ")[0];
|
||||
|
||||
boolean whitelisted = Config.COMMAND_WHITELIST.PREFIXES.stream()
|
||||
.anyMatch(prefix -> command.startsWith(prefix.toLowerCase()));
|
||||
|
||||
if (!whitelisted) {
|
||||
event.setCancelled(true);
|
||||
player.sendRichMessage(Messages.GENERIC.COMMAND_BLOCKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.alttd.hunger_games.event_listeners;
|
||||
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import com.alttd.hunger_games.services.RoundService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDeathEvent;
|
||||
import org.bukkit.event.entity.ItemSpawnEvent;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ItemSpawnListener implements Listener {
|
||||
|
||||
private final RoundService roundService;
|
||||
private final Set<UUID> deathDrops = new HashSet<>();
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onItemSpawn(ItemSpawnEvent event) {
|
||||
if (deathDrops.remove(event.getEntity().getUniqueId())) {
|
||||
event.getEntity().setTicksLived(5400); // 300 seconds total - 5400 ticks = 30 seconds left
|
||||
return;
|
||||
}
|
||||
setDespawnDelay(event.getEntity());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerDeath(EntityDeathEvent event) {
|
||||
if (!(event.getEntity() instanceof Player)) {
|
||||
return;
|
||||
}
|
||||
ROUND_STATE roundState = roundService.getRoundState();
|
||||
if (roundState == ROUND_STATE.SAFE_PHASE || roundState == ROUND_STATE.KILL_PHASE || roundState == ROUND_STATE.FINALE) {
|
||||
event.getDrops().forEach(itemStack -> {
|
||||
Item item = event.getEntity().getWorld().dropItemNaturally(event.getEntity().getLocation(), itemStack);
|
||||
deathDrops.add(item.getUniqueId());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onPlayerDropItem(PlayerDropItemEvent event) {
|
||||
setDespawnDelay(event.getItemDrop());
|
||||
}
|
||||
|
||||
private void setDespawnDelay(Item item) {
|
||||
ROUND_STATE roundState = roundService.getRoundState();
|
||||
if (roundState == ROUND_STATE.SAFE_PHASE || roundState == ROUND_STATE.KILL_PHASE || roundState == ROUND_STATE.FINALE) {
|
||||
item.setTicksLived(5800);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.alttd.hunger_games.services.StatService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
@@ -39,9 +40,11 @@ public class PlayerDamageListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
event.setShowDeathMessages(false);
|
||||
event.deathMessage(null);
|
||||
playerService.handlePlayerDeath(player);
|
||||
statService.addDeath(player.getUniqueId());
|
||||
|
||||
@@ -53,6 +56,6 @@ public class PlayerDamageListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerRespawn(PlayerRespawnEvent event) {
|
||||
playerService.handlePlayerRespawn(event.getPlayer());
|
||||
playerService.handlePlayerRespawn(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,33 @@
|
||||
package com.alttd.hunger_games.event_listeners;
|
||||
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
import com.alttd.hunger_games.services.TabListService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandException;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PlayerJoinListener implements Listener {
|
||||
|
||||
private final PlayerService playerService;
|
||||
private final TabListService tabListService;
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
playerService.handleJoin(event.getPlayer());
|
||||
Player player = event.getPlayer();
|
||||
playerService.handleJoin(player);
|
||||
tabListService.updatePlayerColor(player);
|
||||
try {
|
||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
|
||||
String.format("lp user %s permission settemp cmi.kit.hunger-games true 7d", player.getName()));
|
||||
} catch (CommandException e) {
|
||||
log.error("Failed to set participant permission for {}", player.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,43 @@
|
||||
package com.alttd.hunger_games.game;
|
||||
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.utils.WorldBorderUtil;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.title.Title;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldBorder;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.time.Duration;
|
||||
|
||||
@UtilityClass
|
||||
public class GameStageHandler {
|
||||
|
||||
private static final int BORDER_TRANSITION_SECONDS = 10;
|
||||
|
||||
public static void handleStageChange(int worldBorderSize) {
|
||||
World world = getWorld().orElseThrow(() -> new IllegalStateException("No worlds available during state change,"));
|
||||
|
||||
WorldBorder border = world.getWorldBorder();
|
||||
border.setSize(worldBorderSize, BORDER_TRANSITION_SECONDS);
|
||||
WorldBorderUtil.setSize(worldBorderSize);
|
||||
|
||||
String message = Messages.GAME.BORDER_SHRINK
|
||||
.replace("<size>", String.valueOf(worldBorderSize));
|
||||
broadcast(message);
|
||||
}
|
||||
|
||||
public static void broadcastMessage(String message) {
|
||||
broadcast(message);
|
||||
}
|
||||
|
||||
public static void handleCountdownEnd() {
|
||||
broadcast(Messages.GAME.STARTED);
|
||||
}
|
||||
|
||||
public static void handleWarmup() {
|
||||
Location center = Config.DESTINATION.START_CENTER;
|
||||
if (center == null || center.getWorld() == null) {
|
||||
throw new IllegalStateException("Start center is not set during warmup.");
|
||||
}
|
||||
|
||||
WorldBorder border = center.getWorld().getWorldBorder();
|
||||
border.setCenter(center.getX(), center.getZ());
|
||||
border.setSize(Config.ROUND.INITIAL_BORDER_SIZE);
|
||||
|
||||
broadcast(Messages.GAME.WARMUP);
|
||||
}
|
||||
|
||||
private static Optional<World> getWorld() {
|
||||
Location center = Config.DESTINATION.START_CENTER;
|
||||
if (center != null && center.getWorld() != null) {
|
||||
return Optional.ofNullable(center.getWorld());
|
||||
}
|
||||
return Bukkit.getWorlds().isEmpty() ? Optional.empty() : Optional.of(Bukkit.getWorlds().getFirst());
|
||||
}
|
||||
|
||||
private static void broadcast(String message) {
|
||||
Component component = MiniMessage.miniMessage().deserialize(message);
|
||||
Bukkit.broadcast(component);
|
||||
Title.Times times = Title.Times.times(Duration.ofSeconds(1), Duration.ofSeconds(5), Duration.ofSeconds(1));
|
||||
Title title = Title.title(Component.empty(), component, times);
|
||||
Bukkit.getOnlinePlayers().forEach(player -> player.showTitle(title));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import net.kyori.adventure.bossbar.BossBar;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BossBarService implements RoundListener {
|
||||
|
||||
private static BossBarService instance = null;
|
||||
private final RoundService roundService;
|
||||
private final BossBar bossBar;
|
||||
private final Set<UUID> viewers = new HashSet<>();
|
||||
private int maxPlayers = 0;
|
||||
|
||||
public BossBarService(Round round, RoundService roundService) {
|
||||
this.roundService = roundService;
|
||||
this.bossBar = BossBar.bossBar(Component.empty(), 1.0f, BossBar.Color.YELLOW, BossBar.Overlay.PROGRESS);
|
||||
round.register(this);
|
||||
}
|
||||
|
||||
public static BossBarService createSingletonInstance(Round round, RoundService roundService) {
|
||||
if (instance != null) {
|
||||
throw new IllegalStateException("BossBarService is already initialized.");
|
||||
}
|
||||
instance = new BossBarService(round, roundService);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void updateCountdown(Duration timeLeft, Duration totalTime, ROUND_STATE state) {
|
||||
String message = switch (state) {
|
||||
case COUNTDOWN -> Messages.GAME.BOSSBAR_COUNTDOWN;
|
||||
case SAFE_PHASE -> Messages.GAME.BOSSBAR_PVP_COUNTDOWN;
|
||||
case KILL_PHASE -> Messages.GAME.BOSSBAR_SAFE_PHASE;
|
||||
default -> {
|
||||
if (state.ordinal() > ROUND_STATE.KILL_PHASE.ordinal()) {
|
||||
yield Messages.GAME.BOSSBAR_KILL_PHASE;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid round state: " + state);
|
||||
}
|
||||
};
|
||||
|
||||
if (message.isEmpty()) {
|
||||
bossBar.name(Component.empty());
|
||||
bossBar.progress(0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
bossBar.name(MiniMessage.miniMessage().deserialize(message, Placeholder.unparsed("time", formatTime(timeLeft))));
|
||||
bossBar.progress(Math.clamp((float) timeLeft.toMillis() / totalTime.toMillis(), 0.0f, 1.0f));
|
||||
|
||||
updateBossBarColor(state);
|
||||
updateViewers();
|
||||
}
|
||||
|
||||
private void updateBossBarColor(ROUND_STATE state) {
|
||||
if (state == ROUND_STATE.ENDED || state == ROUND_STATE.PLAYER_REGISTRATION) {
|
||||
hideAll();
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case COUNTDOWN -> bossBar.color(BossBar.Color.YELLOW);
|
||||
case SAFE_PHASE -> bossBar.color(BossBar.Color.GREEN);
|
||||
case KILL_PHASE -> bossBar.color(BossBar.Color.RED);
|
||||
case FINALE -> bossBar.color(BossBar.Color.PURPLE);
|
||||
default -> bossBar.color(BossBar.Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateFinale(int remainingPlayers) {
|
||||
if (maxPlayers == 0) {
|
||||
maxPlayers = remainingPlayers;
|
||||
}
|
||||
|
||||
if (remainingPlayers <= 1) {
|
||||
hideAll();
|
||||
return;
|
||||
}
|
||||
|
||||
bossBar.name(MiniMessage.miniMessage().deserialize(Messages.GAME.BOSSBAR_FINALE, Placeholder.unparsed("remaining", String.valueOf(remainingPlayers))));
|
||||
bossBar.progress(Math.clamp((float) remainingPlayers / maxPlayers, 0.0f, 1.0f));
|
||||
bossBar.color(BossBar.Color.PURPLE);
|
||||
updateViewers();
|
||||
}
|
||||
|
||||
private void updateViewers() {
|
||||
if (roundService.getRoundState() == ROUND_STATE.ENDED || roundService.getRoundState() == ROUND_STATE.PLAYER_REGISTRATION) {
|
||||
hideAll();
|
||||
return;
|
||||
}
|
||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||
if (viewers.add(player.getUniqueId())) {
|
||||
player.showBossBar(bossBar);
|
||||
}
|
||||
});
|
||||
|
||||
viewers.removeIf(uuid -> {
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
return player == null || !player.isOnline();
|
||||
});
|
||||
}
|
||||
|
||||
private void hideAll() {
|
||||
viewers.stream()
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(player -> player.hideBossBar(bossBar));
|
||||
viewers.clear();
|
||||
}
|
||||
|
||||
private String formatTime(Duration duration) {
|
||||
if (duration.isNegative()) {
|
||||
return "0s";
|
||||
}
|
||||
|
||||
long minutes = duration.toMinutesPart();
|
||||
int seconds = duration.toSecondsPart();
|
||||
|
||||
if (minutes > 0) {
|
||||
return "%dm %ds".formatted(minutes, seconds);
|
||||
}
|
||||
return "%ds".formatted(seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
if (roundState == ROUND_STATE.PLAYER_REGISTRATION || roundState == ROUND_STATE.ENDED) {
|
||||
hideAll();
|
||||
maxPlayers = 0;
|
||||
} else if (roundState == ROUND_STATE.FINALE) {
|
||||
updateFinale(roundService.getPlayers(PLAYER_STATE.REGISTERED).size());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.config.LootItems;
|
||||
import com.alttd.hunger_games.data_objects.LootItem;
|
||||
import com.alttd.hunger_games.data_objects.RARITY;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@@ -38,15 +39,20 @@ public class LootService {
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
int slot = random.nextInt(27);
|
||||
RARITY rarity = decideRarity();
|
||||
List<Material> possibleItems = LootItems.ITEMS.get(rarity);
|
||||
List<LootItem> possibleItems = LootItems.ITEMS.get(rarity);
|
||||
|
||||
if (possibleItems == null || possibleItems.isEmpty()) {
|
||||
log.warn("No items found for rarity: {}", rarity);
|
||||
continue;
|
||||
}
|
||||
|
||||
Material material = possibleItems.get(random.nextInt(possibleItems.size()));
|
||||
inventory.setItem(slot, new ItemStack(material));
|
||||
LootItem lootItem = possibleItems.get(random.nextInt(possibleItems.size()));
|
||||
if (lootItem == null || lootItem.material() == null) {
|
||||
log.warn("LootItem or material is null for rarity: {}", rarity);
|
||||
continue;
|
||||
}
|
||||
int amount = lootItem.maxAmount() > 1 ? random.nextInt(lootItem.maxAmount()) + 1 : 1;
|
||||
inventory.setItem(slot, new ItemStack(lootItem.material(), amount));
|
||||
}
|
||||
|
||||
return inventory;
|
||||
|
||||
@@ -4,11 +4,17 @@ import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.DESTINATION;
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
public class PlayerService implements RoundListener {
|
||||
|
||||
private static PlayerService instance = null;
|
||||
@@ -34,6 +40,7 @@ public class PlayerService implements RoundListener {
|
||||
@Override
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
this.roundState = roundState;
|
||||
playerTeleporterService.reset();
|
||||
switch (roundState) {
|
||||
case PLAYER_REGISTRATION, KILL_PHASE, SAFE_PHASE -> {
|
||||
//Nothing
|
||||
@@ -53,7 +60,7 @@ public class PlayerService implements RoundListener {
|
||||
}
|
||||
case ENDED -> {
|
||||
roundService.clear();
|
||||
Bukkit.getOnlinePlayers().forEach(player -> playerTeleporterService.teleportPlayer(player, DESTINATION.START_AREA));
|
||||
Bukkit.getOnlinePlayers().forEach(player -> playerTeleporterService.teleportPlayer(player, DESTINATION.SPECTATOR_AREA));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +83,7 @@ public class PlayerService implements RoundListener {
|
||||
PLAYER_STATE playerState = optionalPlayerState.get();
|
||||
|
||||
if (playerState == PLAYER_STATE.SPECTATING) {
|
||||
//TODO: teleport player to spectator location
|
||||
playerTeleporterService.teleportPlayer(player, DESTINATION.SPECTATOR_AREA);
|
||||
}
|
||||
|
||||
roundService.setPlayerState(player.getUniqueId(), playerState);
|
||||
@@ -84,6 +91,7 @@ public class PlayerService implements RoundListener {
|
||||
}
|
||||
|
||||
public void handleDisconnect(Player player) {
|
||||
player.playerListName(null);
|
||||
UUID uuid = player.getUniqueId();
|
||||
roundService.setPlayerState(uuid, PLAYER_STATE.DISCONNECTED);
|
||||
}
|
||||
@@ -104,13 +112,24 @@ public class PlayerService implements RoundListener {
|
||||
public void handlePlayerDeath(Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
roundService.setPlayerState(uuid, PLAYER_STATE.SPECTATING);
|
||||
// TODO: send a message when a player is killed
|
||||
|
||||
int remaining = roundService.getPlayers(PLAYER_STATE.REGISTERED).size();
|
||||
Player killer = player.getKiller();
|
||||
if (killer != null) {
|
||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.PLAYER_KILLED,
|
||||
Placeholder.component("victim", player.name()),
|
||||
Placeholder.component("killer", killer.name()),
|
||||
Placeholder.unparsed("remaining", String.valueOf(remaining))));
|
||||
} else {
|
||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.PLAYER_DEATH,
|
||||
Placeholder.component("player", player.name()),
|
||||
Placeholder.unparsed("remaining", String.valueOf(remaining))));
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePlayerRespawn(Player player) {
|
||||
roundService.getPlayerState(player.getUniqueId())
|
||||
.filter(state -> state == PLAYER_STATE.SPECTATING)
|
||||
.ifPresent(state -> playerTeleporterService.teleportPlayer(player, DESTINATION.SPECTATOR_AREA));
|
||||
public void handlePlayerRespawn(PlayerRespawnEvent event) {
|
||||
Location spectatorArea = playerTeleporterService.getLocationFromDestination(DESTINATION.SPECTATOR_AREA);
|
||||
event.setRespawnLocation(spectatorArea);
|
||||
}
|
||||
|
||||
private Optional<PLAYER_STATE> determinePlayerState(UUID playerUuid) {
|
||||
@@ -121,13 +140,18 @@ public class PlayerService implements RoundListener {
|
||||
if (optionalPlayerState.isPresent()) {
|
||||
PLAYER_STATE playerState = optionalPlayerState.get();
|
||||
return Optional.of(switch (playerState) {
|
||||
case DISCONNECTED, SPECTATING -> PLAYER_STATE.SPECTATING;
|
||||
case DISCONNECTED, SPECTATING -> {
|
||||
if (roundState.equals(ROUND_STATE.ENDED)) {
|
||||
yield PLAYER_STATE.REGISTERED;
|
||||
}
|
||||
yield PLAYER_STATE.SPECTATING;
|
||||
}
|
||||
case REGISTERED -> PLAYER_STATE.REGISTERED;
|
||||
});
|
||||
}
|
||||
return switch (roundState) {
|
||||
case PLAYER_REGISTRATION, COUNTDOWN -> Optional.of(PLAYER_STATE.REGISTERED);
|
||||
case KILL_PHASE, FINALE, ENDED, SAFE_PHASE -> Optional.of(PLAYER_STATE.SPECTATING);
|
||||
case PLAYER_REGISTRATION, COUNTDOWN, ENDED -> Optional.of(PLAYER_STATE.REGISTERED);
|
||||
case KILL_PHASE, FINALE, SAFE_PHASE -> Optional.of(PLAYER_STATE.SPECTATING);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.data_objects.DESTINATION;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@@ -12,6 +13,7 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
public class PlayerTeleporterService implements RoundListener {
|
||||
|
||||
@@ -29,10 +31,20 @@ public class PlayerTeleporterService implements RoundListener {
|
||||
}
|
||||
|
||||
public void teleportPlayer(Player player, DESTINATION destination) {
|
||||
player.teleport(getLocationFromDestination(destination));
|
||||
Location locationFromDestination = getLocationFromDestination(destination);
|
||||
try {
|
||||
player.teleportAsync(getLocationFromDestination(destination));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to teleport player {} to destination {} ({})", player.getName(), destination.name(), locationFromDestination, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Location getLocationFromDestination(DESTINATION destination) {
|
||||
public void reset() {
|
||||
placementCount.clear();
|
||||
usedLocations.clear();
|
||||
}
|
||||
|
||||
public Location getLocationFromDestination(DESTINATION destination) {
|
||||
return switch (destination) {
|
||||
case START_AREA -> {
|
||||
Location startCenter = Config.DESTINATION.START_CENTER;
|
||||
@@ -66,7 +78,10 @@ public class PlayerTeleporterService implements RoundListener {
|
||||
double angleRadians = Math.toRadians(angleDegrees);
|
||||
double x = center.getX() + radius * Math.cos(angleRadians);
|
||||
double z = center.getZ() + radius * Math.sin(angleRadians);
|
||||
|
||||
Location location = new Location(center.getWorld(), x, center.getY(), z);
|
||||
location.setDirection(center.toVector().subtract(location.toVector()));
|
||||
|
||||
usedLocations.computeIfAbsent(destination, k -> new HashSet<>()).add(location);
|
||||
return location;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.GameStage;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import com.alttd.hunger_games.game.GameStageHandler;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Round {
|
||||
@@ -19,6 +23,10 @@ public class Round {
|
||||
private final List<RoundListener> listeners = new ArrayList<>();
|
||||
private ROUND_STATE roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
private ScheduledFuture<?> countdownFuture = null;
|
||||
private Runnable onEnd = null;
|
||||
@Setter
|
||||
private BossBarService bossBarService;
|
||||
|
||||
private Round() {
|
||||
|
||||
@@ -42,11 +50,17 @@ public class Round {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (countdownFuture != null) {
|
||||
countdownFuture.cancel(true);
|
||||
}
|
||||
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
}
|
||||
|
||||
public void endRound() {
|
||||
if (countdownFuture != null) {
|
||||
countdownFuture.cancel(true);
|
||||
}
|
||||
roundState = ROUND_STATE.ENDED;
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
}
|
||||
@@ -63,32 +77,76 @@ public class Round {
|
||||
}
|
||||
|
||||
public void startRound() {
|
||||
if (!roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||
if (!roundState.equals(ROUND_STATE.PLAYER_REGISTRATION) && !roundState.equals(ROUND_STATE.ENDED)) {
|
||||
throw new IllegalStateException("Round can not be started before player registration.");
|
||||
}
|
||||
roundState = ROUND_STATE.COUNTDOWN;
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
GameStageHandler.handleWarmup();
|
||||
Duration warmupDuration = Config.ROUND.COUNTDOWN;
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
|
||||
startCountdown(warmupDuration, () -> {
|
||||
GameStageHandler.handleCountdownEnd();
|
||||
nextStage();
|
||||
scheduleNextStage(0);
|
||||
}, warmupDuration.toSeconds(), TimeUnit.SECONDS);
|
||||
nextStage(); // COUNTDOWN -> SAFE_PHASE
|
||||
startCountdown(Config.ROUND.SAFE_PHASE, () -> {
|
||||
nextStage(); // SAFE_PHASE -> KILL_PHASE
|
||||
GameStageHandler.broadcastMessage(Messages.GAME.NEXT_STATE.replace("<round>", roundState.getHumanReadableName()));
|
||||
scheduleNextStage(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void startCountdown(Duration duration, Runnable onEnd) {
|
||||
if (countdownFuture != null) {
|
||||
countdownFuture.cancel(true);
|
||||
}
|
||||
this.onEnd = onEnd;
|
||||
|
||||
final Instant endTime = Instant.now().plus(duration);
|
||||
countdownFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {
|
||||
if (roundState.equals(ROUND_STATE.FINALE)) {
|
||||
if (countdownFuture != null) {
|
||||
countdownFuture.cancel(false);
|
||||
countdownFuture = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Duration timeLeft = Duration.between(Instant.now(), endTime);
|
||||
if (bossBarService != null) {
|
||||
bossBarService.updateCountdown(timeLeft, duration, roundState);
|
||||
}
|
||||
if (timeLeft.isZero() || timeLeft.isNegative()) {
|
||||
onEnd.run();
|
||||
}
|
||||
}, 0, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void scheduleNextStage(int index) {
|
||||
List<GameStage> gameStageList = Config.ROUND.STAGES;
|
||||
if (gameStageList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gameStageList.size() <= index) {
|
||||
nextStage();
|
||||
startCountdown(Config.ROUND.TIME_TO_FINALE, this::nextStage);
|
||||
return;
|
||||
}
|
||||
|
||||
GameStage gameStage = gameStageList.get(index);
|
||||
Duration duration = gameStage.getDuration();
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
|
||||
startCountdown(duration, () -> {
|
||||
GameStageHandler.handleStageChange(gameStage.getWorldBorderSize());
|
||||
scheduleNextStage(index + 1);
|
||||
}, duration.toSeconds(), TimeUnit.SECONDS);
|
||||
});
|
||||
}
|
||||
|
||||
public void forceNextPhase() {
|
||||
if (countdownFuture == null || onEnd == null) {
|
||||
return;
|
||||
}
|
||||
countdownFuture.cancel(true);
|
||||
onEnd.run();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.Main;
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.DESTINATION;
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import com.alttd.hunger_games.event_listeners.PlayerMovementListener;
|
||||
import com.alttd.hunger_games.utils.WorldBorderUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
@@ -20,31 +27,37 @@ import java.util.stream.Collectors;
|
||||
public class RoundService implements RoundListener {
|
||||
|
||||
private static RoundService instance = null;
|
||||
private PlayerMovementListener playerMovementListener;
|
||||
private final PlayerTeleporterService playerTeleporterService;
|
||||
private final Main main;
|
||||
private final LootService lootService;
|
||||
private final StatService statService;
|
||||
private PlayerMovementListener playerMovementListener;
|
||||
|
||||
@Getter
|
||||
private ROUND_STATE roundState;
|
||||
private final HashMap<UUID, PLAYER_STATE> players = new HashMap<>();
|
||||
|
||||
private final Round round;
|
||||
@Setter
|
||||
private BossBarService bossBarService;
|
||||
@Setter
|
||||
private TabListService tabListService;
|
||||
|
||||
public static RoundService createSingletonInstance(Round round, Main main, LootService lootService, StatService statService) {
|
||||
public static RoundService createSingletonInstance(Round round, Main main, LootService lootService, StatService statService, PlayerTeleporterService playerTeleporterService) {
|
||||
if (instance != null) {
|
||||
throw new IllegalStateException("RoundService is already initialized.");
|
||||
}
|
||||
instance = new RoundService(round, main, lootService, statService);
|
||||
instance = new RoundService(round, main, lootService, statService, playerTeleporterService);
|
||||
return instance;
|
||||
}
|
||||
|
||||
private RoundService(Round round, Main main, LootService lootService, StatService statService) {
|
||||
private RoundService(Round round, Main main, LootService lootService, StatService statService, PlayerTeleporterService playerTeleporterService) {
|
||||
this.round = round;
|
||||
this.roundState = round.register(this);
|
||||
this.main = main;
|
||||
this.lootService = lootService;
|
||||
this.statService = statService;
|
||||
this.playerTeleporterService = playerTeleporterService;
|
||||
}
|
||||
|
||||
public Set<UUID> getPlayers(PLAYER_STATE playerState) {
|
||||
@@ -59,6 +72,16 @@ public class RoundService implements RoundListener {
|
||||
if (playerState == PLAYER_STATE.SPECTATING && oldState == PLAYER_STATE.REGISTERED) {
|
||||
int remaining = getPlayers(PLAYER_STATE.REGISTERED).size();
|
||||
statService.setPlacement(uuid, remaining + 1);
|
||||
if (roundState == ROUND_STATE.FINALE && bossBarService != null) {
|
||||
bossBarService.updateFinale(remaining);
|
||||
}
|
||||
}
|
||||
|
||||
if (tabListService != null) {
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if (player != null) {
|
||||
tabListService.updatePlayerColor(player);
|
||||
}
|
||||
}
|
||||
|
||||
if (!roundState.equals(ROUND_STATE.KILL_PHASE) && !roundState.equals(ROUND_STATE.FINALE)) {
|
||||
@@ -83,6 +106,13 @@ public class RoundService implements RoundListener {
|
||||
statService.setWon(winnerUUID, true);
|
||||
|
||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.WINNER, Placeholder.component("player", player.name())));
|
||||
playerTeleporterService.teleportPlayer(player, DESTINATION.SPECTATOR_AREA);
|
||||
player.setHealth(20);
|
||||
player.setFoodLevel(20);
|
||||
player.getInventory().clear();
|
||||
player.setTotalExperience(0);
|
||||
player.setExp(0.0f);
|
||||
player.setLevel(0);
|
||||
round.endRound();
|
||||
}
|
||||
|
||||
@@ -98,12 +128,33 @@ public class RoundService implements RoundListener {
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
this.roundState = roundState;
|
||||
stopFreeze();
|
||||
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION) || roundState.equals(ROUND_STATE.ENDED)) {
|
||||
if (roundState.equals(ROUND_STATE.ENDED)) {
|
||||
cleanupItems();
|
||||
}
|
||||
clear();
|
||||
lootService.clear();
|
||||
}
|
||||
if (roundState.equals(ROUND_STATE.COUNTDOWN)) {
|
||||
Bukkit.getOnlinePlayers().stream()
|
||||
.filter(player -> PLAYER_STATE.REGISTERED.equals(getPlayerState(player.getUniqueId()).orElse(null)))
|
||||
.forEach(player -> {
|
||||
player.setHealth(20);
|
||||
player.setFoodLevel(20);
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
player.getInventory().clear();
|
||||
player.setTotalExperience(0);
|
||||
player.setExp(0.0f);
|
||||
player.setLevel(0);
|
||||
});
|
||||
startFreeze();
|
||||
WorldBorderUtil.resetBorder();
|
||||
}
|
||||
if (roundState.equals(ROUND_STATE.FINALE)) {
|
||||
WorldBorderUtil.setSize(Config.ROUND.FINALE_BORDER_SIZE, 1);
|
||||
}
|
||||
if (!roundState.equals(ROUND_STATE.ENDED) && !roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(Messages.GAME.NEXT_STATE, Placeholder.parsed("round", roundState.getHumanReadableName())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,4 +172,14 @@ public class RoundService implements RoundListener {
|
||||
playerMovementListener = new PlayerMovementListener(uuidSet);
|
||||
main.getServer().getPluginManager().registerEvents(playerMovementListener, main);
|
||||
}
|
||||
|
||||
private void cleanupItems() {
|
||||
World world = Bukkit.getWorld(Config.ROUND.HG_WORLD);
|
||||
if (world == null) {
|
||||
log.error("Unable to cleanup items, world {} not found", Config.ROUND.HG_WORLD);
|
||||
return;
|
||||
}
|
||||
world.getEntitiesByClass(Item.class).forEach(Item::remove);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,8 +45,10 @@ public class StatService implements RoundListener {
|
||||
|
||||
@Override
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
if (roundState == ROUND_STATE.PLAYER_REGISTRATION) {
|
||||
if (roundState.equals(ROUND_STATE.ENDED)) {
|
||||
saveRoundStats();
|
||||
}
|
||||
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||
currentRoundStats.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class TabListService implements RoundListener {
|
||||
|
||||
private final RoundService roundService;
|
||||
private ROUND_STATE roundState;
|
||||
|
||||
public TabListService(Round round, RoundService roundService) {
|
||||
this.roundService = roundService;
|
||||
this.roundState = round.register(this);
|
||||
}
|
||||
|
||||
public void updatePlayerColor(Player player) {
|
||||
if (!player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
if (roundState == null || roundState == ROUND_STATE.ENDED || roundState == ROUND_STATE.PLAYER_REGISTRATION) {
|
||||
player.playerListName(null);
|
||||
return;
|
||||
}
|
||||
|
||||
PLAYER_STATE state = roundService.getPlayerState(player.getUniqueId()).orElse(PLAYER_STATE.SPECTATING);
|
||||
switch (state) {
|
||||
case REGISTERED -> player.playerListName(player.name().color(NamedTextColor.GREEN));
|
||||
case SPECTATING, DISCONNECTED -> player.playerListName(player.name().color(NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAllPlayers() {
|
||||
Bukkit.getOnlinePlayers().forEach(this::updatePlayerColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
this.roundState = roundState;
|
||||
updateAllPlayers();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.alttd.hunger_games.utils;
|
||||
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldBorder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@UtilityClass
|
||||
public class WorldBorderUtil {
|
||||
|
||||
public static void setSize(int size) {
|
||||
setSize(size, Config.ROUND.DEFAULT_BORDER_TRANSITION_SECONDS);
|
||||
}
|
||||
|
||||
public static void setSize(int size, int seconds) {
|
||||
getWorld().ifPresent(world -> {
|
||||
WorldBorder border = world.getWorldBorder();
|
||||
border.setSize(size, seconds);
|
||||
});
|
||||
}
|
||||
|
||||
public static void resetBorder() {
|
||||
Location center = Config.DESTINATION.START_CENTER;
|
||||
if (center == null || center.getWorld() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorldBorder border = center.getWorld().getWorldBorder();
|
||||
border.setCenter(center.getX(), center.getZ());
|
||||
border.setSize(Config.ROUND.INITIAL_BORDER_SIZE);
|
||||
}
|
||||
|
||||
private static Optional<World> getWorld() {
|
||||
Location center = Config.DESTINATION.START_CENTER;
|
||||
if (center != null && center.getWorld() != null) {
|
||||
return Optional.ofNullable(center.getWorld());
|
||||
}
|
||||
return Bukkit.getWorlds().isEmpty() ? Optional.empty() : Optional.of(Bukkit.getWorlds().getFirst());
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@ version: ${version}
|
||||
main: com.alttd.hunger_games.Main
|
||||
api-version: "1.21"
|
||||
|
||||
# Load after Multiverse-Core to be able to find the teleport locations
|
||||
softdepend:
|
||||
- Multiverse-Core
|
||||
|
||||
commands:
|
||||
hungergames:
|
||||
description: Main HungerGames command
|
||||
|
||||
Reference in New Issue
Block a user