diff options
Diffstat (limited to 'src/main/java')
25 files changed, 885 insertions, 516 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index 357b31f8..fa67eef8 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -68,14 +68,6 @@ public class DungeonsCategory { .controller(ConfigUtils.createBooleanController()) .build()) .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay")) - .description(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay.@Tooltip")) - .binding(defaults.dungeons.spiritLeapOverlay, - () -> config.dungeons.spiritLeapOverlay, - newValue -> config.dungeons.spiritLeapOverlay = newValue) - .controller(ConfigUtils.createBooleanController()) - .build()) - .option(Option.<Boolean>createBuilder() .name(Text.translatable("skyblocker.config.dungeons.starredMobGlow")) .description(Text.translatable("skyblocker.config.dungeons.starredMobGlow.@Tooltip")) .binding(defaults.dungeons.starredMobGlow, @@ -143,6 +135,20 @@ public class DungeonsCategory { newValue -> config.dungeons.dungeonMap.enableMap = newValue) .controller(ConfigUtils.createBooleanController()) .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.map.fancyMap")) + .binding(defaults.dungeons.dungeonMap.fancyMap, + () -> config.dungeons.dungeonMap.fancyMap, + newValue -> config.dungeons.dungeonMap.fancyMap = newValue) + .controller(ConfigUtils.createBooleanController()) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.map.showSelfHead")) + .binding(defaults.dungeons.dungeonMap.showSelfHead, + () -> config.dungeons.dungeonMap.showSelfHead, + newValue -> config.dungeons.dungeonMap.showSelfHead = newValue) + .controller(ConfigUtils.createBooleanController()) + .build()) .option(Option.<Float>createBuilder() .name(Text.translatable("skyblocker.config.dungeons.map.mapScaling")) .binding(defaults.dungeons.dungeonMap.mapScaling, @@ -157,6 +163,34 @@ public class DungeonsCategory { .build()) .build()) + // Spirit Leap Overlay + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay")) + .collapsed(true) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay.enableLeapOverlay")) + .description(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay.enableLeapOverlay.@Tooltip")) + .binding(defaults.dungeons.leapOverlay.enableLeapOverlay, + () -> config.dungeons.leapOverlay.enableLeapOverlay, + newValue -> config.dungeons.leapOverlay.enableLeapOverlay = newValue) + .controller(ConfigUtils.createBooleanController()) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay.showMap")) + .binding(defaults.dungeons.leapOverlay.showMap, + () -> config.dungeons.leapOverlay.showMap, + newValue -> config.dungeons.leapOverlay.showMap = newValue) + .controller(ConfigUtils.createBooleanController()) + .build()) + .option(Option.<Float>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.spiritLeapOverlay.scale")) + .binding(defaults.dungeons.leapOverlay.scale, + () -> config.dungeons.leapOverlay.scale, + newValue -> config.dungeons.leapOverlay.scale = newValue) + .controller(FloatController.createBuilder().range(1f, 2f).slider(0.05f).build()) + .build()) + .build()) + // Puzzle Solver .group(OptionGroup.createBuilder() .name(Text.translatable("skyblocker.config.dungeons.puzzle")) diff --git a/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java index 81c1cd9b..4fd9ade9 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java @@ -19,8 +19,6 @@ public class DungeonsConfig { public boolean classBasedPlayerGlow = true; - public boolean spiritLeapOverlay = true; - public boolean starredMobGlow = false; public boolean starredMobBoundingBoxes = true; @@ -35,6 +33,8 @@ public class DungeonsConfig { public DungeonMap dungeonMap = new DungeonMap(); + public SpiritLeapOverlay leapOverlay = new SpiritLeapOverlay(); + public PuzzleSolvers puzzleSolvers = new PuzzleSolvers(); public TheProfessor theProfessor = new TheProfessor(); @@ -60,6 +60,10 @@ public class DungeonsConfig { public static class DungeonMap { public boolean enableMap = true; + public boolean fancyMap = true; + + public boolean showSelfHead = true; + public float mapScaling = 1f; public int mapX = 2; @@ -67,6 +71,14 @@ public class DungeonsConfig { public int mapY = 2; } + public static class SpiritLeapOverlay { + public boolean enableLeapOverlay = true; + + public boolean showMap = true; + + public float scale = 1.2f; + } + public static class PuzzleSolvers { public boolean solveTicTacToe = true; diff --git a/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java b/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java index 27f9624c..b8448ac3 100644 --- a/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java +++ b/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java @@ -7,6 +7,24 @@ import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; public class DungeonEvents { + /** + * Called when the player loads into a dungeon after the location is sent to the scoreboard. + */ + public static final Event<DungeonLoaded> DUNGEON_LOADED = EventFactory.createArrayBacked(DungeonLoaded.class, callbacks -> () -> { + for (DungeonLoaded callback : callbacks) { + callback.onDungeonLoaded(); + } + }); + + /** + * Called after the dungeons starts and after the tab has changed to include additional information about the run such as each player's class. + */ + public static final Event<DungeonStarted> DUNGEON_STARTED = EventFactory.createArrayBacked(DungeonStarted.class, callbacks -> () -> { + for (DungeonStarted callback : callbacks) { + callback.onDungeonStarted(); + } + }); + public static final Event<RoomMatched> PUZZLE_MATCHED = EventFactory.createArrayBacked(RoomMatched.class, callbacks -> room -> { for (RoomMatched callback : callbacks) { callback.onRoomMatched(room); @@ -22,28 +40,10 @@ public class DungeonEvents { } }); - /** - * Note: This event fires after the tab has changed to include additional information about the run such as each player's class. - */ - public static final Event<DungeonStarted> DUNGEON_STARTED = EventFactory.createArrayBacked(DungeonStarted.class, callbacks -> () -> { - for (DungeonStarted callback : callbacks) { - callback.onDungeonStarted(); - } - }); - - /** - * Called when the player loads into a dungeon once Mort has been located. - */ - public static final Event<DungeonLoaded> DUNGEON_LOADED = EventFactory.createArrayBacked(DungeonLoaded.class, callbacks -> () -> { - for (DungeonLoaded callback : callbacks) { - callback.onDungeonLoaded(); - } - }); - @Environment(EnvType.CLIENT) @FunctionalInterface - public interface RoomMatched { - void onRoomMatched(Room room); + public interface DungeonLoaded { + void onDungeonLoaded(); } @Environment(EnvType.CLIENT) @@ -54,7 +54,7 @@ public class DungeonEvents { @Environment(EnvType.CLIENT) @FunctionalInterface - public interface DungeonLoaded { - void onDungeonLoaded(); + public interface RoomMatched { + void onRoomMatched(Room room); } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenProviderMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenProviderMixin.java index d1edb51c..29bc6ee9 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenProviderMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenProviderMixin.java @@ -107,7 +107,7 @@ public interface HandledScreenProviderMixin<T extends ScreenHandler> { } // Leap Overlay - case GenericContainerScreenHandler containerScreenHandler when Utils.isInDungeons() && SkyblockerConfigManager.get().dungeons.spiritLeapOverlay && nameLowercase.contains(LeapOverlay.TITLE.toLowerCase()) -> { + case GenericContainerScreenHandler containerScreenHandler when Utils.isInDungeons() && SkyblockerConfigManager.get().dungeons.leapOverlay.enableLeapOverlay && nameLowercase.contains(LeapOverlay.TITLE.toLowerCase()) -> { client.player.currentScreenHandler = containerScreenHandler; client.setScreen(new LeapOverlay(containerScreenHandler)); diff --git a/src/main/java/de/hysky/skyblocker/mixins/MapRendererMixin.java b/src/main/java/de/hysky/skyblocker/mixins/MapRendererMixin.java new file mode 100644 index 00000000..b0af491a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/MapRendererMixin.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.mixins; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.render.MapRenderer; +import net.minecraft.item.map.MapDecoration; +import net.minecraft.item.map.MapDecorationTypes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(MapRenderer.class) +public class MapRendererMixin { + @ModifyExpressionValue(method = "createDecoration", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/map/MapDecoration;isAlwaysRendered()Z")) + private boolean preventDecorationInDungeons(boolean alwaysRendered, @Local(argsOnly = true) MapDecoration decoration) { + // Allow alwaysRendered if + // 1. not in dungeons OR + // 2. the decoration type is frame (self player) and don't show self head + return (!Utils.isInDungeons() || decoration.type().value().equals(MapDecorationTypes.FRAME.value()) && !SkyblockerConfigManager.get().dungeons.dungeonMap.showSelfHead) && alwaysRendered; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/accessors/MapStateAccessor.java b/src/main/java/de/hysky/skyblocker/mixins/accessors/MapStateAccessor.java new file mode 100644 index 00000000..cbf7764b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/accessors/MapStateAccessor.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.mixins.accessors; + +import net.minecraft.item.map.MapDecoration; +import net.minecraft.item.map.MapState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(MapState.class) +public interface MapStateAccessor { + @Accessor + Map<String, MapDecoration> getDecorations(); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java index c0d371de..17f6416c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/ArrowPoisonWarning.java @@ -7,8 +7,8 @@ import de.hysky.skyblocker.config.configs.CrimsonIsleConfig; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra.KuudraPhase; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.render.RenderHelper; import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; import net.minecraft.client.MinecraftClient; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.BowItem; @@ -44,9 +44,9 @@ public class ArrowPoisonWarning { } if (!hasToxicArrowPoison) { - RenderHelper.displayInTitleContainerAndPlaySound(NONE_TITLE, THREE_SECONDS); + TitleContainer.addTitleAndPlaySound(NONE_TITLE, THREE_SECONDS); } else if (arrowPoisonAmount < CONFIG.get().arrowPoisonThreshold) { - RenderHelper.displayInTitleContainerAndPlaySound(LOW_TITLE, THREE_SECONDS); + TitleContainer.addTitleAndPlaySound(LOW_TITLE, THREE_SECONDS); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/DangerWarning.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/DangerWarning.java index 80028405..db9e1e96 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/DangerWarning.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/DangerWarning.java @@ -5,7 +5,6 @@ import java.util.function.Supplier; import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.render.RenderHelper; import de.hysky.skyblocker.utils.render.title.Title; import de.hysky.skyblocker.utils.render.title.TitleContainer; import de.hysky.skyblocker.utils.scheduler.Scheduler; @@ -37,7 +36,7 @@ public class DangerWarning { Title title = getDangerTitle(under); if (title != null) { - RenderHelper.displayInTitleContainerAndPlaySound(title); + TitleContainer.addTitleAndPlaySound(title); return; } else if (i == 5) { //Prevent removing the title prematurely diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonClass.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonClass.java index 66f03dbe..aeb0d171 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonClass.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonClass.java @@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.dungeon; import de.hysky.skyblocker.skyblock.entity.MobGlow; import de.hysky.skyblocker.skyblock.tabhud.util.Ico; import net.minecraft.item.ItemStack; +import net.minecraft.util.math.ColorHelper; import java.util.Arrays; import java.util.Map; @@ -26,7 +27,7 @@ public enum DungeonClass { DungeonClass(String name, int color, ItemStack icon) { this.name = name; - this.color = color; + this.color = ColorHelper.fullAlpha(color); this.icon = icon; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java index 548ba51b..2966b08c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java @@ -3,8 +3,13 @@ package de.hysky.skyblocker.skyblock.dungeon; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.annotations.Init; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.DungeonsConfig; +import de.hysky.skyblocker.mixins.accessors.MapStateAccessor; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonMapUtils; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonPlayerManager; import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; 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; @@ -17,73 +22,174 @@ import net.minecraft.client.render.LightmapTextureManager; import net.minecraft.client.render.MapRenderState; import net.minecraft.client.render.MapRenderer; import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.MapIdComponent; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.FilledMapItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.item.map.MapDecoration; +import net.minecraft.item.map.MapDecorationTypes; import net.minecraft.item.map.MapState; import net.minecraft.util.Identifier; +import net.minecraft.util.math.RotationAxis; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector2d; +import org.joml.Vector2dc; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Map; +import java.util.UUID; public class DungeonMap { + private static final Logger LOGGER = LoggerFactory.getLogger(DungeonMap.class); private static final Identifier DUNGEON_MAP = Identifier.of(SkyblockerMod.NAMESPACE, "dungeon_map"); - private static final MapIdComponent DEFAULT_MAP_ID_COMPONENT = new MapIdComponent(1024); - private static final MapRenderState MAP_RENDER_STATE = new MapRenderState(); - private static MapIdComponent cachedMapIdComponent = null; + private static final MapIdComponent DEFAULT_MAP_ID_COMPONENT = new MapIdComponent(1024); + private static final MapRenderState MAP_RENDER_STATE = new MapRenderState(); + private static MapIdComponent cachedMapIdComponent = null; - @Init - public static void init() { + @Init + public static void init() { HudLayerRegistrationCallback.EVENT.register(d -> d.attachLayerAfter(IdentifiedLayer.STATUS_EFFECTS, DUNGEON_MAP, (context, tickCounter) -> render(context))); - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("hud") - .then(ClientCommandManager.literal("dungeon") - .executes(Scheduler.queueOpenScreenCommand(DungeonMapConfigScreen::new)) - ) - ) - )); - ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); - } - - public static void render(MatrixStack matrices) { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.player == null || client.world == null) return; - - MapIdComponent mapId = getMapIdComponent(client.player.getInventory().getMainStacks().get(8)); - - MapState state = FilledMapItem.getMapState(mapId, client.world); - if (state == null) return; - - int x = SkyblockerConfigManager.get().dungeons.dungeonMap.mapX; - int y = SkyblockerConfigManager.get().dungeons.dungeonMap.mapY; - float scaling = SkyblockerConfigManager.get().dungeons.dungeonMap.mapScaling; - VertexConsumerProvider.Immediate vertices = client.getBufferBuilders().getEffectVertexConsumers(); - MapRenderer mapRenderer = client.getMapRenderer(); - - matrices.push(); - matrices.translate(x, y, 0); - matrices.scale(scaling, scaling, 0f); - mapRenderer.update(mapId, state, MAP_RENDER_STATE); - mapRenderer.draw(MAP_RENDER_STATE, matrices, vertices, false, LightmapTextureManager.MAX_LIGHT_COORDINATE); - vertices.draw(); - matrices.pop(); - } - - public static MapIdComponent getMapIdComponent(ItemStack stack) { - if (stack.isOf(Items.FILLED_MAP) && stack.contains(DataComponentTypes.MAP_ID)) { - MapIdComponent mapIdComponent = stack.get(DataComponentTypes.MAP_ID); - cachedMapIdComponent = mapIdComponent; - return mapIdComponent; - } else return cachedMapIdComponent != null ? cachedMapIdComponent : DEFAULT_MAP_ID_COMPONENT; - } - - private static void render(DrawContext context) { - if (Utils.isInDungeons() && DungeonScore.isDungeonStarted() && !DungeonManager.isInBoss() && SkyblockerConfigManager.get().dungeons.dungeonMap.enableMap) { - render(context.getMatrices()); - } - } - - private static void reset() { - cachedMapIdComponent = null; - } + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("hud") + .then(ClientCommandManager.literal("dungeon") + .executes(Scheduler.queueOpenScreenCommand(DungeonMapConfigScreen::new)) + ) + ) + )); + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset()); + } + + private static boolean shouldProcess() { + return Utils.isInDungeons() && DungeonScore.isDungeonStarted() && !DungeonManager.isInBoss(); + } + + private static void render(DrawContext context) { + DungeonsConfig.DungeonMap dungeonMap = SkyblockerConfigManager.get().dungeons.dungeonMap; + if (shouldProcess() && dungeonMap.enableMap) { + render(context, dungeonMap.mapX, dungeonMap.mapY, dungeonMap.mapScaling, dungeonMap.fancyMap); + } + } + + public static void render(DrawContext context, int x, int y, float scale, boolean fancy) { + render(context, x, y, scale, fancy, Integer.MIN_VALUE, Integer.MIN_VALUE, null); + } + + /** + * @return the {@link UUID} of the hovered player head, or null if no player head is hovered. + */ + @Nullable + public static UUID render(DrawContext context, int x, int y, float scale, boolean fancy, int mouseX, int mouseY, @Nullable UUID enlarge) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null || client.world == null) return null; + + MapIdComponent mapId = getMapIdComponent(client.player.getInventory().getMainStacks().get(8)); + MapState state = FilledMapItem.getMapState(mapId, client.world); + if (state == null) return null; + + VertexConsumerProvider.Immediate vertices = client.getBufferBuilders().getEffectVertexConsumers(); + MapRenderer mapRenderer = client.getMapRenderer(); + + context.getMatrices().push(); + context.getMatrices().translate(x, y, 0); + context.getMatrices().scale(scale, scale, 0f); + mapRenderer.update(mapId, state, MAP_RENDER_STATE); + mapRenderer.draw(MAP_RENDER_STATE, context.getMatrices(), vertices, fancy, LightmapTextureManager.MAX_LIGHT_COORDINATE); + vertices.draw(); + + UUID hoveredHead = null; + if (fancy) hoveredHead = renderPlayerHeads(context, client.world, state, mouseX / scale, mouseY / scale, enlarge); + context.getMatrices().pop(); + return hoveredHead; + } + + public static MapIdComponent getMapIdComponent(ItemStack stack) { + if (stack.isOf(Items.FILLED_MAP) && stack.contains(DataComponentTypes.MAP_ID)) { + MapIdComponent mapIdComponent = stack.get(DataComponentTypes.MAP_ID); + cachedMapIdComponent = mapIdComponent; + return mapIdComponent; + } else return cachedMapIdComponent != null ? cachedMapIdComponent : DEFAULT_MAP_ID_COMPONENT; + } + + @Nullable + private static UUID renderPlayerHeads(DrawContext context, World world, MapState state, double mouseX, double mouseY, @Nullable UUID enlarge) { + if (!DungeonManager.isClearingDungeon()) return null; + + // Used to index through the player list to find which dungeon player corresponds to which map decoration. + // Start at 1 because the first entry in the player list is the self player. + int i = 1; + UUID hovered = null; + for (Map.Entry<String, MapDecoration> mapDecoration : ((MapStateAccessor) state).getDecorations().entrySet()) { + // Get the corresponding dungeon player for the map decoration. + DungeonPlayerManager.DungeonPlayer dungeonPlayer = null; + // If the map decoration is the self player, use the first player in this list. The self player is always the first player in the list. + if (mapDecoration.getValue().type().value().equals(MapDecorationTypes.FRAME.value())) { + if (!SkyblockerConfigManager.get().dungeons.dungeonMap.showSelfHead) continue; + dungeonPlayer = DungeonPlayerManager.getPlayers()[0]; + } else while (i < DungeonPlayerManager.getPlayers().length && (dungeonPlayer == null || !dungeonPlayer.alive())) { // Find the next alive player in the player list. + dungeonPlayer = DungeonPlayerManager.getPlayers()[i]; + i++; + } + + // If we still didn't find a valid dungeon player after searching though the entire player list, something is wrong. + if (dungeonPlayer == null) { + dungeonPlayerError(mapDecoration.getKey(), "not found", i - 1, DungeonPlayerManager.getPlayers(), ((MapStateAccessor) state).getDecorations()); + continue; + } else if (!dungeonPlayer.alive()) { + dungeonPlayerError(mapDecoration.getKey(), "not alive", i - 1, DungeonPlayerManager.getPlayers(), ((MapStateAccessor) state).getDecorations()); + continue; + } else if (dungeonPlayer.uuid() == null) { + dungeonPlayerError(mapDecoration.getKey(), "has null uuid", i - 1, DungeonPlayerManager.getPlayers(), ((MapStateAccessor) state).getDecorations()); + continue; + } + PlayerRenderState player = PlayerRenderState.of(world, dungeonPlayer, mapDecoration.getValue()); + + // Actually render the player head + context.getMatrices().push(); + context.getMatrices().translate(player.mapPos().x(), player.mapPos().y(), 0); + context.getMatrices().multiply(RotationAxis.POSITIVE_Z.rotationDegrees(player.deg() + 180)); + + if (player.uuid().equals(enlarge)) { + // Enlarge the player head when the corresponding button is hovered + context.getMatrices().scale(2, 2, 1); + } else if (hovered == null && isPlayerHovered(player, mouseX, mouseY)) { + // Enlarge the player head when hovered + context.getMatrices().scale(2, 2, 1); + hovered = player.uuid(); + } + RenderHelper.drawPlayerHead(context, -4, -4, 8, player.uuid()); + context.drawBorder(-5, -5, 10, 10, dungeonPlayer.dungeonClass().color()); + context.fill(-1, -7, 1, -5, dungeonPlayer.dungeonClass().color()); + context.getMatrices().pop(); + } + return hovered; + } + + private static void dungeonPlayerError(String decorationId, String reason, int i, DungeonPlayerManager.DungeonPlayer[] dungeonPlayers, Map<String, MapDecoration> mapDecorations) { + LOGGER.error("[Skyblocker Dungeon Map] Dungeon player for map decoration '{}' {}. Player list index (zero-indexed): {}. Player list: {}. Map decorations: {}", decorationId, reason, i, Arrays.toString(dungeonPlayers), mapDecorations); + } + + public static boolean isPlayerHovered(PlayerRenderState player, double mouseX, double mouseY) { + return player.mapPos().distanceSquared(mouseX, mouseY) < 16; + } + + private static void reset() { + cachedMapIdComponent = null; + } + + public record PlayerRenderState(UUID uuid, String name, Vector2dc mapPos, float deg) { + public static PlayerRenderState of(@NotNull World world, @NotNull DungeonPlayerManager.DungeonPlayer dungeonPlayer, @NotNull MapDecoration mapDecoration) { + // Use the player entity if it exists, since it gives the most accurate position and rotation + PlayerEntity playerEntity = world.getPlayerByUuid(dungeonPlayer.uuid()); + Vector2dc mapPos = playerEntity != null ? DungeonMapUtils.getMapPosFromPhysical(DungeonManager.getPhysicalEntrancePos(), DungeonManager.getMapEntrancePos(), DungeonManager.getMapRoomSize(), playerEntity.getPos()) : new Vector2d(mapDecoration.x() / 2d + 64, mapDecoration.z() / 2d + 64); + float deg = playerEntity != null ? playerEntity.getYaw() : mapDecoration.rotation() * 360 / 16.0F; + + return new PlayerRenderState(dungeonPlayer.uuid(), dungeonPlayer.name(), mapPos, deg); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LeapOverlay.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LeapOverlay.java index 25e7e16d..c7915890 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LeapOverlay.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LeapOverlay.java @@ -1,26 +1,17 @@ package de.hysky.skyblocker.skyblock.dungeon; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.UUID; -import java.util.function.Supplier; - -import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFW; - import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.DungeonsConfig; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonPlayerManager; import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.render.RenderHelper; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; 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.SimplePositioningWidget; -import net.minecraft.client.realms.util.RealmsUtil; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.*; import net.minecraft.client.gui.widget.GridWidget.Adder; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; @@ -36,23 +27,26 @@ import net.minecraft.text.Text; import net.minecraft.util.Colors; import net.minecraft.util.Identifier; import net.minecraft.util.math.ColorHelper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; + |
