Newer
Older
ChallengeSystem / Challenge / src / main / java / de / fanta / challenge / Timer.java
package de.fanta.challenge;

import de.fanta.challenge.events.TimerChangedEvent;
import de.fanta.challenge.utils.Config;
import de.fanta.challenge.utils.SaveWorldUtils;
import de.fanta.challengeutils.Color;
import de.iani.cubesideutils.StringUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.bukkit.Bukkit;
import org.bukkit.Effect;
import org.bukkit.GameMode;
import org.bukkit.World;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;
import java.util.TimeZone;
import java.util.logging.Level;

public class Timer {

    private final Challenge plugin;
    private BukkitTask actionBarTaskId;
    private BukkitTask idleActionBarTaskId;
    private long time; // milliseconds
    private TimerMode mode;
    private long countingSinceTimestamp;
    private Long targetDateTimestamp = null;

    private static final int[] baseColorsTimer = new int[]{0xFF6668, 0xFFB566, 0xFFED66, 0x66FF75, 0x66B8FF, 0xE666FF};
    private static final int[] baseColorsReserveTimer = new int[]{0x7FD4E0, 0x947DFF, 0xFF88F1, 0xFF7D7D};

    public Timer(Challenge plugin) {
        this.plugin = plugin;
        this.mode = TimerMode.UP;

        if (!isRunning()) {
            sendIdleActionBar();
        }
    }

    public void startTimer() {
        countingSinceTimestamp = System.currentTimeMillis();
        if (idleActionBarTaskId != null) {
            idleActionBarTaskId.cancel();
            idleActionBarTaskId = null;
        }
        actionBarTaskId = plugin.getServer().getScheduler().runTaskTimer(plugin, () -> {
            switch (mode) {
                case DOWN -> {
                    if (time <= 0) {
                        handleTimerEnd();
                    } else {
                        updateDownTimer();
                    }
                }
                case UP -> updateUpTimer();
                case DATE -> updateDateTimer();
            }
        }, 1L, 1L);

        if (!Config.getBoolean("firsttimerstart")) {
            Config.setValue("firsttimerstart", true, false);
        }
        Bukkit.getPluginManager().callEvent(new TimerChangedEvent(true));
    }

    private void handleTimerEnd() {
        if (Config.getBoolean("event.enabled")) {
            for (Player p : Bukkit.getOnlinePlayers()) {
                p.setGameMode(GameMode.SPECTATOR);
            }
            plugin.getComponentUtil().sendTitleToAll(Component.text("Event"), Component.text("Die Zeit ist abgelaufen!", Color.GREEN));
        } else {
            for (Player p : Bukkit.getOnlinePlayers()) {
                p.setGameMode(GameMode.SPECTATOR);
            }
            plugin.getComponentUtil().sendTitleToAll(Component.text("ZEIT ABGELAUFEN!", Color.RED), Component.text("Challenge fehlgeschlagen", Color.RED));
            plugin.getComponentUtil().sendBrodCastMessage(Component.text("Die Zeit ist abgelaufen und die Challenge somit fehlgeschlagen!", Color.RED));
            Optional<World> optionalWorld = Bukkit.getWorlds().stream().findFirst();
            optionalWorld.ifPresent(world -> plugin.getComponentUtil().sendBrodCastMessage(Component.text("Seed: ", Color.GREEN).append(Component.text(world.getSeed(), Color.BLUE))));
        }
        stopTimer();
    }

    private void updateDownTimer() {
        time -= (System.currentTimeMillis() - countingSinceTimestamp);
        countingSinceTimestamp = System.currentTimeMillis();
        sendTimerActionBar();
    }

    private void updateUpTimer() {
        time += (System.currentTimeMillis() - countingSinceTimestamp);
        countingSinceTimestamp = System.currentTimeMillis();
        sendTimerActionBar();
    }

    private void updateDateTimer() {
        time = targetDateTimestamp - System.currentTimeMillis();
        if (time <= 0) {
            handleTimerEnd();
        } else {
            sendTimerActionBar();
        }
    }

    public void stopTimer() {
        if (actionBarTaskId != null) {
            actionBarTaskId.cancel();
            actionBarTaskId = null;
            sendIdleActionBar();
        }
        plugin.getServer().getScheduler().runTaskLater(plugin, () -> {
            for (Player pp : Bukkit.getOnlinePlayers()) {
                for (final Entity entity : pp.getNearbyEntities(100, 100, 100)) {
                    if ((entity instanceof final Creature creature)) {
                        if ((creature.getTarget() != null) && creature.getTarget().equals(pp)) {
                            creature.setTarget(null);
                        }
                    }
                }
            }
        }, 1);
        Bukkit.getPluginManager().callEvent(new TimerChangedEvent(false));
    }

