aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker/skyblock/end
diff options
context:
space:
mode:
authorviciscat <51047087+viciscat@users.noreply.github.com>2024-02-15 21:13:19 +0100
committerGitHub <noreply@github.com>2024-02-15 15:13:19 -0500
commit9c106069c2775cafc43da402348d7f35d3a2d67b (patch)
tree895e73757b044e5cbc21f6c49ad644431663b5d0 /src/main/java/de/hysky/skyblocker/skyblock/end
parent41d02daf5f53a761043ae76f5be484f94ceb0128 (diff)
downloadSkyblocker-9c106069c2775cafc43da402348d7f35d3a2d67b.tar.gz
Skyblocker-9c106069c2775cafc43da402348d7f35d3a2d67b.tar.bz2
Skyblocker-9c106069c2775cafc43da402348d7f35d3a2d67b.zip
Add End HUD Widget (#524)
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock/end')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/end/EndHudConfigScreen.java35
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java65
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/end/TheEnd.java249
3 files changed, 349 insertions, 0 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudConfigScreen.java
new file mode 100644
index 00000000..2502afd7
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudConfigScreen.java
@@ -0,0 +1,35 @@
+package de.hysky.skyblocker.skyblock.end;
+
+import de.hysky.skyblocker.config.HudConfigScreen;
+import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+
+public class EndHudConfigScreen extends HudConfigScreen {
+ public EndHudConfigScreen(Screen parent) {
+ super(Text.literal("End HUD Config"), EndHudWidget.INSTANCE, parent);
+ }
+
+ @Override
+ protected int[] getPosFromConfig(SkyblockerConfig config) {
+ return new int[]{
+ config.locations.end.x,
+ config.locations.end.y,
+ };
+ }
+
+ @Override
+ protected void savePos(SkyblockerConfig configManager, int x, int y) {
+ configManager.locations.end.x = x;
+ configManager.locations.end.y = y;
+ }
+
+ @Override
+ protected void renderWidget(DrawContext context, int x, int y) {
+ EndHudWidget.INSTANCE.setX(x);
+ EndHudWidget.INSTANCE.setY(y);
+ EndHudWidget.INSTANCE.render(context, SkyblockerConfigManager.get().locations.end.enableBackground);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java
new file mode 100644
index 00000000..59a637dd
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/end/EndHudWidget.java
@@ -0,0 +1,65 @@
+package de.hysky.skyblocker.skyblock.end;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.tabhud.widget.Widget;
+import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import net.minecraft.enchantment.Enchantments;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.text.NumberFormat;
+
+public class EndHudWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("The End").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD);
+
+ public static final EndHudWidget INSTANCE = new EndHudWidget(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ public EndHudWidget(MutableText title, Integer colorValue) {
+ super(title, colorValue);
+ this.setX(5);
+ this.setY(5);
+ this.update();
+ }
+
+ private static final ItemStack ENDERMAN_HEAD = new ItemStack(Items.PLAYER_HEAD);
+ private static final ItemStack POPPY = new ItemStack(Items.POPPY);
+
+ static {
+ ENDERMAN_HEAD.getOrCreateNbt().putString("SkullOwner", "MHF_Enderman");
+ POPPY.addEnchantment(Enchantments.INFINITY, 1);
+
+ INSTANCE.setX(SkyblockerConfigManager.get().locations.end.x);
+ INSTANCE.setY(SkyblockerConfigManager.get().locations.end.y);
+ }
+
+
+ @Override
+ public void updateContent() {
+ // Zealots
+ addComponent(new IcoTextComponent(ENDERMAN_HEAD, Text.literal("Zealots").formatted(Formatting.BOLD)));
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.zealotsSinceLastEye", TheEnd.zealotsSinceLastEye)));
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.zealotsTotalKills", TheEnd.zealotsKilled)));
+ NumberFormat instance = NumberFormat.getInstance();
+ instance.setMinimumFractionDigits(0);
+ instance.setMaximumFractionDigits(2);
+ String avg = TheEnd.eyes == 0 ? "???" : instance.format((float)TheEnd.zealotsKilled / TheEnd.eyes);
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.avgKillsPerEye", avg)));
+
+ // Endstone protector
+ addComponent(new IcoTextComponent(POPPY, Text.literal("Endstone Protector").formatted(Formatting.BOLD)));
+ if (TheEnd.stage == 5) {
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.stage", "IMMINENT")));
+ } else {
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.stage", String.valueOf(TheEnd.stage))));
+ }
+ if (TheEnd.currentProtectorLocation == null) {
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.location", "?")));
+ } else {
+ addComponent(new PlainTextComponent(Text.translatable("skyblocker.end.hud.location", TheEnd.currentProtectorLocation.name())));
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/TheEnd.java b/src/main/java/de/hysky/skyblocker/skyblock/end/TheEnd.java
new file mode 100644
index 00000000..1db27769
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/end/TheEnd.java
@@ -0,0 +1,249 @@
+package de.hysky.skyblocker.skyblock.end;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.waypoint.Waypoint;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
+import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
+import net.minecraft.block.Blocks;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.entity.mob.EndermanEntity;
+import net.minecraft.text.Text;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.ChunkPos;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+public class TheEnd {
+ protected static final Logger LOGGER = LoggerFactory.getLogger(TheEnd.class);
+
+ public static Set<UUID> hitZealots = new HashSet<>();
+ public static int zealotsSinceLastEye = 0;
+ public static int zealotsKilled = 0;
+ public static int eyes = 0;
+ /**
+ * needs to be saved?
+ */
+ private static boolean dirty = false;
+ private static String currentProfile = "";
+ private static JsonObject PROFILES_STATS;
+
+ private static final Path FILE = SkyblockerMod.CONFIG_DIR.resolve("end.json");
+ public static List<ProtectorLocation> protectorLocations = List.of(
+ new ProtectorLocation(-649, -219, Text.translatable("skyblocker.end.hud.protectorLocations.left")),
+ new ProtectorLocation(-644, -269, Text.translatable("skyblocker.end.hud.protectorLocations.front")),
+ new ProtectorLocation(-689, -273, Text.translatable("skyblocker.end.hud.protectorLocations.center")),
+ new ProtectorLocation(-727, -284, Text.translatable("skyblocker.end.hud.protectorLocations.back")),
+ new ProtectorLocation(-639, -328, Text.translatable("skyblocker.end.hud.protectorLocations.rightFront")),
+ new ProtectorLocation(-678, -332, Text.translatable("skyblocker.end.hud.protectorLocations.rightBack"))
+ );
+
+ public static ProtectorLocation currentProtectorLocation = null;
+ public static int stage = 0;
+
+ public static void init() {
+ AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
+ if (entity instanceof EndermanEntity enderman && isZealot(enderman)) {
+ hitZealots.add(enderman.getUuid());
+ }
+ return ActionResult.PASS;
+ });
+
+
+ HudRenderCallback.EVENT.register((drawContext, tickDelta) -> {
+ if (!Utils.isInTheEnd()) return;
+ if (!SkyblockerConfigManager.get().locations.end.hudEnabled) return;
+
+ EndHudWidget.INSTANCE.render(drawContext, SkyblockerConfigManager.get().locations.end.enableBackground);
+ });
+
+ ClientChunkEvents.CHUNK_LOAD.register((world, chunk) -> {
+ String lowerCase = Utils.getIslandArea().toLowerCase();
+ if (Utils.isInTheEnd() || lowerCase.contains("the end") || lowerCase.contains("dragon's nest")) {
+ ChunkPos pos = chunk.getPos();
+ //
+ Box box = new Box(pos.getStartX(), 0, pos.getStartZ(), pos.getEndX(), 1, pos.getEndZ());
+ locationsLoop: for (ProtectorLocation protectorLocation : protectorLocations) {
+ if (box.contains(protectorLocation.x, 0.5, protectorLocation.z)) {
+ //MinecraftClient.getInstance().player.sendMessage(Text.literal("Checking: ").append(protectorLocation.name));//MinecraftClient.getInstance().player.sendMessage(Text.literal(pos.getStartX() + " " + pos.getStartZ() + " " + pos.getEndX() + " " + pos.getEndZ()));
+ for (int i = 0; i < 5; i++) {
+ if (world.getBlockState(new BlockPos(protectorLocation.x, i+5, protectorLocation.z)).isOf(Blocks.PLAYER_HEAD)) {
+ stage = i + 1;
+ currentProtectorLocation = protectorLocation;
+ EndHudWidget.INSTANCE.update();
+ break locationsLoop;
+ }
+ }
+ }
+ }
+ if (currentProfile.isEmpty()) load(); // Wacky fix for when you join skyblock, and you are directly in the end (profile id isn't parsed yet most of the time)
+ }
+
+
+ });
+ // Reset when changing island
+ // TODO: Replace when a changed island event is added
+ ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
+ resetLocation();
+ save();
+ load();
+ });
+ // Save when leaving as well
+ ClientLifecycleEvents.CLIENT_STOPPING.register((client) -> save());
+
+ ClientReceiveMessageEvents.GAME.register((message, overlay) -> {
+ if (Utils.isInTheEnd()) return;
+ String lowerCase = message.getString().toLowerCase();
+ if (lowerCase.contains("tremor") && stage != 0) stage += 1; // TODO: If stage is 0 re-scan.
+ else if (lowerCase.contains("rises from below")) stage = 5;
+ else if (lowerCase.contains("protector down") || lowerCase.contains("has risen")) resetLocation();
+ else return;
+ EndHudWidget.INSTANCE.update();
+ });
+
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(TheEnd::renderWaypoint);
+ ClientLifecycleEvents.CLIENT_STARTED.register((client -> loadFile()));
+ }
+
+ private static void resetLocation() {
+ stage = 0;
+ currentProtectorLocation = null;
+ }
+
+ public static void onEntityDeath(Entity entity) {
+ if (!(entity instanceof EndermanEntity enderman) || !isZealot(enderman)) return;
+ if (hitZealots.contains(enderman.getUuid())) {
+ //MinecraftClient.getInstance().player.sendMessage(Text.literal("You killed a zealot!!!"));
+ if (isSpecialZealot(enderman)) {
+ zealotsSinceLastEye = 0;
+ eyes++;
+ }
+ else zealotsSinceLastEye++;
+ zealotsKilled++;
+ dirty = true;
+ hitZealots.remove(enderman.getUuid());
+ EndHudWidget.INSTANCE.update();
+ }
+ }
+
+ public static boolean isZealot(EndermanEntity enderman) {
+ if (enderman.getName().getString().toLowerCase().contains("zealot")) return true; // Future-proof. If they someday decide to actually rename the entities
+ assert MinecraftClient.getInstance().world != null;
+ List<ArmorStandEntity> entities = MinecraftClient.getInstance().world.getEntitiesByClass(
+ ArmorStandEntity.class,
+ enderman.getDimensions(null).getBoxAt(enderman.getPos()).expand(1),
+ armorStandEntity -> armorStandEntity.getName().getString().toLowerCase().contains("zealot"));
+ if (entities.isEmpty()) {
+ return false;
+ }
+ return entities.get(0).getName().getString().toLowerCase().contains("zealot");
+ }
+
+ public static boolean isSpecialZealot(EndermanEntity enderman) {
+ return isZealot(enderman) && enderman.getCarriedBlock() != null && enderman.getCarriedBlock().isOf(Blocks.END_PORTAL_FRAME);
+ }
+
+ /**
+ * Loads if needed
+ */
+ public static void load() {
+ if (!Utils.isOnSkyblock() || Utils.getProfileId().isEmpty()) return;
+ String id = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", "");
+ String profile = Utils.getProfileId();
+ if (!profile.equals(currentProfile) && PROFILES_STATS != null) {
+ currentProfile = profile;
+ JsonElement jsonElement = PROFILES_STATS.get(id);
+ if (jsonElement == null) return;
+ JsonElement jsonElement1 = jsonElement.getAsJsonObject().get(profile);
+ if (jsonElement1 == null) return;
+ zealotsKilled = jsonElement1.getAsJsonObject().get("totalZealotKills").getAsInt();
+ zealotsSinceLastEye = jsonElement1.getAsJsonObject().get("zealotsSinceLastEye").getAsInt();
+ eyes = jsonElement1.getAsJsonObject().get("eyes").getAsInt();
+ EndHudWidget.INSTANCE.update();
+ }
+ }
+
+ private static void loadFile() {
+ CompletableFuture.runAsync(() -> {
+ try (BufferedReader reader = Files.newBufferedReader(FILE)) {
+ PROFILES_STATS = SkyblockerMod.GSON.fromJson(reader, JsonObject.class);
+ LOGGER.debug("[Skyblocker End] Loaded end stats");
+ } catch (NoSuchFileException ignored) {
+ PROFILES_STATS = new JsonObject();
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker End] Failed to load end stats", e);
+ }
+ });
+ }
+
+ /**
+ * Saves if dirty
+ */
+ public static void save() {
+ if (dirty && PROFILES_STATS != null) {
+ String uuid = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", "");
+ JsonObject jsonObject = PROFILES_STATS.getAsJsonObject(uuid);
+ if (jsonObject == null) {
+ PROFILES_STATS.add(uuid, new JsonObject());
+ jsonObject = PROFILES_STATS.getAsJsonObject(uuid);
+ }
+
+ jsonObject.add(currentProfile, new JsonObject());
+ JsonElement jsonElement1 = jsonObject.get(currentProfile);
+
+ jsonElement1.getAsJsonObject().addProperty("totalZealotKills", zealotsKilled);
+ jsonElement1.getAsJsonObject().addProperty("zealotsSinceLastEye", zealotsSinceLastEye);
+ jsonElement1.getAsJsonObject().addProperty("eyes", eyes);
+
+ if (Utils.isOnSkyblock()) {
+ CompletableFuture.runAsync(TheEnd::performSave);
+ } else {
+ performSave();
+ }
+ }
+ }
+
+ private static void performSave() {
+ try (BufferedWriter writer = Files.newBufferedWriter(FILE)) {
+ SkyblockerMod.GSON.toJson(PROFILES_STATS, writer);
+ LOGGER.info("[Skyblocker End] Saved end stats");
+ dirty = false;
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker End] Failed to save end stats", e);
+ }
+ }
+
+ private static void renderWaypoint(WorldRenderContext context) {
+ if (!SkyblockerConfigManager.get().locations.end.waypoint) return;
+ if (currentProtectorLocation == null || stage != 5) return;
+ currentProtectorLocation.waypoint().render(context);
+ }
+
+ public record ProtectorLocation(int x, int z, Text name, Waypoint waypoint) {
+ public ProtectorLocation(int x, int z, Text name) {
+ this(x, z, name, new Waypoint(new BlockPos(x, 0, z), Waypoint.Type.WAYPOINT, DyeColor.MAGENTA.getColorComponents()));
+ }
+ }
+}