Compare commits

..

No commits in common. "9563e9641f150e61614c210bc61f8b64aa6371da" and "4af509eb5fdf7f073bf6cd281e4dfec042b71716" have entirely different histories.

16 changed files with 42 additions and 214 deletions

View File

@ -6,7 +6,7 @@ import com.alttd.ctf.config.GameConfig;
import com.alttd.ctf.config.Messages; import com.alttd.ctf.config.Messages;
import com.alttd.ctf.events.InventoryItemInteractionEvent; import com.alttd.ctf.events.InventoryItemInteractionEvent;
import com.alttd.ctf.events.OnPlayerDeath; import com.alttd.ctf.events.OnPlayerDeath;
import com.alttd.ctf.events.OnPlayerOnlineStatus; import com.alttd.ctf.events.OnPlayerJoin;
import com.alttd.ctf.events.SnowballEvent; import com.alttd.ctf.events.SnowballEvent;
import com.alttd.ctf.flag.Flag; import com.alttd.ctf.flag.Flag;
import com.alttd.ctf.flag.FlagTryCaptureEvent; import com.alttd.ctf.flag.FlagTryCaptureEvent;
@ -87,9 +87,9 @@ public class Main extends JavaPlugin {
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
pluginManager.registerEvents(new SnowballEvent(gameManager), this); pluginManager.registerEvents(new SnowballEvent(gameManager), this);
pluginManager.registerEvents(new FlagTryCaptureEvent(flag), this); pluginManager.registerEvents(new FlagTryCaptureEvent(flag), this);
pluginManager.registerEvents(new OnPlayerDeath(gameManager, worldBorderApi, this, flag), this); pluginManager.registerEvents(new OnPlayerDeath(gameManager, worldBorderApi, this), this);
pluginManager.registerEvents(new InventoryItemInteractionEvent(), this); pluginManager.registerEvents(new InventoryItemInteractionEvent(), this);
pluginManager.registerEvents(new OnPlayerOnlineStatus(gameManager, flag), this); pluginManager.registerEvents(new OnPlayerJoin(gameManager, flag), this);
pluginManager.registerEvents(new GUIListener(), this); pluginManager.registerEvents(new GUIListener(), this);
} }

View File

@ -35,7 +35,6 @@ public class CommandManager implements CommandExecutor, TabExecutor {
subCommands = Arrays.asList( subCommands = Arrays.asList(
new ChangeTeam(gameManager), new ChangeTeam(gameManager),
new SkipPhase(gameManager),
new Start(gameManager, flag), new Start(gameManager, flag),
new CreateTeam(main, gameManager), new CreateTeam(main, gameManager),
new SelectClass(gameManager, worldBorderApi), new SelectClass(gameManager, worldBorderApi),

View File

@ -1,46 +0,0 @@
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;
}
}

View File

@ -86,8 +86,7 @@ public class GameConfig extends AbstractConfig {
public static double y = 0; public static double y = 0;
public static double z = 0; public static double z = 0;
public static double CAPTURE_RADIUS = 5; public static double CAPTURE_RADIUS = 5;
public static int CAPTURE_SCORE = 50; public static int WINNING_SCORE = 50;
public static double TURN_IN_RADIUS = 3;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static void load() { private static void load() {
@ -96,8 +95,7 @@ public class GameConfig extends AbstractConfig {
y = config.getDouble(prefix, "y", y); y = config.getDouble(prefix, "y", y);
z = config.getDouble(prefix, "z", z); z = config.getDouble(prefix, "z", z);
CAPTURE_RADIUS = config.getDouble(prefix, "capture-radius", CAPTURE_RADIUS); CAPTURE_RADIUS = config.getDouble(prefix, "capture-radius", CAPTURE_RADIUS);
CAPTURE_SCORE = config.getInt(prefix, "capture-score", CAPTURE_SCORE); WINNING_SCORE = config.getInt(prefix, "winning-score", WINNING_SCORE);
TURN_IN_RADIUS = config.getDouble(prefix, "turn-in-radius", TURN_IN_RADIUS);
} }
} }

View File

@ -27,7 +27,6 @@ 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 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 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 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") @SuppressWarnings("unused")
private static void load() { private static void load() {
@ -38,7 +37,6 @@ public class Messages extends AbstractConfig {
CREATE_TEAM = config.getString(prefix, "create-team", CREATE_TEAM); CREATE_TEAM = config.getString(prefix, "create-team", CREATE_TEAM);
START = config.getString(prefix, "start", START); START = config.getString(prefix, "start", START);
SELECT_CLASS = config.getString(prefix, "select-class", SELECT_CLASS); SELECT_CLASS = config.getString(prefix, "select-class", SELECT_CLASS);
SKIP_PHASE = config.getString(prefix, "skip-phase", SKIP_PHASE);
} }
} }

