diff options
| author | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-02-04 12:49:53 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-04 12:49:53 -0500 |
| commit | fe1bc0589caef8ff9f2d616a3742e63e2668f00e (patch) | |
| tree | 06e8e1b2ee3e3a9381dc042650f4fe6ac86582c5 /src | |
| parent | ce90376c4a8a934cc4d2f8bc4dd0e5e1c71b3748 (diff) | |
| parent | 71467890a61eb4e05793b1856f6303787216f2ec (diff) | |
| download | Skyblocker-fe1bc0589caef8ff9f2d616a3742e63e2668f00e.tar.gz Skyblocker-fe1bc0589caef8ff9f2d616a3742e63e2668f00e.tar.bz2 Skyblocker-fe1bc0589caef8ff9f2d616a3742e63e2668f00e.zip | |
Merge pull request #523 from olim88/crystal-hollows-fetures
Crystal hollows fetures
Diffstat (limited to 'src')
15 files changed, 927 insertions, 67 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..4acb8064 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -873,11 +873,20 @@ 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 { @SerialEntry - public boolean enabled = true; + public boolean enabledCommissions = true; + + @SerialEntry + public boolean enabledPowder = true; @SerialEntry public DwarvenHudStyle style = DwarvenHudStyle.SIMPLE; @@ -890,6 +899,40 @@ public class SkyblockerConfig { @SerialEntry public int y = 10; + + @SerialEntry + public int powderX = 10; + + @SerialEntry + public int powderY = 70; + } + + public static class CrystalsHud { + @SerialEntry + public boolean enabled = true; + + @SerialEntry + public boolean showLocations = true; + + @SerialEntry + public int locationSize = 8; + + @SerialEntry + public int x = 10; + + @SerialEntry + public int y = 130; + + @SerialEntry + public float mapScaling = 1f; + } + + public static class CrystalsWaypoints { + @SerialEntry + public boolean enabled = true; + + @SerialEntry + public boolean findInChat = true; } public enum DwarvenHudStyle { 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..97b48bc4 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java @@ -2,12 +2,15 @@ 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; import dev.isxander.yacl3.api.OptionDescription; import dev.isxander.yacl3.api.OptionGroup; import de.hysky.skyblocker.skyblock.dwarven.DwarvenHudConfigScreen; +import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; @@ -45,15 +48,22 @@ public class DwarvenMinesCategory { .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud")) .collapsed(false) .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled")) - .binding(defaults.locations.dwarvenMines.dwarvenHud.enabled, - () -> config.locations.dwarvenMines.dwarvenHud.enabled, - newValue -> config.locations.dwarvenMines.dwarvenHud.enabled = newValue) + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabledCommissions")) + .binding(defaults.locations.dwarvenMines.dwarvenHud.enabledCommissions, + () -> config.locations.dwarvenMines.dwarvenHud.enabledCommissions, + newValue -> config.locations.dwarvenMines.dwarvenHud.enabledCommissions = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabledPowder")) + .binding(defaults.locations.dwarvenMines.dwarvenHud.enabledPowder, + () -> config.locations.dwarvenMines.dwarvenHud.enabledPowder, + newValue -> config.locations.dwarvenMines.dwarvenHud.enabledPowder = newValue) .controller(ConfigUtils::createBooleanController) .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 +84,68 @@ public class DwarvenMinesCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) + //crystal HUD + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud")) + .collapsed(false) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.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.crystalsHud.screen")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new CrystalsHudConfigScreen(screen))) + .build()) + .option(Option.<Float>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.mapScaling")) + .binding(defaults.locations.dwarvenMines.crystalsHud.mapScaling, + () -> config.locations.dwarvenMines.crystalsHud.mapScaling, + newValue -> config.locations.dwarvenMines.crystalsHud.mapScaling = newValue) + .controller(FloatFieldControllerBuilder::create) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.@Tooltip"))) + .binding(defaults.locations.dwarvenMines.crystalsHud.showLocations, + () -> config.locations.dwarvenMines.crystalsHud.showLocations, + newValue -> config.locations.dwarvenMines.crystalsHud.showLocations = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Integer>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.locationSize")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsHud.showLocations.locationSize.@Tooltip"))) + .binding(defaults.locations.dwarvenMines.crystalsHud.locationSize, + () -> config.locations.dwarvenMines.crystalsHud.locationSize, + newValue -> config.locations.dwarvenMines.crystalsHud.locationSize = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(4, 12).step(2)) + .build()) + .build()) + //crystals waypoints + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints")) + .collapsed(false) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.enabled")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.enabled.@Tooltip"))) + .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")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.crystalsWaypoints.findInChat.@Tooltip"))) + .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..7b15c61e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java @@ -0,0 +1,164 @@ +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.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +import java.awt.*; +import java.util.Arrays; +import java.util.Map; + +import org.joml.Vector2i; +import org.joml.Vector2ic; + +public class CrystalsHud { + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + protected static final Identifier MAP_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/crystals_map.png"); + private static final Identifier MAP_ICON = new Identifier("textures/map/map_icons.png"); + private static final String[] SMALL_LOCATIONS = { "Fairy Grotto", "King Yolkar", "Corleone", "Odawa", "Key Guardian" }; + + public static boolean visible = 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.player == null + || !visible) { + return; + } + render(context, tickDelta, SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.x, + SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.y); + }); + } + + protected static IntIntPair getDimensionsForConfig() { + int size = (int) (62 * SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.mapScaling); + return IntIntPair.of(size, size); + } + + + /** + * Renders the map to the players UI. renders the background image ({@link CrystalsHud#MAP_TEXTURE}) of the map then if enabled special locations on the map. then finally the player to the map. + * + * @param context DrawContext to draw map to + * @param tickDelta For interpolating the player's yaw for map marker + * @param hudX Top left X coordinate of the map + * @param hudY Top left Y coordinate of the map + */ + private static void render(DrawContext context, float tickDelta, int hudX, int hudY) { + float scale = SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.mapScaling; + + //make sure the map renders infront of some stuff - improve this in the future with better layering (1.20.5?) + //and set position and scale + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(hudX, hudY, 200f); + matrices.scale(scale, scale, 0f); + + //draw map texture + context.drawTexture(MAP_TEXTURE, 0, 0, 0, 0, 62, 62, 62, 62); + + //if enabled add waypoint locations to map + if (SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.showLocations) { + Map<String,CrystalsWaypoint> ActiveWaypoints = CrystalsLocationsManager.activeWaypoints; + + for (CrystalsWaypoint waypoint : ActiveWaypoints.values()) { + Color waypointColor = waypoint.category.color; + Vector2ic renderPos = transformLocation(waypoint.pos.getX(), waypoint.pos.getZ()); + int locationSize = SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.locationSize; + + if (Arrays.asList(SMALL_LOCATIONS).contains(waypoint.name.getString())) {//if small location half the location size + locationSize = locationSize / 2; + } + + //fill square of size locationSize around the coordinates of the location + context.fill(renderPos.x() - locationSize / 2, renderPos.y() - locationSize / 2, renderPos.x() + locationSize / 2, renderPos.y() + locationSize / 2, waypointColor.getRGB()); + } + } + + //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(); + float playerRotation = CLIENT.player.getYaw(); //TODO make the transitions more rough? + Vector2ic renderPos = transformLocation(playerX, playerZ); + + int renderX = renderPos.x() - 2; + int renderY = renderPos.y() - 3; + + //position, scale and rotate the player marker + matrices.translate(renderX, renderY, 0f); + matrices.scale(0.75f, 0.75f, 0f); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(yaw2Cardinal(playerRotation)), 2.5f, 3.5f, 0); + + //draw marker on map + context.drawTexture(MAP_ICON, 0, 0, 2, 0, 5, 7, 128, 128); + + //todo add direction (can not work out how to rotate) + matrices.pop(); + } + + /** + * Converts an X and Z coordinate in the crystal hollow to a X and Y coordinate on the map. + * + * @param x the world X coordinate + * @param z the world Z coordinate + * @return a vector representing the x and y values + */ + protected static Vector2ic transformLocation(double x, double z) { + //converts an x and z to a location on the map + int transformedX = (int) ((x - 202) / 621 * 62); + int transformedY = (int) ((z - 202) / 621 * 62); + transformedX = MathHelper.clamp(transformedX, 0, 62); + transformedY = MathHelper.clamp(transformedY, 0, 62); + + return new Vector2i(transformedX, transformedY); + } + + /** + * Converts yaw to the cardinal directions that a player marker can be rotated towards on a map. + * The rotations of a marker follow this order: N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW. + * <br><br> + * Based off code from {@link net.minecraft.client.render.MapRenderer} + */ + private static float yaw2Cardinal(float yaw) { + yaw += + 180; //flip direction + byte clipped = (byte) ((yaw += yaw < 0.0 ? -8.0 : 8.0) * 16.0 / 360.0); + + return (clipped * 360f) / 16f; + } + + /** + * Works out if the crystals map should be rendered and sets {@link CrystalsHud#visible} accordingly. + * + */ + public static void update() { + if (CLIENT.player == null || CLIENT.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.enabled) { + visible = false; + return; + } + + //get if the player is in the crystals + visible = Utils.isInCrystalHollows(); + } +} 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..b4e423e9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHudConfigScreen.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +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); + renderHUDMap(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.getDimensionsForConfig(); + if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + dims.leftInt(), hudY + dims.rightInt()) && 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.getDimensionsForConfig(); + hudX = this.width / 2 - dims.leftInt(); + hudY = this.height / 2 - dims.rightInt(); + } + return super.mouseClicked(mouseX, mouseY, button); + } + + private void renderHUDMap(DrawContext context, int x, int y) { + float scaling = SkyblockerConfigManager.get().locations.dwarvenMines.crystalsHud.mapScaling; + int size = (int) (62 * scaling); + context.drawTexture(CrystalsHud.MAP_TEXTURE, x, y, 0, 0, size, size, size, size); + } + + @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..0a4e4518 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java @@ -0,0 +1,195 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.logging.LogUtils; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.Utils; +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.networking.v1.ClientPlayConnectionEvents; +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.PosArgument; +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.awt.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.slf4j.Logger; + +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 { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + + /** + * A look-up table to convert between location names and waypoint in the {@link CrystalsWaypoint.Category} values. + */ + protected static final Map<String, CrystalsWaypoint.Category> WAYPOINT_LOCATIONS = Arrays.stream(CrystalsWaypoint.Category.values()).collect(Collectors.toMap(CrystalsWaypoint.Category::toString, Function.identity())); + 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])"); + + protected static Map<String, CrystalsWaypoint> activeWaypoints = new HashMap<>(); + + public static void init() { + WorldRenderEvents.AFTER_TRANSLUCENT.register(CrystalsLocationsManager::render); + ClientReceiveMessageEvents.GAME.register(CrystalsLocationsManager::extractLocationFromMessage); + ClientCommandRegistrationCallback.EVENT.register(CrystalsLocationsManager::registerWaypointLocationCommands); + ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset()); + } + + private static void extractLocationFromMessage(Text message, Boolean overlay) { + if (!SkyblockerConfigManager.get().locations.dwarvenMines.crystalsWaypoints.findInChat || !Utils.isInCrystalHollows()) { + return; + } + + try { + //get the message text + String value = message.getString(); + Matcher matcher = TEXT_CWORDS_PATTERN.matcher(value); + //if there are coordinates in the message try to get them and what they are talking about + if (matcher.find()) { + String location = matcher.group(); + int[] coordinates = Arrays.stream(location.split(" ", 3)).mapToInt(Integer::parseInt).toArray(); + BlockPos blockPos = new BlockPos(coordinates[0], coordinates[1], coordinates[2]); + + //if position is not in the hollows do not add it + if (!checkInCrystals(blockPos)) { + return; + } + + //see if there is a name of a location to add to this + for (String waypointLocation : WAYPOINT_LOCATIONS.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); + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Crystals Locations Manager] Encountered an exception while extracing a location from a chat message!", e); + } + } + protected static Boolean checkInCrystals(BlockPos pos){ + //checks if a location is inside crystal hollows bounds + return pos.getX() >= 202 && pos.getX() <= 823 + && pos.getZ() >= 202 && pos.getZ() <= 823 + && pos.getY() >= 31 && pos.getY() <= 188; + } + + 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))) + ) + ) + ) + ); + } + + protected static Text getSetLocationMessage(String location,BlockPos blockPos) { + MutableText text = Constants.PREFIX.get(); + text.append(Text.literal("Added waypoint for ")); + Color locationColor = WAYPOINT_LOCATIONS.get(location).color; + text.append(Text.literal(location).withColor(locationColor.getRGB())); + text.append(Text.literal(" at : " + blockPos.getX() + " " + blockPos.getY() + " " + blockPos.getZ() + ".")); + + return text; + } + + private static Text getLocationInputText(String location) { + MutableText text = Constants.PREFIX.get(); + + for (String waypointLocation : WAYPOINT_LOCATIONS.keySet()) { + Color locationColor = WAYPOINT_LOCATIONS.get(waypointLocation).color; + text.append(Text.literal("[" + waypointLocation + "]").withColor(locationColor.getRGB()).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 (WAYPOINT_LOCATIONS.containsKey(place)) { + addCustomWaypoint(Text.of(place), blockPos); + + //tell the client it has done this + if (CLIENT.player == null || CLIENT.getNetworkHandler() == null) { + return 0; + } + + CLIENT.player.sendMessage(getSetLocationMessage(place, blockPos), false); + } + + return Command.SINGLE_SUCCESS; + } + + + private static void addCustomWaypoint( Text waypointName, BlockPos pos) { + CrystalsWaypoint.Category category = WAYPOINT_LOCATIONS.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); + } + } + } + } + + private static void reset() { + activeWaypoints.clear(); + } + + public static void update() { + if (CLIENT.player == null || CLIENT.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.crystalsWaypoints.enabled || !Utils.isInCrystalHollows()) { + 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") && WAYPOINT_LOCATIONS.containsKey(location) && !activeWaypoints.containsKey(location)) { + //add waypoint at player location + BlockPos playerLocation = CLIENT.player.getBlockPos(); + addCustomWaypoint(Text.of(location), playerLocation); + } + } +} 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..fbb43083 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java @@ -0,0 +1,98 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +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.entity.Entity; +import net.minecraft.text.Text; +import net.minecraft.util.DyeColor; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +import java.awt.*; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; + +public class CrystalsWaypoint extends Waypoint { + private static final Supplier<SkyblockerConfig.Waypoints> CONFIG = () -> SkyblockerConfigManager.get().general.waypoints; + private static final Supplier<Type> TYPE_SUPPLIER = () -> CONFIG.get().waypointType; + final Category category; + final Text name; + private final Vec3d centerPos; + + 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; + } + |
