Compare commits
13 Commits
4af509eb5f
...
9563e9641f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9563e9641f | ||
|
|
aad63f174e | ||
|
|
66c24d852d | ||
|
|
7acf39b600 | ||
|
|
6f99402d16 | ||
|
|
5f2fb8fe0a | ||
|
|
dbcbb10079 | ||
|
|
7070165a94 | ||
|
|
8411db57a1 | ||
|
|
498ed774a4 | ||
|
|
e95dabccac | ||
|
|
6ae2563d16 | ||
|
|
91ee3cf8f9 |
|
|
@ -6,7 +6,7 @@ import com.alttd.ctf.config.GameConfig;
|
|||
import com.alttd.ctf.config.Messages;
|
||||
import com.alttd.ctf.events.InventoryItemInteractionEvent;
|
||||
import com.alttd.ctf.events.OnPlayerDeath;
|
||||
import com.alttd.ctf.events.OnPlayerJoin;
|
||||
import com.alttd.ctf.events.OnPlayerOnlineStatus;
|
||||
import com.alttd.ctf.events.SnowballEvent;
|
||||
import com.alttd.ctf.flag.Flag;
|
||||
import com.alttd.ctf.flag.FlagTryCaptureEvent;
|
||||
|
|
@ -87,9 +87,9 @@ public class Main extends JavaPlugin {
|
|||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
pluginManager.registerEvents(new SnowballEvent(gameManager), this);
|
||||
pluginManager.registerEvents(new FlagTryCaptureEvent(flag), this);
|
||||
pluginManager.registerEvents(new OnPlayerDeath(gameManager, worldBorderApi, this), this);
|
||||
pluginManager.registerEvents(new OnPlayerDeath(gameManager, worldBorderApi, this, flag), this);
|
||||
pluginManager.registerEvents(new InventoryItemInteractionEvent(), this);
|
||||
pluginManager.registerEvents(new OnPlayerJoin(gameManager, flag), this);
|
||||
pluginManager.registerEvents(new OnPlayerOnlineStatus(gameManager, flag), this);
|
||||
pluginManager.registerEvents(new GUIListener(), this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public class CommandManager implements CommandExecutor, TabExecutor {
|
|||
|
||||
subCommands = Arrays.asList(
|
||||
new ChangeTeam(gameManager),
|
||||
new SkipPhase(gameManager),
|
||||
new Start(gameManager, flag),
|
||||
new CreateTeam(main, gameManager),
|
||||
new SelectClass(gameManager, worldBorderApi),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
package com.alttd.ctf.commands.subcommands;
|
||||
|
||||
import com.alttd.ctf.commands.SubCommand;
|
||||
import com.alttd.ctf.config.Messages;
|
||||
import com.alttd.ctf.game.GameManager;
|
||||
import com.alttd.ctf.team.Team;
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class SkipPhase extends SubCommand {
|
||||
|
||||
private final GameManager gameManager;
|
||||
|
||||
@Override
|
||||
public int onCommand(CommandSender commandSender, String[] args) {
|
||||
if (!gameManager.skipPhase()) {
|
||||
commandSender.sendRichMessage("<red>The phase was not skipped because there is no running game or there is no next phase</red>");
|
||||
return 0;
|
||||
}
|
||||
commandSender.sendRichMessage("<green>The current phase was skipped!</green>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "skipphase";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return Messages.HELP.SKIP_PHASE;
|
||||
}
|
||||
}
|
||||
|
|
@ -86,7 +86,8 @@ public class GameConfig extends AbstractConfig {
|
|||
public static double y = 0;
|
||||
public static double z = 0;
|
||||
public static double CAPTURE_RADIUS = 5;
|
||||
public static int WINNING_SCORE = 50;
|
||||
public static int CAPTURE_SCORE = 50;
|
||||
public static double TURN_IN_RADIUS = 3;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
|
|
@ -95,7 +96,8 @@ public class GameConfig extends AbstractConfig {
|
|||
y = config.getDouble(prefix, "y", y);
|
||||
z = config.getDouble(prefix, "z", z);
|
||||
CAPTURE_RADIUS = config.getDouble(prefix, "capture-radius", CAPTURE_RADIUS);
|
||||
WINNING_SCORE = config.getInt(prefix, "winning-score", WINNING_SCORE);
|
||||
CAPTURE_SCORE = config.getInt(prefix, "capture-score", CAPTURE_SCORE);
|
||||
TURN_IN_RADIUS = config.getDouble(prefix, "turn-in-radius", TURN_IN_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ public class Messages extends AbstractConfig {
|
|||
public static String CREATE_TEAM = "<green>Create a team: <gold>/ctf createteam <team_name> <hex_color></gold></green>";
|
||||
public static String START = "<green>Start a new game: <gold>/ctf start <time_in_minutes></gold></green>";
|
||||
public static String SELECT_CLASS = "<green>Open class selection: <gold>/ctf selectclass</gold></green>";
|
||||
public static String SKIP_PHASE = "<green>Skip the current phase: <gold>/ctf skipphase</gold></green>";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
|
|
@ -37,6 +38,7 @@ public class Messages extends AbstractConfig {
|
|||
CREATE_TEAM = config.getString(prefix, "create-team", CREATE_TEAM);
|
||||
START = config.getString(prefix, "start", START);
|
||||
SELECT_CLASS = config.getString(prefix, "select-class", SELECT_CLASS);
|
||||
SKIP_PHASE = config.getString(prefix, "skip-phase", SKIP_PHASE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
package com.alttd.ctf.events;
|
||||
|
||||
import com.alttd.ctf.Main;
|
||||
import com.alttd.ctf.config.Config;
|
||||
import com.alttd.ctf.config.GameConfig;
|
||||
import com.alttd.ctf.flag.Flag;
|
||||
import com.alttd.ctf.game.GameManager;
|
||||
import com.alttd.ctf.game.GamePhase;
|
||||
import com.alttd.ctf.team.TeamPlayer;
|
||||
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
|
@ -23,11 +25,13 @@ public class OnPlayerDeath implements Listener {
|
|||
private final GameManager gameManager;
|
||||
private final WorldBorderApi worldBorderApi;
|
||||
private final Main main;
|
||||
private final Flag flag;
|
||||
|
||||
public OnPlayerDeath(GameManager gameManager, WorldBorderApi worldBorderApi, Main main) {
|
||||
public OnPlayerDeath(GameManager gameManager, WorldBorderApi worldBorderApi, Main main, Flag flag) {
|
||||
this.gameManager = gameManager;
|
||||
this.worldBorderApi = worldBorderApi;
|
||||
this.main = main;
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
@ -35,11 +39,12 @@ public class OnPlayerDeath implements Listener {
|
|||
if (gameManager.getGamePhase().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
event.deathMessage(null);
|
||||
event.deathMessage(Component.empty());
|
||||
event.setShouldDropExperience(false);
|
||||
Player player = event.getPlayer();
|
||||
player.getInventory().clear();
|
||||
player.updateInventory();
|
||||
flag.handleCarrierDeathOrDisconnect(player);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
|
@ -56,6 +61,9 @@ public class OnPlayerDeath implements Listener {
|
|||
}
|
||||
TeamPlayer teamPlayer = optionalTeamPlayer.get();
|
||||
event.setRespawnLocation(player.getWorld().getSpawnLocation());
|
||||
player.sendRichMessage("<red>You died</red><nl><green>You will respawn in <seconds> seconds.</green>",
|
||||
Placeholder.component("nl", Component.newline()),
|
||||
Placeholder.parsed("seconds", String.valueOf(GameConfig.RESPAWN.TIME)));
|
||||
Bukkit.getScheduler().runTaskLater(main, () -> teamPlayer.getGameClass().apply(teamPlayer, worldBorderApi, gamePhase.get(), true), GameConfig.RESPAWN.TIME * 20L);//10 x 20 ticks aka 10 seconds
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,19 @@ import org.bukkit.entity.Player;
|
|||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class OnPlayerJoin implements Listener {
|
||||
public class OnPlayerOnlineStatus implements Listener {
|
||||
|
||||
private final GameManager gameManager;
|
||||
private final Flag flag;
|
||||
|
||||
public OnPlayerJoin(GameManager gameManager, Flag flag) {
|
||||
public OnPlayerOnlineStatus(GameManager gameManager, Flag flag) {
|
||||
this.gameManager = gameManager;
|
||||
this.flag = flag;
|
||||
}
|
||||
|
|
@ -64,4 +66,9 @@ public class OnPlayerJoin implements Listener {
|
|||
player.teleportAsync(teamPlayer.getTeam().getSpawnLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(@NotNull PlayerQuitEvent event) {
|
||||
flag.handleCarrierDeathOrDisconnect(event.getPlayer());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.alttd.ctf.game.GamePhase;
|
|||
import com.alttd.ctf.game_class.GameClass;
|
||||
import com.alttd.ctf.team.TeamPlayer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Snowball;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
|
@ -12,6 +13,8 @@ import org.bukkit.event.Listener;
|
|||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
|
|
@ -26,7 +29,7 @@ public class SnowballEvent implements Listener {
|
|||
|
||||
@FunctionalInterface
|
||||
private interface SnowballHitConsumer {
|
||||
void apply(Player hitPlayer, Player shooter, TeamPlayer shooterTeamPlayer);
|
||||
void apply(Player hitPlayer, Player shooter, TeamPlayer shooterTeamPlayer, Snowball snowball);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
|
@ -36,7 +39,10 @@ public class SnowballEvent implements Listener {
|
|||
|
||||
@EventHandler
|
||||
public void onSnowballHit(EntityDamageByEntityEvent event) {
|
||||
handleSnowballHit(event, (hitPlayer, shooter, shooterTeamPlayer) -> {
|
||||
handleSnowballHit(event, (hitPlayer, shooter, shooterTeamPlayer, snowball) -> {
|
||||
if (blockedAttack(hitPlayer, snowball)) { //Disable damage when it is blocked
|
||||
return;
|
||||
}
|
||||
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
||||
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
||||
|
||||
|
|
@ -55,6 +61,20 @@ public class SnowballEvent implements Listener {
|
|||
});
|
||||
}
|
||||
|
||||
private boolean blockedAttack(@NotNull Player hitPlayer, @NotNull Snowball snowball) {
|
||||
if (!hitPlayer.isBlocking()) {
|
||||
return false;
|
||||
}
|
||||
Location playerLocation = hitPlayer.getLocation();
|
||||
Vector playerFacing = playerLocation.getDirection().normalize();
|
||||
Location snowballLocation = snowball.getLocation();
|
||||
Vector impactDirection = snowballLocation.toVector().subtract(playerLocation.toVector()).normalize();
|
||||
|
||||
double angle = playerFacing.angle(impactDirection);
|
||||
|
||||
return !(Math.toDegrees(angle) > 80); //Blocked if the angle was <= 80
|
||||
}
|
||||
|
||||
private void handleSnowballThrown(ProjectileLaunchEvent event, SnowballThrownConsumer consumer) {
|
||||
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
||||
if (optionalGamePhase.isEmpty()) {
|
||||
|
|
@ -121,6 +141,6 @@ public class SnowballEvent implements Listener {
|
|||
log.debug("The shooter hit a member of their own team");
|
||||
return;
|
||||
}
|
||||
consumer.apply(hitPlayer, shooter, teamPlayerHit.get());
|
||||
consumer.apply(hitPlayer, shooter, teamPlayerHit.get(), snowball);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
|
|||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import net.kyori.adventure.title.Title;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarStyle;
|
||||
|
|
@ -28,6 +29,8 @@ import java.util.stream.Collectors;
|
|||
@Slf4j
|
||||
public class Flag implements Runnable {
|
||||
|
||||
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||
|
||||
private final HashMap<Integer, Integer> teamFlagPointCount = new HashMap<>();
|
||||
private final ItemStack flagItem = new ItemStack(Material.BLACK_BANNER);
|
||||
private final BossBar bossBar = createBossBar();
|
||||
|
|
@ -84,23 +87,54 @@ public class Flag implements Runnable {
|
|||
)));
|
||||
flagCarrier = player;
|
||||
teamFlagPointCount.clear();
|
||||
winningTeam = null;
|
||||
lastWinningTeamId = -1;
|
||||
bossBar.setProgress(0);
|
||||
}
|
||||
|
||||
public void spawnFlag() {
|
||||
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(flagItem.getType()));
|
||||
}
|
||||
|
||||
private void spawnFlagParticleRing() {
|
||||
Location center = flagLocation.clone();
|
||||
World world = center.getWorld();
|
||||
double radius = 0.7;
|
||||
double gap = 0.2;
|
||||
double circumference = 2 * Math.PI * radius;
|
||||
int particleCount = (int) (circumference / gap);
|
||||
|
||||
Particle particle = Particle.DUST;
|
||||
TeamColor color = winningTeam.getColor();
|
||||
// Generate particle positions
|
||||
for (double heightOffset = 0; heightOffset < 2; heightOffset += 0.5) {
|
||||
center.setY(center.getY() + 0.5);
|
||||
for (int i = 0; i < particleCount; i++) {
|
||||
double angle = 2 * Math.PI * i / particleCount;
|
||||
double x = center.getX() + radius * Math.cos(angle);
|
||||
double z = center.getZ() + radius * Math.sin(angle);
|
||||
double y = center.getY();
|
||||
|
||||
world.spawnParticle(particle, x, y, z, 1, 0, 0, 0, new Particle.DustOptions(Color.fromRGB(color.r(), color.g(), color.b()), 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (flagCarrier != null) {
|
||||
checkFlagCarrier();
|
||||
return;
|
||||
}
|
||||
if (winningTeam != null) {
|
||||
spawnFlagParticleRing();
|
||||
return;
|
||||
}
|
||||
if (flagLocation == null) {
|
||||
log.warn("Tried to run Flag without a flag location, spawn it first");
|
||||
return;
|
||||
}
|
||||
spawnParticlesOnSquareBorder(flagLocation.getWorld(), flagLocation);
|
||||
spawnParticlesOnSquareBorder(flagLocation, GameConfig.FLAG.CAPTURE_RADIUS);
|
||||
if (!updateScoreBasedOnNearbyPlayers().join()) {
|
||||
return; //Score didn't change
|
||||
}
|
||||
|
|
@ -118,9 +152,9 @@ public class Flag implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private void spawnParticlesOnSquareBorder(World world, Location center) {
|
||||
double size = 10;
|
||||
private void spawnParticlesOnSquareBorder(Location center, double size) {
|
||||
double step = 0.2;
|
||||
World world = center.getWorld();
|
||||
Location finalCenter = center.clone().add(0, 0.5, 0);
|
||||
Bukkit.getScheduler().runTask(main, () -> {
|
||||
// Top and Bottom (Z varies, X constant)
|
||||
|
|
@ -141,7 +175,7 @@ public class Flag implements Runnable {
|
|||
|
||||
private void spawnTrail() {
|
||||
TeamColor color = winningTeam.getColor();
|
||||
particleTrail.forEach(location -> location.getWorld().spawnParticle(Particle.DUST, location, 1, 0, 0, 0,
|
||||
particleTrail.forEach(location -> location.getWorld().spawnParticle(Particle.DUST, location, 3, 0, 0, 0,
|
||||
new Particle.DustOptions(Color.fromRGB(color.r(), color.g(), color.b()), 1)));
|
||||
if (particleTrail.size() > 15) {
|
||||
particleTrail.removeFirst();
|
||||
|
|
@ -156,18 +190,15 @@ public class Flag implements Runnable {
|
|||
return;
|
||||
}
|
||||
double distance = winningTeam.getFlagTurnInLocation().distance(flagCarrier.getLocation());
|
||||
if (distance > 2) {
|
||||
if (distance > GameConfig.FLAG.TURN_IN_RADIUS) {
|
||||
Location location = flagCarrier.getLocation();
|
||||
location.setY(location.getY() + 1);
|
||||
particleTrail.add(location);
|
||||
spawnTrail();
|
||||
//TODO spawn some particles or something so a trail is made for specific classes to follow?
|
||||
spawnParticlesOnSquareBorder(winningTeam.getWorldBorderCenter(), GameConfig.FLAG.TURN_IN_RADIUS);
|
||||
return;
|
||||
}
|
||||
//TODO better message? mayb with a text thing on the screen?
|
||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<player> captured the flag for <team>!",
|
||||
Placeholder.component("player", flagCarrier.displayName()),
|
||||
Placeholder.component("team", winningTeam.getName())));
|
||||
notifyAboutCapture();
|
||||
spawnFlag();
|
||||
wins.merge(winningTeam.getId(), 1, Integer::sum);
|
||||
winningTeam = null;
|
||||
|
|
@ -183,13 +214,30 @@ public class Flag implements Runnable {
|
|||
particleTrail.clear();
|
||||
}
|
||||
|
||||
private void notifyAboutCapture() {
|
||||
Bukkit.broadcast(miniMessage.deserialize("<player> captured the flag for <team>!",
|
||||
Placeholder.component("player", flagCarrier.displayName()),
|
||||
Placeholder.component("team", winningTeam.getName())));
|
||||
Title capturingTeamTitle = Title.title(miniMessage.deserialize("<green><team> captured the flag!</green>",
|
||||
Placeholder.component("team", winningTeam.getName())),
|
||||
miniMessage.deserialize("<green>protect <player> while they bring it to your base.</green>",
|
||||
Placeholder.component("player", flagCarrier.displayName())));
|
||||
Title huntingTeamTitle = Title.title(miniMessage.deserialize("<red><team> captured the flag!</red>",
|
||||
Placeholder.component("team", winningTeam.getName())),
|
||||
miniMessage.deserialize("<red>kill <player> before they bring it to their base.</red>",
|
||||
Placeholder.component("player", flagCarrier.displayName())));
|
||||
Bukkit.getOnlinePlayers().forEach(player ->
|
||||
gameManager.getTeam(player.getUniqueId()).ifPresent(team ->
|
||||
player.showTitle(team.getId() == winningTeam.getId() ? capturingTeamTitle : huntingTeamTitle)));
|
||||
}
|
||||
|
||||
private Optional<Team> winnerExists() {
|
||||
Optional<Map.Entry<Integer, Integer>> max = teamFlagPointCount.entrySet().stream()
|
||||
.max(Map.Entry.comparingByValue());
|
||||
if (max.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (max.get().getValue() < GameConfig.FLAG.WINNING_SCORE) {
|
||||
if (max.get().getValue() < GameConfig.FLAG.CAPTURE_SCORE) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return gameManager.getTeam(max.get().getKey());
|
||||
|
|
@ -215,7 +263,7 @@ public class Flag implements Runnable {
|
|||
|
||||
/**
|
||||
* Updates the score of teams based on the nearby players within a specified range.
|
||||
* This method identifies nearby players around the current location within a 10-block radius,
|
||||
* This method identifies nearby players around the current location within a CAPTURE_RADIUS-block radius,
|
||||
* determines their respective teams, and calculates the team with the maximum number of players
|
||||
* in proximity. If there is a tie for the maximum count, the method exits without updating scores.
|
||||
* If a single team has the highest number of nearby players, scores are updated accordingly:
|
||||
|
|
@ -283,7 +331,7 @@ public class Flag implements Runnable {
|
|||
bossBar.setTitle(String.format("Team %s is capturing the flag", PlainTextComponentSerializer.plainText().serialize(team.get().getName())));
|
||||
lastWinningTeamId = highestKey;
|
||||
}
|
||||
bossBar.setProgress(Math.min(GameConfig.FLAG.WINNING_SCORE, teamFlagPointCount.get(highestKey)) / (double) GameConfig.FLAG.WINNING_SCORE);
|
||||
bossBar.setProgress(Math.min(GameConfig.FLAG.CAPTURE_SCORE, teamFlagPointCount.get(highestKey)) / (double) GameConfig.FLAG.CAPTURE_SCORE);
|
||||
bossBar.setVisible(teamFlagPointCount.get(highestKey) > 0);
|
||||
}
|
||||
|
||||
|
|
@ -311,4 +359,21 @@ public class Flag implements Runnable {
|
|||
wins.clear();
|
||||
lastWinningTeamId = -1;
|
||||
}
|
||||
|
||||
public void handleCarrierDeathOrDisconnect(Player player) {
|
||||
if (flagCarrier == null) {
|
||||
return;
|
||||
}
|
||||
if (!flagCarrier.getUniqueId().equals(player.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
flagCarrier = null;
|
||||
particleTrail.clear();
|
||||
spawnFlag();
|
||||
gameManager.getTeam(player.getUniqueId())
|
||||
.ifPresentOrElse(team -> Bukkit.broadcast(MiniMessage.miniMessage()
|
||||
.deserialize("<red><team>'s flag carrier died! The flag has respawned",
|
||||
Placeholder.component("team", team.getName()))),
|
||||
() -> log.warn("A flag carrier died who was not part of a team"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ public class GameManager {
|
|||
executorService.shutdown();
|
||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
}
|
||||
runningGame = new RunningGame(this, duration, flag);
|
||||
runningGame = new RunningGame(this, duration, flag, executorService);
|
||||
executorService.scheduleAtFixedRate(runningGame, 0, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
|
@ -99,4 +99,11 @@ public class GameManager {
|
|||
.max()
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
public boolean skipPhase() {
|
||||
if (runningGame == null) {
|
||||
return false;
|
||||
}
|
||||
return runningGame.skipCurrentPhase();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import javax.annotation.Nullable;
|
|||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
@Slf4j
|
||||
public class RunningGame implements Runnable {
|
||||
|
|
@ -20,14 +21,16 @@ public class RunningGame implements Runnable {
|
|||
private final HashMap<GamePhase, Duration> phaseDurations = GameConfig.PHASES.getGAME_PHASE_DURATION();
|
||||
private final GameManager gameManager;
|
||||
private final Flag flag;
|
||||
private final ScheduledExecutorService executorService;
|
||||
@Getter
|
||||
private GamePhase currentPhase = GamePhase.values()[0];
|
||||
private Instant phaseStartTime = null;
|
||||
private int lastMinuteBroadcast = 0;
|
||||
|
||||
public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag) {
|
||||
public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag, ScheduledExecutorService executorService) {
|
||||
this.gameManager = gameManager;
|
||||
this.flag = flag;
|
||||
this.executorService = executorService;
|
||||
phaseDurations.put(GamePhase.COMBAT, gameDuration);
|
||||
}
|
||||
|
||||
|
|
@ -37,16 +40,16 @@ public class RunningGame implements Runnable {
|
|||
GamePhase nextPhase = (currentPhase.ordinal() + 1 < GamePhase.values().length) ? GamePhase.values()[currentPhase.ordinal() + 1] : null;
|
||||
if (phaseStartTime == null) {
|
||||
phaseStartTime = Instant.now();
|
||||
nextPhaseActions(null, currentPhase, nextPhase);
|
||||
nextPhaseActions(null, currentPhase);
|
||||
}
|
||||
|
||||
if (Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
|
||||
GamePhase previousPhase = currentPhase;
|
||||
currentPhase = GamePhase.values()[currentPhase.ordinal() + 1]; //TODO fix this running out of bounds
|
||||
nextPhaseActions(previousPhase, currentPhase, nextPhase);
|
||||
if (nextPhase != null && Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
|
||||
nextPhaseActions(currentPhase, nextPhase);
|
||||
phaseStartTime = Instant.now();
|
||||
} else if (nextPhase != null) {
|
||||
broadcastNextPhaseStartTime(currentPhase, nextPhase);
|
||||
} else {
|
||||
executorService.shutdown();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error in running game", e);
|
||||
|
|
@ -54,15 +57,13 @@ public class RunningGame implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase, @Nullable GamePhase nextPhase) {
|
||||
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase) {
|
||||
//TODO command to go to next phase
|
||||
this.currentPhase = phase;
|
||||
if (previousPhase != null) {
|
||||
gameManager.getPhaseExecutor(previousPhase).end(phase);
|
||||
}
|
||||
gameManager.getPhaseExecutor(phase).start(flag);
|
||||
if (nextPhase != null) {
|
||||
broadcastNextPhaseStartTime(phase, nextPhase);
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastNextPhaseStartTime(GamePhase currentPhase, GamePhase nextPhase) {//TODO check how this works/what it should do
|
||||
|
|
@ -86,9 +87,18 @@ public class RunningGame implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean skipCurrentPhase() {
|
||||
GamePhase nextPhase = (currentPhase.ordinal() + 1 < GamePhase.values().length) ? GamePhase.values()[currentPhase.ordinal() + 1] : null;
|
||||
if (nextPhase == null) {
|
||||
log.warn("Tried to skip phase {} but there is no next phase", currentPhase);
|
||||
return false;
|
||||
}
|
||||
nextPhaseActions(currentPhase, nextPhase);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void end() {
|
||||
//TODO say the phase ended early?
|
||||
currentPhase = GamePhase.ENDED;
|
||||
nextPhaseActions(null, currentPhase, null);
|
||||
nextPhaseActions(currentPhase, GamePhase.ENDED);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public abstract class GameClass {
|
|||
ItemStack itemStack = new ItemStack(material);
|
||||
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||
if (itemMeta instanceof LeatherArmorMeta leatherArmorMeta) {
|
||||
leatherArmorMeta.setColor(Color.fromBGR(r, g, b));
|
||||
leatherArmorMeta.setColor(Color.fromRGB(r, g, b));
|
||||
itemStack.setItemMeta(leatherArmorMeta);
|
||||
}
|
||||
return itemStack;
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public class EngineerCreator {
|
|||
ItemMeta meta = shovel.getItemMeta();
|
||||
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow excavator</color>", teamColor.hex())));
|
||||
meta.addEnchant(Enchantment.EFFICIENCY, 4, false);
|
||||
meta.setUnbreakable(true);
|
||||
shovel.setItemMeta(meta);
|
||||
return shovel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ public class FighterCreator {
|
|||
ItemMeta meta = shovel.getItemMeta();
|
||||
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex())));
|
||||
meta.addEnchant(Enchantment.EFFICIENCY, 1, false);
|
||||
meta.setUnbreakable(true);
|
||||
shovel.setItemMeta(meta);
|
||||
return shovel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public class TankCreator {
|
|||
itemMeta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE);
|
||||
itemMeta.itemName(MiniMessage.miniMessage().deserialize(
|
||||
String.format("<color:%s>Shield</color>", teamColor.hex())));
|
||||
itemMeta.setUnbreakable(true);
|
||||
shield.setItemMeta(itemMeta);
|
||||
return shield;
|
||||
}
|
||||
|
|
@ -51,6 +52,7 @@ public class TankCreator {
|
|||
ItemStack shovel = new ItemStack(Material.WOODEN_SHOVEL);
|
||||
ItemMeta meta = shovel.getItemMeta();
|
||||
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex())));
|
||||
meta.setUnbreakable(true);
|
||||
shovel.setItemMeta(meta);
|
||||
return shovel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#Sat Feb 08 23:31:55 CET 2025
|
||||
buildNumber=33
|
||||
#Tue Feb 11 21:41:29 CET 2025
|
||||
buildNumber=37
|
||||
version=0.1
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user