View File

@ -1,15 +1,13 @@
package com.alttd.ctf.events; package com.alttd.ctf.events;
import com.alttd.ctf.Main; import com.alttd.ctf.Main;
import com.alttd.ctf.config.Config;
import com.alttd.ctf.config.GameConfig; import com.alttd.ctf.config.GameConfig;
import com.alttd.ctf.flag.Flag;
import com.alttd.ctf.game.GameManager; import com.alttd.ctf.game.GameManager;
import com.alttd.ctf.game.GamePhase; import com.alttd.ctf.game.GamePhase;
import com.alttd.ctf.team.TeamPlayer; import com.alttd.ctf.team.TeamPlayer;
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi; import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
import lombok.extern.slf4j.Slf4j; 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.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -25,13 +23,11 @@ public class OnPlayerDeath implements Listener {
private final GameManager gameManager; private final GameManager gameManager;
private final WorldBorderApi worldBorderApi; private final WorldBorderApi worldBorderApi;
private final Main main; private final Main main;
private final Flag flag;
public OnPlayerDeath(GameManager gameManager, WorldBorderApi worldBorderApi, Main main, Flag flag) { public OnPlayerDeath(GameManager gameManager, WorldBorderApi worldBorderApi, Main main) {
this.gameManager = gameManager; this.gameManager = gameManager;
this.worldBorderApi = worldBorderApi; this.worldBorderApi = worldBorderApi;
this.main = main; this.main = main;
this.flag = flag;
} }
@EventHandler @EventHandler
@ -39,12 +35,11 @@ public class OnPlayerDeath implements Listener {
if (gameManager.getGamePhase().isEmpty()) { if (gameManager.getGamePhase().isEmpty()) {
return; return;
} }
event.deathMessage(Component.empty()); event.deathMessage(null);
event.setShouldDropExperience(false); event.setShouldDropExperience(false);
Player player = event.getPlayer(); Player player = event.getPlayer();
player.getInventory().clear(); player.getInventory().clear();
player.updateInventory(); player.updateInventory();
flag.handleCarrierDeathOrDisconnect(player);
} }
@EventHandler @EventHandler
@ -61,9 +56,6 @@ public class OnPlayerDeath implements Listener {
} }
TeamPlayer teamPlayer = optionalTeamPlayer.get(); TeamPlayer teamPlayer = optionalTeamPlayer.get();
event.setRespawnLocation(player.getWorld().getSpawnLocation()); 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 Bukkit.getScheduler().runTaskLater(main, () -> teamPlayer.getGameClass().apply(teamPlayer, worldBorderApi, gamePhase.get(), true), GameConfig.RESPAWN.TIME * 20L);//10 x 20 ticks aka 10 seconds
} }

View File

@ -12,19 +12,17 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator; import java.util.Comparator;
import java.util.Optional; import java.util.Optional;
@Slf4j @Slf4j
public class OnPlayerOnlineStatus implements Listener { public class OnPlayerJoin implements Listener {
private final GameManager gameManager; private final GameManager gameManager;
private final Flag flag; private final Flag flag;
public OnPlayerOnlineStatus(GameManager gameManager, Flag flag) { public OnPlayerJoin(GameManager gameManager, Flag flag) {
this.gameManager = gameManager; this.gameManager = gameManager;
this.flag = flag; this.flag = flag;
} }
@ -66,9 +64,4 @@ public class OnPlayerOnlineStatus implements Listener {
player.teleportAsync(teamPlayer.getTeam().getSpawnLocation()); player.teleportAsync(teamPlayer.getTeam().getSpawnLocation());
} }
@EventHandler
public void onPlayerJoin(@NotNull PlayerQuitEvent event) {
flag.handleCarrierDeathOrDisconnect(event.getPlayer());
}
} }

