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
}
}