Skip to content

Fancy Dungeon Map #1239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig
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.<Float>createBuilder()
.name(Text.translatable("skyblocker.config.dungeons.map.mapScaling"))
.binding(defaults.dungeons.dungeonMap.mapScaling,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public static class DungeonMap {
@SerialEntry
public boolean enableMap = true;

@SerialEntry
public boolean fancyMap = true;

@SerialEntry
public float mapScaling = 1f;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public static class ItemInfoDisplay {
public RarityBackgroundStyle itemRarityBackgroundStyle = RarityBackgroundStyle.SQUARE;

@SerialEntry
public float itemRarityBackgroundsOpacity = 1f;
public float itemRarityBackgroundsOpacity = 0.5f;
}

public enum RarityBackgroundStyle {
Expand Down
37 changes: 26 additions & 11 deletions src/main/java/de/hysky/skyblocker/events/DungeonEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@
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);
Expand All @@ -22,24 +40,21 @@ 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();
}
});

@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface RoomMatched {
void onRoomMatched(Room room);
public interface DungeonLoaded {
void onDungeonLoaded();
}

@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface DungeonStarted {
void onDungeonStarted();
}

@Environment(EnvType.CLIENT)
@FunctionalInterface
public interface RoomMatched {
void onRoomMatched(Room room);
}
}
62 changes: 45 additions & 17 deletions src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
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.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.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
Expand All @@ -13,18 +16,23 @@
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.realms.util.RealmsUtil;
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.Entity;
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.MapState;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ColorHelper;
import net.minecraft.util.math.RotationAxis;
import org.joml.Vector2dc;

public class DungeonMap {
private static final Identifier DUNGEON_MAP = Identifier.of(SkyblockerMod.NAMESPACE, "dungeon_map");
Expand All @@ -45,30 +53,56 @@ public static void init() {
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset());
}

