aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorolim <bobq4582@gmail.com>2024-01-30 12:28:25 +0000
committerolim <bobq4582@gmail.com>2024-01-30 12:28:25 +0000
commitfb583f4d085e4b34609e24399a72701210bb682c (patch)
treeefbd102fe3b27380db54f39ee1754bb34dcbbae5 /src
parent81e17a23645f429f91f77663928372bdf0833e16 (diff)
downloadSkyblocker-fb583f4d085e4b34609e24399a72701210bb682c.tar.gz
Skyblocker-fb583f4d085e4b34609e24399a72701210bb682c.tar.bz2
Skyblocker-fb583f4d085e4b34609e24399a72701210bb682c.zip
start of crystal hollows fetures
basic implementation of a map and way-points features for the crystal hollows
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java30
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java47
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java101
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHudConfigScreen.java64
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java171
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java149
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Utils.java6
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json3
-rw-r--r--src/main/resources/assets/skyblocker/textures/gui/crystals_map.pngbin0 -> 39156 bytes
10 files changed, 576 insertions, 1 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index 3fca09ce..c11e4c86 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -13,6 +13,8 @@ import de.hysky.skyblocker.skyblock.dungeon.puzzle.TicTacToe;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard.Waterboard;
import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
import de.hysky.skyblocker.skyblock.dungeon.secrets.SecretsTracker;
+import de.hysky.skyblocker.skyblock.dwarven.CrystalsHud;
+import de.hysky.skyblocker.skyblock.dwarven.CrystalsLocationsManager;
import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud;
import de.hysky.skyblocker.skyblock.end.BeaconHighlighter;
import de.hysky.skyblocker.skyblock.item.*;
@@ -99,6 +101,8 @@ public class SkyblockerMod implements ClientModInitializer {
QuickNav.init();
ItemCooldowns.init();
DwarvenHud.init();
+ CrystalsHud.init();
+ CrystalsLocationsManager.init();
ChatMessageListener.init();
Shortcuts.init();
DiscordRPCManager.init();
@@ -142,6 +146,8 @@ public class SkyblockerMod implements ClientModInitializer {
Scheduler.INSTANCE.scheduleCyclic(LividColor::update, 10);
Scheduler.INSTANCE.scheduleCyclic(BackpackPreview::tick, 50);
Scheduler.INSTANCE.scheduleCyclic(DwarvenHud::update, 40);
+ Scheduler.INSTANCE.scheduleCyclic(CrystalsHud::update, 40);
+ Scheduler.INSTANCE.scheduleCyclic(CrystalsLocationsManager::update, 40);
Scheduler.INSTANCE.scheduleCyclic(PlayerListMgr::updateList, 20);
}
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index a7569adb..886f81fb 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -873,6 +873,12 @@ public class SkyblockerConfig {
@SerialEntry
public DwarvenHud dwarvenHud = new DwarvenHud();
+
+ @SerialEntry
+ public CrystalsHud crystalsHud = new CrystalsHud();
+
+ @SerialEntry
+ public CrystalsWaypoints crystalsWaypoints = new CrystalsWaypoints();
}
public static class DwarvenHud {
@@ -891,6 +897,30 @@ public class SkyblockerConfig {
@SerialEntry
public int y = 10;
}
+ public static class CrystalsHud {
+ @SerialEntry
+ public boolean enabled = true;
+
+
+ @SerialEntry
+ public boolean enableBackground = true;
+
+ @SerialEntry
+ public int x = 10;
+
+ @SerialEntry
+ public int y = 50;
+ }
+ public static class CrystalsWaypoints {
+ @SerialEntry
+ public boolean enabled = true;
+
+ @SerialEntry
+ public boolean findInChat = true;
+
+ @SerialEntry
+ public Waypoint.Type waypointType = Waypoint.Type.WAYPOINT;
+ }
public enum DwarvenHudStyle {
SIMPLE, FANCY, CLASSIC;
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java
index 80d6485b..4a7e6854 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java
@@ -2,6 +2,7 @@ package de.hysky.skyblocker.config.categories;
import de.hysky.skyblocker.config.ConfigUtils;
import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.skyblock.dwarven.CrystalsHudConfigScreen;
import dev.isxander.yacl3.api.ButtonOption;
import dev.isxander.yacl3.api.ConfigCategory;
import dev.isxander.yacl3.api.Option;
@@ -53,7 +54,7 @@ public class DwarvenMinesCategory {
.build())
.option(Option.<SkyblockerConfig.DwarvenHudStyle>createBuilder()
.name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style"))
- .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]"),
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]"),
Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]"),
Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]")))
.binding(defaults.locations.dwarvenMines.dwarvenHud.style,
@@ -74,6 +75,50 @@ public class DwarvenMinesCategory {
.controller(ConfigUtils::createBooleanController)
.build())
.build())
+ //crystal HUD
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud")) //todo i do not know if i need to duplicate text
+ .collapsed(false)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled"))
+ .binding(defaults.locations.dwarvenMines.crystalsHud.enabled,
+ () -> config.locations.dwarvenMines.crystalsHud.enabled,
+ newValue -> config.locations.dwarvenMines.crystalsHud.enabled = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(ButtonOption.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.screen"))
+ .text(Text.translatable("text.skyblocker.open"))
+ .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new CrystalsHudConfigScreen(screen)))
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground"))
+ .binding(defaults.locations.dwarvenMines.crystalsHud.enableBackground,
+ () -> config.locations.dwarvenMines.crystalsHud.enableBackground,
+ newValue -> config.locations.dwarvenMines.crystalsHud.enableBackground = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .build())
+ //crystals waypoints
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints")) //todo i do not know if i need to duplicate text
+ .collapsed(false)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled"))
+ .binding(defaults.locations.dwarvenMines.crystalsWaypoints.enabled,
+ () -> config.locations.dwarvenMines.crystalsWaypoints.enabled,
+ newValue -> config.locations.dwarvenMines.crystalsWaypoints.enabled = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.findInChat"))
+ .binding(defaults.locations.dwarvenMines.crystalsWaypoints.findInChat,
+ () -> config.locations.dwarvenMines.crystalsWaypoints.findInChat,
+ newValue -> config.locations.dwarvenMines.crystalsWaypoints.findInChat = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+
+ .build())
.build();
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java
new file mode 100644
index 00000000..7ec9cefa
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java
@@ -0,0 +1,101 @@
+package de.hysky.skyblocker.skyblock.dwarven;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.scheduler.Scheduler;
+import it.unimi.dsi.fastutil.ints.IntIntPair;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.texture.atlas.Sprite;
+import net.minecraft.util.Identifier;
+import org.apache.commons.math3.analysis.UnivariateMatrixFunction;
+
+import java.awt.*;
+import java.util.Arrays;
+
+public class CrystalsHud {
+ public static final MinecraftClient client = MinecraftClient.getInstance();
+
+ protected static final Identifier MAP_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/crystals_map.png"); //todo is this the right place to store file
+
+ private static final Identifier MAP_ICON = new Identifier("textures/map/map_icons.png");
+
+ public static boolean visable = false;
+
+
+
+
+ public static void init() {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker")
+ .then(ClientCommandManager.literal("hud")
+ .then(ClientCommandManager.literal("crystals")
+ .executes(Scheduler.queueOpenScreenCommand(CrystalsHudConfigScreen::new))))));
+
+ HudRenderCallback.EVENT.register((context, tickDelta) -> {
+ if (!SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.enabled
+ || client.options.playerListKey.isPressed()
+ || client.player == null
+ || !visable) {
+ return;
+ }
+ render(context, SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.x,
+ SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.y);
+ });
+ }
+
+ public static IntIntPair getDimForConfig() {
+ return IntIntPair.of(62, 62);
+ }
+
+ public static void render( DrawContext context, int hudX, int hudY) {
+
+ if (SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.enableBackground) {
+ context.fill(hudX, hudY, hudX + 62, hudY + 62, 0x64000000);
+ }
+
+
+ //draw map texture
+ context.
+ drawTexture(MAP_TEXTURE,hudX,hudY,0,0,62,62,62,62);
+ //draw player on map
+ if (client.player == null || client.getNetworkHandler() == null) {
+ return;
+ }
+ //get player location
+ double playerX = client.player.getX();
+ double playerZ = client.player.getZ();
+ double facing = client.player.getYaw();
+ //map location to map
+ int renderX = (int)((playerX-202)/621 * 62);
+ int renderY = (int)((playerZ -202)/621 * 62);
+ int renderAngle = (int)(facing %360);
+ if (renderAngle < 0){//make sure the angle is always correct between 0 and 360
+ renderAngle = 360 + renderAngle;
+ }
+ //clamp location to map
+ renderX = Math.max(0, Math.min(62, renderX));
+ renderY = Math.max(0, Math.min(62, renderY));
+ //draw marker on map
+ context.
+ drawTexture(MAP_ICON,hudX+renderX,hudY+renderY,2,0,5,7,128,128);
+
+ //todo add direction and scale (could be wrong drawing methods) and offset to center on player
+
+ }
+
+ public static void update() {
+ if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.enabled) {
+ visable = false;
+ return;
+ }
+ //get if the player is in the crystals
+ visable = Utils.isInCrystals();
+
+
+ }
+
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHudConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHudConfigScreen.java
new file mode 100644
index 00000000..18be8bed
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHudConfigScreen.java
@@ -0,0 +1,64 @@
+package de.hysky.skyblocker.skyblock.dwarven;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import it.unimi.dsi.fastutil.ints.IntIntPair;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+
+import java.awt.*;
+
+public class CrystalsHudConfigScreen extends Screen {
+
+ private int hudX = SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.x;
+ private int hudY = SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.y;
+ private final Screen parent;
+
+ protected CrystalsHudConfigScreen() {
+ this(null);
+ }
+
+ public CrystalsHudConfigScreen(Screen parent) {
+ super(Text.of("Crystals HUD Config"));
+ this.parent = parent;
+ }
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ renderBackground(context, mouseX, mouseY, delta);
+ CrystalsHud.render( context, hudX, hudY);
+ context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB());
+ }
+
+ @Override
+ public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
+ IntIntPair dims = CrystalsHud.getDimForConfig();
+ if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + 200, hudY + 40) && button == 0) {
+ hudX = (int) Math.max(Math.min(mouseX - (double) dims.leftInt() / 2, this.width - dims.leftInt()), 0);
+ hudY = (int) Math.max(Math.min(mouseY - (double) dims.rightInt() / 2, this.height - dims.rightInt()), 0);
+ }
+ return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (button == 1) {
+ IntIntPair dims = CrystalsHud.getDimForConfig();
+ hudX = this.width / 2 - dims.leftInt();
+ hudY = this.height / 2 - dims.rightInt();
+ }
+ return super.mouseClicked(mouseX, mouseY, button);
+ }
+
+ @Override
+ public void close() {
+ SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.x = hudX;
+ SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.y = hudY;
+ SkyblockerConfigManager.save();
+
+ client.setScreen(parent);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java
new file mode 100644
index 00000000..d9f31f1d
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java
@@ -0,0 +1,171 @@
+package de.hysky.skyblocker.skyblock.dwarven;
+
+import com.mojang.authlib.GameProfile;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.scheduler.Scheduler;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.command.CommandRegistryAccess;
+import net.minecraft.command.argument.BlockPosArgumentType;
+import net.minecraft.command.argument.DefaultPosArgument;
+import net.minecraft.command.argument.PosArgument;
+import net.minecraft.network.message.MessageType;
+import net.minecraft.network.message.SignedMessage;
+import net.minecraft.server.command.ServerCommandSource;
+import net.minecraft.text.ClickEvent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.math.BlockPos;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.mojang.brigadier.arguments.StringArgumentType.getString;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class CrystalsLocationsManager {
+ public static final MinecraftClient client = MinecraftClient.getInstance();
+ public static Map<String,CrystalsWaypoint> ActiveWaypoints= new HashMap<>() {};
+
+ public static final Map<String, CrystalsWaypoint.Category> WAYPOINTLOCATIONS = Map.of(
+ "Jungle Temple", CrystalsWaypoint.Category.JUNGLETEMPLE,
+ "Mines Of Divan", CrystalsWaypoint.Category.MINESOFDIVAN,
+ "Goblin Queen's Den", CrystalsWaypoint.Category.GOBLINQUEENSDEN,
+ "Lost Precursor City", CrystalsWaypoint.Category.LOSTPRECURSORCITY,
+ "Khazad-dûm", CrystalsWaypoint.Category.KHAZADUM,
+ "Fairy Grotto", CrystalsWaypoint.Category.FAIRYGROTTO,
+ "Dragon's Lair", CrystalsWaypoint.Category.DRAGONSLAIR
+ );
+ private static final Pattern TEXT_CWORDS_PATTERN = Pattern.compile("([0-9][0-9][0-9]) ([0-9][0-9][0-9]?) ([0-9][0-9][0-9])");
+
+
+ public static void init() {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker")
+ .then(ClientCommandManager.literal("hud")
+ .then(ClientCommandManager.literal("crystals")
+ .executes(Scheduler.queueOpenScreenCommand(CrystalsHudConfigScreen::new))))));
+
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(CrystalsLocationsManager::render);
+ ClientReceiveMessageEvents.CHAT.register(CrystalsLocationsManager::extractLocationFromMessage);
+ ClientCommandRegistrationCallback.EVENT.register(CrystalsLocationsManager::registerWaypointLocationCommands);
+ }
+ private static void extractLocationFromMessage(Text message, SignedMessage signedMessage, GameProfile sender, MessageType.Parameters params, Instant receptionTimestamp){
+ if (!SkyblockerConfigManager.get().locations.dwarvenMines.crystalsWaypoints.findInChat ) { //todo || !Utils.isInCrystals()
+ return;
+ }
+ //get the message text
+ String value = signedMessage.getContent().getString();
+ Matcher matcher = TEXT_CWORDS_PATTERN.matcher(value);
+ //if there are cwords in the message try to get them and what they are talking about
+ if (matcher.find()){
+ String location = matcher.group();
+ Integer[] cowordinates = Arrays.stream(location.split(" ",3)).map(Integer::parseInt).toArray(Integer[]::new);
+ BlockPos blockPos = new BlockPos(cowordinates[0],cowordinates[1],cowordinates[2]);
+ //todo make sure this is in bounds of crystals
+ //see if there is a name of a location to add to this
+ for (String waypointLocation : WAYPOINTLOCATIONS.keySet()){
+ if (value.toLowerCase().contains(waypointLocation.toLowerCase())){ //todo be more lenient
+ //all data found to create waypoint
+ addCustomWaypoint(Text.of(waypointLocation),blockPos);
+ return;
+ }
+ }
+ //if the location is not found ask the user for the location (could have been in a previous chat message)
+ if (client.player == null || client.getNetworkHandler() == null ) {
+ return;
+ }
+ client.player.sendMessage(getLocationInputText(location), false);
+ }
+
+
+ }
+ private static void registerWaypointLocationCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) {
+ dispatcher.register(literal(SkyblockerMod.NAMESPACE)
+ .then(literal("crystalWaypoints")
+ .then(argument("pos", BlockPosArgumentType.blockPos())
+ .then(argument("place", StringArgumentType.greedyString())
+ .executes(context -> addWaypointFromCommand(context.getSource(), getString(context, "place"),context.getArgument("pos", PosArgument.class)))
+ )
+ )
+ )
+ );
+ }
+ private static Text getSetLocationMessage(String location,BlockPos blockPos) {
+ MutableText text = Text.empty();
+ text.append(Text.literal("Added waypoint for "+location+" at :"+blockPos.getX()+" "+blockPos.getY()+" "+blockPos.getZ()+".")); //todo add colours
+
+ return text;
+ }
+ private static Text getLocationInputText(String location) {
+ MutableText text = Text.empty();
+ for (String waypointLocation : WAYPOINTLOCATIONS.keySet()){
+ //todo add colour codes
+ text.append(Text.literal("["+waypointLocation+"]").styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/skyblocker crystalWaypoints "+location+" "+waypointLocation))));
+ }
+
+ return text;
+ }
+ public static int addWaypointFromCommand(FabricClientCommandSource source, String place, PosArgument location) {
+ // TODO Less hacky way with custom ClientBlockPosArgumentType
+ BlockPos blockPos = location.toAbsoluteBlockPos(new ServerCommandSource(null, source.getPosition(), source.getRotation(), null, 0, null, null, null, null));
+ if (WAYPOINTLOCATIONS.containsKey(place)){
+ addCustomWaypoint(Text.of(place), blockPos);
+ //todo send to map
+ //tell the client it has done this
+ if (client.player == null || client.getNetworkHandler() == null ) {
+ return Command.SINGLE_SUCCESS;
+ }
+ client.player.sendMessage(getSetLocationMessage(place, blockPos), false);
+ }
+ return Command.SINGLE_SUCCESS;
+ }
+
+
+ private static void addCustomWaypoint( Text waypointName, BlockPos pos) {
+ CrystalsWaypoint.Category category = WAYPOINTLOCATIONS.get(waypointName.getString());
+ CrystalsWaypoint waypoint = new CrystalsWaypoint(category, waypointName, pos);
+ ActiveWaypoints.put(waypointName.getString(),waypoint);
+ }
+ public static void render(WorldRenderContext context) {
+ if (SkyblockerConfigManager.get().locations.dwarvenMines.crystalsWaypoints.enabled ) {
+ for (CrystalsWaypoint crystalsWaypoint : ActiveWaypoints.values()) {
+ if (crystalsWaypoint.shouldRender()) {
+ crystalsWaypoint.render(context);
+ }
+ }
+ }
+ }
+
+ public static void update() {
+ if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.crystalsWaypoints.enabled) {
+ ActiveWaypoints= new HashMap<>();
+ return;
+ }
+ //get if the player is in the crystals
+ String location = Utils.getIslandArea().replace("⏣ ","");
+ //if new location and needs waypoint add waypoint
+ if (!location.equals("Unknown") && WAYPOINTLOCATIONS.containsKey(location) && !ActiveWaypoints.containsKey(location)){
+ //add waypoint at player location
+ BlockPos playerLocation = client.player.getBlockPos();
+ addCustomWaypoint(Text.of(location),playerLocation);
+ //todo send to map gui
+ }
+
+
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java
new file mode 100644
index 00000000..9c6db04f
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java
@@ -0,0 +1,149 @@
+package de.hysky.skyblocker.skyblock.dwarven;
+
+import com.google.gson.JsonObject;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.JsonOps;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import de.hysky.skyblocker.utils.waypoint.Waypoint;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.command.argument.EnumArgumentType;
+import net.minecraft.entity.Entity;
+import net.minecraft.text.Text;
+import net.minecraft.text.TextCodecs;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.StringIdentifiable;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.ToDoubleFunction;
+
+public class CrystalsWaypoint extends Waypoint {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CrystalsWaypoint.class);
+ public static final Codec<CrystalsWaypoint> CODEC = RecordCodecBuilder.create(instance -> instance.group(
+ Category.CODEC.fieldOf("category").forGetter(crystalsWaypoint -> crystalsWaypoint.category),
+ TextCodecs.CODEC.fieldOf("name").forGetter(crystalsWaypoint -> crystalsWaypoint.name),
+ BlockPos.CODEC.fieldOf("pos").forGetter(crystalsWaypoint -> crystalsWaypoint.pos)
+ ).apply(instance, CrystalsWaypoint::new));
+ public static final Codec<List<CrystalsWaypoint>> LIST_CODEC = CODEC.listOf();
+ static final List<String> SECRET_ITEMS = List.of("Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "Treasure Talisman");
+ private static final Supplier<SkyblockerConfig.CrystalsWaypoints> CONFIG = () -> SkyblockerConfigManager.get().locations.dwarvenMines.crystalsWaypoints;
+ static final Supplier<Type> TYPE_SUPPLIER = () -> CONFIG.get().waypointType;
+ final Category category;
+ final Text name;
+ private final Vec3d centerPos;
+
+ CrystalsWaypoint( JsonObject waypoint, String name, BlockPos pos) {
+ this(Category.get(waypoint), name, pos);
+ }
+
+ CrystalsWaypoint(Category category, String name, BlockPos pos) {
+ this( category, Text.of(name), pos);
+ }
+
+ CrystalsWaypoint( Category category, Text name, BlockPos pos) {
+ super(pos, TYPE_SUPPLIER, category.colorComponents);
+ this.category = category;
+ this.name = name;
+ this.centerPos = pos.toCenterPos();
+ }
+
+ static ToDoubleFunction<CrystalsWaypoint> getSquaredDistanceToFunction(Entity entity) {
+ return crystalsWaypoint -> entity.squaredDistanceTo(crystalsWaypoint.centerPos);
+ }
+
+ static Predicate<CrystalsWaypoint> getRangePredicate(Entity entity) {
+ return crystalsWaypoint -> entity.squaredDistanceTo(crystalsWaypoint.centerPos) <= 36D;
+ }
+
+ @Override
+ public boolean shouldRender() {
+ return super.shouldRender() ;
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj) || obj instanceof CrystalsWaypoint other && category == other.category && name.equals(other.name) && pos.equals(other.pos);
+ }
+
+ /**
+ * Renders the secret waypoint, including a waypoint through {@link Waypoint#render(WorldRenderContext)}, the name, and the distance from the player.
+ */
+ @Override
+ public void render(WorldRenderContext context) {
+ //TODO In the future, shrink the box for wither essence and items so its more realistic
+ super.render(context);
+
+
+ Vec3d posUp = centerPos.add(0, 1, 0);
+ RenderHelper.renderText(context, name, posUp, true);
+ double distance = context.camera().getPos().distanceTo(centerPos);
+ RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true);
+
+ }
+
+
+
+ enum Category implements StringIdentifiable { //todo set better colours and remove extra data
+ JUNGLETEMPLE("Jungle Temple", 0, 255, 0),
+ MINESOFDIVAN("Mines Of Divan", 255, 0, 0),
+ GOBLINQUEENSDEN("Goblin Queen's Den", 2, 213, 250),
+ LOSTPRECURSORCITY("Lost Precursor City", 2, 64, 250),
+ KHAZADUM("Khazad-dûm", 142, 66, 0),
+ FAIRYGROTTO("Fairy Grotto", 30, 30, 30),
+ DRAGONSLAIR("Dragon's Lair", 250, 217, 2),
+ DEFAULT("default", 190, 255, 252);
+
+ private static final Codec<Category> CODEC = StringIdentifiable.createCodec(Category::values);
+ private final String name;
+
+ private final float[] colorComponents;
+
+ Category(String name, int... intColorComponents) {
+ this.name = name;
+
+ colorComponents = new float[intColorComponents.length];
+ for (int i = 0; i < intColorComponents.length; i++) {
+ colorComponents[i] = intColorComponents[i] / 255F;
+ }
+ }
+
+ static Category get(JsonObject waypointJson) {
+ return CODEC.parse(JsonOps.INSTANCE, waypointJson.get("category")).resultOrPartial(LOGGER::error).orElse(Category.DEFAULT);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public String asString() {
+ return name;
+ }
+
+ static class CategoryArgumentType extends EnumArgumentType<Category> {
+ CategoryArgumentType() {
+ super(Category.CODEC, Category::values);
+ }
+
+ static CategoryArgumentType category() {
+ return new CategoryArgumentType();
+ }
+
+ static <S> Category getCategory(CommandContext<S> context, String name) {
+ return context.getArgument(name, Category.class);
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java
index 3f07622c..a8a0d3d6 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Utils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -36,6 +37,8 @@ public class Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", "");
private static final String DUNGEONS_LOCATION = "dungeon";
+ public static final String CRYSTALS_LOCATION = "crystal_hollows";
+
private static final String PROFILE_PREFIX = "Profile: ";
private static boolean isOnHypixel = false;
private static boolean isOnSkyblock = false;
@@ -85,6 +88,9 @@ public class Utils {
public static boolean isInDungeons() {
return getLocationRaw().equals(DUNGEONS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
}
+ public static boolean isInCrystals(){
+ return getLocationRaw().equals(CRYSTALS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ }
public static boolean isInTheRift() {
return getLocationRaw().equals(TheRift.LOCATION);
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 527205cf..903d702e 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -275,6 +275,9 @@
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]": "\nClassic: Shows name and percentage in a very simple box.",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.screen": "Dwarven HUD Config...",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground": "Enable Background",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud": "Crystal Hollows Map",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints": "Crystal Hollows Waypoints",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.findInChat": "Find Waypoints In Chat",
"text.autoconfig.skyblocker.option.locations.rift": "The Rift",
"text.autoconfig.skyblocker.option.locations.rift.mirrorverseWaypoints": "Enable Mirrorverse Waypoints",
diff --git a/src/main/resources/assets/skyblocker/textures/gui/crystals_map.png b/src/main/resources/assets/skyblocker/textures/gui/crystals_map.png
new file mode 100644
index 00000000..4e807ea1
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/textures/gui/crystals_map.png
Binary files differ