Newer
Older
ChallengeSystem / src / main / java / de / fanta / challenges / challenges / ChallengeEvents / DeathrunChallengeEvent.java
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);
                }
            }
        }
    }
}