public static void render(MatrixStack matrices) {
private static void render(DrawContext context) {
DungeonsConfig.DungeonMap dungeonMap = SkyblockerConfigManager.get().dungeons.dungeonMap;
if (Utils.isInDungeons() && DungeonScore.isDungeonStarted() && !DungeonManager.isInBoss() && 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) {
MinecraftClient client = MinecraftClient.getInstance();
if (client.player == null || client.world == null) return;

MapIdComponent mapId = getMapIdComponent(client.player.getInventory().main.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);
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, matrices, vertices, false, LightmapTextureManager.MAX_LIGHT_COORDINATE);
mapRenderer.draw(MAP_RENDER_STATE, context.getMatrices(), vertices, false, LightmapTextureManager.MAX_LIGHT_COORDINATE);
vertices.draw();
matrices.pop();

if (fancy) renderPlayerHeads(client, context);
context.getMatrices().pop();
}

private static void renderPlayerHeads(MinecraftClient client, DrawContext context) {
if (!DungeonManager.isClearingDungeon()) return;

for (Entity entity : client.world.getEntities()) {
if (!(entity instanceof PlayerEntity player)) {
continue;
}
DungeonClass dungeonClass = DungeonPlayerManager.getClassFromPlayer(player);
if (dungeonClass == DungeonClass.UNKNOWN) continue;

Vector2dc mapPos = DungeonMapUtils.getMapPosFromPhysical(DungeonManager.getPhysicalEntrancePos(), DungeonManager.getMapEntrancePos(), DungeonManager.getMapRoomSize(), entity.getPos());
context.getMatrices().push();
context.getMatrices().translate(mapPos.x(), mapPos.y(), 0);
context.getMatrices().multiply(RotationAxis.POSITIVE_Z.rotationDegrees(entity.getYaw() + 180));
RealmsUtil.drawPlayerHead(context, -4, -4, 8, entity.getUuid());
context.drawBorder(-5, -5, 10, 10, ColorHelper.fullAlpha(dungeonClass.color()));
context.fill(-1, -7, 1, -5, ColorHelper.fullAlpha(dungeonClass.color()));
context.getMatrices().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);
Expand All @@ -77,12 +111,6 @@ public static MapIdComponent getMapIdComponent(ItemStack stack) {
} 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;
}
Expand Down
104 changes: 42 additions & 62 deletions src/main/java/de/hysky/skyblocker/skyblock/dungeon/LeapOverlay.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
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.*;
import java.util.function.Supplier;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;

Expand All @@ -19,11 +15,10 @@
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.GridWidget.Adder;
import net.minecraft.client.gui.widget.SimplePositioningWidget;
import net.minecraft.client.realms.util.RealmsUtil;
import net.minecraft.client.gui.widget.GridWidget.Adder;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.ProfileComponent;
import net.minecraft.item.ItemStack;
Expand All @@ -43,16 +38,10 @@ public class LeapOverlay extends Screen implements ScreenHandlerListener {
private static final Identifier BUTTON = Identifier.of(SkyblockerMod.NAMESPACE, "button/button");
private static final Identifier BUTTON_HIGHLIGHTED = Identifier.of(SkyblockerMod.NAMESPACE, "button/button_highlighted");
private static final int BUTTON_SPACING = 8;
private static final float SCALE = 1.5f;
private static final int BUTTON_WIDTH = (int) (130f * SCALE);
private static final int BUTTON_HEIGHT = (int) (50f * SCALE);
/**
* Compares first by class name then by player name.
*/
private static final Comparator<PlayerReference> COMPARATOR = Comparator.<PlayerReference, String>comparing(ref -> ref.dungeonClass().displayName())
.thenComparing(PlayerReference::name);
private static final int BUTTON_WIDTH = 130;
private static final int BUTTON_HEIGHT = 50;
private final GenericContainerScreenHandler handler;
private final List<PlayerReference> references = new ArrayList<>();
private final SortedSet<PlayerReference> references = new TreeSet<>();

public LeapOverlay(GenericContainerScreenHandler handler) {
super(Text.literal("Skyblocker Leap Overlay"));
Expand All @@ -75,7 +64,7 @@ protected void init() {
}

gridWidget.refreshPositions();
SimplePositioningWidget.setPos(gridWidget, 0, 0, this.width, this.height, 0.5f, 0.5f);
SimplePositioningWidget.setPos(gridWidget, 0, 0, this.width, this.height, 0.5f, 0.75f);
gridWidget.forEachChild(this::addDrawableChild);
}

Expand All @@ -99,34 +88,27 @@ public void onSlotUpdate(ScreenHandler handler, int slotId, ItemStack stack) {
default -> null;
};

PlayerReference reference = new PlayerReference(uuid, name, dungeonClass, status, handler.syncId, slotId);
tryInsertReference(reference);
updateReference(new PlayerReference(uuid, name, dungeonClass, status, handler.syncId, slotId));
}
}

@Override
public void onPropertyUpdate(ScreenHandler handler, int property, int value) {}

/**
* Inserts the {@code reference} into the list if it doesn't exist or updates current value then updates the screen.
*/
private void tryInsertReference(PlayerReference reference) {
Optional<PlayerReference> existing = references.stream()
.filter(ref -> ref.uuid().equals(reference.uuid()))
.findAny();

if (existing.isEmpty()) {
references.add(reference);
references.sort(COMPARATOR);

this.clearAndInit();
} else if (!existing.get().equals(reference)) {
references.remove(existing.get());
references.add(reference);
references.sort(COMPARATOR);

this.clearAndInit();
}
private void updateReference(PlayerReference reference) {
references.remove(reference);
references.add(reference);
clearAndInit();
}

@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);

int x = (width >> 1) - 64;
int y = (height >> 2) - 64;
DungeonMap.render(context, x, y, 1, true);
context.drawBorder(x, y, 128, 128, -1);
}

