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.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.OnPlayerJoin;
|
import com.alttd.ctf.events.OnPlayerOnlineStatus;
|
||||||
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), this);
|
pluginManager.registerEvents(new OnPlayerDeath(gameManager, worldBorderApi, this, flag), this);
|
||||||
pluginManager.registerEvents(new InventoryItemInteractionEvent(), 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);
|
pluginManager.registerEvents(new GUIListener(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ 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),
|
||||||
|
|
|
||||||
|
|
@ -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 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 WINNING_SCORE = 50;
|
public static int CAPTURE_SCORE = 50;
|
||||||
|
public static double TURN_IN_RADIUS = 3;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
|
|
@ -95,7 +96,8 @@ 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);
|
||||||
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 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() {
|
||||||
|
|
@ -37,6 +38,7 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
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;
|
||||||
|
|
@ -23,11 +25,13 @@ 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) {
|
public OnPlayerDeath(GameManager gameManager, WorldBorderApi worldBorderApi, Main main, Flag flag) {
|
||||||
this.gameManager = gameManager;
|
this.gameManager = gameManager;
|
||||||
this.worldBorderApi = worldBorderApi;
|
this.worldBorderApi = worldBorderApi;
|
||||||
this.main = main;
|
this.main = main;
|
||||||
|
this.flag = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
@ -35,11 +39,12 @@ public class OnPlayerDeath implements Listener {
|
||||||
if (gameManager.getGamePhase().isEmpty()) {
|
if (gameManager.getGamePhase().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
event.deathMessage(null);
|
event.deathMessage(Component.empty());
|
||||||
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
|
||||||
|
|
@ -56,6 +61,9 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,19 @@ 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 OnPlayerJoin implements Listener {
|
public class OnPlayerOnlineStatus implements Listener {
|
||||||
|
|
||||||
private final GameManager gameManager;
|
private final GameManager gameManager;
|
||||||
private final Flag flag;
|
private final Flag flag;
|
||||||
|
|
||||||
public OnPlayerJoin(GameManager gameManager, Flag flag) {
|
public OnPlayerOnlineStatus(GameManager gameManager, Flag flag) {
|
||||||
this.gameManager = gameManager;
|
this.gameManager = gameManager;
|
||||||
this.flag = flag;
|
this.flag = flag;
|
||||||
}
|
}
|
||||||
|
|
@ -64,4 +66,9 @@ public class OnPlayerJoin implements Listener {
|
||||||
player.teleportAsync(teamPlayer.getTeam().getSpawnLocation());
|
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.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;
|
||||||
|
|
@ -12,6 +13,8 @@ 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;
|
||||||
|
|
||||||
|
|
@ -26,7 +29,7 @@ public class SnowballEvent implements Listener {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface SnowballHitConsumer {
|
private interface SnowballHitConsumer {
|
||||||
void apply(Player hitPlayer, Player shooter, TeamPlayer shooterTeamPlayer);
|
void apply(Player hitPlayer, Player shooter, TeamPlayer shooterTeamPlayer, Snowball snowball);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
|
|
@ -36,7 +39,10 @@ public class SnowballEvent implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onSnowballHit(EntityDamageByEntityEvent event) {
|
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();
|
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
||||||
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
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) {
|
private void handleSnowballThrown(ProjectileLaunchEvent event, SnowballThrownConsumer consumer) {
|
||||||
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
||||||
if (optionalGamePhase.isEmpty()) {
|
if (optionalGamePhase.isEmpty()) {
|
||||||
|
|
@ -121,6 +141,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());
|
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.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;
|
||||||
|
|
@ -28,6 +29,8 @@ 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();
|
||||||
|
|
@ -84,23 +87,54 @@ 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.getWorld(), flagLocation);
|
spawnParticlesOnSquareBorder(flagLocation, GameConfig.FLAG.CAPTURE_RADIUS);
|
||||||
if (!updateScoreBasedOnNearbyPlayers().join()) {
|
if (!updateScoreBasedOnNearbyPlayers().join()) {
|
||||||
return; //Score didn't change
|
return; //Score didn't change
|
||||||
}
|
}
|
||||||
|
|
@ -118,9 +152,9 @@ public class Flag implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnParticlesOnSquareBorder(World world, Location center) {
|
private void spawnParticlesOnSquareBorder(Location center, double size) {
|
||||||
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)
|
||||||
|
|
@ -141,7 +175,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, 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)));
|
new Particle.DustOptions(Color.fromRGB(color.r(), color.g(), color.b()), 1)));
|
||||||
if (particleTrail.size() > 15) {
|
if (particleTrail.size() > 15) {
|
||||||
particleTrail.removeFirst();
|
particleTrail.removeFirst();
|
||||||
|
|
@ -156,18 +190,15 @@ public class Flag implements Runnable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
double distance = winningTeam.getFlagTurnInLocation().distance(flagCarrier.getLocation());
|
double distance = winningTeam.getFlagTurnInLocation().distance(flagCarrier.getLocation());
|
||||||
if (distance > 2) {
|
if (distance > GameConfig.FLAG.TURN_IN_RADIUS) {
|
||||||
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();
|
||||||
//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;
|
return;
|
||||||
}
|
}
|
||||||
//TODO better message? mayb with a text thing on the screen?
|
notifyAboutCapture();
|
||||||
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;
|
||||||
|
|
@ -183,13 +214,30 @@ 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.WINNING_SCORE) {
|
if (max.get().getValue() < GameConfig.FLAG.CAPTURE_SCORE) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
return gameManager.getTeam(max.get().getKey());
|
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.
|
* 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
|
* 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:
|
||||||
|
|
@ -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())));
|
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.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);
|
bossBar.setVisible(teamFlagPointCount.get(highestKey) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,4 +359,21 @@ 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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ public class GameManager {
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
}
|
}
|
||||||
runningGame = new RunningGame(this, duration, flag);
|
runningGame = new RunningGame(this, duration, flag, executorService);
|
||||||
executorService.scheduleAtFixedRate(runningGame, 0, 1, TimeUnit.SECONDS);
|
executorService.scheduleAtFixedRate(runningGame, 0, 1, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,4 +99,11 @@ public class GameManager {
|
||||||
.max()
|
.max()
|
||||||
.orElse(0);
|
.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.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 {
|
||||||
|
|
@ -20,14 +21,16 @@ 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) {
|
public RunningGame(GameManager gameManager, Duration gameDuration, Flag flag, ScheduledExecutorService executorService) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,16 +40,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, nextPhase);
|
nextPhaseActions(null, currentPhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
|
if (nextPhase != null && Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
|
||||||
GamePhase previousPhase = currentPhase;
|
nextPhaseActions(currentPhase, nextPhase);
|
||||||
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);
|
||||||
|
|
@ -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
|
//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
|
||||||
|
|
@ -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() {
|
public void end() {
|
||||||
//TODO say the phase ended early?
|
//TODO say the phase ended early?
|
||||||
currentPhase = GamePhase.ENDED;
|
nextPhaseActions(currentPhase, GamePhase.ENDED);
|
||||||
nextPhaseActions(null, currentPhase, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.fromBGR(r, g, b));
|
leatherArmorMeta.setColor(Color.fromRGB(r, g, b));
|
||||||
itemStack.setItemMeta(leatherArmorMeta);
|
itemStack.setItemMeta(leatherArmorMeta);
|
||||||
}
|
}
|
||||||
return itemStack;
|
return itemStack;
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
@ -51,6 +52,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#Sat Feb 08 23:31:55 CET 2025
|
#Tue Feb 11 21:41:29 CET 2025
|
||||||
buildNumber=33
|
buildNumber=37
|
||||||
version=0.1
|
version=0.1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user