View File

@ -5,7 +5,6 @@ import com.alttd.ctf.game.GamePhase;
import com.alttd.ctf.game_class.GameClass; import com.alttd.ctf.game_class.GameClass;
import com.alttd.ctf.team.TeamPlayer; import com.alttd.ctf.team.TeamPlayer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Snowball; import org.bukkit.entity.Snowball;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -13,8 +12,6 @@ import org.bukkit.event.Listener;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.Optional; import java.util.Optional;
@ -29,7 +26,7 @@ public class SnowballEvent implements Listener {
@FunctionalInterface @FunctionalInterface
private interface SnowballHitConsumer { private interface SnowballHitConsumer {
void apply(Player hitPlayer, Player shooter, TeamPlayer shooterTeamPlayer, Snowball snowball); void apply(Player hitPlayer, Player shooter, TeamPlayer shooterTeamPlayer);
} }
@FunctionalInterface @FunctionalInterface
@ -39,10 +36,7 @@ public class SnowballEvent implements Listener {
@EventHandler @EventHandler
public void onSnowballHit(EntityDamageByEntityEvent event) { public void onSnowballHit(EntityDamageByEntityEvent event) {
handleSnowballHit(event, (hitPlayer, shooter, shooterTeamPlayer, snowball) -> { handleSnowballHit(event, (hitPlayer, shooter, shooterTeamPlayer) -> {
if (blockedAttack(hitPlayer, snowball)) { //Disable damage when it is blocked
return;
}
GameClass shooterClass = shooterTeamPlayer.getGameClass(); GameClass shooterClass = shooterTeamPlayer.getGameClass();
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed()); shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
@ -61,20 +55,6 @@ 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) { private void handleSnowballThrown(ProjectileLaunchEvent event, SnowballThrownConsumer consumer) {
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase(); Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
if (optionalGamePhase.isEmpty()) { if (optionalGamePhase.isEmpty()) {
@ -141,6 +121,6 @@ public class SnowballEvent implements Listener {
log.debug("The shooter hit a member of their own team"); log.debug("The shooter hit a member of their own team");
return; return;
} }
consumer.apply(hitPlayer, shooter, teamPlayerHit.get(), snowball); consumer.apply(hitPlayer, shooter, teamPlayerHit.get());
} }
} }

View File

@ -12,7 +12,6 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.kyori.adventure.title.Title;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.boss.BarColor; import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle; import org.bukkit.boss.BarStyle;
@ -29,8 +28,6 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
public class Flag implements Runnable { public class Flag implements Runnable {
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
private final HashMap<Integer, Integer> teamFlagPointCount = new HashMap<>(); private final HashMap<Integer, Integer> teamFlagPointCount = new HashMap<>();
private final ItemStack flagItem = new ItemStack(Material.BLACK_BANNER); private final ItemStack flagItem = new ItemStack(Material.BLACK_BANNER);
private final BossBar bossBar = createBossBar(); private final BossBar bossBar = createBossBar();
@ -87,54 +84,23 @@ public class Flag implements Runnable {
))); )));
flagCarrier = player; flagCarrier = player;
teamFlagPointCount.clear(); teamFlagPointCount.clear();
winningTeam = null;
lastWinningTeamId = -1;
bossBar.setProgress(0);
} }
public void spawnFlag() { public void spawnFlag() {
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(flagItem.getType())); 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 @Override
public void run() { public void run() {
if (flagCarrier != null) { if (flagCarrier != null) {
checkFlagCarrier(); checkFlagCarrier();
return; return;
} }
if (winningTeam != null) {
spawnFlagParticleRing();
return;
}
if (flagLocation == null) { if (flagLocation == null) {
log.warn("Tried to run Flag without a flag location, spawn it first"); log.warn("Tried to run Flag without a flag location, spawn it first");
return; return;
} }
spawnParticlesOnSquareBorder(flagLocation, GameConfig.FLAG.CAPTURE_RADIUS); spawnParticlesOnSquareBorder(flagLocation.getWorld(), flagLocation);
if (!updateScoreBasedOnNearbyPlayers().join()) { if (!updateScoreBasedOnNearbyPlayers().join()) {
return; //Score didn't change return; //Score didn't change
} }
@ -152,9 +118,9 @@ public class Flag implements Runnable {
} }
} }
private void spawnParticlesOnSquareBorder(Location center, double size) { private void spawnParticlesOnSquareBorder(World world, Location center) {
double size = 10;
double step = 0.2; double step = 0.2;
World world = center.getWorld();
Location finalCenter = center.clone().add(0, 0.5, 0); Location finalCenter = center.clone().add(0, 0.5, 0);
Bukkit.getScheduler().runTask(main, () -> { Bukkit.getScheduler().runTask(main, () -> {
// Top and Bottom (Z varies, X constant) // Top and Bottom (Z varies, X constant)
@ -175,7 +141,7 @@ public class Flag implements Runnable {
private void spawnTrail() { private void spawnTrail() {
TeamColor color = winningTeam.getColor(); TeamColor color = winningTeam.getColor();
particleTrail.forEach(location -> location.getWorld().spawnParticle(Particle.DUST, location, 3, 0, 0, 0, particleTrail.forEach(location -> location.getWorld().spawnParticle(Particle.DUST, location, 1, 0, 0, 0,
new Particle.DustOptions(Color.fromRGB(color.r(), color.g(), color.b()), 1))); new Particle.DustOptions(Color.fromRGB(color.r(), color.g(), color.b()), 1)));
if (particleTrail.size() > 15) { if (particleTrail.size() > 15) {
particleTrail.removeFirst(); particleTrail.removeFirst();
@ -190,15 +156,18 @@ public class Flag implements Runnable {
return; return;
} }
double distance = winningTeam.getFlagTurnInLocation().distance(flagCarrier.getLocation()); double distance = winningTeam.getFlagTurnInLocation().distance(flagCarrier.getLocation());
if (distance > GameConfig.FLAG.TURN_IN_RADIUS) { if (distance > 2) {
Location location = flagCarrier.getLocation(); Location location = flagCarrier.getLocation();
location.setY(location.getY() + 1); location.setY(location.getY() + 1);
particleTrail.add(location); particleTrail.add(location);
spawnTrail(); spawnTrail();
spawnParticlesOnSquareBorder(winningTeam.getWorldBorderCenter(), GameConfig.FLAG.TURN_IN_RADIUS); //TODO spawn some particles or something so a trail is made for specific classes to follow?
return; return;
} }
notifyAboutCapture(); //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())));
spawnFlag(); spawnFlag();
wins.merge(winningTeam.getId(), 1, Integer::sum); wins.merge(winningTeam.getId(), 1, Integer::sum);
winningTeam = null; winningTeam = null;
@ -214,30 +183,13 @@ public class Flag implements Runnable {
particleTrail.clear(); 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() { private Optional<Team> winnerExists() {
Optional<Map.Entry<Integer, Integer>> max = teamFlagPointCount.entrySet().stream() Optional<Map.Entry<Integer, Integer>> max = teamFlagPointCount.entrySet().stream()
.max(Map.Entry.comparingByValue()); .max(Map.Entry.comparingByValue());
if (max.isEmpty()) { if (max.isEmpty()) {
return Optional.empty(); return Optional.empty();
} }
if (max.get().getValue() < GameConfig.FLAG.CAPTURE_SCORE) { if (max.get().getValue() < GameConfig.FLAG.WINNING_SCORE) {
return Optional.empty(); return Optional.empty();
} }
return gameManager.getTeam(max.get().getKey()); return gameManager.getTeam(max.get().getKey());
@ -263,7 +215,7 @@ public class Flag implements Runnable {
/** /**
* Updates the score of teams based on the nearby players within a specified range. * 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 CAPTURE_RADIUS-block radius, * This method identifies nearby players around the current location within a 10-block radius,
* determines their respective teams, and calculates the team with the maximum number of players * 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. * 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: * If a single team has the highest number of nearby players, scores are updated accordingly:
@ -331,7 +283,7 @@ public class Flag implements Runnable {
bossBar.setTitle(String.format("Team %s is capturing the flag", PlainTextComponentSerializer.plainText().serialize(team.get().getName()))); bossBar.setTitle(String.format("Team %s is capturing the flag", PlainTextComponentSerializer.plainText().serialize(team.get().getName())));
lastWinningTeamId = highestKey; lastWinningTeamId = highestKey;
} }
bossBar.setProgress(Math.min(GameConfig.FLAG.CAPTURE_SCORE, teamFlagPointCount.get(highestKey)) / (double) GameConfig.FLAG.CAPTURE_SCORE); bossBar.setProgress(Math.min(GameConfig.FLAG.WINNING_SCORE, teamFlagPointCount.get(highestKey)) / (double) GameConfig.FLAG.WINNING_SCORE);
bossBar.setVisible(teamFlagPointCount.get(highestKey) > 0); bossBar.setVisible(teamFlagPointCount.get(highestKey) > 0);
} }
@ -359,21 +311,4 @@ public class Flag implements Runnable {
wins.clear(); wins.clear();
lastWinningTeamId = -1; 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"));
}
} }

View File

@ -77,7 +77,7 @@ public class GameManager {
executorService.shutdown(); executorService.shutdown();
executorService = Executors.newSingleThreadScheduledExecutor(); executorService = Executors.newSingleThreadScheduledExecutor();
} }
runningGame = new RunningGame(this, duration, flag, executorService); runningGame = new RunningGame(this, duration, flag);
executorService.scheduleAtFixedRate(runningGame, 0, 1, TimeUnit.SECONDS); executorService.scheduleAtFixedRate(runningGame, 0, 1, TimeUnit.SECONDS);
} }
@ -99,11 +99,4 @@ public class GameManager {
.max() .max()
.orElse(0); .orElse(0);
} }
public boolean skipPhase() {
if (runningGame == null) {
return false;
}
return runningGame.skipCurrentPhase();
}
} }

View File

@ -13,7 +13,6 @@ import javax.annotation.Nullable;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.ScheduledExecutorService;
@Slf4j @Slf4j
public class RunningGame implements Runnable { public class RunningGame implements Runnable {
@ -21,16 +20,14 @@ public class RunningGame implements Runnable {
private final HashMap<GamePhase, Duration> phaseDurations = GameConfig.PHASES.getGAME_PHASE_DURATION(); private final HashMap<GamePhase, Duration> phaseDurations = GameConfig.PHASES.getGAME_PHASE_DURATION();
private final GameManager gameManager; private final GameManager gameManager;
private final Flag flag; private final Flag flag;
private final ScheduledExecutorService executorService;
@Getter @Getter
private GamePhase currentPhase = GamePhase.values()[0]; private GamePhase currentPhase = GamePhase.values()[0];
private Instant phaseStartTime = null; private Instant phaseStartTime = null;
private int lastMinuteBroadcast = 0; private int lastMinuteBroadcast = 0;
public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag, ScheduledExecutorService executorService) { public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag) {
this.gameManager = gameManager; this.gameManager = gameManager;
this.flag = flag; this.flag = flag;
this.executorService = executorService;
phaseDurations.put(GamePhase.COMBAT, gameDuration); phaseDurations.put(GamePhase.COMBAT, gameDuration);
} }
@ -40,16 +37,16 @@ public class RunningGame implements Runnable {
GamePhase nextPhase = (currentPhase.ordinal() + 1 < GamePhase.values().length) ? GamePhase.values()[currentPhase.ordinal() + 1] : null; GamePhase nextPhase = (currentPhase.ordinal() + 1 < GamePhase.values().length) ? GamePhase.values()[currentPhase.ordinal() + 1] : null;
if (phaseStartTime == null) { if (phaseStartTime == null) {
phaseStartTime = Instant.now(); phaseStartTime = Instant.now();
nextPhaseActions(null, currentPhase); nextPhaseActions(null, currentPhase, nextPhase);
} }
if (nextPhase != null && Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) { if (Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
nextPhaseActions(currentPhase, nextPhase); GamePhase previousPhase = currentPhase;
currentPhase = GamePhase.values()[currentPhase.ordinal() + 1]; //TODO fix this running out of bounds
nextPhaseActions(previousPhase, currentPhase, nextPhase);
phaseStartTime = Instant.now(); phaseStartTime = Instant.now();
} else if (nextPhase != null) { } else if (nextPhase != null) {
broadcastNextPhaseStartTime(currentPhase, nextPhase); broadcastNextPhaseStartTime(currentPhase, nextPhase);
} else {
executorService.shutdown();
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Unexpected error in running game", e); log.error("Unexpected error in running game", e);
@ -57,13 +54,15 @@ public class RunningGame implements Runnable {
} }
} }
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase) { private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase, @Nullable GamePhase nextPhase) {
//TODO command to go to next phase //TODO command to go to next phase
this.currentPhase = phase;
if (previousPhase != null) { if (previousPhase != null) {
gameManager.getPhaseExecutor(previousPhase).end(phase); gameManager.getPhaseExecutor(previousPhase).end(phase);
} }
gameManager.getPhaseExecutor(phase).start(flag); 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 private void broadcastNextPhaseStartTime(GamePhase currentPhase, GamePhase nextPhase) {//TODO check how this works/what it should do
@ -87,18 +86,9 @@ 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() { public void end() {
//TODO say the phase ended early? //TODO say the phase ended early?
nextPhaseActions(currentPhase, GamePhase.ENDED); currentPhase = GamePhase.ENDED;
nextPhaseActions(null, currentPhase, null);
} }
} }

View File

@ -103,7 +103,7 @@ public abstract class GameClass {
ItemStack itemStack = new ItemStack(material); ItemStack itemStack = new ItemStack(material);
ItemMeta itemMeta = itemStack.getItemMeta(); ItemMeta itemMeta = itemStack.getItemMeta();
if (itemMeta instanceof LeatherArmorMeta leatherArmorMeta) { if (itemMeta instanceof LeatherArmorMeta leatherArmorMeta) {
leatherArmorMeta.setColor(Color.fromRGB(r, g, b)); leatherArmorMeta.setColor(Color.fromBGR(r, g, b));
itemStack.setItemMeta(leatherArmorMeta); itemStack.setItemMeta(leatherArmorMeta);
} }
return itemStack; return itemStack;

View File

@ -57,7 +57,6 @@ public class EngineerCreator {
ItemMeta meta = shovel.getItemMeta(); ItemMeta meta = shovel.getItemMeta();
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow excavator</color>", teamColor.hex()))); meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow excavator</color>", teamColor.hex())));
meta.addEnchant(Enchantment.EFFICIENCY, 4, false); meta.addEnchant(Enchantment.EFFICIENCY, 4, false);
meta.setUnbreakable(true);
shovel.setItemMeta(meta); shovel.setItemMeta(meta);
return shovel; return shovel;
} }

View File

@ -56,7 +56,6 @@ public class FighterCreator {
ItemMeta meta = shovel.getItemMeta(); ItemMeta meta = shovel.getItemMeta();
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex()))); meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex())));
meta.addEnchant(Enchantment.EFFICIENCY, 1, false); meta.addEnchant(Enchantment.EFFICIENCY, 1, false);
meta.setUnbreakable(true);
shovel.setItemMeta(meta); shovel.setItemMeta(meta);
return shovel; return shovel;
} }

View File

@ -43,7 +43,6 @@ public class TankCreator {
itemMeta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE); itemMeta.addItemFlags(ItemFlag.HIDE_UNBREAKABLE);
itemMeta.itemName(MiniMessage.miniMessage().deserialize( itemMeta.itemName(MiniMessage.miniMessage().deserialize(
String.format("<color:%s>Shield</color>", teamColor.hex()))); String.format("<color:%s>Shield</color>", teamColor.hex())));
itemMeta.setUnbreakable(true);
shield.setItemMeta(itemMeta); shield.setItemMeta(itemMeta);
return shield; return shield;
} }
@ -52,7 +51,6 @@ public class TankCreator {
ItemStack shovel = new ItemStack(Material.WOODEN_SHOVEL); ItemStack shovel = new ItemStack(Material.WOODEN_SHOVEL);
ItemMeta meta = shovel.getItemMeta(); ItemMeta meta = shovel.getItemMeta();
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex()))); meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex())));
meta.setUnbreakable(true);
shovel.setItemMeta(meta); shovel.setItemMeta(meta);
return shovel; return shovel;
} }

View File

@ -1,3 +1,3 @@
#Tue Feb 11 21:41:29 CET 2025 #Sat Feb 08 23:31:55 CET 2025
buildNumber=37 buildNumber=33
version=0.1 version=0.1