Compare commits
47
Commits
237cae37ca
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4953cec215 | ||
|
|
92f9e732a1 | ||
|
|
95b54d1a03 | ||
|
|
05a63b9a52 | ||
|
|
137e5a5125 | ||
|
|
3c5842a92c | ||
|
|
f5bb1564ae | ||
|
|
fa8d320da9 | ||
|
|
816c429346 | ||
|
|
afbd1a9f09 | ||
|
|
5430af2149 | ||
|
|
ce7297afb8 | ||
|
|
a10607092b | ||
|
|
fe39cdf5a1 | ||
|
|
6008378258 | ||
|
|
18b211f681 | ||
|
|
b5c59cf173 | ||
|
|
436b462ffe | ||
|
|
d11cf25fc8 | ||
|
|
e8c015790f | ||
|
|
ba8fbe83e0 | ||
|
|
d56e8eaea5 | ||
|
|
3550d75634 | ||
|
|
14158f73b8 | ||
|
|
c738f02d17 | ||
|
|
e62d0df9df | ||
|
|
eeed5b4c54 | ||
|
|
8386e773ce | ||
|
|
e5656f23ce | ||
|
|
409a1aa596 | ||
|
|
57f2898451 | ||
|
|
2242fea737 | ||
|
|
e04892156c | ||
|
|
c09c55ed7a | ||
|
|
2c80b2d474 | ||
|
|
4f0a4eff6f | ||
|
|
c02bda6ea7 | ||
|
|
400032a94c | ||
|
|
6893e07619 | ||
|
|
f27038f91d | ||
|
|
07b700bc32 | ||
|
|
7ae91bcf06 | ||
|
|
56455b4c50 | ||
|
|
3b4beefb37 | ||
|
|
9ae63ba1e4 | ||
|
|
c3b11995c7 | ||
|
|
8b09e54106 |
@@ -2,6 +2,7 @@ import java.util.Properties
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
|
id("com.gradleup.shadow") version "9.0.0-beta4"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.alttd.ctf"
|
group = "com.alttd.ctf"
|
||||||
@@ -33,6 +34,10 @@ dependencies {
|
|||||||
// End JSON config dependencies
|
// End JSON config dependencies
|
||||||
// WorldBorderAPI
|
// WorldBorderAPI
|
||||||
compileOnly("com.github.yannicklamprecht:worldborderapi:1.210.0:dev")
|
compileOnly("com.github.yannicklamprecht:worldborderapi:1.210.0:dev")
|
||||||
|
|
||||||
|
//Database
|
||||||
|
implementation("org.mybatis:mybatis:3.5.13")
|
||||||
|
implementation("mysql:mysql-connector-java:8.0.33")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
@@ -43,6 +48,21 @@ tasks.jar {
|
|||||||
archiveFileName.set("CaptureTheFlag.jar")
|
archiveFileName.set("CaptureTheFlag.jar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
shadowJar {
|
||||||
|
archiveFileName.set("CaptureTheFlag.jar")
|
||||||
|
listOf(
|
||||||
|
"org.apache.ibatis"
|
||||||
|
).forEach { relocate(it, "${rootProject.group}.lib.$it") }
|
||||||
|
}
|
||||||
|
|
||||||
|
build {
|
||||||
|
dependsOn(shadowJar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val versionPropsFile = file("version.properties")
|
val versionPropsFile = file("version.properties")
|
||||||
val versionProps = Properties().apply {
|
val versionProps = Properties().apply {
|
||||||
if (versionPropsFile.exists()) {
|
if (versionPropsFile.exists()) {
|
||||||
@@ -70,6 +90,10 @@ tasks.named("build") {
|
|||||||
dependsOn(incrementBuildNumber)
|
dependsOn(incrementBuildNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.named("shadowJar") {
|
||||||
|
dependsOn(incrementBuildNumber)
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType<Jar> {
|
tasks.withType<Jar> {
|
||||||
manifest {
|
manifest {
|
||||||
attributes(
|
attributes(
|
||||||
|
|||||||
@@ -4,18 +4,17 @@ import com.alttd.ctf.commands.CommandManager;
|
|||||||
import com.alttd.ctf.config.Config;
|
import com.alttd.ctf.config.Config;
|
||||||
import com.alttd.ctf.config.GameConfig;
|
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.*;
|
||||||
import com.alttd.ctf.events.OnPlayerDeath;
|
|
||||||
import com.alttd.ctf.events.OnPlayerOnlineStatus;
|
|
||||||
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;
|
||||||
import com.alttd.ctf.game.GameManager;
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.game.phases.EndedPhase;
|
||||||
import com.alttd.ctf.gui.ClassSelectionGUI;
|
import com.alttd.ctf.gui.ClassSelectionGUI;
|
||||||
import com.alttd.ctf.gui.GUIInventory;
|
import com.alttd.ctf.gui.GUIInventory;
|
||||||
import com.alttd.ctf.gui.GUIListener;
|
import com.alttd.ctf.gui.GUIListener;
|
||||||
import com.alttd.ctf.json_config.JacksonConfig;
|
import com.alttd.ctf.json_config.JacksonConfig;
|
||||||
import com.alttd.ctf.json_config.JsonConfigManager;
|
import com.alttd.ctf.json_config.JsonConfigManager;
|
||||||
|
import com.alttd.ctf.stats.PlayerStat;
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -29,16 +28,18 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Main extends JavaPlugin {
|
public class Main extends JavaPlugin {
|
||||||
|
|
||||||
private GameManager gameManager = null;
|
private GameManager gameManager = null;
|
||||||
private Flag flag;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
PlayerStat.main = this;
|
||||||
GUIInventory.setMain(this); // sorry
|
GUIInventory.setMain(this); // sorry
|
||||||
ClassSelectionGUI.setMain(this); // sorry
|
ClassSelectionGUI.setMain(this); // sorry
|
||||||
Package pkg = Main.class.getPackage();
|
Package pkg = Main.class.getPackage();
|
||||||
@@ -47,18 +48,13 @@ public class Main extends JavaPlugin {
|
|||||||
|
|
||||||
reloadConfigs();
|
reloadConfigs();
|
||||||
WorldBorderApi worldBorderApi = worldBorder();
|
WorldBorderApi worldBorderApi = worldBorder();
|
||||||
this.gameManager = new GameManager(worldBorderApi);
|
this.gameManager = new GameManager(this, worldBorderApi);
|
||||||
registerTeams(); //Skipped in reloadConfig if gameManager is not created yet
|
registerTeams(); //Skipped in reloadConfig if gameManager is not created yet
|
||||||
flag = new Flag(this, gameManager);
|
loadPlayerStats(); //Skipped in reloadConfig if gameManager is not created yet
|
||||||
|
Flag flag = new Flag(this, gameManager);
|
||||||
new CommandManager(this, gameManager, flag, worldBorderApi);
|
new CommandManager(this, gameManager, flag, worldBorderApi);
|
||||||
//Ensuring immediate respawn is on in all worlds
|
//Ensuring immediate respawn is on in all worlds
|
||||||
log.info("Enabling immediate respawn for {}.", GameConfig.FLAG.world);
|
enableImmediateRespawn();
|
||||||
World world = Bukkit.getWorld(GameConfig.FLAG.world);
|
|
||||||
if (world != null) {
|
|
||||||
world.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true);
|
|
||||||
} else {
|
|
||||||
log.error("No valid flag world defined, unable to modify game rules");
|
|
||||||
}
|
|
||||||
registerEvents(flag, worldBorderApi);
|
registerEvents(flag, worldBorderApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +64,7 @@ public class Main extends JavaPlugin {
|
|||||||
GameConfig.reload(this);
|
GameConfig.reload(this);
|
||||||
if (gameManager != null) {
|
if (gameManager != null) {
|
||||||
registerTeams();
|
registerTeams();
|
||||||
|
loadPlayerStats();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +80,24 @@ public class Main extends JavaPlugin {
|
|||||||
return worldBorderApiRegisteredServiceProvider.getProvider();
|
return worldBorderApiRegisteredServiceProvider.getProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enableImmediateRespawn() {
|
||||||
|
log.info("Enabling immediate respawn for {}.", GameConfig.FLAG.world);
|
||||||
|
World world = Bukkit.getWorld(GameConfig.FLAG.world);
|
||||||
|
if (world != null) {
|
||||||
|
world.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true);
|
||||||
|
} else {
|
||||||
|
log.error("No valid flag world defined, unable to modify game rules");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void registerEvents(Flag flag, WorldBorderApi worldBorderApi) {
|
private void registerEvents(Flag flag, WorldBorderApi worldBorderApi) {
|
||||||
PluginManager pluginManager = getServer().getPluginManager();
|
PluginManager pluginManager = getServer().getPluginManager();
|
||||||
pluginManager.registerEvents(new SnowballEvent(gameManager), this);
|
pluginManager.registerEvents(new SnowballEvent(gameManager), this);
|
||||||
|
pluginManager.registerEvents(new OtherGameEvents(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, flag), this);
|
||||||
pluginManager.registerEvents(new InventoryItemInteractionEvent(), this);
|
pluginManager.registerEvents(new InventoryItemInteractionEvent(), this);
|
||||||
pluginManager.registerEvents(new OnPlayerOnlineStatus(gameManager, flag), this);
|
pluginManager.registerEvents(new OnPlayerOnlineStatus(gameManager, flag, worldBorderApi), this);
|
||||||
pluginManager.registerEvents(new GUIListener(), this);
|
pluginManager.registerEvents(new GUIListener(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,4 +127,39 @@ public class Main extends JavaPlugin {
|
|||||||
teams.forEach(gameManager::registerTeam);
|
teams.forEach(gameManager::registerTeam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadPlayerStats() {
|
||||||
|
JsonConfigManager<PlayerStat> config = new JsonConfigManager<>(JacksonConfig.configureMapper());
|
||||||
|
List<PlayerStat> playerStats;
|
||||||
|
|
||||||
|
try {
|
||||||
|
File playerStatsDirectory = new File(getDataFolder(), "player_stats");
|
||||||
|
if (!playerStatsDirectory.exists() && !playerStatsDirectory.mkdirs()) {
|
||||||
|
log.error("Unable to make playerStats directory at {} shutting down plugin", playerStatsDirectory.getAbsolutePath());
|
||||||
|
}
|
||||||
|
playerStats = config.loadConfigs(PlayerStat.class, playerStatsDirectory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Unable to load teams, shutting down plugin", e);
|
||||||
|
getServer().getPluginManager().disablePlugin(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameManager.setPlayerStats(playerStats);
|
||||||
|
final JsonConfigManager<PlayerStat> jsonConfigManager = new JsonConfigManager<>(JacksonConfig.configureMapper());
|
||||||
|
Runnable runnable = () -> {
|
||||||
|
log.info("Saving player stats");
|
||||||
|
File playerStatsDirectory = new File(getDataFolder(), "player_stats");
|
||||||
|
gameManager.getPlayerStats().stream().filter(PlayerStat::isTouched).forEach(playerStat -> {
|
||||||
|
try {
|
||||||
|
jsonConfigManager.saveConfig(playerStat, playerStatsDirectory, playerStat.getUuid().toString());
|
||||||
|
playerStat.setUnTouched();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Failed to save player stats for [{}].", playerStat.getUuid(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
scheduledExecutorService.scheduleAtFixedRate(runnable, 0, 1, java.util.concurrent.TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,7 @@ public class CommandManager implements CommandExecutor, TabExecutor {
|
|||||||
new SkipPhase(gameManager),
|
new SkipPhase(gameManager),
|
||||||
new Start(gameManager, flag),
|
new Start(gameManager, flag),
|
||||||
new CreateTeam(main, gameManager),
|
new CreateTeam(main, gameManager),
|
||||||
|
new HighestStat(gameManager),
|
||||||
new SelectClass(gameManager, worldBorderApi),
|
new SelectClass(gameManager, worldBorderApi),
|
||||||
new Reload(main)
|
new Reload(main)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class ChangeTeam extends SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void changeTeam(CommandSender commandSender, Player player, Team team) {
|
private void changeTeam(CommandSender commandSender, Player player, Team team) {
|
||||||
Optional<Team> optionalOldTeam = gameManager.getTeam(player.getUniqueId());
|
Optional<Team> optionalOldTeam = gameManager.getTeam(player);
|
||||||
if (optionalOldTeam.isPresent()) {
|
if (optionalOldTeam.isPresent()) {
|
||||||
moveBetweenTeams(commandSender, player, team, optionalOldTeam.get());
|
moveBetweenTeams(commandSender, player, team, optionalOldTeam.get());
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
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 org.bukkit.Material;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
@@ -75,8 +76,8 @@ public class CreateTeam extends SubCommand {
|
|||||||
|
|
||||||
int highestId = gameManager.getMaxTeamId();
|
int highestId = gameManager.getMaxTeamId();
|
||||||
Team team = new Team(MiniMessage.miniMessage().deserialize(String.format("<color:%s>%s</color>", color, name)),
|
Team team = new Team(MiniMessage.miniMessage().deserialize(String.format("<color:%s>%s</color>", color, name)),
|
||||||
highestId + 1, player.getLocation(), player.getLocation(), player.getLocation(), teamColor);
|
highestId + 1, player.getLocation(), player.getLocation(), player.getLocation(), teamColor,
|
||||||
|
Material.RED_BANNER, "§c", -1L);
|
||||||
return consumer.apply(team);
|
return consumer.apply(team);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
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.stats.PlayerStat;
|
||||||
|
import com.alttd.ctf.stats.Stat;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.TextComponent;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class HighestStat extends SubCommand {
|
||||||
|
|
||||||
|
private final GameManager gameManager;
|
||||||
|
private final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||||
|
|
||||||
|
public HighestStat(GameManager gameManager) {
|
||||||
|
this.gameManager = gameManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record HighestStatValue(Stat stat, UUID uuid, double value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onCommand(CommandSender commandSender, String[] args) {
|
||||||
|
ArrayList<HighestStatValue> highestStatValues = new ArrayList<>();
|
||||||
|
Collection<PlayerStat> playerStats = gameManager.getPlayerStats();
|
||||||
|
for (Stat stat : Stat.values()) {
|
||||||
|
Optional<PlayerStat> max = playerStats.stream().max(Comparator.comparingDouble(playerStat -> playerStat.getStat(stat)));
|
||||||
|
if (max.isEmpty()) {
|
||||||
|
log.warn("No max stat found for {}", stat.toString());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PlayerStat playerStat = max.get();
|
||||||
|
highestStatValues.add(new HighestStatValue(stat, playerStat.getUuid(), playerStat.getStat(stat)));
|
||||||
|
}
|
||||||
|
Component message = highestStatValues.stream().map(highestStatValue -> {
|
||||||
|
TextComponent messageBuilder = Component.empty();
|
||||||
|
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(highestStatValue.uuid);
|
||||||
|
Player player = offlinePlayer.getPlayer();
|
||||||
|
if (!offlinePlayer.isOnline() || player == null) {
|
||||||
|
messageBuilder = messageBuilder.append(miniMessage.deserialize(offlinePlayer.getName() == null ? highestStatValue.uuid.toString() : offlinePlayer.getName()));
|
||||||
|
} else {
|
||||||
|
messageBuilder = messageBuilder.append(player.name());
|
||||||
|
}
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(": "));
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(highestStatValue.stat.toString()));
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(": "));
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(highestStatValue.value()));
|
||||||
|
return messageBuilder;
|
||||||
|
}).reduce(Component.empty(), (a, b) -> a.append(Component.newline()).append(b));
|
||||||
|
commandSender.sendMessage(message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "higheststat";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpMessage() {
|
||||||
|
return Messages.HELP.HIGHEST_STAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,13 +40,15 @@ public class SelectClass extends SubCommand {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
GamePhase gamePhase = optionalGamePhase.get();
|
GamePhase gamePhase = optionalGamePhase.get();
|
||||||
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player.getUniqueId());
|
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player);
|
||||||
if (optionalTeamPlayer.isEmpty()) {
|
if (optionalTeamPlayer.isEmpty()) {
|
||||||
commandSender.sendRichMessage("<red>You have to be in a CTF team to select a class.</red>");
|
commandSender.sendRichMessage("<red>You have to be in a CTF team to select a class.</red>");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
TeamPlayer teamPlayer = optionalTeamPlayer.get();
|
TeamPlayer teamPlayer = optionalTeamPlayer.get();
|
||||||
if (!gamePhase.equals(GamePhase.CLASS_SELECTION) && teamPlayer.getTeam().getSpawnLocation().distance(player.getLocation()) > 5) {
|
if (!teamPlayer.isDead()
|
||||||
|
&& !gamePhase.equals(GamePhase.CLASS_SELECTION)
|
||||||
|
&& teamPlayer.getTeam().getSpawnLocation().distance(player.getLocation()) > 10) {
|
||||||
commandSender.sendRichMessage("<red>You have to be near your spawn to change classes.</red>");
|
commandSender.sendRichMessage("<red>You have to be near your spawn to change classes.</red>");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,10 @@ package com.alttd.ctf.commands.subcommands;
|
|||||||
import com.alttd.ctf.commands.SubCommand;
|
import com.alttd.ctf.commands.SubCommand;
|
||||||
import com.alttd.ctf.config.Messages;
|
import com.alttd.ctf.config.Messages;
|
||||||
import com.alttd.ctf.game.GameManager;
|
import com.alttd.ctf.game.GameManager;
|
||||||
import com.alttd.ctf.team.Team;
|
|
||||||
import lombok.AllArgsConstructor;
|
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.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class SkipPhase extends SubCommand {
|
public class SkipPhase extends SubCommand {
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ abstract class AbstractConfig {
|
|||||||
return yaml.getInt(path, yaml.getInt(path));
|
return yaml.getInt(path, yaml.getInt(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLong(String prefix, String path, long def) {
|
||||||
|
path = prefix + path;
|
||||||
|
yaml.addDefault(path, def);
|
||||||
|
return yaml.getLong(path, yaml.getLong(path));
|
||||||
|
}
|
||||||
|
|
||||||
double getDouble(String prefix, String path, double def) {
|
double getDouble(String prefix, String path, double def) {
|
||||||
path = prefix + path;
|
path = prefix + path;
|
||||||
yaml.addDefault(path, def);
|
yaml.addDefault(path, def);
|
||||||
|
|||||||
@@ -41,4 +41,36 @@ public class Config extends AbstractConfig{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class DISCORD {
|
||||||
|
private static final String prefix = "discord.";
|
||||||
|
|
||||||
|
public static long PLAYER_SERVER_ID = 0L;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void load() {
|
||||||
|
PLAYER_SERVER_ID = config.getLong(prefix, "player-server-id", PLAYER_SERVER_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static class DISCORD_DATABASE {
|
||||||
|
private static final String prefix = "discord-database.";
|
||||||
|
|
||||||
|
public static String HOST = "localhost";
|
||||||
|
public static int PORT = 3306;
|
||||||
|
public static String DATABASE = "discordLink";
|
||||||
|
public static String USERNAME = "username";
|
||||||
|
public static String PASSWORD = "password";
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void load() {
|
||||||
|
HOST = config.getString(prefix, "host", HOST);
|
||||||
|
DATABASE = config.getString(prefix, "database", DATABASE);
|
||||||
|
PORT = config.getInt(prefix, "port", PORT);
|
||||||
|
USERNAME = config.getString(prefix, "username", USERNAME);
|
||||||
|
PASSWORD = config.getString(prefix, "password", PASSWORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.alttd.ctf.config;
|
|||||||
import com.alttd.ctf.Main;
|
import com.alttd.ctf.Main;
|
||||||
import com.alttd.ctf.game.GamePhase;
|
import com.alttd.ctf.game.GamePhase;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -88,6 +90,7 @@ public class GameConfig extends AbstractConfig {
|
|||||||
public static double CAPTURE_RADIUS = 5;
|
public static double CAPTURE_RADIUS = 5;
|
||||||
public static int CAPTURE_SCORE = 50;
|
public static int CAPTURE_SCORE = 50;
|
||||||
public static double TURN_IN_RADIUS = 3;
|
public static double TURN_IN_RADIUS = 3;
|
||||||
|
public static @NotNull Material MATERIAL = Material.BLACK_BANNER;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
@@ -98,6 +101,7 @@ public class GameConfig extends AbstractConfig {
|
|||||||
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);
|
CAPTURE_SCORE = config.getInt(prefix, "capture-score", CAPTURE_SCORE);
|
||||||
TURN_IN_RADIUS = config.getDouble(prefix, "turn-in-radius", TURN_IN_RADIUS);
|
TURN_IN_RADIUS = config.getDouble(prefix, "turn-in-radius", TURN_IN_RADIUS);
|
||||||
|
MATERIAL = Material.valueOf(config.getString(prefix, "material", MATERIAL.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public class Messages extends AbstractConfig {
|
|||||||
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>";
|
public static String SKIP_PHASE = "<green>Skip the current phase: <gold>/ctf skipphase</gold></green>";
|
||||||
|
public static String HIGHEST_STAT = "<green>Display the highest stat and the player: <gold>/ctf higheststat</gold></green>";
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
@@ -39,6 +40,7 @@ public class Messages extends AbstractConfig {
|
|||||||
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);
|
SKIP_PHASE = config.getString(prefix, "skip-phase", SKIP_PHASE);
|
||||||
|
HIGHEST_STAT = config.getString(prefix, "highest-stat", HIGHEST_STAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.alttd.ctf.database;
|
||||||
|
|
||||||
|
import com.alttd.ctf.config.Config;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.ibatis.datasource.pooled.PooledDataSource;
|
||||||
|
import org.apache.ibatis.mapping.Environment;
|
||||||
|
import org.apache.ibatis.session.Configuration;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
|
||||||
|
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class Connections {
|
||||||
|
|
||||||
|
private static SqlSessionFactory discordSqlSessionFactory = null;
|
||||||
|
|
||||||
|
public static void runDiscordQuery(Consumer<SqlSession> consumer) {
|
||||||
|
new Thread(() -> {
|
||||||
|
if (discordSqlSessionFactory == null) {
|
||||||
|
discordSqlSessionFactory = createDiscordSqlSessionFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (SqlSession session = discordSqlSessionFactory.openSession()) {
|
||||||
|
consumer.accept(session);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to run discord query", e);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SqlSessionFactory createDiscordSqlSessionFactory() {
|
||||||
|
PooledDataSource dataSource = new PooledDataSource();
|
||||||
|
dataSource.setDriver("com.mysql.cj.jdbc.Driver");
|
||||||
|
dataSource.setUrl(String.format("jdbc:mysql://%s:%d/%s", Config.DISCORD_DATABASE.HOST,
|
||||||
|
Config.DISCORD_DATABASE.PORT, Config.DISCORD_DATABASE.DATABASE));
|
||||||
|
dataSource.setUsername(Config.DISCORD_DATABASE.USERNAME);
|
||||||
|
dataSource.setPassword(Config.DISCORD_DATABASE.PASSWORD);
|
||||||
|
|
||||||
|
Environment environment = new Environment("production", new JdbcTransactionFactory(), dataSource);
|
||||||
|
Configuration configuration = new Configuration(environment);
|
||||||
|
configuration.addMapper(DiscordUserMapper.class);
|
||||||
|
|
||||||
|
return new SqlSessionFactoryBuilder().build(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.alttd.ctf.database;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
public interface DiscordUserMapper {
|
||||||
|
@Select("SELECT discord_id FROM linked_accounts WHERE player_uuid = #{uuid}")
|
||||||
|
Long getDiscordId(String uuid);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@ package com.alttd.ctf.events;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
import org.bukkit.event.player.PlayerAttemptPickupItemEvent;
|
import org.bukkit.event.player.PlayerAttemptPickupItemEvent;
|
||||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||||
|
|
||||||
@@ -26,4 +29,20 @@ public class InventoryItemInteractionEvent implements Listener {
|
|||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryClick(InventoryClickEvent event) {
|
||||||
|
if (event.getSlotType() != InventoryType.SlotType.ARMOR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onInventoryDrag(InventoryDragEvent event) {
|
||||||
|
if (event.getRawSlots().stream().noneMatch(slot -> slot >= 5 && slot <= 8)) { // Slot numbers 5-8 for armor
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import com.alttd.ctf.config.GameConfig;
|
|||||||
import com.alttd.ctf.flag.Flag;
|
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.stats.Stat;
|
||||||
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.Component;
|
||||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.damage.DamageEffect;
|
||||||
import org.bukkit.entity.Player;
|
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;
|
||||||
@@ -44,7 +46,18 @@ public class OnPlayerDeath implements Listener {
|
|||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
player.getInventory().clear();
|
player.getInventory().clear();
|
||||||
player.updateInventory();
|
player.updateInventory();
|
||||||
|
gameManager.getTeamPlayer(player)
|
||||||
|
.ifPresent(TeamPlayer::setDead);
|
||||||
flag.handleCarrierDeathOrDisconnect(player);
|
flag.handleCarrierDeathOrDisconnect(player);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (event.getDamageSource().getDamageType().getDamageEffect().equals(DamageEffect.FREEZING)) {
|
||||||
|
gameManager.getTeamPlayer(player)
|
||||||
|
.ifPresent(teamPlayer -> teamPlayer.increaseStat(Stat.DEATHS_IN_POWDERED_SNOW));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to check for death cause due to exception", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@@ -55,7 +68,7 @@ public class OnPlayerDeath implements Listener {
|
|||||||
log.warn("Player {} died while the game wasn't running", player.getName());
|
log.warn("Player {} died while the game wasn't running", player.getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player.getUniqueId());
|
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player);
|
||||||
if (optionalTeamPlayer.isEmpty()) {
|
if (optionalTeamPlayer.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
package com.alttd.ctf.events;
|
package com.alttd.ctf.events;
|
||||||
|
|
||||||
|
import com.alttd.ctf.database.Connections;
|
||||||
|
import com.alttd.ctf.database.DiscordUserMapper;
|
||||||
import com.alttd.ctf.flag.Flag;
|
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.game_class.creation.FighterCreator;
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
import com.alttd.ctf.team.TeamPlayer;
|
import com.alttd.ctf.team.TeamPlayer;
|
||||||
|
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
import org.bukkit.attribute.Attribute;
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.attribute.AttributeInstance;
|
import org.bukkit.attribute.AttributeInstance;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@@ -23,15 +28,53 @@ public class OnPlayerOnlineStatus implements Listener {
|
|||||||
|
|
||||||
private final GameManager gameManager;
|
private final GameManager gameManager;
|
||||||
private final Flag flag;
|
private final Flag flag;
|
||||||
|
private final WorldBorderApi worldBorderApi;
|
||||||
|
|
||||||
public OnPlayerOnlineStatus(GameManager gameManager, Flag flag) {
|
public OnPlayerOnlineStatus(GameManager gameManager, Flag flag, WorldBorderApi worldBorderApi) {
|
||||||
this.gameManager = gameManager;
|
this.gameManager = gameManager;
|
||||||
this.flag = flag;
|
this.flag = flag;
|
||||||
|
this.worldBorderApi = worldBorderApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
|
resetPlayer(player);
|
||||||
|
if (!player.hasPermission("ctf.bypass")) {
|
||||||
|
handleRunningGame(player);
|
||||||
|
}
|
||||||
|
handleDiscordLink(player);
|
||||||
|
gameManager.getTeamPlayer(player).ifPresent(teamPlayer ->
|
||||||
|
player.teleportAsync(teamPlayer.getTeam().getSpawnLocation()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRunningGame(Player player) {
|
||||||
|
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
||||||
|
if (optionalGamePhase.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GamePhase gamePhase = optionalGamePhase.get();
|
||||||
|
if (gamePhase.equals(GamePhase.ENDED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player);
|
||||||
|
TeamPlayer teamPlayer;
|
||||||
|
if (optionalTeamPlayer.isEmpty()) {
|
||||||
|
Optional<Team> min = gameManager.getTeams().stream().min(Comparator.comparingInt(team -> team.getPlayers().size()));
|
||||||
|
if (min.isEmpty()) {
|
||||||
|
log.error("No team found when attempting to add freshly joined player to a team");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
teamPlayer = gameManager.registerPlayer(min.get(), player);
|
||||||
|
} else {
|
||||||
|
teamPlayer = optionalTeamPlayer.get();
|
||||||
|
teamPlayer.getTeam().addToScoreboard(player);
|
||||||
|
}
|
||||||
|
FighterCreator.createFighter(teamPlayer.getTeam().getColor())
|
||||||
|
.apply(teamPlayer, worldBorderApi, gamePhase, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetPlayer(Player player) {
|
||||||
AttributeInstance maxHealthAttribute = player.getAttribute(Attribute.GENERIC_MAX_HEALTH);
|
AttributeInstance maxHealthAttribute = player.getAttribute(Attribute.GENERIC_MAX_HEALTH);
|
||||||
if (maxHealthAttribute == null) {
|
if (maxHealthAttribute == null) {
|
||||||
log.error("Player does not have max health attribute");
|
log.error("Player does not have max health attribute");
|
||||||
@@ -43,32 +86,29 @@ public class OnPlayerOnlineStatus implements Listener {
|
|||||||
flag.addPlayer(player);
|
flag.addPlayer(player);
|
||||||
player.getInventory().clear();
|
player.getInventory().clear();
|
||||||
player.updateInventory();
|
player.updateInventory();
|
||||||
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
}
|
||||||
if (optionalGamePhase.isEmpty()) {
|
|
||||||
return;
|
private void handleDiscordLink(Player player) {
|
||||||
}
|
Connections.runDiscordQuery(sqlSession -> {
|
||||||
GamePhase gamePhase = optionalGamePhase.get();
|
try {
|
||||||
if (gamePhase.equals(GamePhase.ENDED)) {
|
DiscordUserMapper mapper = sqlSession.getMapper(DiscordUserMapper.class);
|
||||||
return;
|
Long discordId = mapper.getDiscordId(player.getUniqueId().toString());
|
||||||
}
|
if (discordId == null) {
|
||||||
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player.getUniqueId());
|
log.info("Player {} is not linked", player.getName());
|
||||||
TeamPlayer teamPlayer;
|
return;
|
||||||
if (optionalTeamPlayer.isEmpty()) {
|
}
|
||||||
Optional<Team> min = gameManager.getTeams().stream().min(Comparator.comparingInt(team -> team.getPlayers().size()));
|
log.info("Set discord id to {} for {}", discordId, player.getName());
|
||||||
if (min.isEmpty()) {
|
player.setDiscordId(discordId);
|
||||||
log.error("No team found when attempting to add freshly joined player to a team");
|
} catch (PersistenceException e) {
|
||||||
return;
|
log.error("Failed to set discord id for {}", player.getName(), e);
|
||||||
}
|
}
|
||||||
teamPlayer = min.get().addPlayer(player.getUniqueId());
|
});
|
||||||
} else {
|
|
||||||
teamPlayer = optionalTeamPlayer.get();
|
|
||||||
}
|
|
||||||
player.teleportAsync(teamPlayer.getTeam().getSpawnLocation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(@NotNull PlayerQuitEvent event) {
|
public void onPlayerQuit(@NotNull PlayerQuitEvent event) {
|
||||||
flag.handleCarrierDeathOrDisconnect(event.getPlayer());
|
Player player = event.getPlayer();
|
||||||
|
flag.handleCarrierDeathOrDisconnect(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package com.alttd.ctf.events;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.game.GamePhase;
|
||||||
|
import com.alttd.ctf.stats.Stat;
|
||||||
|
import com.alttd.ctf.team.TeamPlayer;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bukkit.Tag;
|
||||||
|
import org.bukkit.attribute.Attribute;
|
||||||
|
import org.bukkit.attribute.AttributeInstance;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.ThrownPotion;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
|
import org.bukkit.event.entity.PotionSplashEvent;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class OtherGameEvents implements Listener {
|
||||||
|
|
||||||
|
private final GameManager gameManager;
|
||||||
|
|
||||||
|
public OtherGameEvents(GameManager gameManager) {
|
||||||
|
this.gameManager = gameManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBlockBreak(BlockBreakEvent event) {
|
||||||
|
Optional<GamePhase> gamePhase = gameManager.getGamePhase();
|
||||||
|
if (gamePhase.isEmpty() || gamePhase.get() == GamePhase.ENDED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Tag.SNOW.isTagged(event.getBlock().getType())) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gameManager.getTeamPlayer(event.getPlayer())
|
||||||
|
.ifPresent(teamPlayer -> teamPlayer.increaseStat(Stat.SNOW_MINED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
|
Optional<GamePhase> gamePhase = gameManager.getGamePhase();
|
||||||
|
if (gamePhase.isEmpty() || gamePhase.get() == GamePhase.ENDED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Tag.SNOW.isTagged(event.getBlock().getType())) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
log.warn("Player {} placed a block that wasn't snow: {}",
|
||||||
|
event.getPlayer().getName(), event.getBlock().getType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gameManager.getTeamPlayer(event.getPlayer())
|
||||||
|
.ifPresent(teamPlayer -> teamPlayer.increaseStat(Stat.BLOCKS_PLACED));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPotionSplash(PotionSplashEvent event) {
|
||||||
|
ThrownPotion thrownPotion = event.getPotion();
|
||||||
|
if (!(thrownPotion.getShooter() instanceof Player player))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player);
|
||||||
|
if (optionalTeamPlayer.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
TeamPlayer teamPlayer = optionalTeamPlayer.get();
|
||||||
|
|
||||||
|
event.getAffectedEntities().stream()
|
||||||
|
.filter(livingEntity -> livingEntity instanceof Player)
|
||||||
|
.map(livingEntity -> (Player) livingEntity)
|
||||||
|
.forEach(target -> {
|
||||||
|
if (shouldHeal(teamPlayer, target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.setIntensity(target, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
double totalHealing = thrownPotion.getEffects().stream()
|
||||||
|
.filter(effect -> effect.getType() == PotionEffectType.INSTANT_HEALTH)
|
||||||
|
.flatMapToDouble(effect -> event.getAffectedEntities().stream()
|
||||||
|
.filter(livingEntity -> livingEntity instanceof Player)
|
||||||
|
.map(livingEntity -> (Player) livingEntity)
|
||||||
|
.mapToDouble(target -> calculateActualHealing(target, effect, event.getIntensity(target))))
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
teamPlayer.increaseStat(Stat.DAMAGE_HEALED, totalHealing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldHeal(TeamPlayer healer, Player target) {
|
||||||
|
Optional<TeamPlayer> optionalTeamTarget = gameManager.getTeamPlayer(target);
|
||||||
|
return optionalTeamTarget.isPresent() && healer.getTeam() == optionalTeamTarget.get().getTeam();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculateActualHealing(Player target, PotionEffect effect, double intensity) {
|
||||||
|
AttributeInstance playerMaxHealth = target.getAttribute(Attribute.GENERIC_MAX_HEALTH);
|
||||||
|
if (playerMaxHealth == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double missingHealth = playerMaxHealth.getValue() - target.getHealth();
|
||||||
|
//Only counts healing on teammates since intensity was set to 0 for non teammates
|
||||||
|
double potentialHealing = (effect.getAmplifier() + 1) * intensity;
|
||||||
|
return Math.min(potentialHealing, missingHealth);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,10 +3,14 @@ package com.alttd.ctf.events;
|
|||||||
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.game_class.GameClass;
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import com.alttd.ctf.game_class.implementations.Mage;
|
||||||
|
import com.alttd.ctf.stats.Stat;
|
||||||
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.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Particle;
|
||||||
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.entity.Snowball;
|
import org.bukkit.entity.Snowball;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
@@ -16,7 +20,11 @@ import org.bukkit.event.entity.ProjectileLaunchEvent;
|
|||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SnowballEvent implements Listener {
|
public class SnowballEvent implements Listener {
|
||||||
@@ -34,37 +42,81 @@ public class SnowballEvent implements Listener {
|
|||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface SnowballThrownConsumer {
|
private interface SnowballThrownConsumer {
|
||||||
void apply(Player shooter, TeamPlayer shooterTeamPlayer);
|
void apply(Player shooter, TeamPlayer shooterTeamPlayer, Snowball snowball);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onSnowballHit(EntityDamageByEntityEvent event) {
|
public void onSnowballHit(EntityDamageByEntityEvent event) {
|
||||||
handleSnowballHit(event, (hitPlayer, shooter, shooterTeamPlayer, snowball) -> {
|
handleSnowballHit(event, (hitPlayer, shooter, shooterTeamPlayer, snowball) -> {
|
||||||
if (blockedAttack(hitPlayer, snowball)) { //Disable damage when it is blocked
|
if (blockedAttack(hitPlayer, shooter, snowball)) {
|
||||||
|
playBlockSounds(hitPlayer, shooter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
||||||
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
||||||
|
|
||||||
double newHealth = hitPlayer.getHealth() - shooterClass.getDamage();
|
double newHealth = Math.max(hitPlayer.getHealth() - shooterClass.getDamage(), 0);
|
||||||
hitPlayer.setHealth(Math.max(newHealth, 0));
|
hitPlayer.setHealth(newHealth);
|
||||||
|
|
||||||
|
shooterTeamPlayer.increaseStat(Stat.DAMAGE_DONE, shooterClass.getDamage());
|
||||||
|
if (newHealth <= 0) {
|
||||||
|
shooterTeamPlayer.increaseStat(Stat.KILLS);
|
||||||
|
}
|
||||||
log.debug("{} health was set to {} because of a snowball thrown by {}",
|
log.debug("{} health was set to {} because of a snowball thrown by {}",
|
||||||
hitPlayer.getName(), Math.max(newHealth, 0), shooter.getName());
|
hitPlayer.getName(), Math.max(newHealth, 0), shooter.getName());
|
||||||
|
applyDamageEffects(hitPlayer, shooter);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void playBlockSounds(Player hitPlayer, Player shooter) {
|
||||||
|
hitPlayer.playSound(hitPlayer.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1.0f, 1.5f);
|
||||||
|
shooter.playSound(shooter.getLocation(), Sound.ITEM_SHIELD_BLOCK, 1.0f, 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyDamageEffects(Player hitPlayer, Player shooter) {
|
||||||
|
hitPlayer.getWorld().playSound(hitPlayer.getLocation(), Sound.ENTITY_PLAYER_HURT, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
hitPlayer.getWorld().spawnParticle(
|
||||||
|
Particle.DAMAGE_INDICATOR,
|
||||||
|
hitPlayer.getLocation().add(0, 1, 0), 10, 0.5, 0.5, 0.5, 0.1
|
||||||
|
);
|
||||||
|
|
||||||
|
shooter.playSound(shooter.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onSnowballThrown(ProjectileLaunchEvent event) {
|
public void onSnowballThrown(ProjectileLaunchEvent event) {
|
||||||
handleSnowballThrown(event, (shooter, shooterTeamPlayer) -> {
|
handleSnowballThrown(event, (shooter, shooterTeamPlayer, snowball) -> {
|
||||||
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
GameClass shooterClass = shooterTeamPlayer.getGameClass();
|
||||||
|
if (shooterClass == null) {
|
||||||
|
shooter.sendRichMessage("<red>You appear to not have selected a class, please do so by going to spawn and using /ctf selectclass</red>");
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
shooter.setCooldown(Material.SNOWBALL, shooterClass.getThrowTickSpeed());
|
||||||
|
shooterTeamPlayer.increaseStat(Stat.SNOWBALLS_THROWN);
|
||||||
|
if (shooterClass instanceof Mage mage) {
|
||||||
|
mage.duplicateSnowBalls(shooter, snowball);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean blockedAttack(@NotNull Player hitPlayer, @NotNull Snowball snowball) {
|
private final HashMap<UUID, LinkedList<Instant>> lastTankHits = new HashMap<>();
|
||||||
|
|
||||||
|
private boolean blockedAttack(@NotNull Player hitPlayer, @NotNull Player shooter, @NotNull Snowball snowball) {
|
||||||
if (!hitPlayer.isBlocking()) {
|
if (!hitPlayer.isBlocking()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (reachedMaxHits(hitPlayer)) {
|
||||||
|
hitPlayer.setCooldown(Material.SHIELD, 40);
|
||||||
|
if (hitPlayer.getInventory().getItemInMainHand().getType().equals(Material.SHIELD)) {
|
||||||
|
hitPlayer.swingMainHand();
|
||||||
|
} else if (hitPlayer.getInventory().getItemInOffHand().getType().equals(Material.SHIELD)) {
|
||||||
|
hitPlayer.swingOffHand();
|
||||||
|
}
|
||||||
|
applyBlockBrokenEffects(hitPlayer, shooter);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Location playerLocation = hitPlayer.getLocation();
|
Location playerLocation = hitPlayer.getLocation();
|
||||||
Vector playerFacing = playerLocation.getDirection().normalize();
|
Vector playerFacing = playerLocation.getDirection().normalize();
|
||||||
Location snowballLocation = snowball.getLocation();
|
Location snowballLocation = snowball.getLocation();
|
||||||
@@ -75,6 +127,37 @@ public class SnowballEvent implements Listener {
|
|||||||
return !(Math.toDegrees(angle) > 80); //Blocked if the angle was <= 80
|
return !(Math.toDegrees(angle) > 80); //Blocked if the angle was <= 80
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyBlockBrokenEffects(@NotNull Player hitPlayer, @NotNull Player shooter) {
|
||||||
|
hitPlayer.getWorld().playSound(hitPlayer.getLocation(), Sound.ITEM_SHIELD_BREAK, 1.0f, 1.0f);
|
||||||
|
|
||||||
|
Location shieldLocation = hitPlayer.getLocation()
|
||||||
|
.add(hitPlayer.getLocation().getDirection().normalize().multiply(0.5))
|
||||||
|
.add(0, 0.5, 0);
|
||||||
|
hitPlayer.getWorld().spawnParticle(
|
||||||
|
Particle.CRIT,
|
||||||
|
shieldLocation, 10, 0.5, 0.5, 0.5, 0.1
|
||||||
|
);
|
||||||
|
|
||||||
|
shooter.playSound(shooter.getLocation(), Sound.ITEM_SHIELD_BREAK, 1.0f, 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean reachedMaxHits(@NotNull Player hitPlayer) {
|
||||||
|
boolean reachedMaxHits = false;
|
||||||
|
Instant now = Instant.now();
|
||||||
|
LinkedList<Instant> lastHits = lastTankHits.getOrDefault(hitPlayer.getUniqueId(), new LinkedList<>());
|
||||||
|
Instant cutoffTime = now.minusMillis(300);
|
||||||
|
while (!lastHits.isEmpty() && lastHits.peek().isBefore(cutoffTime)) {
|
||||||
|
lastHits.poll();
|
||||||
|
}
|
||||||
|
lastHits.addLast(now);
|
||||||
|
if (lastHits.size() >= 3) {
|
||||||
|
lastHits.clear();
|
||||||
|
reachedMaxHits = true;
|
||||||
|
}
|
||||||
|
lastTankHits.put(hitPlayer.getUniqueId(), lastHits);
|
||||||
|
return reachedMaxHits;
|
||||||
|
}
|
||||||
|
|
||||||
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()) {
|
||||||
@@ -89,24 +172,25 @@ public class SnowballEvent implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<TeamPlayer> teamPlayer = gameManager.getTeamPlayer(shooter.getUniqueId());
|
Optional<TeamPlayer> teamPlayer = gameManager.getTeamPlayer(shooter);
|
||||||
if (teamPlayer.isEmpty()) {
|
if (teamPlayer.isEmpty()) {
|
||||||
log.debug("The shooter that threw a snowball was not a team player");
|
log.debug("The shooter that threw a snowball was not a team player");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
consumer.apply(shooter, teamPlayer.get());
|
consumer.apply(shooter, teamPlayer.get(), snowball);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSnowballHit(EntityDamageByEntityEvent event, SnowballHitConsumer consumer) {
|
private void handleSnowballHit(EntityDamageByEntityEvent event, SnowballHitConsumer consumer) {
|
||||||
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
Optional<GamePhase> optionalGamePhase = gameManager.getGamePhase();
|
||||||
if (optionalGamePhase.isEmpty()) {
|
if (optionalGamePhase.isEmpty()) {
|
||||||
log.debug("No game is running but player was hit by snowball");
|
log.debug("No game is running but player was hit");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GamePhase gamePhase = optionalGamePhase.get();
|
GamePhase gamePhase = optionalGamePhase.get();
|
||||||
|
|
||||||
if (!gamePhase.equals(GamePhase.COMBAT)) {
|
if (!gamePhase.equals(GamePhase.COMBAT)) {
|
||||||
log.debug("Not in combat phase but player was hit by snowball");
|
log.debug("Not in combat phase but player was hit, cancelling event");
|
||||||
|
event.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +200,8 @@ public class SnowballEvent implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(event.getDamager() instanceof org.bukkit.entity.Snowball snowball)) {
|
if (!(event.getDamager() instanceof org.bukkit.entity.Snowball snowball)) {
|
||||||
log.debug("The player was hit by something other than a snowball");
|
log.debug("The player was hit by something other than a snowball, canceling event");
|
||||||
|
event.setCancelled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,22 +210,22 @@ public class SnowballEvent implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<TeamPlayer> teamPlayerShooter = gameManager.getTeamPlayer(shooter.getUniqueId());
|
Optional<TeamPlayer> teamPlayerShooter = gameManager.getTeamPlayer(shooter);
|
||||||
if (teamPlayerShooter.isEmpty()) {
|
if (teamPlayerShooter.isEmpty()) {
|
||||||
log.debug("The shooter that hit a player with a snowball was not a team player");
|
log.debug("The shooter that hit a player with a snowball was not a team player");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<TeamPlayer> teamPlayerHit = gameManager.getTeamPlayer(hitPlayer.getUniqueId());
|
Optional<TeamPlayer> teamPlayerHit = gameManager.getTeamPlayer(hitPlayer);
|
||||||
if (teamPlayerHit.isEmpty()) {
|
if (teamPlayerHit.isEmpty()) {
|
||||||
log.debug("The shooter that hit a player with a snowball was not a team player");
|
log.debug("The shooter that hit a player with a snowball was not a team player");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamPlayerHit.get().getTeam().getId() == teamPlayerShooter.get().getTeam().getId()) {
|
if (teamPlayerHit.get().getTeam().getId().equals(teamPlayerShooter.get().getTeam().getId())) {
|
||||||
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, teamPlayerShooter.get(), snowball);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ package com.alttd.ctf.flag;
|
|||||||
import com.alttd.ctf.Main;
|
import com.alttd.ctf.Main;
|
||||||
import com.alttd.ctf.config.GameConfig;
|
import com.alttd.ctf.config.GameConfig;
|
||||||
import com.alttd.ctf.game.GameManager;
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.stats.Stat;
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
import com.alttd.ctf.team.TeamColor;
|
import com.alttd.ctf.team.TeamColor;
|
||||||
import com.alttd.ctf.team.TeamPlayer;
|
import com.alttd.ctf.team.TeamPlayer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
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.serializer.plain.PlainTextComponentSerializer;
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
import net.kyori.adventure.title.Title;
|
import net.kyori.adventure.title.Title;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
@@ -21,6 +22,9 @@ import org.bukkit.boss.KeyedBossBar;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -32,7 +36,6 @@ public class Flag implements Runnable {
|
|||||||
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
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 BossBar bossBar = createBossBar();
|
private final BossBar bossBar = createBossBar();
|
||||||
private final HashMap<Integer, Integer> wins = new HashMap<>();
|
private final HashMap<Integer, Integer> wins = new HashMap<>();
|
||||||
private int lastWinningTeamId = -1;
|
private int lastWinningTeamId = -1;
|
||||||
@@ -52,6 +55,7 @@ public class Flag implements Runnable {
|
|||||||
throw new IllegalStateException(String.format("Tried to spawn flag in world [%s] that doesn't exist", GameConfig.FLAG.world));
|
throw new IllegalStateException(String.format("Tried to spawn flag in world [%s] that doesn't exist", GameConfig.FLAG.world));
|
||||||
}
|
}
|
||||||
this.flagLocation = new Location(world, GameConfig.FLAG.x, GameConfig.FLAG.y, GameConfig.FLAG.z);
|
this.flagLocation = new Location(world, GameConfig.FLAG.x, GameConfig.FLAG.y, GameConfig.FLAG.z);
|
||||||
|
gameManager.getTeams().forEach(team -> team.setScore(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private BossBar createBossBar() {
|
private BossBar createBossBar() {
|
||||||
@@ -77,17 +81,33 @@ public class Flag implements Runnable {
|
|||||||
if (flagCarrier != null) {
|
if (flagCarrier != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//TODO knockback enemies from flag location to create space for person who captured mayb short speed boost and heal?
|
Team team = teamPlayer.getTeam();
|
||||||
//TODO add de-buffs and enable buffs for others?
|
flagLocation.getNearbyPlayers(5).forEach(nearbyPlayer -> {
|
||||||
player.getInventory().setItem(EquipmentSlot.HEAD, flagItem);
|
if (nearbyPlayer.getUniqueId().equals(player.getUniqueId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Optional<TeamPlayer> nearByTeamPlayer = gameManager.getTeamPlayer(nearbyPlayer);
|
||||||
|
if (nearByTeamPlayer.isEmpty()) {
|
||||||
|
return;
|
||||||
|
} else if (nearByTeamPlayer.get().getTeam().equals(team)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector direction = nearbyPlayer.getLocation().toVector().subtract(flagLocation.toVector()).normalize();
|
||||||
|
direction.setY(0.2);
|
||||||
|
nearbyPlayer.setVelocity(direction.multiply(5));
|
||||||
|
});
|
||||||
|
player.getInventory().setItem(EquipmentSlot.HEAD, new ItemStack(teamPlayer.getTeam().getFlagMaterial()));
|
||||||
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(Material.AIR));
|
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(Material.AIR));
|
||||||
notifyAboutCapture();
|
|
||||||
flagCarrier = player;
|
flagCarrier = player;
|
||||||
|
player.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, PotionEffect.INFINITE_DURATION, 0, false, false));
|
||||||
|
player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, PotionEffect.INFINITE_DURATION, 0, false, false));
|
||||||
|
notifyAboutCapture();
|
||||||
resetFlag();
|
resetFlag();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawnFlag() {
|
public void spawnFlag(Material material) {
|
||||||
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(flagItem.getType()));
|
Bukkit.getScheduler().runTask(main, () -> flagLocation.getBlock().setType(material));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnFlagParticleRing() {
|
private void spawnFlagParticleRing() {
|
||||||
@@ -140,8 +160,11 @@ public class Flag implements Runnable {
|
|||||||
updateDisplay();
|
updateDisplay();
|
||||||
} else {
|
} else {
|
||||||
winningTeam = optionalTeam.get();
|
winningTeam = optionalTeam.get();
|
||||||
resetFlag();
|
spawnFlag(winningTeam.getFlagMaterial());
|
||||||
//TODO stop capture and let ppl know they can now capture the flag
|
updateDisplay();
|
||||||
|
Title title = Title.title(MiniMessage.miniMessage().deserialize("Team <team> can capture the flag",
|
||||||
|
Placeholder.component("team", winningTeam.getName())), Component.empty());
|
||||||
|
Bukkit.getOnlinePlayers().forEach(player -> player.showTitle(title));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,8 +181,9 @@ public class Flag implements Runnable {
|
|||||||
miniMessage.deserialize("<red>kill <player> before they bring it to their base.</red>",
|
miniMessage.deserialize("<red>kill <player> before they bring it to their base.</red>",
|
||||||
Placeholder.component("player", flagCarrier.displayName())));
|
Placeholder.component("player", flagCarrier.displayName())));
|
||||||
Bukkit.getOnlinePlayers().forEach(player ->
|
Bukkit.getOnlinePlayers().forEach(player ->
|
||||||
gameManager.getTeam(player.getUniqueId()).ifPresent(team ->
|
gameManager.getTeam(player).ifPresent(team ->
|
||||||
player.showTitle(team.getId() == winningTeam.getId() ? capturingTeamTitle : huntingTeamTitle)));
|
player.showTitle(team.getId().intValue() == winningTeam.getId().intValue()
|
||||||
|
? capturingTeamTitle : huntingTeamTitle)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void spawnParticlesOnSquareBorder(Location center, double size) {
|
private void spawnParticlesOnSquareBorder(Location center, double size) {
|
||||||
@@ -179,7 +203,7 @@ public class Flag implements Runnable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkedList<Location> particleTrail = new LinkedList<>();
|
private final LinkedList<Location> particleTrail = new LinkedList<>();
|
||||||
|
|
||||||
private void spawnTrail() {
|
private void spawnTrail() {
|
||||||
TeamColor color = winningTeam.getColor();
|
TeamColor color = winningTeam.getColor();
|
||||||
@@ -193,9 +217,10 @@ public class Flag implements Runnable {
|
|||||||
private void checkFlagCarrier() {
|
private void checkFlagCarrier() {
|
||||||
if (flagCarrier.isDead() || !flagCarrier.isOnline()) {
|
if (flagCarrier.isDead() || !flagCarrier.isOnline()) {
|
||||||
resetFlagCarrier();
|
resetFlagCarrier();
|
||||||
spawnFlag();
|
spawnFlag(GameConfig.FLAG.MATERIAL);
|
||||||
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 > GameConfig.FLAG.TURN_IN_RADIUS) {
|
||||||
Location location = flagCarrier.getLocation();
|
Location location = flagCarrier.getLocation();
|
||||||
@@ -205,18 +230,20 @@ public class Flag implements Runnable {
|
|||||||
spawnParticlesOnSquareBorder(winningTeam.getFlagTurnInLocation(), GameConfig.FLAG.TURN_IN_RADIUS);
|
spawnParticlesOnSquareBorder(winningTeam.getFlagTurnInLocation(), GameConfig.FLAG.TURN_IN_RADIUS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyAboutTurnIn();
|
notifyAboutTurnIn();
|
||||||
spawnFlag();
|
spawnFlag(GameConfig.FLAG.MATERIAL);
|
||||||
|
|
||||||
wins.merge(winningTeam.getId(), 1, Integer::sum);
|
wins.merge(winningTeam.getId(), 1, Integer::sum);
|
||||||
winningTeam = null;
|
wins.forEach((id, score) -> gameManager.getTeam(id).ifPresent(team -> team.setScore(score)));
|
||||||
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(flagCarrier.getUniqueId());
|
|
||||||
if (optionalTeamPlayer.isEmpty()) {
|
flagCarrier.getInventory().setItem(EquipmentSlot.HEAD, null);
|
||||||
flagCarrier.getInventory().setItem(EquipmentSlot.HEAD, null);
|
gameManager.getTeamPlayer(flagCarrier)
|
||||||
} else {
|
.ifPresent(teamPlayer -> {
|
||||||
TeamPlayer teamPlayer = optionalTeamPlayer.get();
|
teamPlayer.getGameClass().setArmor(flagCarrier, teamPlayer);
|
||||||
teamPlayer.getGameClass().setArmor(flagCarrier, teamPlayer);
|
teamPlayer.increaseStat(Stat.FLAGS_CAPTURED);
|
||||||
flagCarrier.getInventory().setItem(EquipmentSlot.HEAD, null);
|
});
|
||||||
}
|
|
||||||
resetFlagCarrier();
|
resetFlagCarrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,10 +251,10 @@ public class Flag implements Runnable {
|
|||||||
Bukkit.broadcast(miniMessage.deserialize("<player> captured the flag for <team>!",
|
Bukkit.broadcast(miniMessage.deserialize("<player> captured the flag for <team>!",
|
||||||
Placeholder.component("player", flagCarrier.displayName()),
|
Placeholder.component("player", flagCarrier.displayName()),
|
||||||
Placeholder.component("team", winningTeam.getName())));
|
Placeholder.component("team", winningTeam.getName())));
|
||||||
Title title = Title.title(miniMessage.deserialize("<green><player> captured the flag for team <team>!</green>",
|
Title title = Title.title(Component.empty(),
|
||||||
Placeholder.component("team", winningTeam.getName())),
|
miniMessage.deserialize("<green><player> captured the flag for <team> team</green>",
|
||||||
miniMessage.deserialize("<green>protect <player> while they bring it to your base.</green>",
|
Placeholder.component("player", flagCarrier.displayName()),
|
||||||
Placeholder.component("player", flagCarrier.displayName())));
|
Placeholder.component("team", winningTeam.getName())));
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> player.showTitle(title));
|
Bukkit.getOnlinePlayers().forEach(player -> player.showTitle(title));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +302,7 @@ public class Flag implements Runnable {
|
|||||||
*/
|
*/
|
||||||
private boolean updateScoreBasedOnNearbyPlayers(Collection<Player> players) {
|
private boolean updateScoreBasedOnNearbyPlayers(Collection<Player> players) {
|
||||||
List<TeamPlayer> nearbyPlayers = players.stream()
|
List<TeamPlayer> nearbyPlayers = players.stream()
|
||||||
.map(player -> gameManager.getTeamPlayer(player.getUniqueId()))
|
.map(gameManager::getTeamPlayer)
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.toList();
|
.toList();
|
||||||
@@ -305,13 +332,15 @@ public class Flag implements Runnable {
|
|||||||
}
|
}
|
||||||
Team winningTeam = teamLongEntry.getKey();
|
Team winningTeam = teamLongEntry.getKey();
|
||||||
|
|
||||||
teamCounts.forEach((team, count) -> {
|
teamFlagPointCount.putIfAbsent(winningTeam.getId(), 0);
|
||||||
teamFlagPointCount.merge(team.getId(), team.equals(winningTeam) ? 1 : -1, (oldValue, delta) -> {
|
teamFlagPointCount.entrySet().forEach(entry -> {
|
||||||
int updatedValue = oldValue + delta;
|
if (entry.getKey().equals(winningTeam.getId())) {
|
||||||
log.debug("Set count to {} for team {}", updatedValue, team.getId());
|
entry.setValue(entry.getValue() + 1);
|
||||||
return Math.max(updatedValue, 0);
|
} else {
|
||||||
});
|
entry.setValue(Math.max(0, entry.getValue() - 1));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
nearbyPlayers.forEach(teamPlayer -> teamPlayer.increaseStat(Stat.TIME_SPEND_CAPTURING_FLAG));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +379,10 @@ public class Flag implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void resetFlagCarrier() {
|
public void resetFlagCarrier() {
|
||||||
|
final Player player = flagCarrier;
|
||||||
|
if (player != null) {
|
||||||
|
Bukkit.getScheduler().runTask(main, player::clearActivePotionEffects);
|
||||||
|
}
|
||||||
flagCarrier = null;
|
flagCarrier = null;
|
||||||
winningTeam = null;
|
winningTeam = null;
|
||||||
particleTrail.clear();
|
particleTrail.clear();
|
||||||
@@ -366,6 +399,7 @@ public class Flag implements Runnable {
|
|||||||
resetFlagCarrier();
|
resetFlagCarrier();
|
||||||
resetFlag();
|
resetFlag();
|
||||||
wins.clear();
|
wins.clear();
|
||||||
|
gameManager.getTeams().forEach(team -> team.setScore(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleCarrierDeathOrDisconnect(Player player) {
|
public void handleCarrierDeathOrDisconnect(Player player) {
|
||||||
@@ -376,8 +410,8 @@ public class Flag implements Runnable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resetFlagCarrier();
|
resetFlagCarrier();
|
||||||
spawnFlag();
|
spawnFlag(GameConfig.FLAG.MATERIAL);
|
||||||
gameManager.getTeam(player.getUniqueId())
|
gameManager.getTeam(player)
|
||||||
.ifPresentOrElse(team -> Bukkit.broadcast(MiniMessage.miniMessage()
|
.ifPresentOrElse(team -> Bukkit.broadcast(MiniMessage.miniMessage()
|
||||||
.deserialize("<red><team>'s flag carrier died! The flag has respawned",
|
.deserialize("<red><team>'s flag carrier died! The flag has respawned",
|
||||||
Placeholder.component("team", team.getName()))),
|
Placeholder.component("team", team.getName()))),
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ 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.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ public class FlagTryCaptureEvent implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
Optional<TeamPlayer> teamPlayer = winningTeam.getPlayer(player.getUniqueId());
|
Optional<TeamPlayer> teamPlayer = winningTeam.getPlayer(player);
|
||||||
if (teamPlayer.isEmpty()) {
|
if (teamPlayer.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package com.alttd.ctf.game;
|
package com.alttd.ctf.game;
|
||||||
|
|
||||||
|
import com.alttd.ctf.Main;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
import com.alttd.ctf.game.phases.ClassSelectionPhase;
|
import com.alttd.ctf.game.phases.ClassSelectionPhase;
|
||||||
import com.alttd.ctf.game.phases.CombatPhase;
|
import com.alttd.ctf.game.phases.CombatPhase;
|
||||||
import com.alttd.ctf.game.phases.EndedPhase;
|
import com.alttd.ctf.game.phases.EndedPhase;
|
||||||
import com.alttd.ctf.game.phases.GatheringPhase;
|
import com.alttd.ctf.game.phases.GatheringPhase;
|
||||||
import com.alttd.ctf.game_class.creation.FighterCreator;
|
import com.alttd.ctf.game_class.creation.FighterCreator;
|
||||||
|
import com.alttd.ctf.stats.PlayerStat;
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
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;
|
||||||
@@ -24,29 +26,34 @@ public class GameManager {
|
|||||||
private final HashMap<GamePhase, GamePhaseExecutor> phases;
|
private final HashMap<GamePhase, GamePhaseExecutor> phases;
|
||||||
private RunningGame runningGame;
|
private RunningGame runningGame;
|
||||||
private final HashMap<Integer, Team> teams = new HashMap<>();
|
private final HashMap<Integer, Team> teams = new HashMap<>();
|
||||||
|
private final HashMap<UUID, PlayerStat> playerStats = new HashMap<>();
|
||||||
|
|
||||||
public GameManager(WorldBorderApi worldBorderApi) {
|
public GameManager(Main main, WorldBorderApi worldBorderApi) {
|
||||||
phases = new HashMap<>();
|
phases = new HashMap<>();
|
||||||
phases.put(GamePhase.CLASS_SELECTION, new ClassSelectionPhase(this, FighterCreator::createFighter, worldBorderApi));
|
phases.put(GamePhase.CLASS_SELECTION, new ClassSelectionPhase(this, FighterCreator::createFighter, worldBorderApi));
|
||||||
phases.put(GamePhase.GATHERING, new GatheringPhase(this, worldBorderApi));
|
phases.put(GamePhase.GATHERING, new GatheringPhase(this, worldBorderApi));
|
||||||
phases.put(GamePhase.COMBAT, new CombatPhase());
|
phases.put(GamePhase.COMBAT, new CombatPhase());
|
||||||
phases.put(GamePhase.ENDED, new EndedPhase());
|
phases.put(GamePhase.ENDED, new EndedPhase(main, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<GamePhase> getGamePhase() {
|
public Optional<GamePhase> getGamePhase() {
|
||||||
return runningGame == null ? Optional.empty() : Optional.of(runningGame.getCurrentPhase());
|
return runningGame == null ? Optional.empty() : Optional.of(runningGame.getCurrentPhase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerPlayer(Team team, Player player) {
|
public TeamPlayer registerPlayer(Team team, Player player) {
|
||||||
unregisterPlayer(player);
|
unregisterPlayer(player);
|
||||||
teams.get(team.getId()).addPlayer(player.getUniqueId());
|
UUID uuid = player.getUniqueId();
|
||||||
|
PlayerStat playerStat = playerStats
|
||||||
|
.computeIfAbsent(uuid, (ignored) -> new PlayerStat(uuid, player.getName()));
|
||||||
|
return teams.get(team.getId()).addPlayer(player, playerStat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterPlayer(Player player) {
|
public void unregisterPlayer(Player player) {
|
||||||
teams.values().forEach(team -> team.removePlayer(player.getUniqueId()));
|
teams.values().forEach(team -> team.removePlayer(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerTeam(Team team) {
|
public void registerTeam(Team team) {
|
||||||
|
team.registerGameManger(this);
|
||||||
teams.put(team.getId(), team);
|
teams.put(team.getId(), team);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,17 +61,17 @@ public class GameManager {
|
|||||||
return teams.values();
|
return teams.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Team> getTeam(@NotNull UUID uuid) {
|
public Optional<Team> getTeam(@NotNull Player player) {
|
||||||
return getTeams().stream().filter(filterTeam -> filterTeam.getPlayer(uuid).isPresent()).findAny();
|
return getTeams().stream().filter(filterTeam -> filterTeam.getPlayer(player).isPresent()).findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Team> getTeam(int teamId) {
|
public Optional<Team> getTeam(int teamId) {
|
||||||
return getTeams().stream().filter(filterTeam -> filterTeam.getId() == teamId).findAny();
|
return getTeams().stream().filter(filterTeam -> filterTeam.getId() == teamId).findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<TeamPlayer> getTeamPlayer(@NotNull UUID uuid) {
|
public Optional<TeamPlayer> getTeamPlayer(@NotNull Player player) {
|
||||||
return getTeams().stream()
|
return getTeams().stream()
|
||||||
.map(team -> team.getPlayer(uuid))
|
.map(team -> team.getPlayer(player))
|
||||||
.filter(Optional::isPresent)
|
.filter(Optional::isPresent)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseGet(Optional::empty);
|
.orElseGet(Optional::empty);
|
||||||
@@ -106,4 +113,20 @@ public class GameManager {
|
|||||||
}
|
}
|
||||||
return runningGame.skipCurrentPhase();
|
return runningGame.skipCurrentPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPlayerStats(List<PlayerStat> playerStats) {
|
||||||
|
this.playerStats.clear();
|
||||||
|
playerStats.forEach(playerStat -> this.playerStats.put(playerStat.getUuid(), playerStat));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<PlayerStat> getPlayerStats() {
|
||||||
|
return this.playerStats.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Duration> getRemainingTime() {
|
||||||
|
if (runningGame == null || runningGame.getCurrentPhase().equals(GamePhase.ENDED)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(runningGame.getRemainingTime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.alttd.ctf.game;
|
|||||||
|
|
||||||
import com.alttd.ctf.config.GameConfig;
|
import com.alttd.ctf.config.GameConfig;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
|
import com.alttd.ctf.team.TeamScoreboard;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
@@ -13,6 +14,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.List;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -39,7 +41,6 @@ public class RunningGame implements Runnable {
|
|||||||
try {
|
try {
|
||||||
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();
|
|
||||||
nextPhaseActions(null, currentPhase);
|
nextPhaseActions(null, currentPhase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ public class RunningGame implements Runnable {
|
|||||||
} else {
|
} else {
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
}
|
}
|
||||||
|
TeamScoreboard.refreshScoreboard(gameManager);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Unexpected error in running game", e);
|
log.error("Unexpected error in running game", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@@ -58,28 +60,41 @@ public class RunningGame implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase) {
|
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase) {
|
||||||
//TODO command to go to next phase
|
|
||||||
this.currentPhase = phase;
|
this.currentPhase = phase;
|
||||||
|
this.phaseStartTime = Instant.now();
|
||||||
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 (phase.equals(GamePhase.ENDED)) {
|
||||||
|
executorService.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastNextPhaseStartTime(GamePhase currentPhase, GamePhase nextPhase) {//TODO check how this works/what it should do
|
private static final List<Integer> NOTIFY_SECONDS = List.of(45, 30, 15, 10, 5, 3, 2, 1);
|
||||||
|
private void broadcastNextPhaseStartTime(GamePhase currentPhase, GamePhase nextPhase) {
|
||||||
//Remaining time for this phase
|
//Remaining time for this phase
|
||||||
Duration duration = phaseDurations.get(currentPhase).minus(Duration.between(phaseStartTime, Instant.now()));
|
Duration duration = phaseDurations.get(currentPhase).minus(Duration.between(phaseStartTime, Instant.now()));
|
||||||
log.debug(duration.toString());//TODO remove debug
|
|
||||||
if ((duration.toMinutes() > 1 && (duration.toMinutes() % 15 == 0 || duration.toMinutes() <= 5)) && duration.toSecondsPart() < 2) {
|
if ((duration.toMinutes() > 1 && (duration.toMinutes() % 15 == 0 || duration.toMinutes() <= 5)) && duration.toSecondsPart() < 2) {
|
||||||
if (lastMinuteBroadcast == duration.toMinutes()) {
|
if (lastMinuteBroadcast == duration.toMinutes()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastMinuteBroadcast = (int) duration.toMinutes();
|
lastMinuteBroadcast = (int) duration.toMinutes();
|
||||||
|
if (nextPhase.equals(GamePhase.ENDED)) {
|
||||||
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(
|
||||||
|
"<green>The game will end in <bold><red>" + duration.toMinutes() + "</red> minutes</bold></green>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(
|
||||||
"<green>The <phase> will start in <bold><red>" + duration.toMinutes() + "</red> minutes</bold></green>",
|
"<green>The <phase> will start in <bold><red>" + duration.toMinutes() + "</red> minutes</bold></green>",
|
||||||
Placeholder.component("phase", nextPhase.getDisplayName())
|
Placeholder.component("phase", nextPhase.getDisplayName())
|
||||||
));
|
));
|
||||||
} else if (duration.toMinutes() < 1 && duration.toSeconds() % 15 == 0) {
|
} else if (duration.toMinutes() < 1 && NOTIFY_SECONDS.contains(duration.toSecondsPart())) {
|
||||||
|
if (nextPhase.equals(GamePhase.ENDED)) {
|
||||||
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(
|
||||||
|
"<green>The game will end in <bold><red>" + duration.toSeconds() + "</red> seconds</bold></green>"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize(
|
||||||
"<green>The <phase> will start in <bold><red>" + duration.toSeconds() + "</red> seconds</bold></green>",
|
"<green>The <phase> will start in <bold><red>" + duration.toSeconds() + "</red> seconds</bold></green>",
|
||||||
Placeholder.component("phase", nextPhase.getDisplayName())
|
Placeholder.component("phase", nextPhase.getDisplayName())
|
||||||
@@ -101,4 +116,9 @@ public class RunningGame implements Runnable {
|
|||||||
//TODO say the phase ended early?
|
//TODO say the phase ended early?
|
||||||
nextPhaseActions(currentPhase, GamePhase.ENDED);
|
nextPhaseActions(currentPhase, GamePhase.ENDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Duration getRemainingTime() {
|
||||||
|
Duration duration = phaseDurations.get(currentPhase);
|
||||||
|
return duration.minus(Duration.between(phaseStartTime, Instant.now()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import org.bukkit.Location;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -52,12 +54,14 @@ public class ClassSelectionPhase implements GamePhaseExecutor {
|
|||||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<green>Select your class with <gold>/ctf selectclass</gold></green>"));
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<green>Select your class with <gold>/ctf selectclass</gold></green>"));
|
||||||
CircularIterator<Team> teamCircularIterator = new CircularIterator<>(gameManager.getTeams());
|
CircularIterator<Team> teamCircularIterator = new CircularIterator<>(gameManager.getTeams());
|
||||||
if (teamCircularIterator.hasNext()) {
|
if (teamCircularIterator.hasNext()) {
|
||||||
Bukkit.getOnlinePlayers().stream()
|
ArrayList<? extends Player> players = new ArrayList<>(Bukkit.getOnlinePlayers());
|
||||||
|
Collections.shuffle(players);
|
||||||
|
players.stream()
|
||||||
.filter(player -> !player.hasPermission("ctf.bypass"))
|
.filter(player -> !player.hasPermission("ctf.bypass"))
|
||||||
.filter(player -> gameManager.getTeamPlayer(player.getUniqueId()).isEmpty())
|
.filter(player -> gameManager.getTeamPlayer(player).isEmpty())
|
||||||
.forEach(player -> {
|
.forEach(player -> {
|
||||||
Team team = teamCircularIterator.next();
|
Team team = teamCircularIterator.next();
|
||||||
team.addPlayer(player.getUniqueId());
|
gameManager.registerPlayer(team, player);
|
||||||
player.sendRichMessage("You joined <team>!", Placeholder.component("team", team.getName()));
|
player.sendRichMessage("You joined <team>!", Placeholder.component("team", team.getName()));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -68,7 +72,7 @@ public class ClassSelectionPhase implements GamePhaseExecutor {
|
|||||||
|
|
||||||
private void teleportPlayersToStartingZone() {
|
private void teleportPlayersToStartingZone() {
|
||||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||||
Optional<TeamPlayer> teamPlayer = gameManager.getTeamPlayer(player.getUniqueId());
|
Optional<TeamPlayer> teamPlayer = gameManager.getTeamPlayer(player);
|
||||||
if (teamPlayer.isEmpty()) {
|
if (teamPlayer.isEmpty()) {
|
||||||
log.warn("{} is not a team player when teleporting to starting zone", player.getName());
|
log.warn("{} is not a team player when teleporting to starting zone", player.getName());
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.alttd.ctf.game.phases;
|
package com.alttd.ctf.game.phases;
|
||||||
|
|
||||||
|
import com.alttd.ctf.config.GameConfig;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
import com.alttd.ctf.game.GamePhase;
|
import com.alttd.ctf.game.GamePhase;
|
||||||
import com.alttd.ctf.game.GamePhaseExecutor;
|
import com.alttd.ctf.game.GamePhaseExecutor;
|
||||||
@@ -15,21 +16,24 @@ public class CombatPhase implements GamePhaseExecutor {
|
|||||||
private ScheduledExecutorService executorService = null;
|
private ScheduledExecutorService executorService = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Flag flag) {
|
public synchronized void start(Flag flag) {
|
||||||
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<green>CAPTURE THE FLAG</green>"));
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<green>CAPTURE THE FLAG</green>"));
|
||||||
flag.spawnFlag();
|
flag.spawnFlag(GameConfig.FLAG.MATERIAL);
|
||||||
if (executorService == null || !executorService.isShutdown()) {
|
if (executorService == null) {
|
||||||
if (executorService != null) {
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
executorService.shutdown();
|
} else if (executorService.isTerminated() || executorService.isShutdown()) {
|
||||||
}
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
} else {
|
||||||
|
executorService.shutdown();
|
||||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
}
|
}
|
||||||
executorService.scheduleAtFixedRate(flag, 0, 1, TimeUnit.SECONDS);
|
executorService.scheduleAtFixedRate(flag, 0, 1, TimeUnit.SECONDS);
|
||||||
// TODO Add players to bossbar list for capture progress(or maybe only when any progress is made)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void end(GamePhase ignored) {
|
public void end(GamePhase ignored) {
|
||||||
executorService.shutdown();
|
if (executorService != null) {
|
||||||
|
executorService.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.alttd.ctf.game.phases;
|
package com.alttd.ctf.game.phases;
|
||||||
|
|
||||||
|
import com.alttd.ctf.Main;
|
||||||
import com.alttd.ctf.config.GameConfig;
|
import com.alttd.ctf.config.GameConfig;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
import com.alttd.ctf.game.GamePhase;
|
import com.alttd.ctf.game.GamePhase;
|
||||||
import com.alttd.ctf.game.GamePhaseExecutor;
|
import com.alttd.ctf.game.GamePhaseExecutor;
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
@@ -12,9 +14,11 @@ 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 org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.attribute.Attribute;
|
import org.bukkit.attribute.Attribute;
|
||||||
import org.bukkit.attribute.AttributeInstance;
|
import org.bukkit.attribute.AttributeInstance;
|
||||||
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -25,6 +29,13 @@ import java.util.Map;
|
|||||||
public class EndedPhase implements GamePhaseExecutor {
|
public class EndedPhase implements GamePhaseExecutor {
|
||||||
|
|
||||||
private final MiniMessage miniMessage = MiniMessage.miniMessage();
|
private final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||||
|
private final Main main;
|
||||||
|
private final GameManager gameManager;
|
||||||
|
|
||||||
|
public EndedPhase(Main main, GameManager gameManager) {
|
||||||
|
this.main = main;
|
||||||
|
this.gameManager = gameManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Flag flag) {
|
public void start(Flag flag) {
|
||||||
@@ -51,8 +62,8 @@ public class EndedPhase implements GamePhaseExecutor {
|
|||||||
player.setHealth(20);
|
player.setHealth(20);
|
||||||
});
|
});
|
||||||
flag.resetAll();
|
flag.resetAll();
|
||||||
|
gameManager.getTeams().forEach(team -> Bukkit.getOnlinePlayers().forEach(team::removePlayer));
|
||||||
}).start();
|
}).start();
|
||||||
// TODO reset world (coreprotect) to prep for next round
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Component> getWinnerMessages(HashMap<Team, Integer> wins) {
|
private List<Component> getWinnerMessages(HashMap<Team, Integer> wins) {
|
||||||
@@ -71,18 +82,29 @@ public class EndedPhase implements GamePhaseExecutor {
|
|||||||
return messages;
|
return messages;
|
||||||
} else if (topTeams.size() > 1) { // Draw scenario, multiple teams have the same top score
|
} else if (topTeams.size() > 1) { // Draw scenario, multiple teams have the same top score
|
||||||
messages.add(miniMessage.deserialize("<yellow>It's a draw! Top teams:</yellow>"));
|
messages.add(miniMessage.deserialize("<yellow>It's a draw! Top teams:</yellow>"));
|
||||||
topTeams.forEach(team -> {
|
topTeams.forEach(team -> messages.add(
|
||||||
messages.add(miniMessage.deserialize("<team> had <score> captures.",
|
miniMessage.deserialize("<team> had <score> captures.",
|
||||||
Placeholder.component("team", team.getName()),
|
Placeholder.component("team", team.getName()),
|
||||||
Placeholder.parsed("score", String.valueOf(highestScore))));
|
Placeholder.parsed("score", String.valueOf(highestScore)))));
|
||||||
});
|
|
||||||
addOtherTeamsScore(wins, highestScore, messages);
|
addOtherTeamsScore(wins, highestScore, messages);
|
||||||
return messages;
|
return messages;
|
||||||
} else { // Single winner
|
} else { // Single winner
|
||||||
Team winner = topTeams.getFirst();
|
Team winner = topTeams.getFirst();
|
||||||
messages.add(miniMessage.deserialize("<green><team> has won with <score> captures!</green>",
|
messages.add(miniMessage.deserialize("<green><team> has won with <score> captures!</green>",
|
||||||
Placeholder.component("team", winner.getName()),
|
Placeholder.component("team", winner.getName()),
|
||||||
Placeholder.parsed("score", String.valueOf(highestScore))));
|
Placeholder.parsed("score", String.valueOf(highestScore))));
|
||||||
|
ConsoleCommandSender consoleSender = Bukkit.getConsoleSender();
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(main, () -> {
|
||||||
|
winner.getPlayers().forEach(teamPlayer -> {
|
||||||
|
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(teamPlayer.getUuid());
|
||||||
|
String name = offlinePlayer.getName();
|
||||||
|
if (name == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bukkit.dispatchCommand(consoleSender, String.format("lp user %s permission settemp ctf.victory.%s true 14d", name, winner.getId()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (wins.size() <= 1) {
|
if (wins.size() <= 1) {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
@@ -97,8 +119,8 @@ public class EndedPhase implements GamePhaseExecutor {
|
|||||||
wins.entrySet().stream()
|
wins.entrySet().stream()
|
||||||
.filter(entry -> entry.getValue() < winningScore)
|
.filter(entry -> entry.getValue() < winningScore)
|
||||||
.forEach(entry -> messages.add(miniMessage.deserialize("<yellow><team> had <score> captures.</yellow>",
|
.forEach(entry -> messages.add(miniMessage.deserialize("<yellow><team> had <score> captures.</yellow>",
|
||||||
Placeholder.component("team", entry.getKey().getName()),
|
Placeholder.component("team", entry.getKey().getName()),
|
||||||
Placeholder.parsed("score", String.valueOf(entry.getValue())))));
|
Placeholder.parsed("score", String.valueOf(entry.getValue())))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ 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.game.GamePhaseExecutor;
|
import com.alttd.ctf.game.GamePhaseExecutor;
|
||||||
import com.alttd.ctf.game_class.GameClass;
|
|
||||||
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.minimessage.MiniMessage;
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GatheringPhase implements GamePhaseExecutor {
|
public class GatheringPhase implements GamePhaseExecutor {
|
||||||
@@ -36,10 +36,13 @@ public class GatheringPhase implements GamePhaseExecutor {
|
|||||||
log.error("Unable to update world border due to missing Flag");
|
log.error("Unable to update world border due to missing Flag");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gameManager.getTeams().forEach(team -> {
|
gameManager.getTeams()
|
||||||
team.getPlayers().forEach(player -> {
|
.forEach(team -> team.getPlayers().forEach(teamPlayer -> {
|
||||||
player.resetWorldBorder(Bukkit.getPlayer(player.getUuid()), worldBorderApi, nextPhase, flag.getFlagLocation());
|
Player player = Bukkit.getPlayer(teamPlayer.getUuid());
|
||||||
});
|
if (player == null || !player.isOnline()) {
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
teamPlayer.resetWorldBorder(player, worldBorderApi, nextPhase, flag.getFlagLocation());
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.alttd.ctf.game_class;
|
package com.alttd.ctf.game_class;
|
||||||
|
|
||||||
import com.alttd.ctf.game.GameManager;
|
import com.alttd.ctf.game.GameManager;
|
||||||
import com.alttd.ctf.game_class.creation.EngineerCreator;
|
import com.alttd.ctf.game_class.creation.*;
|
||||||
import com.alttd.ctf.game_class.creation.FighterCreator;
|
|
||||||
import com.alttd.ctf.game_class.creation.TankCreator;
|
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -14,9 +12,7 @@ public class GameClassRetrieval {
|
|||||||
|
|
||||||
public static HashMap<Integer, List<GameClass>> getGameClassesForAllTeams(GameManager gameManager) {
|
public static HashMap<Integer, List<GameClass>> getGameClassesForAllTeams(GameManager gameManager) {
|
||||||
final HashMap<Integer, List<GameClass>> gameClasses = new HashMap<>();
|
final HashMap<Integer, List<GameClass>> gameClasses = new HashMap<>();
|
||||||
gameManager.getTeams().forEach(team -> {
|
gameManager.getTeams().forEach(team -> gameClasses.put(team.getId(), getGameClassesForTeam(team)));
|
||||||
gameClasses.put(team.getId(), getGameClassesForTeam(team));
|
|
||||||
});
|
|
||||||
return gameClasses;
|
return gameClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +20,9 @@ public class GameClassRetrieval {
|
|||||||
final List<GameClass> gameClasses = new ArrayList<>();
|
final List<GameClass> gameClasses = new ArrayList<>();
|
||||||
gameClasses.add(FighterCreator.createFighter(team.getColor()));
|
gameClasses.add(FighterCreator.createFighter(team.getColor()));
|
||||||
gameClasses.add(TankCreator.createTank(team.getColor()));
|
gameClasses.add(TankCreator.createTank(team.getColor()));
|
||||||
|
gameClasses.add(TrapperCreator.createTrapper(team.getColor()));
|
||||||
gameClasses.add(EngineerCreator.createEngineer(team.getColor()));
|
gameClasses.add(EngineerCreator.createEngineer(team.getColor()));
|
||||||
|
gameClasses.add(MageCreator.createMage(team.getColor()));
|
||||||
return gameClasses;
|
return gameClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.alttd.ctf.game_class.creation;
|
|||||||
|
|
||||||
import com.alttd.ctf.game_class.GameClass;
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
import com.alttd.ctf.game_class.implementations.Engineer;
|
import com.alttd.ctf.game_class.implementations.Engineer;
|
||||||
import com.alttd.ctf.game_class.implementations.Fighter;
|
|
||||||
import com.alttd.ctf.team.TeamColor;
|
import com.alttd.ctf.team.TeamColor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.alttd.ctf.game_class.creation;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import com.alttd.ctf.game_class.implementations.Mage;
|
||||||
|
import com.alttd.ctf.team.TeamColor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MageCreator {
|
||||||
|
|
||||||
|
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull GameClass createMage(@NotNull TeamColor teamColor) {
|
||||||
|
return new Mage(getArmor(), getTools(teamColor), getDisplayItem(teamColor),
|
||||||
|
20, 100, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(value = " -> new", pure = true)
|
||||||
|
private static @NotNull @Unmodifiable List<Material> getArmor() {
|
||||||
|
return (List.of(Material.CHAINMAIL_BOOTS, Material.AIR, Material.AIR, Material.LEATHER_HELMET));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
private static @NotNull @Unmodifiable List<ItemStack> getTools(@NotNull TeamColor teamColor) {
|
||||||
|
return (List.of(getShovel(teamColor)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ItemStack getShovel(@NotNull TeamColor teamColor) {
|
||||||
|
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);
|
||||||
|
meta.addEnchant(Enchantment.EFFICIENCY, 1, false);
|
||||||
|
shovel.setItemMeta(meta);
|
||||||
|
return shovel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ItemStack getDisplayItem(@NotNull TeamColor teamColor) {
|
||||||
|
ItemStack itemStack = new ItemStack(Material.SNOWBALL);
|
||||||
|
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||||
|
itemMeta.displayName(miniMessage.deserialize(String.format("<color:%s>Mage</color>", teamColor.hex())));
|
||||||
|
itemMeta.lore(List.of(
|
||||||
|
miniMessage.deserialize("<gold>The Mage can throw many snowballs at once</gold>"),
|
||||||
|
miniMessage.deserialize("<gold>But it has a long cooldown.</gold>")
|
||||||
|
));
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.alttd.ctf.game_class.creation;
|
package com.alttd.ctf.game_class.creation;
|
||||||
|
|
||||||
import com.alttd.ctf.game_class.GameClass;
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
import com.alttd.ctf.game_class.implementations.Fighter;
|
|
||||||
import com.alttd.ctf.game_class.implementations.Tank;
|
import com.alttd.ctf.game_class.implementations.Tank;
|
||||||
import com.alttd.ctf.team.TeamColor;
|
import com.alttd.ctf.team.TeamColor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -22,7 +21,7 @@ public class TankCreator {
|
|||||||
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||||
|
|
||||||
@Contract("_ -> new")
|
@Contract("_ -> new")
|
||||||
public static @NotNull GameClass createTank(@NotNull TeamColor teamColor) {//TODO add ability to become temp invulnerable (with some particle effects mayb?)
|
public static @NotNull GameClass createTank(@NotNull TeamColor teamColor) {
|
||||||
return new Tank(getArmor(), getTools(teamColor), getDisplayItem(teamColor),
|
return new Tank(getArmor(), getTools(teamColor), getDisplayItem(teamColor),
|
||||||
30, 7, 4);
|
30, 7, 4);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.alttd.ctf.game_class.creation;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import com.alttd.ctf.game_class.implementations.Trapper;
|
||||||
|
import com.alttd.ctf.team.TeamColor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class TrapperCreator {
|
||||||
|
|
||||||
|
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
public static @NotNull GameClass createTrapper(@NotNull TeamColor teamColor) {
|
||||||
|
return new Trapper(getArmor(), getTools(teamColor), getDisplayItem(teamColor),
|
||||||
|
20, 5, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(value = " -> new", pure = true)
|
||||||
|
private static @Unmodifiable List<Material> getArmor() {
|
||||||
|
return (List.of(Material.LEATHER_BOOTS, Material.LEATHER_LEGGINGS, Material.LEATHER_CHESTPLATE, Material.AIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_ -> new")
|
||||||
|
private static @NotNull @Unmodifiable List<ItemStack> getTools(TeamColor teamColor) {
|
||||||
|
List<ItemStack> itemStacks = new ArrayList<>(List.of(getShovel(teamColor)));
|
||||||
|
itemStacks.addAll(getPowderedSnow(teamColor));
|
||||||
|
return itemStacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull List<ItemStack> getPowderedSnow(@NotNull TeamColor teamColor) {
|
||||||
|
ItemStack powderedSnow = new ItemStack(Material.POWDER_SNOW_BUCKET);
|
||||||
|
ItemMeta itemMeta = powderedSnow.getItemMeta();
|
||||||
|
itemMeta.itemName(MiniMessage.miniMessage().deserialize(
|
||||||
|
String.format("<color:%s>Snow Trap</color>", teamColor.hex())));
|
||||||
|
powderedSnow.setItemMeta(itemMeta);
|
||||||
|
List<ItemStack> snowBuckets = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
snowBuckets.add(powderedSnow.clone());
|
||||||
|
}
|
||||||
|
return snowBuckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ItemStack getShovel(@NotNull TeamColor teamColor) {
|
||||||
|
ItemStack shovel = new ItemStack(Material.WOODEN_SHOVEL);
|
||||||
|
ItemMeta meta = shovel.getItemMeta();
|
||||||
|
meta.setUnbreakable(true);
|
||||||
|
meta.itemName(miniMessage.deserialize(String.format("<color:%s>Snow shovel</color>", teamColor.hex())));
|
||||||
|
shovel.setItemMeta(meta);
|
||||||
|
return shovel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull ItemStack getDisplayItem(@NotNull TeamColor teamColor) {
|
||||||
|
ItemStack itemStack = new ItemStack(Material.POWDER_SNOW_BUCKET);
|
||||||
|
ItemMeta itemMeta = itemStack.getItemMeta();
|
||||||
|
itemMeta.displayName(miniMessage.deserialize(String.format("<color:%s>Trapper</color>", teamColor.hex())));
|
||||||
|
itemMeta.lore(List.of(
|
||||||
|
miniMessage.deserialize("<gold>The Trapper is a normal class, but it has a trick up it's sleeve</gold>"),
|
||||||
|
miniMessage.deserialize("<gold>They can place their powdered snow to trap the enemy team.</gold>")
|
||||||
|
));
|
||||||
|
itemStack.setItemMeta(itemMeta);
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package com.alttd.ctf.game_class.implementations;
|
package com.alttd.ctf.game_class.implementations;
|
||||||
|
|
||||||
import com.alttd.ctf.game_class.GameClass;
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
import com.alttd.ctf.team.TeamColor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.alttd.ctf.game_class.implementations;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.Snowball;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class Mage extends GameClass {
|
||||||
|
public Mage(@NotNull List<Material> armor, @NotNull List<ItemStack> tools, @NotNull ItemStack displayItem,
|
||||||
|
double health, int throwTickSpeed, int damage) {
|
||||||
|
super(armor, tools, displayItem, health, throwTickSpeed, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void duplicateSnowBalls(Player shooter, Snowball snowball) {
|
||||||
|
Location location = snowball.getLocation();
|
||||||
|
PlayerInventory inventory = shooter.getInventory();
|
||||||
|
ItemStack itemStack = inventory.getItemInMainHand();
|
||||||
|
if (itemStack.getType() != Material.SNOWBALL) {
|
||||||
|
ItemStack itemInOffHand = inventory.getItemInOffHand();
|
||||||
|
if (itemInOffHand.getType() != Material.SNOWBALL) {
|
||||||
|
log.warn("Unable to find snowballs in main/off hand");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itemStack = itemInOffHand;
|
||||||
|
}
|
||||||
|
if (itemStack.getAmount() <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
duplicate(shooter, location, snowball.getVelocity(), itemStack.getAmount() - 1);
|
||||||
|
itemStack.setAmount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void duplicate(@NotNull Player shooter, @NotNull Location location, @NotNull Vector velocity, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
double offsetX = (Math.random() - 0.5) * 2;
|
||||||
|
double offsetY = (Math.random() - 0.5) * 2;
|
||||||
|
double offsetZ = (Math.random() - 0.5) * 2;
|
||||||
|
|
||||||
|
Location newLocation = location.clone().add(offsetX, offsetY, offsetZ);
|
||||||
|
Snowball snowball = location.getWorld().spawn(newLocation, Snowball.class);
|
||||||
|
|
||||||
|
snowball.setShooter(shooter);
|
||||||
|
|
||||||
|
double velocityChangeX = (Math.random() - 0.5) * 0.5;
|
||||||
|
double velocityChangeY = (Math.random() - 0.5) * 0.5;
|
||||||
|
double velocityChangeZ = (Math.random() - 0.5) * 0.5;
|
||||||
|
|
||||||
|
Vector newVelocity = velocity.clone().add(new Vector(velocityChangeX, velocityChangeY, velocityChangeZ));
|
||||||
|
|
||||||
|
snowball.setVelocity(newVelocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.alttd.ctf.game_class.implementations;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Trapper extends GameClass {
|
||||||
|
public Trapper(@NotNull List<Material> armor, @NotNull List<ItemStack> tools, @NotNull ItemStack displayItem,
|
||||||
|
double health, int throwTickSpeed, int damage) {
|
||||||
|
super(armor, tools, displayItem, health, throwTickSpeed, damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@ package com.alttd.ctf.gui;
|
|||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.Merchant;
|
|
||||||
import org.bukkit.inventory.MerchantInventory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package com.alttd.ctf.stats;
|
||||||
|
|
||||||
|
import com.alttd.ctf.Main;
|
||||||
|
import com.alttd.ctf.config.GameConfig;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public final class PlayerStat {
|
||||||
|
@JsonIgnore
|
||||||
|
public static Main main;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private boolean touched;
|
||||||
|
@JsonIgnore
|
||||||
|
private static final CommandSender commandSender = Bukkit.getConsoleSender();
|
||||||
|
@JsonIgnore
|
||||||
|
private static boolean someoneDiedInPowderedSnow = false;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@NotNull
|
||||||
|
private UUID uuid;
|
||||||
|
@JsonProperty
|
||||||
|
@NotNull
|
||||||
|
private String inGameName;
|
||||||
|
|
||||||
|
private boolean completedGame = false;
|
||||||
|
private int flagsCaptured = 0;
|
||||||
|
private int kills = 0;
|
||||||
|
private double damageDone = 0;
|
||||||
|
private double damageHealed = 0;
|
||||||
|
private int snowMined = 0;
|
||||||
|
private int blocksPlaced = 0;
|
||||||
|
private int snowballsThrown = 0;
|
||||||
|
private long timeSpendCapturingFlag = 0;
|
||||||
|
private int deathsInPowderedSnow = 0;
|
||||||
|
|
||||||
|
public void increaseStat(Stat stat) throws IllegalArgumentException {
|
||||||
|
switch (stat) {
|
||||||
|
case COMPLETED_GAME -> {
|
||||||
|
if (!completedGame) {
|
||||||
|
Bukkit.getScheduler().runTask(main, () -> {
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(main, () -> {
|
||||||
|
Bukkit.dispatchCommand(commandSender, String.format("lp user %s permission settemp ctf.game.completed true 14d", inGameName));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
completedGame = true;
|
||||||
|
}
|
||||||
|
case FLAGS_CAPTURED -> flagsCaptured++;
|
||||||
|
case KILLS -> kills++;
|
||||||
|
case SNOW_MINED -> snowMined++;
|
||||||
|
case BLOCKS_PLACED -> blocksPlaced++;
|
||||||
|
case SNOWBALLS_THROWN -> snowballsThrown++;
|
||||||
|
case TIME_SPEND_CAPTURING_FLAG -> timeSpendCapturingFlag++;
|
||||||
|
case DEATHS_IN_POWDERED_SNOW -> {
|
||||||
|
if (someoneDiedInPowderedSnow) {
|
||||||
|
deathsInPowderedSnow++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||||
|
if (!offlinePlayer.isOnline()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player player = offlinePlayer.getPlayer();
|
||||||
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<red><player> will receive a consolation prize for being the first to die in powdered snow.</red>",
|
||||||
|
Placeholder.component("player",player.displayName())));
|
||||||
|
Bukkit.getScheduler().runTaskAsynchronously(main, () -> {
|
||||||
|
Bukkit.dispatchCommand(commandSender, String.format("lp user %s permission settemp ctf.game.first_powdered_snow_death true 14d", inGameName));
|
||||||
|
});
|
||||||
|
|
||||||
|
someoneDiedInPowderedSnow = true;
|
||||||
|
}
|
||||||
|
case DAMAGE_DONE, DAMAGE_HEALED -> throw new IllegalArgumentException(String.format("%s requires a number", stat.name()));
|
||||||
|
}
|
||||||
|
touched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseStat(Stat stat, double value) throws IllegalArgumentException {
|
||||||
|
switch (stat) {
|
||||||
|
case DAMAGE_DONE -> damageDone += value;
|
||||||
|
case DAMAGE_HEALED -> damageHealed += value;
|
||||||
|
default -> throw new IllegalArgumentException(String.format("%s cannot be passed with a number", stat.name()));
|
||||||
|
}
|
||||||
|
touched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnTouched() {
|
||||||
|
touched = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getStat(Stat stat) {
|
||||||
|
return switch (stat) {
|
||||||
|
case COMPLETED_GAME -> completedGame ? 1 : 0;
|
||||||
|
case FLAGS_CAPTURED -> flagsCaptured;
|
||||||
|
case KILLS -> kills;
|
||||||
|
case DAMAGE_DONE -> damageDone;
|
||||||
|
case DAMAGE_HEALED -> damageHealed;
|
||||||
|
case SNOW_MINED -> snowMined;
|
||||||
|
case BLOCKS_PLACED -> blocksPlaced;
|
||||||
|
case SNOWBALLS_THROWN -> snowballsThrown;
|
||||||
|
case TIME_SPEND_CAPTURING_FLAG -> timeSpendCapturingFlag;
|
||||||
|
case DEATHS_IN_POWDERED_SNOW -> deathsInPowderedSnow;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.alttd.ctf.stats;
|
||||||
|
|
||||||
|
public enum Stat {
|
||||||
|
COMPLETED_GAME,
|
||||||
|
FLAGS_CAPTURED,
|
||||||
|
KILLS,
|
||||||
|
DAMAGE_DONE,
|
||||||
|
DAMAGE_HEALED,
|
||||||
|
SNOW_MINED,
|
||||||
|
BLOCKS_PLACED,
|
||||||
|
SNOWBALLS_THROWN,
|
||||||
|
TIME_SPEND_CAPTURING_FLAG,
|
||||||
|
DEATHS_IN_POWDERED_SNOW
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.alttd.ctf.team;
|
||||||
|
|
||||||
|
import com.alttd.ctf.config.Config;
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.galaxy.discord.Bot;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.entities.*;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class DiscordTeam {
|
||||||
|
|
||||||
|
private final GameManager gameManager;
|
||||||
|
|
||||||
|
public DiscordTeam(GameManager gameManager) {
|
||||||
|
this.gameManager = gameManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface Validate {
|
||||||
|
void apply(Role role, Member member);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateModifyRole(Team team, Player player, Validate consumer) {
|
||||||
|
if (!player.isDiscordLinked()) {
|
||||||
|
log.info("{} is not discord linked", player.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (team.getDiscordRole() == -1) {
|
||||||
|
log.warn("No valid discord role set for team {}", team.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bot bot = Bukkit.getBot();
|
||||||
|
if (bot == null) {
|
||||||
|
log.error("Unable to get bot");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JDA jda = bot.getJDA();
|
||||||
|
if (jda == null) {
|
||||||
|
log.error("Unable to get JDA");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Guild guild = jda.getGuildById(Config.DISCORD.PLAYER_SERVER_ID);
|
||||||
|
if (guild == null) {
|
||||||
|
log.warn("Unable to get guild {}", Config.DISCORD.PLAYER_SERVER_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Role role = guild.getRoleById(team.getDiscordRole());
|
||||||
|
if (role == null) {
|
||||||
|
log.warn("Unable to get role {} for team {}", team.getDiscordRole(), team.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Member nullableMember = guild.getMemberById(player.getDiscordID());
|
||||||
|
if (nullableMember == null) {
|
||||||
|
guild.retrieveMemberById(player.getDiscordID()).queue(member -> consumer.apply(role, member));
|
||||||
|
} else {
|
||||||
|
consumer.apply(role, nullableMember);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRole(Team team, @NotNull Player player) {
|
||||||
|
validateModifyRole(team, player, (role, member) -> {
|
||||||
|
if (member.isOwner()) {
|
||||||
|
log.info("Unable to remove team role from server owner");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
member.getGuild().removeRoleFromMember(member, role).queue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addRole(Team team, Player player) {
|
||||||
|
validateModifyRole(team, player, (role, member) -> {
|
||||||
|
if (member.isOwner()) {
|
||||||
|
log.info("Unable to add team role to server owner");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gameManager.getTeams().forEach(otherTeam ->
|
||||||
|
member.getRoles().stream()
|
||||||
|
.filter(otherRole -> otherRole.getIdLong() == otherTeam.getDiscordRole())
|
||||||
|
.forEach(otherRole -> member.getGuild().removeRoleFromMember(member, otherRole).queue()));
|
||||||
|
member.getGuild().addRoleToMember(member, role).queue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,28 @@
|
|||||||
package com.alttd.ctf.team;
|
package com.alttd.ctf.team;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.stats.PlayerStat;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class Team {
|
public class Team {
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private final TeamScoreboard scoreboard = new TeamScoreboard(this);
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private final HashMap<UUID, TeamPlayer> players = new HashMap<>();
|
private final HashMap<UUID, TeamPlayer> players = new HashMap<>();
|
||||||
@JsonProperty("name")
|
@JsonProperty("name")
|
||||||
@@ -25,7 +31,8 @@ public class Team {
|
|||||||
private Component name;
|
private Component name;
|
||||||
@JsonProperty("id")
|
@JsonProperty("id")
|
||||||
@Getter
|
@Getter
|
||||||
private int id;
|
@NotNull
|
||||||
|
private Integer id;
|
||||||
@JsonProperty("spawnLocation")
|
@JsonProperty("spawnLocation")
|
||||||
@NotNull
|
@NotNull
|
||||||
@Getter
|
@Getter
|
||||||
@@ -42,15 +49,42 @@ public class Team {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Getter
|
@Getter
|
||||||
private TeamColor color;
|
private TeamColor color;
|
||||||
|
@JsonProperty("flagMaterial")
|
||||||
|
@NotNull
|
||||||
|
@Getter
|
||||||
|
private Material flagMaterial;
|
||||||
|
@JsonProperty("legacyTeamColor")
|
||||||
|
@NotNull
|
||||||
|
@Getter
|
||||||
|
private String legacyTeamColor;
|
||||||
|
@JsonProperty("discordRole")
|
||||||
|
@Getter
|
||||||
|
@NotNull
|
||||||
|
private Long discordRole;
|
||||||
|
@JsonIgnore
|
||||||
|
private DiscordTeam discordTeam;
|
||||||
|
|
||||||
public TeamPlayer addPlayer(UUID uuid) {
|
public void registerGameManger(GameManager gameManager) {
|
||||||
TeamPlayer teamPlayer = new TeamPlayer(uuid, this);
|
discordTeam = new DiscordTeam(gameManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TeamPlayer addPlayer(Player player, PlayerStat playerStat) {
|
||||||
|
removeFromScoreboard(player);
|
||||||
|
UUID uuid = player.getUniqueId();
|
||||||
|
TeamPlayer teamPlayer = new TeamPlayer(player, this, playerStat);
|
||||||
players.put(uuid, teamPlayer);
|
players.put(uuid, teamPlayer);
|
||||||
log.debug("Added player with uuid {} to team with id {}", uuid, id);
|
addToScoreboard(player);
|
||||||
|
if (discordTeam != null) {
|
||||||
|
discordTeam.addRole(this, player);
|
||||||
|
} else {
|
||||||
|
log.warn("No discord team set for {} to add role", id);
|
||||||
|
}
|
||||||
|
log.debug("Added player {} to team with id {}", player.getName(), id);
|
||||||
return teamPlayer;
|
return teamPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<TeamPlayer> getPlayer(@NotNull UUID uuid) {
|
public Optional<TeamPlayer> getPlayer(@NotNull Player player) {
|
||||||
|
UUID uuid = player.getUniqueId();
|
||||||
if (!players.containsKey(uuid))
|
if (!players.containsKey(uuid))
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
return Optional.of(players.get(uuid));
|
return Optional.of(players.get(uuid));
|
||||||
@@ -60,11 +94,30 @@ public class Team {
|
|||||||
return players.values();
|
return players.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removePlayer(@NotNull UUID uuid) {
|
public void removePlayer(@NotNull Player player) {
|
||||||
TeamPlayer remove = players.remove(uuid);
|
removeFromScoreboard(player);
|
||||||
if (remove != null) {
|
TeamPlayer remove = players.remove(player.getUniqueId());
|
||||||
log.debug("Removed player with uuid {} from team with id {}", uuid, id);
|
if (remove == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (discordTeam != null) {
|
||||||
|
discordTeam.removeRole(this, player);
|
||||||
|
} else {
|
||||||
|
log.warn("No discord team set for {} to remove role", id);
|
||||||
|
}
|
||||||
|
log.debug("Removed player {} from team with id {}", player.getName(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToScoreboard(Player player) {
|
||||||
|
scoreboard.addToScoreboard(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFromScoreboard(Player player) {
|
||||||
|
scoreboard.removeFromScoreboard(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScore(int newScore) {
|
||||||
|
scoreboard.setScore(newScore);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import com.alttd.ctf.game.GamePhase;
|
|||||||
import com.alttd.ctf.game_class.GameClass;
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
import com.alttd.ctf.game_class.GameClassRetrieval;
|
import com.alttd.ctf.game_class.GameClassRetrieval;
|
||||||
import com.alttd.ctf.gui.ClassSelectionGUI;
|
import com.alttd.ctf.gui.ClassSelectionGUI;
|
||||||
|
import com.alttd.ctf.stats.PlayerStat;
|
||||||
|
import com.alttd.ctf.stats.Stat;
|
||||||
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
import com.github.yannicklamprecht.worldborder.api.WorldBorderApi;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -21,14 +23,17 @@ import java.util.*;
|
|||||||
@Getter
|
@Getter
|
||||||
public class TeamPlayer {
|
public class TeamPlayer {
|
||||||
|
|
||||||
|
private final PlayerStat playerStat;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final Team team;
|
private final Team team;
|
||||||
@Setter
|
@Setter
|
||||||
private GameClass gameClass;
|
private GameClass gameClass;
|
||||||
|
private boolean isDead = false;
|
||||||
|
|
||||||
protected TeamPlayer(UUID uuid, Team team) {
|
protected TeamPlayer(Player player, Team team, PlayerStat playerStat) {
|
||||||
this.uuid = uuid;
|
this.uuid = player.getUniqueId();
|
||||||
this.team = team;
|
this.team = team;
|
||||||
|
this.playerStat = playerStat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void respawn(@NotNull Player player, @NotNull WorldBorderApi worldBorderApi, @NotNull GamePhase gamePhase) {
|
public void respawn(@NotNull Player player, @NotNull WorldBorderApi worldBorderApi, @NotNull GamePhase gamePhase) {
|
||||||
@@ -48,9 +53,17 @@ public class TeamPlayer {
|
|||||||
}
|
}
|
||||||
player.teleportAsync(spawnLocation).thenAcceptAsync(unused ->
|
player.teleportAsync(spawnLocation).thenAcceptAsync(unused ->
|
||||||
resetWorldBorder(player, worldBorderApi, gamePhase, worldBorderCenter));
|
resetWorldBorder(player, worldBorderApi, gamePhase, worldBorderCenter));
|
||||||
|
isDead = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDead() {
|
||||||
|
isDead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetWorldBorder(Player player, WorldBorderApi worldBorderApi, GamePhase gamePhase, Location worldBorderCenter) {
|
public void resetWorldBorder(Player player, WorldBorderApi worldBorderApi, GamePhase gamePhase, Location worldBorderCenter) {
|
||||||
|
if (player == null || !player.isOnline()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
WorldBorderSettings worldBorderSettings = GameConfig.WORLD_BORDER.getGAME_PHASE_WORLD_BORDER().get(gamePhase);
|
WorldBorderSettings worldBorderSettings = GameConfig.WORLD_BORDER.getGAME_PHASE_WORLD_BORDER().get(gamePhase);
|
||||||
if (worldBorderSettings == null) {
|
if (worldBorderSettings == null) {
|
||||||
throw new IllegalStateException("All phases need to have world border settings");
|
throw new IllegalStateException("All phases need to have world border settings");
|
||||||
@@ -78,4 +91,12 @@ public class TeamPlayer {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(uuid);
|
return Objects.hash(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void increaseStat(Stat stat) {
|
||||||
|
playerStat.increaseStat(stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increaseStat(Stat stat, double amount) {
|
||||||
|
playerStat.increaseStat(stat, amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package com.alttd.ctf.team;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.game.GamePhase;
|
||||||
|
import net.kyori.adventure.text.format.*;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scoreboard.*;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
public class TeamScoreboard {
|
||||||
|
|
||||||
|
private final static Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
|
||||||
|
private final Team team;
|
||||||
|
|
||||||
|
public TeamScoreboard(Team team) {
|
||||||
|
this.team = team;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToScoreboard(Player player) {
|
||||||
|
org.bukkit.scoreboard.Team scoreboardTeam = scoreboard.getTeam("ctf_" + team.getId());
|
||||||
|
if (scoreboardTeam == null) {
|
||||||
|
scoreboardTeam = scoreboard.registerNewTeam("ctf_" + team.getId());
|
||||||
|
scoreboardTeam.displayName(team.getName());
|
||||||
|
TeamColor color = team.getColor();
|
||||||
|
NamedTextColor namedTextColor = NamedTextColor.nearestTo(TextColor.color(color.r(), color.g(), color.b()));
|
||||||
|
scoreboardTeam.color(namedTextColor);
|
||||||
|
}
|
||||||
|
scoreboardTeam.addPlayer(player);
|
||||||
|
player.setScoreboard(scoreboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeFromScoreboard(Player player) {
|
||||||
|
scoreboard.getTeams().stream()
|
||||||
|
.filter(team -> team.getName().startsWith("ctf_"))
|
||||||
|
.filter(team -> team.hasPlayer(player))
|
||||||
|
.forEach(team -> team.removePlayer(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setScore(int newScore) {
|
||||||
|
Objective objective = getOrCreateObjective();
|
||||||
|
Score score = objective.getScore(team.getLegacyTeamColor() +
|
||||||
|
PlainTextComponentSerializer.plainText().serialize(team.getName()));
|
||||||
|
score.setScore(newScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Objective getOrCreateObjective() {
|
||||||
|
Objective objective = scoreboard.getObjective("teamScores");
|
||||||
|
if (objective == null) {
|
||||||
|
objective = scoreboard.registerNewObjective("teamScores", Criteria.DUMMY,
|
||||||
|
MiniMessage.miniMessage().deserialize("<gold>CTF score</gold>"));
|
||||||
|
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||||
|
}
|
||||||
|
return objective;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PhaseScore(GamePhase gamePhase, Score score) {}
|
||||||
|
private static PhaseScore phaseScore = null;
|
||||||
|
private static void updateTime(GamePhase gamePhase, Duration duration) {
|
||||||
|
if (phaseScore == null) {
|
||||||
|
phaseScore = new PhaseScore(gamePhase, getOrCreateObjective()
|
||||||
|
.getScore(ChatColor.GREEN + PlainTextComponentSerializer.plainText().serialize(gamePhase.getDisplayName())));
|
||||||
|
} else if (phaseScore.gamePhase() != gamePhase) {
|
||||||
|
phaseScore.score.resetScore();
|
||||||
|
phaseScore = new PhaseScore(gamePhase, getOrCreateObjective()
|
||||||
|
.getScore(ChatColor.GREEN + PlainTextComponentSerializer.plainText().serialize(gamePhase.getDisplayName())));
|
||||||
|
}
|
||||||
|
phaseScore.score.setScore(duration.toMinutesPart() == 0 ? duration.toSecondsPart() : duration.toMinutesPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void refreshScoreboard(GameManager gameManager) {
|
||||||
|
gameManager.getGamePhase().ifPresent(gamePhase ->
|
||||||
|
gameManager.getRemainingTime().ifPresent(duration ->
|
||||||
|
updateTime(gamePhase, duration)));
|
||||||
|
// TODO if game is active show time + game phase
|
||||||
|
// if someone has flag show that
|
||||||
|
// show how many of each team in circle?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
#Tue Feb 11 22:04:16 CET 2025
|
#Sun Mar 02 00:42:30 CET 2025
|
||||||
buildNumber=42
|
buildNumber=112
|
||||||
version=0.1
|
version=0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user