    private void sendIdleActionBar() {
        idleActionBarTaskId = plugin.getServer().getScheduler().runTaskTimer(plugin, () -> {
            for (Player p : Bukkit.getOnlinePlayers()) {
                if (!plugin.getVanish().isVanish(p)) {
                    sendFormatedTimerActionBarToPlayer(p, Component.text("Der Timer ist pausiert.", Style.style(Color.RED, TextDecoration.BOLD)));
                    if (p.getGameMode() != GameMode.SPECTATOR) {
                        p.getWorld().playEffect(p.getLocation(), Effect.ENDER_SIGNAL, 2);
                    }
                }
            }
        }, 20L, 20L);
    }

    private void sendTimerActionBar() {
        for (Player p : Bukkit.getOnlinePlayers()) {
            if (!plugin.getVanish().isVanish(p)) {
                if (Config.getBoolean("showtimer")) {
                    sendFormatedTimerActionBarToPlayer(p, formateRainbowTime(!Config.getBoolean("editsettings")));
                }
            }
        }
    }

    private void sendFormatedTimerActionBarToPlayer(Player player, Component text) {
        player.sendActionBar(text.append(SaveWorldUtils.isCopyWorld ? Component.text(" Save: ", Color.GREEN).append(SaveWorldUtils.getProgressBar(SaveWorldUtils.progress)) : Component.empty()));
    }

    public Component formatTime(TextColor color) {
        return Component.text(formatTime()).color(color);
    }

    public String formatTime() {
        String formatTime = StringUtil.formatTimespan((time / 1000) * 1000, " Tage, ", ":", "", "", "", ":", false, true);
        if (formatTime.startsWith("1 Tage")) {
            return StringUtil.formatTimespan((time / 1000) * 1000, " Tag, ", ":", "", "", "", ":", false, true);
        }
        return formatTime;
    }

    public Component formateRainbowTime(boolean speedRun) {
        return plugin.getColorUtils().addChatColorToString((speedRun ? "SpeedRun " : "") + formatTime(), mode == TimerMode.UP ? baseColorsTimer : baseColorsReserveTimer, 1.5);
    }

    public void reverseTimer() {
        if (mode == TimerMode.DOWN) {
            mode = TimerMode.UP;
        } else {
            mode = TimerMode.DOWN;
        }
        if (isRunning()) {
            countingSinceTimestamp = System.currentTimeMillis();
        }
    }

    public boolean isRunning() {
        return actionBarTaskId != null;
    }

    public boolean isReverse() {
        return mode == TimerMode.DOWN;
    }

    public long getTime() {
        return time / 1000;
    }

    public long getTimeInMs() {
        return time;
    }

    public void setTime(int seconds) {
        this.time = seconds * 1000L;
        this.countingSinceTimestamp = System.currentTimeMillis();
    }

    public boolean setDate(String targetDate) {
        SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        try {
            Date date = sdf.parse(targetDate);
            this.targetDateTimestamp = date.getTime();
            this.time = targetDateTimestamp - System.currentTimeMillis();
            this.mode = TimerMode.DATE;
            return true;
        } catch (ParseException e) {
            plugin.getLogger().log(Level.SEVERE, "Error while parsing Date", e);
        }
        return false;
    }

    public void addTime(int seconds) {
        if (mode != TimerMode.DATE) {
            this.time = Math.max(time + (seconds * 1000L), 0);
            this.countingSinceTimestamp = System.currentTimeMillis();
            Config.setValue("editsettings", true);
        }
    }

    public void removeTime(int seconds) {
        if (mode != TimerMode.DATE) {
            this.time = Math.max(time - (seconds * 1000L), 0);
            this.countingSinceTimestamp = System.currentTimeMillis();
            Config.setValue("editsettings", true);
        }
    }

    public TimerMode getMode() {
        return mode;
    }

    public void setMode(TimerMode mode) {
        this.mode = mode;
    }

    public Long getTargetDateTimestamp() {
        return targetDateTimestamp;
    }

    public void setTargetDateTimestamp(Long targetDateTimestamp) {
        this.targetDateTimestamp = targetDateTimestamp;
        this.time = this.targetDateTimestamp - System.currentTimeMillis();
    }

    public enum TimerMode {
        UP,
        DOWN,
        DATE
    }
}