diff options
Diffstat (limited to 'src/main/java')
7 files changed, 278 insertions, 1 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 485a2103..3d96cc50 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -21,6 +21,7 @@ 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.end.EnderNodes; import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.garden.FarmingHud; import de.hysky.skyblocker.skyblock.garden.LowerSensitivity; @@ -98,6 +99,7 @@ public class SkyblockerMod implements ClientModInitializer { ClientTickEvents.END_CLIENT_TICK.register(this::tick); Utils.init(); SkyblockerConfigManager.init(); + SkyblockerScreen.initClass(); Tips.init(); NEURepoManager.init(); ImageRepoLoader.init(); @@ -110,6 +112,7 @@ public class SkyblockerMod implements ClientModInitializer { FairySouls.init(); Relics.init(); MythologicalRitual.init(); + EnderNodes.init(); OrderedWaypoints.init(); BackpackPreview.init(); QuickNav.init(); diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java b/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java new file mode 100644 index 00000000..ba0745ed --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/SkyblockerScreen.java @@ -0,0 +1,125 @@ +package de.hysky.skyblocker; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.Tips; +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.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ConfirmLinkScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.GridWidget; +import net.minecraft.client.gui.widget.MultilineTextWidget; +import net.minecraft.client.gui.widget.TextWidget; +import net.minecraft.client.gui.widget.ThreePartsLayoutWidget; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.OrderedText; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.Language; + +public class SkyblockerScreen extends Screen { + private static final int SPACING = 8; + private static final int BUTTON_WIDTH = 210; + private static final int HALF_BUTTON_WIDTH = 101; //Same as (210 - 8) / 2 + private static final Text TITLE = Text.literal("Skyblocker " + SkyblockerMod.VERSION); + private static final Identifier ICON = new Identifier(SkyblockerMod.NAMESPACE, "icon.png"); + private static final Text CONFIGURATION_TEXT = Text.translatable("text.skyblocker.config"); + private static final Text SOURCE_TEXT = Text.translatable("text.skyblocker.source"); + private static final Text REPORT_BUGS_TEXT = Text.translatable("menu.reportBugs"); + private static final Text WEBSITE_TEXT = Text.translatable("text.skyblocker.website"); + private static final Text TRANSLATE_TEXT = Text.translatable("text.skyblocker.translate"); + private static final Text MODRINTH_TEXT = Text.translatable("text.skyblocker.modrinth"); + private static final Text DISCORD_TEXT = Text.translatable("text.skyblocker.discord"); + private final ThreePartsLayoutWidget layout = new ThreePartsLayoutWidget(this); + + private SkyblockerScreen() { + super(TITLE); + } + + public static void initClass() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE) + .executes(Scheduler.queueOpenScreenCommand(SkyblockerScreen::new))); + }); + } + + @Override + protected void init() { + this.layout.addHeader(new IconTextWidget(this.getTitle(), this.textRenderer, ICON)); + + GridWidget gridWidget = this.layout.addBody(new GridWidget()).setSpacing(SPACING); + gridWidget.getMainPositioner().alignHorizontalCenter(); + GridWidget.Adder adder = gridWidget.createAdder(2); + + adder.add(ButtonWidget.builder(CONFIGURATION_TEXT, button -> this.openConfig()).width(BUTTON_WIDTH).build(), 2); + adder.add(ButtonWidget.builder(SOURCE_TEXT, ConfirmLinkScreen.opening(this, "https://github.com/SkyblockerMod/Skyblocker")).width(HALF_BUTTON_WIDTH).build()); + adder.add(ButtonWidget.builder(REPORT_BUGS_TEXT, ConfirmLinkScreen.opening(this, "https://github.com/SkyblockerMod/Skyblocker/issues")).width(HALF_BUTTON_WIDTH).build()); + adder.add(ButtonWidget.builder(WEBSITE_TEXT, ConfirmLinkScreen.opening(this, "https://hysky.de/")).width(HALF_BUTTON_WIDTH).build()); + adder.add(ButtonWidget.builder(TRANSLATE_TEXT, ConfirmLinkScreen.opening(this, "https://translate.hysky.de/")).width(HALF_BUTTON_WIDTH).build()); + adder.add(ButtonWidget.builder(MODRINTH_TEXT, ConfirmLinkScreen.opening(this, "https://modrinth.com/mod/skyblocker-liap")).width(HALF_BUTTON_WIDTH).build()); + adder.add(ButtonWidget.builder(DISCORD_TEXT, ConfirmLinkScreen.opening(this, "https://discord.gg/aNNJHQykck")).width(HALF_BUTTON_WIDTH).build()); + adder.add(ButtonWidget.builder(ScreenTexts.DONE, button -> this.close()).width(BUTTON_WIDTH).build(), 2); + + MultilineTextWidget tip = new MultilineTextWidget(Text.translatable("skyblocker.tips.tip", Tips.nextTipInternal()), this.textRenderer) + .setCentered(true) + .setMaxWidth((int) (this.width * 0.7)); + + this.layout.addFooter(tip); + this.layout.refreshPositions(); + this.layout.forEachChild(this::addDrawableChild); + } + + @Override + protected void initTabNavigation() { + this.layout.refreshPositions(); + } + + private void openConfig() { + this.client.setScreen(SkyblockerConfigManager.createGUI(this)); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + this.renderBackground(context, mouseX, mouseY, delta); + super.render(context, mouseX, mouseY, delta); + } + + private static class IconTextWidget extends TextWidget { + private final Identifier icon; + + IconTextWidget(Text message, TextRenderer textRenderer, Identifier icon) { + super(message, textRenderer); + this.icon = icon; + } + + @Override + public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + Text text = this.getMessage(); + TextRenderer textRenderer = this.getTextRenderer(); + + int width = this.getWidth(); + int textWidth = textRenderer.getWidth(text); + float horizontalAlignment = 0.5f; // default + //17 = (32 + 2) / 2 • 32 + 2 is the width of the icon + spacing between icon and text + int x = this.getX() + 17 + Math.round(horizontalAlignment * (float) (width - textWidth)); + int y = this.getY() + (this.getHeight() - textRenderer.fontHeight) / 2; + OrderedText orderedText = textWidth > width ? this.trim(text, width) : text.asOrderedText(); + + int iconX = x - 34; + int iconY = y - 13; + + context.drawTextWithShadow(textRenderer, orderedText, x, y, this.getTextColor()); + context.drawTexture(this.icon, iconX, iconY, 0, 0, 32, 32, 32, 32); + } + + private OrderedText trim(Text text, int width) { + TextRenderer textRenderer = this.getTextRenderer(); + StringVisitable stringVisitable = textRenderer.trimToWidth(text, width - textRenderer.getWidth(ScreenTexts.ELLIPSIS)); + return Language.getInstance().reorder(StringVisitable.concat(stringVisitable, ScreenTexts.ELLIPSIS)); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index 418cc4d1..bf98ac1f 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -1071,6 +1071,8 @@ public class SkyblockerConfig { } public static class TheEnd { + @SerialEntry + public boolean enableEnderNodeHelper = true; @SerialEntry public boolean hudEnabled = true; diff --git a/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java index 67512b78..46f3067c 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java @@ -85,6 +85,13 @@ public class LocationsCategory { .name(Text.translatable("text.autoconfig.skyblocker.option.locations.end")) .collapsed(false) .option(Option.<Boolean>createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.end.enableEnderNodeHelper")) + .binding(defaults.locations.end.enableEnderNodeHelper, + () -> config.locations.end.enableEnderNodeHelper, + newValue -> config.locations.end.enableEnderNodeHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.end.hudEnabled")) .binding(defaults.locations.end.hudEnabled, () -> config.locations.end.hudEnabled, diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java index 8397292b..743f949f 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -7,6 +7,7 @@ import de.hysky.skyblocker.skyblock.FishingHelper; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; +import de.hysky.skyblocker.skyblock.end.EnderNodes; import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual; import de.hysky.skyblocker.utils.SlayerUtils; @@ -91,6 +92,7 @@ public abstract class ClientPlayNetworkHandlerMixin { @Inject(method = "onParticle", at = @At("RETURN")) private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { MythologicalRitual.onParticle(packet); + EnderNodes.onParticle(packet); } @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/Tips.java b/src/main/java/de/hysky/skyblocker/skyblock/Tips.java index c483555e..513dc4b7 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/Tips.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/Tips.java @@ -103,7 +103,7 @@ public class Tips { .append(Text.translatable("skyblocker.tips.clickDisable").styled(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/skyblocker tips disable")))); } - private static Text nextTipInternal() { + public static Text nextTipInternal() { int randomInt = RANDOM.nextInt(TIPS.size()); while (randomInt == previousTipIndex) randomInt = RANDOM.nextInt(TIPS.size()); previousTipIndex = randomInt; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/EnderNodes.java b/src/main/java/de/hysky/skyblocker/skyblock/end/EnderNodes.java new file mode 100644 index 00000000..b4af7256 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/end/EnderNodes.java @@ -0,0 +1,138 @@ +package de.hysky.skyblocker.skyblock.end; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import de.hysky.skyblocker.utils.waypoint.Waypoint; +import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import it.unimi.dsi.fastutil.ints.IntIntPair; +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.fabricmc.fabric.api.event.player.AttackBlockCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; +import net.minecraft.particle.ParticleType; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.util.ActionResult; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; + +import java.util.HashMap; +import java.util.Map; + +public class EnderNodes { + private static final MinecraftClient client = MinecraftClient.getInstance(); + private static final Map<BlockPos, EnderNode> enderNodes = new HashMap<>(); + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(EnderNodes::update, 20); + WorldRenderEvents.AFTER_TRANSLUCENT.register(EnderNodes::render); + AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> { + enderNodes.remove(pos); + return ActionResult.PASS; + }); + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); + } + + public static void onParticle(ParticleS2CPacket packet) { + if (!shouldProcess()) return; + ParticleType<?> particleType = packet.getParameters().getType(); + if (!ParticleTypes.PORTAL.getType().equals(particleType) && !ParticleTypes.WITCH.getType().equals(particleType)) + return; + + double x = packet.getX(); + double y = packet.getY(); + double z = packet.getZ(); + double xFrac = MathHelper.floorMod(x, 1); + double yFrac = MathHelper.floorMod(y, 1); + double zFrac = MathHelper.floorMod(z, 1); + BlockPos pos; + Direction direction; + if (yFrac == 0.25) { + pos = BlockPos.ofFloored(x, y - 1, z); + direction = Direction.UP; + } else if (yFrac == 0.75) { + pos = BlockPos.ofFloored(x, y + 1, z); + direction = Direction.DOWN; + } else if (xFrac == 0.25) { + pos = BlockPos.ofFloored(x - 1, y, z); + direction = Direction.EAST; + } else if (xFrac == 0.75) { + pos = BlockPos.ofFloored(x + 1, y, z); + direction = Direction.WEST; + } else if (zFrac == 0.25) { + pos = BlockPos.ofFloored(x, y, z - 1); + direction = Direction.SOUTH; + } else if (zFrac == 0.75) { + pos = BlockPos.ofFloored(x, y, z + 1); + direction = Direction.NORTH; + } else { + return; + } + + EnderNode enderNode = enderNodes.computeIfAbsent(pos, EnderNode::new); + IntIntPair particles = enderNode.particles.get(direction); + particles.left(particles.leftInt() + 1); + particles.right(particles.rightInt() + 1); + } + + private static void update() { + if (shouldProcess()) { + for (EnderNode enderNode : enderNodes.values()) { + enderNode.updateParticles(); + } + } + } + + private static void render(WorldRenderContext context) { + if (shouldProcess()) { + for (EnderNode enderNode : enderNodes.values()) { + if (enderNode.shouldRender()) { + enderNode.render(context); + } + } + } + } + + private static boolean shouldProcess() { + return SkyblockerConfigManager.get().locations.end.enableEnderNodeHelper && Utils.isInTheEnd(); + } + + private static void reset() { + enderNodes.clear(); + } + + public static class EnderNode extends Waypoint { + private final Map<Direction, IntIntPair> particles = Map.of( + Direction.UP, new IntIntMutablePair(0, 0), + Direction.DOWN, new IntIntMutablePair(0, 0), + Direction.EAST, new IntIntMutablePair(0, 0), + Direction.WEST, new IntIntMutablePair(0, 0), + Direction.SOUTH, new IntIntMutablePair(0, 0), + Direction.NORTH, new IntIntMutablePair(0, 0) + ); + private long lastConfirmed; + + private EnderNode(BlockPos pos) { + super(pos, () -> SkyblockerConfigManager.get().general.waypoints.waypointType, DyeColor.CYAN.getColorComponents(), false); + } + + private void updateParticles() { + long currentTimeMillis = System.currentTimeMillis(); + if (lastConfirmed + 2000 > currentTimeMillis || client.world == null || !particles.entrySet().stream().allMatch(entry -> entry.getValue().leftInt() >= 5 && entry.getValue().rightInt() >= 5 || !client.world.getBlockState(pos.offset(entry.getKey())).isAir())) return; + lastConfirmed = currentTimeMillis; + for (Map.Entry<Direction, IntIntPair> entry : particles.entrySet()) { + entry.getValue().left(0); + entry.getValue().right(0); + } + } + + @Override + public boolean shouldRender() { + return super.shouldRender() && lastConfirmed + 5000 > System.currentTimeMillis(); + } + } +} |