package de.fanta.challenges.challenges.ChallengeEvents;
import de.fanta.challenges.Challenges;
import de.fanta.challenges.events.TimerChangedEvent;
import de.fanta.challenges.schedular.CancellableTask;
import de.fanta.challenges.scoreboard.ChallengePlayer;
import de.fanta.challenges.scoreboard.Scorable;
import de.fanta.challenges.scoreboard.ScoreManager;
import de.fanta.challenges.utils.ChatSkullAPI.ChatSkull;
import de.fanta.challenges.utils.ChatUtil;
import de.fanta.challenges.utils.Config;
import de.fanta.challenges.utils.CoordsTargeter;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Boat;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityMountEvent;
import org.bukkit.event.entity.EntityPlaceEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.vehicle.VehicleDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.UUID;
public class DeathrunChallengeEvent implements Listener {
private static final Challenges plugin = Challenges.getPlugin();
private static final List<UUID> deadPlayer = new ArrayList<>();
private final Map<Player, BossBar> bossBarMap = new HashMap<>();
public static Location spawnLocation;
private final Map<UUID, List<Location>> currentBarrier = new HashMap<>();
private static final Material barrierBlock = Material.GLASS;
private CancellableTask task;
private static final NamespacedKey UUIDKey = new NamespacedKey(Challenges.getPlugin(), "uuid_deathrun");
private final Random random = new Random();
private static World world;
@EventHandler
public void onMove(PlayerMoveEvent e) {
Player p = e.getPlayer();
World world = p.getWorld();
if (Objects.equals(Config.getString("event.type"), "deathrun") && plugin.getTimer().isRunning() && !plugin.getVanish().isVanish(p) && p.getGameMode() != GameMode.SPECTATOR) {
if (!deadPlayer.contains(p.getUniqueId())) {
Location playerLoc = p.getLocation();
double posRelatedToCenter = playerLoc.getZ() - spawnLocation.getZ();
double distanceToCenter = Math.abs(posRelatedToCenter);
List<Location> barrierLocations = currentBarrier.computeIfAbsent(p.getUniqueId(), uuid -> new ArrayList<>());
int BARRIER_POS = 15;
int BARRIER_SIZE = 6;
if (distanceToCenter >= BARRIER_POS) { //Outside border
ChatUtil.sendTitleToPlayer(p, Component.text("⟲ ⟲ ⟲ ⟲", Style.style(ChatUtil.RED, TextDecoration.BOLD)), Component.text("Kehre zurück auf die Strecke!", ChatUtil.RED), 0, 20, 0, true);
resetBarrier(p, world, barrierLocations);
CoordsTargeter.addLocation(p.getUniqueId(), new Location(p.getWorld(), p.getLocation().getX(), p.getLocation().getY(), spawnLocation.getZ()));
if (distanceToCenter >= BARRIER_POS + 3) {
p.damage(0.5);
}
} else if (distanceToCenter >= BARRIER_POS - 6) { //Inside border
//Send barrier
double barrierZ = spawnLocation.getZ() + BARRIER_POS * (posRelatedToCenter > 0 ? 1 : -1);
Location loc = new Location(p.getWorld(), playerLoc.getX() - BARRIER_SIZE / 2d, playerLoc.getY() - BARRIER_SIZE / 2d, barrierZ);
resetBarrier(p, world, barrierLocations);
if (CoordsTargeter.containsLocation(p.getUniqueId())) {
CoordsTargeter.removeLocation(p.getUniqueId());
}
for (int y = 0; y < BARRIER_SIZE; y++) {
for (int x = 0; x < BARRIER_SIZE; x++) {
Location blockLoc = loc.clone().add(x, y, 0);
if (!world.getBlockAt(blockLoc).isSolid()) {
barrierLocations.add(blockLoc);
p.sendBlockChange(blockLoc, Bukkit.createBlockData(barrierBlock));
}
}
}
} else if (!barrierLocations.isEmpty()) { //Away from border
resetBarrier(p, world, barrierLocations);
}
if (playerLoc.getY() > world.getMaxHeight() - 1) {
p.damage(1);
}
}
}
}
@EventHandler
public void onDamage(EntityDamageEvent e) {
if (e.getEntity() instanceof Player p) {
if (Objects.equals(Config.getString("event.type"), "deathrun") && plugin.getTimer().isRunning()) {
plugin.getScheduler().runDelayedOnEntity(p, () -> p.setWalkSpeed((float) (p.getHealth() / 100f)), 1L);
}
}
}
@EventHandler
public void onDeath(PlayerDeathEvent e) {
Player p = e.getEntity();
if (Objects.equals(Config.getString("event.type"), "deathrun") && plugin.getTimer().isRunning()) {
deadPlayer.add(p.getUniqueId());
CoordsTargeter.removeLocation(e.getPlayer().getUniqueId());
plugin.getSBManager().removeScoreboard(p);
if (plugin.getSBManager().countScoreboardPlayers() == 0) {
plugin.getTimer().stopTimer();
ChatUtil.sendBrodCastMessage(Component.text("Das Event wurde beendet, da alle Spieler gestorben sind!", ChatUtil.GREEN));
}
}
}
@EventHandler
public void onPlayerRespawn(PlayerRespawnEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
Location spawnlocation = world.getSpawnLocation();
e.setRespawnLocation(spawnlocation);
}
}
@EventHandler
public void onPortalUse(PlayerPortalEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
e.setCancelled(true);
}
}
@EventHandler
public void onTimerChange(TimerChangedEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
if (!e.isRunning()) {
for (Player player : Bukkit.getOnlinePlayers()) {
CoordsTargeter.removeLocation(player.getUniqueId());
}
stopUpdateTask();
for (Scorable scorable : plugin.getScoreManager().getByPositon(1)) {
OfflinePlayer p = (Bukkit.getServer().getOfflinePlayer(scorable.getName()));
if (p.isOnline()) {
Component[] lines = {Component.empty(), Component.empty(), Component.empty(), Component.text(" " + p.getName(), ChatUtil.BLUE), Component.text(" ist " + plugin.getScoreManager().getScore(scorable) + " Blöcke gelaufen und", ChatUtil.GREEN), Component.text(" hat damit gewonnen!", ChatUtil.GREEN), Component.empty(), Component.empty()};
ChatSkull.sendAll((Player) p, lines);
} else {
ChatUtil.sendBrodCastMessage(Component.text(scorable.getName(), ChatUtil.BLUE).append(Component.text(" ist " + plugin.getScoreManager().getScore(scorable) + " Blöcke gelaufen und hat damit gewonnen!", ChatUtil.GREEN)));
}
}
} else {
plugin.getScheduler().run(() -> world.setGameRule(GameRule.MAX_ENTITY_CRAMMING, Bukkit.getServer().getMaxPlayers()));
plugin.getScheduler().runGlobalDelayed(() -> {
for (Player pp : Bukkit.getOnlinePlayers()) {
plugin.getScoreManager().join(new ChallengePlayer(pp.getUniqueId()));
Location spawn = world.getSpawnLocation();
spawn.setYaw(-90f);
pp.teleportAsync(spawn);
}
}, 1L);
startUpdateTask();
}
}
}
@EventHandler
public void onBarrierBreak(BlockBreakEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
Block block = e.getBlock();
Location loc = block.getLocation();
int x = loc.getBlockX();
int z = loc.getBlockZ();
int spawnX = spawnLocation.getBlockX();
int spawnZ = spawnLocation.getBlockZ();
int distanceX = spawnX - x;
if (distanceX > -15 && (Math.abs(distanceX) >= 15 || Math.abs(spawnZ - z) >= 15)) {
ChatUtil.sendWarningMessage(e.getPlayer(), "Niemand hat die Absicht, eine Mauer zu errichten!");
e.setCancelled(true);
}
}
}
@EventHandler
public void onBoatMount(EntityMountEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
if (e.getMount() instanceof Boat boat && e.getEntity() instanceof Player player) {
PersistentDataContainer container = boat.getPersistentDataContainer();
if (container.get(UUIDKey, PersistentDataType.STRING) != null && !container.get(UUIDKey, PersistentDataType.STRING).equals(player.getUniqueId().toString())) {
ChatUtil.sendWarningMessage(player, "Du kannst nur in deine eigenen Boote einsteigen.");
e.setCancelled(true);
}
}
}
}
@EventHandler
public void onVehiclePlace(EntityPlaceEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
if (e.getEntity() instanceof Boat boat) {
if (e.getPlayer() != null) {
PersistentDataContainer container = boat.getPersistentDataContainer();
container.set(UUIDKey, PersistentDataType.STRING, e.getPlayer().getUniqueId().toString());
}
}
}
}
@EventHandler
public void onBoatBreak(VehicleDamageEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
if (e.getVehicle() instanceof Boat boat && e.getAttacker() instanceof Player player) {
PersistentDataContainer container = boat.getPersistentDataContainer();
if (container.get(UUIDKey, PersistentDataType.STRING) != null && !container.get(UUIDKey, PersistentDataType.STRING).equals(player.getUniqueId().toString())) {
ChatUtil.sendWarningMessage(player, "Du kannst nur deine eignen Boote zerstören.");
e.setCancelled(true);
}
}
}
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
if (deadPlayer.contains(e.getPlayer().getUniqueId())) {
plugin.getSBManager().setScoreboardtoPlayer(e.getPlayer());
}
}
}
@EventHandler
public void onPlayerLeave(PlayerQuitEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun")) {
CoordsTargeter.removeLocation(e.getPlayer().getUniqueId());
}
}
@EventHandler
public void onPlantBreak(BlockBreakEvent e) {
if (Objects.equals(Config.getString("event.type"), "deathrun") && plugin.getTimer().isRunning()) {
if (e.getBlock().getType() != Material.SHORT_GRASS) {
return;
}
if (random.nextInt(10) == 0) {
e.getBlock().getWorld().dropItem(e.getBlock().getLocation(), new ItemStack(Material.MELON_SLICE));
}
}
}
public static void load(Player player) {
world = player.getWorld();
spawnLocation = world.getSpawnLocation();
int height = world.getMaxHeight() - world.getMinHeight();
int width = 30;
Location loc = spawnLocation.clone().subtract(15, 0, 15);
loc.setY(world.getMinHeight());
for (int y = 0; y < height; y++) {
for (int z = 0; z < width; z++) {
Block block = loc.clone().add(0, y, z).getBlock();
if (!block.isSolid()) {
block.setType(barrierBlock);
}
}
}
mexico(15, world);
mexico(-15, world);
}
private void resetBarrier(Player p, World world, List<Location> locations) {
for (Location location : locations) {
p.sendBlockChange(location, world.getBlockAt(location).getBlockData());
}
locations.clear();
}
private static void mexico(int startPos, World world) {
int height = world.getMaxHeight() - world.getMinHeight();
int width = 30;
Location loc = spawnLocation.clone().add(-15, 0, startPos);
loc.setY(world.getMinHeight());
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Block block = loc.clone().add(x, y, 0).getBlock();
if (!block.isSolid()) {
block.setType(barrierBlock);
}
}
}
}
public static List<UUID> getDeadPlayer() {
return deadPlayer;
}
public void startUpdateTask() {
task = plugin.getScheduler().runGlobalAtFixedRate(this::updateScoreAndBossBar, 1L, 1L);
}
public void stopUpdateTask() {
if (task != null) {
task.cancel();
task = null;
}
}
private void updateScoreAndBossBar() {
if (Objects.equals(Config.getString("event.type"), "deathrun") && plugin.getTimer().isRunning()) {
ScoreManager scoreManager = plugin.getScoreManager();
for (Player p : plugin.getVanish().getPlayerListWithoutVanishPlayers()) {
if (!plugin.getVanish().isVanish(p)) {
if (!deadPlayer.contains(p.getUniqueId()) && p.getGameMode() != GameMode.SPECTATOR) {
int distance = (int) p.getLocation().toVector().subtract(p.getWorld().getSpawnLocation().toVector()).length();
scoreManager.setScore(new ChallengePlayer(p.getUniqueId()), distance);
}
Component component = Component.text("Position: ", ChatUtil.GREEN).append(Component.text(scoreManager.getPosition(new ChallengePlayer(p.getUniqueId())), ChatUtil.BLUE).append(Component.text(" | ", ChatUtil.RED).append(deadPlayer.contains(p.getUniqueId()) ? Component.text("† ", Style.style(ChatUtil.RED, TextDecoration.BOLD)) : Component.empty())).append(Component.text("Distanz zum Spawn: ", ChatUtil.GREEN).append(Component.text(scoreManager.getScore(new ChallengePlayer(p.getUniqueId())) + " Blöcke", ChatUtil.BLUE))));
BossBar bossBar = bossBarMap.computeIfAbsent(p, player -> {
BossBar newBossBar = BossBar.bossBar(component, 1.0f, BossBar.Color.GREEN, BossBar.Overlay.PROGRESS);
newBossBar.addViewer(p);
return newBossBar;
});
bossBar.name(component);
}
}
}
}
}