@Override
Expand Down Expand Up @@ -170,40 +152,23 @@ protected void renderWidget(DrawContext context, int mouseX, int mouseY, float d
int baseX = this.getX() + BORDER_THICKNESS;
int centreX = this.getX() + (this.getWidth() >> 1);
int centreY = this.getY() + (this.getHeight() >> 1);
int halfFontHeight = CLIENT.textRenderer.fontHeight >> 1;

//Draw Player Head
RealmsUtil.drawPlayerHead(context, baseX + 4, centreY - (HEAD_SIZE >> 1), HEAD_SIZE, reference.uuid());

MatrixStack matrices = context.getMatrices();
int halfFontHeight = (int) (CLIENT.textRenderer.fontHeight * SCALE) >> 1;

//Draw class as heading
matrices.push();
matrices.translate(centreX, this.getY() + halfFontHeight, 0f);
matrices.scale(SCALE, SCALE, 1f);
context.drawCenteredTextWithShadow(CLIENT.textRenderer, reference.dungeonClass().displayName(), 0, 0, ColorHelper.fullAlpha(reference.dungeonClass().color()));
matrices.pop();
context.drawCenteredTextWithShadow(CLIENT.textRenderer, reference.dungeonClass().displayName(), centreX, this.getY() + halfFontHeight, ColorHelper.fullAlpha(reference.dungeonClass().color()));

//Draw name next to head
matrices.push();
matrices.translate(baseX + HEAD_SIZE + 8, centreY - halfFontHeight, 0f);
matrices.scale(SCALE, SCALE, 1f);
context.drawTextWithShadow(CLIENT.textRenderer, Text.literal(reference.name()), 0, 0, Colors.WHITE);
matrices.pop();
context.drawTextWithShadow(CLIENT.textRenderer, Text.literal(reference.name()), baseX + HEAD_SIZE + 8, centreY - halfFontHeight, Colors.WHITE);

if (reference.status() != null) {
//Text
matrices.push();
matrices.translate(centreX, this.getY() + this.getHeight() - (halfFontHeight * 3), 0f);
matrices.scale(SCALE, SCALE, 1f);
context.drawCenteredTextWithShadow(CLIENT.textRenderer, reference.status().text.get(), 0, 0, Colors.WHITE);
matrices.pop();
context.drawCenteredTextWithShadow(CLIENT.textRenderer, reference.status().text.get(), centreX, this.getY() + this.getHeight() - (halfFontHeight * 3), Colors.WHITE);

//Overlay
matrices.push();
matrices.scale(1f, 1f, 1f);
context.fill(this.getX(), this.getY(), this.getX() + this.getWidth(), this.getY() + this.getHeight(), reference.status().overlayColor);
matrices.pop();
}
}

Expand All @@ -213,7 +178,22 @@ public void onClick(double mouseX, double mouseY) {
}
}

private record PlayerReference(UUID uuid, String name, DungeonClass dungeonClass, @Nullable PlayerStatus status, int syncId, int slotId) {}
private record PlayerReference(UUID uuid, String name, DungeonClass dungeonClass, @Nullable PlayerStatus status, int syncId, int slotId) implements Comparable<PlayerReference> {
/**
* Compares first by class name then by player name.
*/
private static final Comparator<PlayerReference> COMPARATOR = Comparator.<PlayerReference, String>comparing(ref -> ref.dungeonClass().displayName()).thenComparing(PlayerReference::name);

@Override
public boolean equals(Object obj) {
return obj instanceof PlayerReference playerRef && uuid.equals(playerRef.uuid);
}

@Override
public int compareTo(@NotNull LeapOverlay.PlayerReference o) {
return COMPARATOR.compare(this, o);
}
}

private enum PlayerStatus {
DEAD(() -> Text.translatable("text.skyblocker.dead").withColor(Colors.RED), ColorHelper.withAlpha(64, Colors.LIGHT_RED)),
Expand Down
Loading