From 9efcaf6c28418bcc8a74f1a3acfdfddb94ec503b Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Wed, 7 Aug 2024 01:34:36 +0200 Subject: [PATCH 1/6] Optimize drawing of screens by batching sprite draws into single draw calls --- .../java/appeng/client/gui/AEBaseScreen.java | 154 +++++++++++------ .../client/gui/widgets/PanelBlitter.java | 78 +++------ .../client/gui/widgets/SpriteLayer.java | 163 ++++++++++++++++++ src/main/java/appeng/menu/AEBaseMenu.java | 7 + .../assets/ae2/screens/cell_workbench.json | 6 +- .../assets/ae2/textures/gui/sprites/slot.png | Bin 0 -> 134 bytes .../ae2/textures/gui/sprites/slot.png.mcmeta | 9 + .../ae2/textures/gui/sprites/slot_border.png | Bin 0 -> 131 bytes .../gui/sprites/slot_border.png.mcmeta | 15 ++ 9 files changed, 322 insertions(+), 110 deletions(-) create mode 100644 src/main/java/appeng/client/gui/widgets/SpriteLayer.java create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot.png.mcmeta create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_border.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_border.png.mcmeta diff --git a/src/main/java/appeng/client/gui/AEBaseScreen.java b/src/main/java/appeng/client/gui/AEBaseScreen.java index bfac8358d96..f2ee3e9b716 100644 --- a/src/main/java/appeng/client/gui/AEBaseScreen.java +++ b/src/main/java/appeng/client/gui/AEBaseScreen.java @@ -18,45 +18,6 @@ package appeng.client.gui; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import com.google.common.base.Stopwatch; -import com.mojang.blaze3d.platform.InputConstants; - -import org.jetbrains.annotations.MustBeInvokedByOverriders; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFW; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.ComponentRenderUtils; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.Rect2i; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.chat.Component; -import net.minecraft.util.FormattedCharSequence; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.network.PacketDistributor; - import appeng.api.behaviors.ContainerItemStrategies; import appeng.api.behaviors.EmptyingAction; import appeng.api.implementations.menuobjects.ItemMenuHost; @@ -73,6 +34,7 @@ import appeng.client.gui.widgets.ITooltip; import appeng.client.gui.widgets.OpenGuideButton; import appeng.client.gui.widgets.PanelBlitter; +import appeng.client.gui.widgets.SpriteLayer; import appeng.client.gui.widgets.VerticalButtonBar; import appeng.client.guidebook.PageAnchor; import appeng.client.guidebook.color.SymbolicColor; @@ -100,6 +62,42 @@ import appeng.menu.slot.IOptionalSlot; import appeng.menu.slot.ResizableSlot; import appeng.util.ConfigMenuInventory; +import com.google.common.base.Stopwatch; +import com.mojang.blaze3d.platform.InputConstants; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ComponentRenderUtils; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.MustBeInvokedByOverriders; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; public abstract class AEBaseScreen extends AbstractContainerScreen { private static final Logger LOG = LoggerFactory.getLogger(AEBaseScreen.class); @@ -348,8 +346,8 @@ private void renderTooltips(GuiGraphics guiGraphics, int mouseX, int mouseY) { var area = tooltipWidget.getTooltipArea(); if (mouseX >= area.getX() && mouseY >= area.getY() && - mouseX < area.getX() + area.getWidth() - && mouseY < area.getY() + area.getHeight()) { + mouseX < area.getX() + area.getWidth() + && mouseY < area.getY() + area.getHeight()) { var tooltip = new Tooltip(tooltipWidget.getTooltipMessage()); if (!tooltip.getContent().isEmpty()) { drawTooltipWithHeader(guiGraphics, tooltip, mouseX, mouseY); @@ -498,7 +496,7 @@ public void drawFG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX @Override protected final void renderBg(GuiGraphics guiGraphics, float f, int x, - int y) { + int y) { this.drawBG(guiGraphics, leftPos, topPos, x, y, f); @@ -639,8 +637,8 @@ protected void slotClicked(@Nullable Slot slot, int slotIdx, int mouseButton, Cl } if (this.drag_click.size() <= 1 - && mouseButton == InputConstants.MOUSE_BUTTON_RIGHT - && getEmptyingAction(slot, menu.getCarried()) != null) { + && mouseButton == InputConstants.MOUSE_BUTTON_RIGHT + && getEmptyingAction(slot, menu.getCarried()) != null) { var p = new InventoryActionPacket(InventoryAction.EMPTY_ITEM, slotIdx, 0); PacketDistributor.sendToServer(p); return; @@ -671,7 +669,7 @@ && getEmptyingAction(slot, menu.getCarried()) != null) { } if (slot != null && - InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_SPACE)) { + InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_SPACE)) { int slotNum = slot.index; final InventoryActionPacket p = new InventoryActionPacket(InventoryAction.MOVE_REGION, slotNum, 0); PacketDistributor.sendToServer(p); @@ -682,7 +680,7 @@ && getEmptyingAction(slot, menu.getCarried()) != null) { this.disableShiftClick = true; if (this.dbl_whichItem.isEmpty() || this.bl_clicked != slot - || this.dbl_clickTimer.elapsed(TimeUnit.MILLISECONDS) > 250) { + || this.dbl_clickTimer.elapsed(TimeUnit.MILLISECONDS) > 250) { // some simple double click logic. this.bl_clicked = slot; this.dbl_clickTimer = Stopwatch.createStarted(); @@ -693,8 +691,8 @@ && getEmptyingAction(slot, menu.getCarried()) != null) { final List slots = this.getInventorySlots(); for (Slot inventorySlot : slots) { if (inventorySlot != null && inventorySlot.mayPickup(getPlayer()) && inventorySlot.hasItem() - && isSameInventory(inventorySlot, slot) - && AbstractContainerMenu.canItemQuickReplace(inventorySlot, this.dbl_whichItem, true)) { + && isSameInventory(inventorySlot, slot) + && AbstractContainerMenu.canItemQuickReplace(inventorySlot, this.dbl_whichItem, true)) { this.slotClicked(inventorySlot, inventorySlot.index, 0, ClickType.QUICK_MOVE); } } @@ -739,7 +737,7 @@ protected boolean checkHotbarKeyPressed(int keyCode, int scanCode) { final List slots = this.getInventorySlots(); for (Slot s : slots) { if (s.slot == j && s.container == this.menu.getPlayerInventory() - && !s.mayPickup(this.menu.getPlayerInventory().player)) { + && !s.mayPickup(this.menu.getPlayerInventory().player)) { return false; } } @@ -750,7 +748,7 @@ protected boolean checkHotbarKeyPressed(int keyCode, int scanCode) { } else { for (Slot s : slots) { if (s.slot == j - && s.container == this.menu.getPlayerInventory()) { + && s.container == this.menu.getPlayerInventory()) { ServerboundPacket message = new SwapSlotsPacket(s.index, theSlot.index); PacketDistributor.sendToServer(message); return true; @@ -775,7 +773,14 @@ protected boolean isHovering(Slot slot, double x, double y) { } public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, - float partialTicks) { + float partialTicks) { + + var background = style.getBackground(); + if (background != null) { + background.dest(offsetX, offsetY).blit(guiGraphics); + } + + var bgLayer = new SpriteLayer(); var generatedBackground = style.getGeneratedBackground(); if (generatedBackground != null) { @@ -784,14 +789,51 @@ public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX 0, 0, generatedBackground.getWidth(), generatedBackground.getHeight()); - blitter.blit(guiGraphics, offsetX, offsetY); + blitter.render(bgLayer, 0, 0xffffffff); } - var background = style.getBackground(); - if (background != null) { - background.dest(offsetX, offsetY).blit(guiGraphics); - } + // Group all slots by semantic. We make the assumption here + // that visually the slots belonging to the same semantic are contiguous. + for (var semantic : menu.getSlotBySemantic().keySet()) { + var slots = menu.getSlotBySemantic().get(semantic); + int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE; + for (var slot : slots) { + if (!slot.isActive()) { + continue; + } + + // Slots are 16x16 but our border is 18x18 + minX = Math.min(minX, slot.x - 1); + minY = Math.min(minY, slot.y - 1); + maxX = Math.max(maxX, slot.x + 17); + maxY = Math.max(maxY, slot.y + 17); + } + + if (minX == Integer.MAX_VALUE) { + continue; // No visible slots found + } + bgLayer.fillSprite( + AppEng.makeId("slot"), + minX, + minY, + 1, + maxX - minX , + maxY - minY, + 0xffffffff + ); + bgLayer.fillSprite( + AppEng.makeId("slot_border"), + minX, + minY, + 2, + maxX - minX , + maxY - minY, + 0xffffffff + ); + } + + bgLayer.render(guiGraphics.pose(), offsetX, offsetY, 0); } public void drawItem(GuiGraphics guiGraphics, int x, int y, ItemStack is) { diff --git a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java index ad0db4c49ec..2ced2c8cd49 100644 --- a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java +++ b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java @@ -1,24 +1,13 @@ package appeng.client.gui.widgets; -import java.util.ArrayList; -import java.util.List; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferUploader; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.Tesselator; -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.blaze3d.vertex.VertexFormat; - -import org.joml.Matrix4f; - +import appeng.client.gui.assets.GuiAssets; +import appeng.core.AppEng; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.Rect2i; import net.minecraft.resources.ResourceLocation; -import appeng.client.gui.assets.GuiAssets; -import appeng.core.AppEng; +import java.util.ArrayList; +import java.util.List; public class PanelBlitter { @@ -76,7 +65,12 @@ public void blit(GuiGraphics graphics, int xOffset, int yOffset) { } public void blit(GuiGraphics graphics, int xOffset, int yOffset, int zOffset, int color) { + SpriteLayer layer = new SpriteLayer(); + render(layer, 0, color); + layer.render(graphics.pose(), xOffset, yOffset, zOffset); + } + public void render(SpriteLayer layer, int z, int color) { // Update processed rectangles lazily if (processedRects.size() != rects.size()) { processedRects.clear(); @@ -99,9 +93,6 @@ public void blit(GuiGraphics graphics, int xOffset, int yOffset, int zOffset, in } } - var builder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - var matrix = graphics.pose().last().pose(); - for (var rect : processedRects) { var outerTop = rect.outerTop(); var outerRight = rect.outerRight(); @@ -145,8 +136,7 @@ public void blit(GuiGraphics graphics, int xOffset, int yOffset, int zOffset, in default -> throw new IndexOutOfBoundsException("side"); } - renderEdge(matrix, builder, edge.style, side, xOffset + el, yOffset + et, xOffset + er, - yOffset + eb, zOffset, color); + renderEdge(layer, edge.style, side, el, et, er, eb, z, color); } } @@ -170,39 +160,31 @@ public void blit(GuiGraphics graphics, int xOffset, int yOffset, int zOffset, in var x = switch (i) { // Left aligned - default -> xOffset + rect.x; + default -> rect.x; // Right aligned - case 1, 2 -> xOffset + rect.outerRight() - width; + case 1, 2 -> rect.outerRight() - width; }; var y = switch (i) { // Top aligned - default -> yOffset + rect.y; + default -> rect.y; // Bottom aligned - case 2, 3 -> yOffset + rect.outerBottom() - height; + case 2, 3 -> rect.outerBottom() - height; }; - cornerStyle.addQuad(matrix, builder, x, y, zOffset, width, height, color); + cornerStyle.addQuad(layer, x, y, z, width, height, color); } - CENTER.addQuad(matrix, builder, xOffset + innerLeft, yOffset + innerTop, zOffset, + CENTER.addQuad(layer, innerLeft, innerTop, z, innerRight - innerLeft, innerBottom - innerTop, color); } - - try (var meshData = builder.build()) { - if (meshData != null) { - RenderSystem.setShaderTexture(0, CENTER.nineSlice.atlasLocation()); - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); - BufferUploader.drawWithShader(meshData); - } - } } /** * Rendering an edge potentially involves rendering a start or end cap if the edge hits an adjacent rectangle. */ - private void renderEdge(Matrix4f matrix, - VertexConsumer vertices, + private void renderEdge( + SpriteLayer layer, EdgeStyle style, int side, int left, @@ -222,7 +204,7 @@ private void renderEdge(Matrix4f matrix, case 2 -> BOTTOM_BORDER; case 3 -> LEFT_BORDER; }; - edgeStyle.addQuad(matrix, vertices, left, top, z, right - left, bottom - top, color); + edgeStyle.addQuad(layer, left, top, z, right - left, bottom - top, color); } else { SpriteSlice innerStartCorner; SpriteSlice innerEndCorner; @@ -255,32 +237,31 @@ private void renderEdge(Matrix4f matrix, if (side == 1 || side == 3) { // Vertical if (innerStartCorner != null) { - innerStartCorner.addQuad(matrix, vertices, left, top, z, innerStartCorner.width(), + innerStartCorner.addQuad(layer, left, top, z, innerStartCorner.width(), innerStartCorner.height(), color); top += innerStartCorner.height(); } if (innerEndCorner != null) { - innerEndCorner.addQuad(matrix, vertices, left, bottom - innerEndCorner.height(), z, + innerEndCorner.addQuad(layer, left, bottom - innerEndCorner.height(), z, innerEndCorner.width(), innerEndCorner.height(), color); bottom -= innerEndCorner.height(); } } else { // Horizontal if (innerStartCorner != null) { - innerStartCorner.addQuad(matrix, vertices, left, top, z, innerStartCorner.width(), + innerStartCorner.addQuad(layer, left, top, z, innerStartCorner.width(), innerStartCorner.height(), color); left += innerStartCorner.width(); } if (innerEndCorner != null) { - innerEndCorner.addQuad(matrix, vertices, right - innerEndCorner.width(), top, z, + innerEndCorner.addQuad(layer, right - innerEndCorner.width(), top, z, innerEndCorner.width(), innerEndCorner.height(), color); right -= innerEndCorner.width(); } } if (right - left > 0 && bottom - top > 0) { CENTER.addQuad( - matrix, - vertices, + layer, left, top, z, right - left, bottom - top, color); @@ -295,7 +276,7 @@ public Edge(int start, int end) { } private final class Rectangle { - SpriteSlice[] corners = new SpriteSlice[] { + SpriteSlice[] corners = new SpriteSlice[]{ OUTER_TOP_LEFT, OUTER_TOP_RIGHT, OUTER_BOTTOM_RIGHT, @@ -485,8 +466,7 @@ int width() { return slice % 3 == 2 ? nineSlice.border().right() : nineSlice.border().left(); } - public void addQuad(Matrix4f matrix, VertexConsumer vertices, float x, float y, float z, float width, - float height, int color) { + public void addQuad(SpriteLayer layer, float x, float y, float z, float width, float height, int color) { int row = slice / 3; int col = slice % 3; @@ -496,11 +476,7 @@ public void addQuad(Matrix4f matrix, VertexConsumer vertices, float x, float y, var minV = uv[4 + row]; var maxV = uv[4 + row + 1]; - vertices.addVertex(matrix, x, y, z).setUv(minU, minV).setColor(color); - vertices.addVertex(matrix, x, y + height, z).setUv(minU, maxV).setColor(color); - vertices.addVertex(matrix, x + width, y + height, z).setUv(maxU, maxV).setColor(color); - vertices.addVertex(matrix, x + width, y, z).setUv(maxU, minV).setColor(color); + layer.addQuad(x, y, z, width, height, color, minU, maxU, minV, maxV); } } - } diff --git a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java new file mode 100644 index 00000000000..ce4c655715a --- /dev/null +++ b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java @@ -0,0 +1,163 @@ +package appeng.client.gui.widgets; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.BufferUploader; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling; +import net.minecraft.resources.ResourceLocation; + +/** + * Helper to build and draw a layer of sprites in a single draw-call. + */ +public final class SpriteLayer { + /** + * @see net.minecraft.client.gui.GuiSpriteManager + */ + private static final ResourceLocation GUI_SPRITE_ATLAS = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png"); + private final ResourceLocation atlasLocation; + private BufferBuilder builder; + + public SpriteLayer() { + atlasLocation = GUI_SPRITE_ATLAS; + builder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); + } + + public void addSprite(ResourceLocation sprite, int x, int y) { + + } + + public void fillSprite(ResourceLocation id, float x, float y, float z, float width, float height, int color) { + // Too large values for width / height cause immediate crashes of the VM due to graphics driver bugs + // These maximum values are picked without too much thought. + width = Math.min(65535, width); + height = Math.min(65535, height); + + var guiSprites = Minecraft.getInstance().getGuiSprites(); + var sprite = guiSprites.getSprite(id); + var scaling = guiSprites.getSpriteScaling(sprite); + switch (scaling) { + case GuiSpriteScaling.Tile tiled -> { + fillTiled(x, y, z, width, height, color, tiled.width(), tiled.height(), sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); + } + case GuiSpriteScaling.Stretch stretch -> { + addQuad(x, y, z, width, height, color, sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); + } + case GuiSpriteScaling.NineSlice nineSlice -> { + addTiledNineSlice(id, x, y, z, width, height, color, nineSlice.width(), nineSlice.height(), nineSlice.border(), sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); + } + default -> { + } + } + } + + private void addTiledNineSlice(ResourceLocation id, + float x, + float y, + float z, + float width, + float height, + int color, + float nineSliceWidth, + float nineSliceHeight, + GuiSpriteScaling.NineSlice.Border border, + float u0, + float u1, + float v0, + float v1) { + + var leftWidth = Math.min(border.left(), width / 2); + var rightWidth = Math.min(border.right(), width / 2); + var topHeight = Math.min(border.top(), height / 2); + var bottomHeight = Math.min(border.bottom(), height / 2); + var innerWidth = nineSliceWidth - border.left() - border.right(); + var innerHeight = nineSliceHeight - border.top() - border.bottom(); + + // The U/V values for the cuts through the nine-slice we'll use + var leftU = u0 + leftWidth / nineSliceWidth * (u1 - u0); + var rightU = u1 - rightWidth / nineSliceWidth * (u1 - u0); + var topV = v0 + topHeight / nineSliceHeight * (v1 - v0); + var bottomV = v1 - bottomHeight / nineSliceHeight * (v1 - v0); + + // Destination pixel values of the inner rectangle + var dstInnerLeft = x + leftWidth; + var dstInnerTop = y + topHeight; + var dstInnerRight = x + width - rightWidth; + var dstInnerBottom = y + height - bottomHeight; + var dstInnerWidth = dstInnerRight - dstInnerLeft; + var dstInnerHeight = dstInnerBottom - dstInnerTop; + + // Corners are always untiled, but may be cropped + addQuad(x, y, z, leftWidth, topHeight, color, u0, leftU, v0, topV); // Top left + addQuad(dstInnerRight, y, z, rightWidth, topHeight, color, rightU, u1, v0, topV); // Top right + addQuad(dstInnerRight, dstInnerBottom, z, rightWidth, bottomHeight, color, rightU, u1, bottomV, v1); // Bottom right + addQuad(x, dstInnerBottom, z, leftWidth, bottomHeight, color, u0, leftU, bottomV, v1); // Bottom left + + // The edges are tiled + fillTiled(dstInnerLeft, y, z, dstInnerWidth, topHeight, color, innerWidth, border.top(), leftU, rightU, v0, topV); // Top Edge + fillTiled(dstInnerLeft, dstInnerBottom, z, dstInnerWidth, bottomHeight, color, innerWidth, border.bottom(), leftU, rightU, bottomV, v1); // Bottom Edge + fillTiled(x, dstInnerTop, z, leftWidth, dstInnerHeight, color, border.left(), innerHeight, u0, leftU, topV, bottomV); // Left Edge + fillTiled(dstInnerRight, dstInnerTop, z, rightWidth, dstInnerHeight, color, border.right(), innerHeight, rightU, u1, topV, bottomV); // Right Edge + + // The center is tiled too + fillTiled(dstInnerLeft, dstInnerTop, z, dstInnerWidth, dstInnerHeight, color, innerWidth, innerHeight, leftU, rightU, topV, bottomV); + } + + private void fillTiled(float x, float y, float z, float width, float height, int color, float destTileWidth, float destTileHeight, float u0, float u1, float v0, float v1) { + if (destTileWidth <= 0 || destTileHeight <= 0) { + return; + } + + var right = x + width; + var bottom = y + height; + for (var cy = y; cy < bottom; cy += destTileHeight) { + // This handles not stretching the potentially partial last column + var tileHeight = Math.min(bottom - cy, destTileHeight); + var tileV1 = v0 + (v1 - v0) * tileHeight / destTileHeight; + + for (var cx = x; cx < right; cx += destTileWidth) { + // This handles not stretching the potentially partial last row + var tileWidth = Math.min(right - cx, destTileWidth); + var tileU1 = u0 + (u1 - u0) * tileWidth / destTileWidth; + + addQuad(cx, cy, z, tileWidth, tileHeight, color, u0, tileU1, v0, tileV1); + } + } + } + + public void addQuad(float x, float y, float z, float width, float height, int color, float minU, float maxU, float minV, float maxV) { + builder.addVertex(x, y, z).setUv(minU, minV).setColor(color); + builder.addVertex(x, y + height, z).setUv(minU, maxV).setColor(color); + builder.addVertex(x + width, y + height, z).setUv(maxU, maxV).setColor(color); + builder.addVertex(x + width, y, z).setUv(maxU, minV).setColor(color); + } + + public void render(PoseStack poseStack, int x, int y, int z) { + if (builder == null) { + throw new IllegalStateException("Already rendered."); + } + + try (var meshData = builder.build()) { + builder = null; + if (meshData == null) { + return; + } + + RenderSystem.setShaderTexture(0, atlasLocation); + RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + var modelViewStack = RenderSystem.getModelViewStack(); + modelViewStack.pushMatrix(); + modelViewStack.mul(poseStack.last().pose()); + modelViewStack.translate(x, y, z); + RenderSystem.applyModelViewMatrix(); + BufferUploader.drawWithShader(meshData); + modelViewStack.popMatrix(); + RenderSystem.applyModelViewMatrix(); + } + } +} diff --git a/src/main/java/appeng/menu/AEBaseMenu.java b/src/main/java/appeng/menu/AEBaseMenu.java index b20e0f643dd..6dae1a31bb6 100644 --- a/src/main/java/appeng/menu/AEBaseMenu.java +++ b/src/main/java/appeng/menu/AEBaseMenu.java @@ -29,6 +29,9 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -271,6 +274,10 @@ public List getSlots(SlotSemantic semantic) { return slotsBySemantic.get(semantic); } + public ListMultimap getSlotBySemantic() { + return Multimaps.unmodifiableListMultimap(slotsBySemantic); + } + @Override protected Slot addSlot(Slot newSlot) { if (newSlot instanceof AppEngSlot s) { diff --git a/src/main/resources/assets/ae2/screens/cell_workbench.json b/src/main/resources/assets/ae2/screens/cell_workbench.json index 39d862eebf2..3c93086f848 100644 --- a/src/main/resources/assets/ae2/screens/cell_workbench.json +++ b/src/main/resources/assets/ae2/screens/cell_workbench.json @@ -5,9 +5,9 @@ "common/player_inventory.json", "common/toolbox.json" ], - "background": { - "texture": "guis/cellworkbench.png", - "srcRect": [0, 0, 176, 253] + "generatedBackground": { + "width": 176, + "height": 253 }, "slots": { "STORAGE_CELL": { diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot.png b/src/main/resources/assets/ae2/textures/gui/sprites/slot.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5f79553fefaa8730175293df4d69551fcd8503 GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1SD^+kpz+qoCO|{#S9F5M?jcysy3fAP|(8D z#W6%h)dEe5d_l071`zRvG-Vt#PTomWX_(&9uDDYq8qh7DZ@r}rg16+GH1 a&G6td>z{Kud5u5=7(8A5T-G@yGywo84=0fT literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_border.png.mcmeta b/src/main/resources/assets/ae2/textures/gui/sprites/slot_border.png.mcmeta new file mode 100644 index 00000000000..fba7643157a --- /dev/null +++ b/src/main/resources/assets/ae2/textures/gui/sprites/slot_border.png.mcmeta @@ -0,0 +1,15 @@ +{ + "gui": { + "scaling": { + "type": "nine_slice", + "width": 18, + "height": 18, + "border": { + "left": 1, + "top": 1, + "right": 1, + "bottom": 1 + } + } + } +} \ No newline at end of file From d19f22a5ce3868a0f72efcd9e1cd081924a5442d Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Thu, 8 Aug 2024 00:26:36 +0200 Subject: [PATCH 2/6] More slot-grid work. --- .../appeng/client/gui/widgets/SpriteLayer.java | 4 ++++ .../client/guidebook/document/block/LytSlot.java | 5 +++-- .../guidebook/document/block/LytSlotGrid.java | 13 +++++++++---- .../client/guidebook/render/RenderContext.java | 4 ++++ .../assets/ae2/ae2guide/gui/slot_light.png | Bin 123 -> 0 bytes .../assets/ae2/textures/guis/cellworkbench.png | Bin 1208 -> 0 bytes 6 files changed, 20 insertions(+), 6 deletions(-) delete mode 100644 src/main/resources/assets/ae2/ae2guide/gui/slot_light.png delete mode 100644 src/main/resources/assets/ae2/textures/guis/cellworkbench.png diff --git a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java index ce4c655715a..98adef14310 100644 --- a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java +++ b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java @@ -131,6 +131,10 @@ private void fillTiled(float x, float y, float z, float width, float height, int } public void addQuad(float x, float y, float z, float width, float height, int color, float minU, float maxU, float minV, float maxV) { + if (width < 0 || height < 0) { + return; + } + builder.addVertex(x, y, z).setUv(minU, minV).setColor(color); builder.addVertex(x, y + height, z).setUv(minU, maxV).setColor(color); builder.addVertex(x + width, y + height, z).setUv(maxU, maxV).setColor(color); diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java index 25de64bd37f..d2292323fcf 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java @@ -3,6 +3,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; +import appeng.client.guidebook.color.ColorValue; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -20,7 +21,7 @@ * Renders a standard Minecraft GUI slot. */ public class LytSlot extends LytBlock implements InteractiveElement { - public static final ResourceLocation SLOT_LIGHT = AppEng.makeId("ae2guide/gui/slot_light.png"); + public static final ResourceLocation SLOT_LIGHT = AppEng.makeId("slot"); public static final ResourceLocation SLOT_DARK = AppEng.makeId("ae2guide/gui/slot_dark.png"); public static final ResourceLocation LARGE_SLOT_LIGHT = AppEng.makeId("ae2guide/gui/large_slot_light.png"); public static final ResourceLocation LARGE_SLOT_DARK = AppEng.makeId("ae2guide/gui/large_slot_dark.png"); @@ -81,7 +82,7 @@ public void render(RenderContext context) { } else { texture = context.isDarkMode() ? SLOT_DARK : SLOT_LIGHT; } - context.fillTexturedRect(bounds, texture); + context.drawIcon(bounds.x(), bounds.y(), texture); var padding = largeSlot ? LARGE_PADDING : PADDING; diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java b/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java index f87a2dcc3ce..f3824f48a84 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java @@ -1,14 +1,14 @@ package appeng.client.guidebook.document.block; -import java.util.List; - -import net.minecraft.world.item.crafting.Ingredient; - import appeng.client.gui.Icon; import appeng.client.guidebook.color.ConstantColor; import appeng.client.guidebook.document.LytRect; import appeng.client.guidebook.layout.LayoutContext; import appeng.client.guidebook.render.RenderContext; +import appeng.core.AppEng; +import net.minecraft.world.item.crafting.Ingredient; + +import java.util.List; public class LytSlotGrid extends LytBox { private final int width; @@ -129,6 +129,11 @@ public void render(RenderContext context) { } } + context.guiGraphics().blitSprite( + AppEng.makeId("slot_border"), + bounds.x(), bounds.y(), 100, bounds.width(), bounds.height() + ); + super.render(context); } diff --git a/src/main/java/appeng/client/guidebook/render/RenderContext.java b/src/main/java/appeng/client/guidebook/render/RenderContext.java index 2e46dd35798..81c14b4308d 100644 --- a/src/main/java/appeng/client/guidebook/render/RenderContext.java +++ b/src/main/java/appeng/client/guidebook/render/RenderContext.java @@ -83,6 +83,10 @@ default void fillTexturedRect(LytRect rect, TextureAtlasSprite sprite, ColorValu sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1()); } + default void drawIcon(int x, int y, ResourceLocation guiSprite) { + drawIcon(x, y, guiSprite, ConstantColor.WHITE); + } + default void drawIcon(int x, int y, ResourceLocation guiSprite, ColorValue color) { var sprite = Minecraft.getInstance().getGuiSprites().getSprite(guiSprite); drawIcon(x, y, sprite, color); diff --git a/src/main/resources/assets/ae2/ae2guide/gui/slot_light.png b/src/main/resources/assets/ae2/ae2guide/gui/slot_light.png deleted file mode 100644 index 218df6e9ff99d80e67ac5a12b0d1ee74449ed9c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1SD^+kpz+qoCO|{#S9F5M?jcysy3fAP*B&? z#W6%FVdQ&MBb@07-ivx&QzG diff --git a/src/main/resources/assets/ae2/textures/guis/cellworkbench.png b/src/main/resources/assets/ae2/textures/guis/cellworkbench.png deleted file mode 100644 index 3c7ef544dbe2e20e0882144ddc251db817726b89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1208 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$6%U;1OBOz`%C|gc+x5^GP!> zuw;3;+TIHm#V{<*}zhS!t<@$g+a7)`3pBoRI=cy&IMG zJ8)_3cB&H$c4XacwA*~M`F+c&s@4CkuP&dv@!g!7&#!*BGirbR-5&3kwm$K!S#r9C5nZABw1`c83(CwdAA8u~G z+-!3~rr5%6AEU#KbNLL%i;R0L&swfq8a++A@l51VnQ4jK42ev{p*$9&bkX97wtNPG zXKo6`eN}zZjnke2Sz(6lsW+G$W-t(iZk)T`cz;h_I{0lDAz|Fr^f)%p4P jH(zJ)NrD7dJ>&cpE4FRk+E4;4iWody{an^LB{Ts53T-$K From 60e075b45f73afd7a1a4bce6d4847530a57ffcf3 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Fri, 9 Aug 2024 18:19:10 +0200 Subject: [PATCH 3/6] TMP --- .../client/gui/widgets/SpriteLayer.java | 1 - .../guidebook/document/block/LytSlot.java | 27 ++++---- .../guidebook/document/block/LytSlotGrid.java | 7 +-- .../guidebook/render/RenderContext.java | 62 +++++++++++-------- 4 files changed, 54 insertions(+), 43 deletions(-) diff --git a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java index 98adef14310..e672fe46feb 100644 --- a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java +++ b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java @@ -29,7 +29,6 @@ public SpriteLayer() { } public void addSprite(ResourceLocation sprite, int x, int y) { - } public void fillSprite(ResourceLocation id, float x, float y, float z, float width, float height, int color) { diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java index d2292323fcf..f3d30e8d82b 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java @@ -1,14 +1,5 @@ package appeng.client.guidebook.document.block; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import appeng.client.guidebook.color.ColorValue; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; - import appeng.client.guidebook.document.LytRect; import appeng.client.guidebook.document.interaction.GuideTooltip; import appeng.client.guidebook.document.interaction.InteractiveElement; @@ -16,6 +7,13 @@ import appeng.client.guidebook.layout.LayoutContext; import appeng.client.guidebook.render.RenderContext; import appeng.core.AppEng; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +import java.util.Optional; +import java.util.concurrent.TimeUnit; /** * Renders a standard Minecraft GUI slot. @@ -23,7 +21,7 @@ public class LytSlot extends LytBlock implements InteractiveElement { public static final ResourceLocation SLOT_LIGHT = AppEng.makeId("slot"); public static final ResourceLocation SLOT_DARK = AppEng.makeId("ae2guide/gui/slot_dark.png"); - public static final ResourceLocation LARGE_SLOT_LIGHT = AppEng.makeId("ae2guide/gui/large_slot_light.png"); + public static final ResourceLocation LARGE_SLOT_LIGHT = AppEng.makeId("slot_large"); public static final ResourceLocation LARGE_SLOT_DARK = AppEng.makeId("ae2guide/gui/large_slot_dark.png"); private static final int ITEM_SIZE = 16; @@ -42,7 +40,7 @@ public LytSlot(Ingredient ingredient) { } public LytSlot(ItemStack stack) { - this.stacks = new ItemStack[] { stack }; + this.stacks = new ItemStack[]{stack}; } public boolean isLargeSlot() { @@ -82,7 +80,12 @@ public void render(RenderContext context) { } else { texture = context.isDarkMode() ? SLOT_DARK : SLOT_LIGHT; } - context.drawIcon(bounds.x(), bounds.y(), texture); + context.fillIcon(bounds, texture); + + // Render a border around the slot if we're not contained in a slot grid + if (!(parent instanceof LytSlotGrid)) { + context.fillIcon(bounds, AppEng.makeId("slot_border")); + } var padding = largeSlot ? LARGE_PADDING : PADDING; diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java b/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java index f3824f48a84..2d8b8149746 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java @@ -129,12 +129,9 @@ public void render(RenderContext context) { } } - context.guiGraphics().blitSprite( - AppEng.makeId("slot_border"), - bounds.x(), bounds.y(), 100, bounds.width(), bounds.height() - ); - super.render(context); + + context.fillIcon(bounds, AppEng.makeId("slot_border")); } private int getSlotIndex(int col, int row) { diff --git a/src/main/java/appeng/client/guidebook/render/RenderContext.java b/src/main/java/appeng/client/guidebook/render/RenderContext.java index 81c14b4308d..07823d5f3fa 100644 --- a/src/main/java/appeng/client/guidebook/render/RenderContext.java +++ b/src/main/java/appeng/client/guidebook/render/RenderContext.java @@ -1,11 +1,17 @@ package appeng.client.guidebook.render; +import appeng.api.stacks.AEFluidKey; +import appeng.client.gui.style.FluidBlitter; +import appeng.client.gui.widgets.PanelBlitter; +import appeng.client.gui.widgets.SpriteLayer; +import appeng.client.guidebook.color.ColorValue; +import appeng.client.guidebook.color.ConstantColor; +import appeng.client.guidebook.color.LightDarkMode; +import appeng.client.guidebook.document.LytRect; +import appeng.client.guidebook.layout.MinecraftFontMetrics; +import appeng.client.guidebook.style.ResolvedTextStyle; import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; - -import org.joml.Matrix4f; -import org.joml.Vector3f; - import net.minecraft.client.Minecraft; import net.minecraft.client.StringSplitter; import net.minecraft.client.gui.Font; @@ -21,16 +27,8 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec2; import net.neoforged.neoforge.fluids.FluidStack; - -import appeng.api.stacks.AEFluidKey; -import appeng.client.gui.style.FluidBlitter; -import appeng.client.gui.widgets.PanelBlitter; -import appeng.client.guidebook.color.ColorValue; -import appeng.client.guidebook.color.ConstantColor; -import appeng.client.guidebook.color.LightDarkMode; -import appeng.client.guidebook.document.LytRect; -import appeng.client.guidebook.layout.MinecraftFontMetrics; -import appeng.client.guidebook.style.ResolvedTextStyle; +import org.joml.Matrix4f; +import org.joml.Vector3f; public interface RenderContext { @@ -53,13 +51,13 @@ default PoseStack poseStack() { void fillRect(LytRect rect, ColorValue topLeft, ColorValue topRight, ColorValue bottomRight, ColorValue bottomLeft); default void fillTexturedRect(LytRect rect, AbstractTexture texture, ColorValue topLeft, ColorValue topRight, - ColorValue bottomRight, ColorValue bottomLeft) { + ColorValue bottomRight, ColorValue bottomLeft) { // Just use the entire texture by default fillTexturedRect(rect, texture, topLeft, topRight, bottomRight, bottomLeft, 0, 0, 1, 1); } void fillTexturedRect(LytRect rect, AbstractTexture texture, ColorValue topLeft, ColorValue topRight, - ColorValue bottomRight, ColorValue bottomLeft, float u0, float v0, float u1, float v1); + ColorValue bottomRight, ColorValue bottomLeft, float u0, float v0, float u1, float v1); default void fillTexturedRect(LytRect rect, GuidePageTexture texture) { fillTexturedRect(rect, texture.use(), ConstantColor.WHITE); @@ -93,15 +91,27 @@ default void drawIcon(int x, int y, ResourceLocation guiSprite, ColorValue color } default void drawIcon(int x, int y, TextureAtlasSprite sprite, ColorValue color) { - var u0 = sprite.getU0(); - var v0 = sprite.getV0(); - var u1 = sprite.getU1(); - var v1 = sprite.getV1(); - var contents = sprite.contents(); - var texture = Minecraft.getInstance().getTextureManager().getTexture(sprite.atlasLocation()); - fillTexturedRect(new LytRect(x, y, contents.width(), contents.height()), texture, color, color, color, color, - u0, v0, u1, v1); + fillIcon(x, y, contents.width(), contents.height(), sprite, color); + } + + default void fillIcon(LytRect bounds, ResourceLocation guiSprite) { + fillIcon(bounds.x(), bounds.y(), bounds.width(), bounds.height(), guiSprite); + } + + default void fillIcon(int x, int y, int width, int height, ResourceLocation guiSprite) { + fillIcon(x, y, width, height, guiSprite, ConstantColor.WHITE); + } + + default void fillIcon(int x, int y, int width, int height, ResourceLocation guiSprite, ColorValue color) { + var sprite = Minecraft.getInstance().getGuiSprites().getSprite(guiSprite); + fillIcon(x, y, width, height, sprite, color); + } + + default void fillIcon(int x, int y, int width, int height, TextureAtlasSprite sprite, ColorValue color) { + var spriteLayer = new SpriteLayer(); + spriteLayer.fillSprite(sprite.contents().name(), 0, 0, 0, width, height, resolveColor(color)); + spriteLayer.render(poseStack(), x, y, 0); } default void fillTexturedRect(LytRect rect, ResourceLocation textureId) { @@ -138,7 +148,7 @@ default void renderTextCenteredIn(String text, ResolvedTextStyle style, LytRect var lineHeight = fontMetrics.getLineHeight(style); var overallHeight = splitLines.size() * lineHeight; var overallWidth = (int) (splitLines.stream().mapToDouble(splitter::stringWidth).max().orElse(0f) - * style.fontScale()); + * style.fontScale()); var textRect = new LytRect(0, 0, overallWidth, overallHeight); textRect = textRect.centerIn(rect); @@ -218,12 +228,14 @@ default void renderItem(ItemStack stack, int x, int y, float width, float height default void renderFluid(Fluid fluid, int x, int y, int z, int width, int height) { FluidBlitter.create(AEFluidKey.of(fluid)) .dest(x, y, width, height) + .zOffset(z) .blit(guiGraphics()); } default void renderFluid(FluidStack stack, int x, int y, int z, int width, int height) { FluidBlitter.create(stack) .dest(x, y, width, height) + .zOffset(z) .blit(guiGraphics()); } From c59a5ecab687142804805a9cd97669a90cfe00b5 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sat, 10 Aug 2024 17:42:23 +0200 Subject: [PATCH 4/6] WIP --- scripts/sprite_split.py | 57 + scripts/sprites.json | 1059 +++++++++++++++++ scripts/states.png | Bin 0 -> 9593 bytes .../java/appeng/client/gui/AEBaseScreen.java | 16 +- .../appeng/client/gui/ICompositeWidget.java | 34 +- .../appeng/client/gui/WidgetContainer.java | 75 +- .../appeng/client/gui/assets/GuiAssets.java | 13 +- .../client/gui/util/RectangleMerger.java | 20 + .../client/gui/widgets/PanelBlitter.java | 13 +- .../client/gui/widgets/SpriteLayer.java | 8 +- .../client/gui/widgets/UpgradesPanel.java | 97 +- .../client/gui/widgets/VerticalButtonBar.java | 34 +- .../guidebook/document/block/LytSlot.java | 14 +- src/main/java/appeng/core/AEConfig.java | 56 +- src/main/java/appeng/hooks/DarkModeHook.java | 32 + .../java/appeng/hooks/GuiGraphicsHooks.java | 2 +- .../appeng/mixins/GuiSpriteManagerMixin.java | 29 + src/main/resources/ae2.mixins.json | 3 +- .../background_blank_pattern_darkmode.png | Bin 0 -> 200 bytes .../background_encoded_pattern_darkmode.png | Bin 0 -> 195 bytes .../icons/background_fuel_darkmode.png | Bin 0 -> 174 bytes .../icons/background_ingot_darkmode.png | Bin 0 -> 178 bytes .../icons/background_plate_darkmode.png | Bin 0 -> 153 bytes .../background_primary_output_darkmode.png | Bin 0 -> 120 bytes .../icons/background_singularity_darkmode.png | Bin 0 -> 168 bytes .../background_spatial_cell_darkmode.png | Bin 0 -> 200 bytes ...ground_spatial_cell_no_shadow_darkmode.png | Bin 0 -> 186 bytes .../background_storage_cell_darkmode.png | Bin 0 -> 204 bytes .../background_storage_component_darkmode.png | Bin 0 -> 202 bytes .../icons/background_trash_darkmode.png | Bin 0 -> 157 bytes .../icons/background_upgrade_darkmode.png | Bin 0 -> 153 bytes .../icons/background_view_cell_darkmode.png | Bin 0 -> 211 bytes .../background_wireless_booster_darkmode.png | Bin 0 -> 166 bytes .../background_wireless_term_darkmode.png | Bin 0 -> 160 bytes .../sprites/icons/horizontal_tab_darkmode.png | Bin 0 -> 148 bytes .../icons/horizontal_tab_focus_darkmode.png | Bin 0 -> 154 bytes .../horizontal_tab_selected_darkmode.png | Bin 0 -> 154 bytes .../icons/inscriber_buffer_1_darkmode.png | Bin 0 -> 105 bytes .../icons/inscriber_buffer_4_darkmode.png | Bin 0 -> 221 bytes .../gui/sprites/icons/locked_darkmode.png | Bin 0 -> 208 bytes .../sprites/icons/s_arrow_down_darkmode.png | Bin 0 -> 136 bytes .../gui/sprites/icons/s_arrow_up_darkmode.png | Bin 0 -> 129 bytes .../gui/sprites/icons/s_clear_darkmode.png | Bin 0 -> 152 bytes .../gui/sprites/icons/s_cycle_darkmode.png | Bin 0 -> 129 bytes ...s_fluid_substitution_disabled_darkmode.png | Bin 0 -> 151 bytes .../s_fluid_substitution_enabled_darkmode.png | Bin 0 -> 152 bytes .../s_substitution_disabled_darkmode.png | Bin 0 -> 131 bytes .../icons/s_substitution_enabled_darkmode.png | Bin 0 -> 131 bytes .../icons/slot_background_darkmode.png | Bin 0 -> 115 bytes ..._button_background_borderless_darkmode.png | Bin 0 -> 179 bytes ...n_background_borderless_focus_darkmode.png | Bin 0 -> 217 bytes .../icons/tab_button_background_darkmode.png | Bin 0 -> 146 bytes .../tab_button_background_focus_darkmode.png | Bin 0 -> 182 bytes .../toolbar_button_background_darkmode.png | Bin 0 -> 140 bytes ...olbar_button_background_focus_darkmode.png | Bin 0 -> 137 bytes ...olbar_button_background_hover_darkmode.png | Bin 0 -> 143 bytes .../gui/sprites/icons/unlocked_darkmode.png | Bin 0 -> 182 bytes .../gui/sprites/slot_border_darkmode.png | Bin 0 -> 141 bytes .../sprites/slot_border_darkmode.png.mcmeta | 15 + .../textures/gui/sprites/slot_darkmode.png | Bin 0 -> 150 bytes .../gui/sprites/slot_darkmode.png.mcmeta | 9 + .../ae2/textures/gui/sprites/slot_large.png | Bin 0 -> 162 bytes .../gui/sprites/slot_large.png.mcmeta | 10 + .../gui/sprites/vertical_buttons_bg.png | Bin 161 -> 0 bytes .../sprites/vertical_buttons_bg.png.mcmeta | 15 - .../textures/gui/sprites/window_darkmode.png | Bin 0 -> 165 bytes .../gui/sprites/window_darkmode.png.mcmeta | 15 + .../gui/sprites/window_inner_darkmode.png | Bin 0 -> 160 bytes .../sprites/window_inner_darkmode.png.mcmeta | 15 + 69 files changed, 1437 insertions(+), 204 deletions(-) create mode 100644 scripts/sprite_split.py create mode 100644 scripts/sprites.json create mode 100644 scripts/states.png create mode 100644 src/main/java/appeng/client/gui/util/RectangleMerger.java create mode 100644 src/main/java/appeng/hooks/DarkModeHook.java create mode 100644 src/main/java/appeng/mixins/GuiSpriteManagerMixin.java create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_blank_pattern_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_encoded_pattern_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_fuel_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_ingot_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_plate_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_primary_output_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_singularity_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_spatial_cell_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_spatial_cell_no_shadow_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_storage_cell_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_storage_component_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_trash_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_upgrade_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_view_cell_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_wireless_booster_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/background_wireless_term_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_focus_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_selected_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/inscriber_buffer_1_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/inscriber_buffer_4_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/locked_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_arrow_down_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_arrow_up_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_clear_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_cycle_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_fluid_substitution_disabled_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_fluid_substitution_enabled_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_substitution_disabled_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/s_substitution_enabled_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/slot_background_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/tab_button_background_borderless_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/tab_button_background_borderless_focus_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/tab_button_background_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/tab_button_background_focus_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_focus_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_hover_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/icons/unlocked_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png.mcmeta create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png.mcmeta create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png.mcmeta delete mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png delete mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png.mcmeta create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/window_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/window_darkmode.png.mcmeta create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/window_inner_darkmode.png create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/window_inner_darkmode.png.mcmeta diff --git a/scripts/sprite_split.py b/scripts/sprite_split.py new file mode 100644 index 00000000000..c5a568f77a8 --- /dev/null +++ b/scripts/sprite_split.py @@ -0,0 +1,57 @@ +import os +import json +from PIL import Image, ImageChops + +# Load JSON data +with open('sprites.json', 'r') as f: + sprites = json.load(f) + +# Open the states.png image +image = Image.open('states.png') + +# Create the output directory if it doesn't exist +output_dir = '../src/main/resources/assets/ae2/textures/gui/sprites/icons' +os.makedirs(output_dir, exist_ok=True) + +# Compare function to check if two images are the same using numpy arrays + +# Compare function to check if two images are the same using pixel-by-pixel comparison +def images_are_equal(img1, img2): + if img1.size != img2.size: + return False + for x in range(img1.width): + for y in range(img1.height): + p1 = img1.getpixel((x, y)) + p2 = img2.getpixel((x, y)) + + # For alpha = 0 it doesnt matter what the transparency color is + if p1[3] == 0 and p2[3] == 0: + continue + + if p1 != p2: + print(f"Diff @ {x},{y}: {p1}!={p2}") + return False + return True + + +# Extract, compare, and save each sprite +for sprite in sprites: + # Crop the sprite from the image + cropped_image = image.crop((sprite['x'], sprite['y'], sprite['x'] + sprite['w'], sprite['y'] + sprite['h'])) + + # Prepare the output filename with "@dark" appended + base_filename, ext = os.path.splitext(sprite['filename']) + output_filename = f"{base_filename}_darkmode{ext}" + output_path = os.path.join(output_dir, output_filename) + + # Path to the corresponding icon image + icon_path = os.path.join(output_dir, sprite['filename']) + + # Check if the icon file exists and compare + print(base_filename) + icon_image = Image.open(icon_path) + if images_are_equal(cropped_image, icon_image): + continue + + # Save the cropped image + cropped_image.save(output_path) diff --git a/scripts/sprites.json b/scripts/sprites.json new file mode 100644 index 00000000000..6a0af733c9a --- /dev/null +++ b/scripts/sprites.json @@ -0,0 +1,1059 @@ +[ + { + "filename": "redstone_low.png", + "x": 0, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_high.png", + "x": 16, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_pulse.png", + "x": 32, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_ignore.png", + "x": 48, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_off.png", + "x": 64, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_on.png", + "x": 80, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_above_equal.png", + "x": 192, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "redstone_below.png", + "x": 208, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "clear.png", + "x": 96, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "enter.png", + "x": 112, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "white_arrow_down.png", + "x": 128, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "locked.png", + "x": 144, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "unlocked.png", + "x": 160, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "help.png", + "x": 176, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "background_primary_output.png", + "x": 224, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "background_storage_cell.png", + "x": 240, + "y": 0, + "w": 16, + "h": 16 + }, + { + "filename": "view_mode_stored.png", + "x": 0, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "view_mode_all.png", + "x": 32, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "view_mode_crafting.png", + "x": 48, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "blocking_mode_no.png", + "x": 64, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "blocking_mode_yes.png", + "x": 80, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "back.png", + "x": 96, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "transparent_facades_off.png", + "x": 96, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "transparent_facades_on.png", + "x": 112, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "type_filter_items.png", + "x": 128, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "type_filter_fluids.png", + "x": 144, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "type_filter_all.png", + "x": 160, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "background_ore.png", + "x": 240, + "y": 16, + "w": 16, + "h": 16 + }, + { + "filename": "search_auto_focus.png", + "x": 48, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_default.png", + "x": 64, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_jei.png", + "x": 80, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_rei.png", + "x": 96, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_auto_focus_remember.png", + "x": 112, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_remember.png", + "x": 128, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_jei_auto_clear.png", + "x": 144, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "search_rei_auto_clear.png", + "x": 160, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "background_plate.png", + "x": 224, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "background_dust.png", + "x": 240, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "tab_crafting.png", + "x": 0, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "tab_processing.png", + "x": 16, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "tab_smithing.png", + "x": 32, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "tab_stonecutting.png", + "x": 48, + "y": 32, + "w": 16, + "h": 16 + }, + { + "filename": "arrow_up.png", + "x": 0, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "arrow_down.png", + "x": 16, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "arrow_right.png", + "x": 32, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "arrow_left.png", + "x": 48, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "substitution_enabled.png", + "x": 64, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "storage_filter_extractable_only.png", + "x": 80, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "storage_filter_extractable_none.png", + "x": 96, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "substitution_disabled.png", + "x": 112, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "fluid_substitution_enabled.png", + "x": 128, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "fluid_substitution_disabled.png", + "x": 144, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "filter_on_extract_enabled.png", + "x": 160, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "filter_on_extract_disabled.png", + "x": 176, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "background_ingot.png", + "x": 224, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "background_storage_component.png", + "x": 240, + "y": 48, + "w": 16, + "h": 16 + }, + { + "filename": "sort_by_name.png", + "x": 0, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "sort_by_amount.png", + "x": 16, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "cog.png", + "x": 32, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "cog_disabled.png", + "x": 48, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "level_item.png", + "x": 64, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "sort_by_inventory_tweaks.png", + "x": 80, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "sort_by_mod.png", + "x": 96, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "priority.png", + "x": 144, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "background_view_cell.png", + "x": 224, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "background_wireless_term.png", + "x": 240, + "y": 64, + "w": 16, + "h": 16 + }, + { + "filename": "fullness_empty.png", + "x": 0, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "fullness_half.png", + "x": 16, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "fullness_full.png", + "x": 32, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "level_energy.png", + "x": 48, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "pattern_access_show.png", + "x": 64, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "pattern_access_hide.png", + "x": 80, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "pattern_terminal_visible.png", + "x": 96, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "pattern_terminal_all.png", + "x": 112, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "pattern_terminal_not_full.png", + "x": 128, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "background_trash.png", + "x": 240, + "y": 80, + "w": 16, + "h": 16 + }, + { + "filename": "fuzzy_percent_25.png", + "x": 0, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "fuzzy_percent_50.png", + "x": 16, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "fuzzy_percent_75.png", + "x": 32, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "fuzzy_percent_99.png", + "x": 48, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "fuzzy_ignore.png", + "x": 64, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "inscriber_separate_sides.png", + "x": 80, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "inscriber_combined_sides.png", + "x": 96, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "auto_export_off.png", + "x": 112, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "auto_export_on.png", + "x": 128, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "inscriber_buffer_4.png", + "x": 144, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "inscriber_buffer_64.png", + "x": 160, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "inscriber_buffer_1.png", + "x": 176, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "background_wireless_booster.png", + "x": 240, + "y": 96, + "w": 16, + "h": 16 + }, + { + "filename": "condenser_output_trash.png", + "x": 0, + "y": 112, + "w": 16, + "h": 16 + }, + { + "filename": "condenser_output_matter_ball.png", + "x": 16, + "y": 112, + "w": 16, + "h": 16 + }, + { + "filename": "condenser_output_singularity.png", + "x": 32, + "y": 112, + "w": 16, + "h": 16 + }, + { + "filename": "background_encoded_pattern.png", + "x": 240, + "y": 112, + "w": 16, + "h": 16 + }, + { + "filename": "invalid.png", + "x": 0, + "y": 128, + "w": 16, + "h": 16 + }, + { + "filename": "valid.png", + "x": 16, + "y": 128, + "w": 16, + "h": 16 + }, + { + "filename": "whitelist.png", + "x": 32, + "y": 128, + "w": 16, + "h": 16 + }, + { + "filename": "blacklist.png", + "x": 48, + "y": 128, + "w": 16, + "h": 16 + }, + { + "filename": "horizontal_tab.png", + "x": 128, + "y": 128, + "w": 22, + "h": 22 + }, + { + "filename": "horizontal_tab_selected.png", + "x": 128, + "y": 150, + "w": 22, + "h": 22 + }, + { + "filename": "horizontal_tab_focus.png", + "x": 150, + "y": 128, + "w": 22, + "h": 22 + }, + { + "filename": "background_blank_pattern.png", + "x": 240, + "y": 128, + "w": 16, + "h": 16 + }, + { + "filename": "toolbar_button_background.png", + "x": 176, + "y": 128, + "w": 18, + "h": 20 + }, + { + "filename": "toolbar_button_background_focus.png", + "x": 194, + "y": 128, + "w": 18, + "h": 20 + }, + { + "filename": "toolbar_button_background_hover.png", + "x": 212, + "y": 128, + "w": 18, + "h": 20 + }, + { + "filename": "access_write.png", + "x": 0, + "y": 144, + "w": 16, + "h": 16 + }, + { + "filename": "access_read.png", + "x": 16, + "y": 144, + "w": 16, + "h": 16 + }, + { + "filename": "access_read_write.png", + "x": 32, + "y": 144, + "w": 16, + "h": 16 + }, + { + "filename": "craft_hammer.png", + "x": 48, + "y": 144, + "w": 16, + "h": 16 + }, + { + "filename": "background_chargable.png", + "x": 240, + "y": 144, + "w": 16, + "h": 16 + }, + { + "filename": "power_unit_ae.png", + "x": 0, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "power_unit_eu.png", + "x": 16, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "power_unit_j.png", + "x": 32, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "power_unit_w.png", + "x": 48, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "power_unit_rf.png", + "x": 64, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "power_unit_tr.png", + "x": 80, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "background_singularity.png", + "x": 240, + "y": 160, + "w": 16, + "h": 16 + }, + { + "filename": "copy_mode_on.png", + "x": 80, + "y": 176, + "w": 16, + "h": 16 + }, + { + "filename": "background_spatial_cell_no_shadow.png", + "x": 224, + "y": 176, + "w": 16, + "h": 16 + }, + { + "filename": "background_spatial_cell.png", + "x": 240, + "y": 176, + "w": 16, + "h": 16 + }, + { + "filename": "copy_mode_off.png", + "x": 80, + "y": 192, + "w": 16, + "h": 16 + }, + { + "filename": "tab_button_background_borderless.png", + "x": 128, + "y": 192, + "w": 25, + "h": 22 + }, + { + "filename": "tab_button_background.png", + "x": 160, + "y": 192, + "w": 20, + "h": 20 + }, + { + "filename": "slot_background.png", + "x": 192, + "y": 192, + "w": 18, + "h": 18 + }, + { + "filename": "background_fuel.png", + "x": 240, + "y": 192, + "w": 16, + "h": 16 + }, + { + "filename": "terminal_style_small.png", + "x": 0, + "y": 208, + "w": 16, + "h": 16 + }, + { + "filename": "terminal_style_medium.png", + "x": 16, + "y": 208, + "w": 16, + "h": 16 + }, + { + "filename": "terminal_style_tall.png", + "x": 32, + "y": 208, + "w": 16, + "h": 16 + }, + { + "filename": "terminal_style_full.png", + "x": 48, + "y": 208, + "w": 16, + "h": 16 + }, + { + "filename": "background_upgrade.png", + "x": 240, + "y": 208, + "w": 16, + "h": 16 + }, + { + "filename": "placement_block.png", + "x": 0, + "y": 224, + "w": 16, + "h": 16 + }, + { + "filename": "placement_item.png", + "x": 16, + "y": 224, + "w": 16, + "h": 16 + }, + { + "filename": "tab_button_background_borderless_focus.png", + "x": 128, + "y": 224, + "w": 25, + "h": 22 + }, + { + "filename": "tab_button_background_focus.png", + "x": 160, + "y": 224, + "w": 22, + "h": 22 + }, + { + "filename": "scheduling_default.png", + "x": 0, + "y": 240, + "w": 16, + "h": 16 + }, + { + "filename": "scheduling_round_robin.png", + "x": 16, + "y": 240, + "w": 16, + "h": 16 + }, + { + "filename": "scheduling_random.png", + "x": 32, + "y": 240, + "w": 16, + "h": 16 + }, + { + "filename": "overlay_off.png", + "x": 48, + "y": 240, + "w": 16, + "h": 16 + }, + { + "filename": "overlay_on.png", + "x": 64, + "y": 240, + "w": 16, + "h": 16 + }, + { + "filename": "s_arrow_up.png", + "x": 224, + "y": 192, + "w": 8, + "h": 8 + }, + { + "filename": "s_arrow_down.png", + "x": 232, + "y": 192, + "w": 8, + "h": 8 + }, + { + "filename": "s_clear.png", + "x": 224, + "y": 200, + "w": 8, + "h": 8 + }, + { + "filename": "s_cycle.png", + "x": 232, + "y": 200, + "w": 8, + "h": 8 + }, + { + "filename": "s_substitution_enabled.png", + "x": 224, + "y": 208, + "w": 8, + "h": 8 + }, + { + "filename": "s_substitution_disabled.png", + "x": 232, + "y": 208, + "w": 8, + "h": 8 + }, + { + "filename": "s_fluid_substitution_enabled.png", + "x": 224, + "y": 216, + "w": 8, + "h": 8 + }, + { + "filename": "s_fluid_substitution_disabled.png", + "x": 232, + "y": 216, + "w": 8, + "h": 8 + }, + { + "filename": "s_storage.png", + "x": 208, + "y": 224, + "w": 10, + "h": 10 + }, + { + "filename": "s_processor.png", + "x": 208, + "y": 234, + "w": 10, + "h": 10 + }, + { + "filename": "s_craft.png", + "x": 208, + "y": 244, + "w": 10, + "h": 10 + }, + { + "filename": "s_terminal.png", + "x": 192, + "y": 224, + "w": 10, + "h": 10 + }, + { + "filename": "s_machine.png", + "x": 192, + "y": 234, + "w": 10, + "h": 10 + } +] \ No newline at end of file diff --git a/scripts/states.png b/scripts/states.png new file mode 100644 index 0000000000000000000000000000000000000000..8dba1bd8e61f801d9788091d6bddf51ae54390f0 GIT binary patch literal 9593 zcmX9^cT`i&)4oZ71ZhEf41O)^HqzjRffPjcJQ9z|i6X_j+5J2fs zdR3})=>h44FYoXBW6th5_uk#vnK}E+%yVDeyQ@QUh5ZTu05p2KnkE1MB27U6jDj@S z`W8JV4V2!xR(=2gHu_IMM{nel06;)gPgC7AC}S%#IEl;bLtE?k`=caep-7X|Ro*0z zI9~lmrnWdMKIY`+S?BWlBN$#W=I*bVU^R_0`Lj85P8h6jO z6_j>7ljG)NPEL7w2h-UO%D4Fw>%Lk?3G-Q6@N4&z-+j<}EYowV=?4S~{W^ezU=VC= z?iY67?}vONz57*rKJ?&qdU><*H8cQ-W}&ay?w#{nhcfcHix{PS5;a=#_-k~tz&B97 zz_+0ayS}P}j@DcC#0Q8kCYSfuSC-WLTszS;Z=gP%J#okJ^xN-;ZmqtRHe3!+@b*TS zJpCZ39tYg{0TD`T{$#Qq?%SxO*pP=-!%8jODu^`|QF#MS#m|JlQhpQcxa0n7pkho- zrc4db2n21?@z!qMdfR|fy7>=B zUOsu}ug7a#w|;@3srZYq8^L!As0kc=N84+26{nMH3d_E7?-cbN_$pra)Co?&mbk|m zf_V5DMpX|0z@e`&-g|rBNYtXxb~}M+xU}wTxb$kT*z|d6mXT#upy&Z{y)}OMPL>fz z1!?{Klx$oonRpsg!LWs6pHUc9vACqCG63jv737Q>o0x|%pEeX-nE^hAq07V2if{?c zIcPKy1a~#U+ya1G%W~Iph_#<^VjL&PBZC39>C=&D1xlX2f$!$wludJj#^)wF!ic^i ze)s#Uq!~-Ani+pmgF5mzk3rkHga`XU_c_jM%?Ffw?vcjpMFD_te})(Nt<N+z&sns zEXWbirOi1#Zwl?huo*nO^v4gfqaCC&yZi*^#9~%XKz|lNORBzJ$Zl|LgMC+D^6Nr# z`sIJiTGCC!1I$Eb)n5##2yT5JO*ZBkz7OFBu#j&V9BoLBr$w z?Cz51p4}xF|5=fyr`%aWv3RGm^9eRJmAS~gYH2R3>LlUVC#{*P1jaSo;KrN3@_x4-0}-;6GB^-nLDCqMN;XFX8c1o>GW92q4P?t zQ4P#9hbMXj5xl3f`{oCE;#!6qf=b$O?jn?zi3KB{-w&zuY{wC$ zTP;hku+fpQVySqZopgAJ$Jo8_N*>KMZr=lrSU+XUWkKnt^4cFUYu|**d}T5pp`?nm)W5;n zv@<~fJg}^iBZV6)g~_qqrX}B-7s*=>xH6z5_<}ebusE#H$MX(9^UBy$&yMH!9(0WG zJlNt5(+3cWW^gjrsHkA%ky^>5O7VwK_$|#5j1+&)NOl2JCKt7V$%9}2>ZERG1XC^E zwkR2!JbOyf`I+}qgVTIsm#jUHNumcA#sn{%yods>EDuh^$0fNvGH%cLwEIzS>PDZt zn_D>P;G2#xU)nv2wE^msITgJsppfTl6|`Mcr{+diz)A*{_}_j}8|w#k)}8d|D@_KN zzHC}I3do1&@POs7MRAlj-q{P*nHv~T9UPWl0VJj0Vq&vzxamNcFYtcGmq>3hEqrG~ zeDS`x*Xx~A7E9Gsx*TEQAY~O3kGl&0C`3=v?3?=Zx%WW;qhc1v@@sLm$3NtOK_g0L z{ITO+$QO|OQRzA_(1uq27yCvO>(Cwn2j=&>dE}C_Z?2Az9vp66njMsrMZkPA8BL+( zAVlotvKxbAg&7UV_`J%o``hT3`*r`#8LS1nALQ{O;SC+sK*=#j)*KW7>{UJWH=BCk zaII`9M-l`c935$((XG|IIZWsu@AtfqcX?TFK32{cWN$5h0%I1lgqK^LF$6u|sCejR zA;?Zg_ArLJtR9rIg1IL;e7H zz=eALU)pTM)N7Yc>Ttt7s|ToMCd(#&VE|A*S$ZoO@YnRcg5UKY843*Cp$EoaJPh!Z z-nTcLf_#o{0)Y1)otM;L)SdF385=KHzl!_wpgmO0yo*tbMc`JWOI8F2`cmmV$=0=; zQ*YMP5xU$6{T<&CIP-!SAVs!;uCgg<+xp$0)4jhs@UmepOv!kU53A>@`b+dF4GSYV^K(+Pt z-!c^Z`1mD5PY>P`-b#V!-)hH`0T@sp+_VIZk+Ywr@WN3`qBhq)ZRjLi!5^>Fm50B^ zJ);3egAsIdM?11!bn4OqKU1^kLwEjv`40m{0yhNnP{TXtS(Xry z*v@Zq0+ri}7A+^reciGrasxn9KL^3#!JNYA-)#BdhyHO0)b%n|H3KK$nr$p5;j*UA($Jll}G9m0!u@`}Fx! z^Upw*TEk`;xp;ov8Uz=l^8csIh8dE*H&}bX)X*+(hYo(fyUjEDVQ2fIrAv<7$eWKp ztw%+f6SfL|K4^)X#YIKQI+RSr{X0Dox0hY}x33g(Y$uBP9Rhw&2ee%h59=A_xm8(~ z5Ih*FwWnmiwcvId5X@3?^HG0n-4FW@^)~qXl_>)pn&DcZXjwIkSSki5m$KDv*USGC zuhe{`u~FJPBxQ&Bx4)_4P(z&X-ahf~hW&ggJ9Q+QXZ)3sE zzz#=`;J`c!(EfZIxHro^ZLaE`{4PuDG1=tVB*E%0HS%Nd(o`-XB)iYZv8428?j~Q5 z%@1ecY^K7-3s-(5&qz6L(^8%S*`Qor-({kug%VB4sw^lFn7?C1kX=|x;Y|*VTH80d z;QiGn2i}aXZrYhK$Msa|_p?LUx3}9yt6pb@etfgGFMXivu}o!D69nLVRB43R)ezm% zEDs8nMbnz2npZ0A>x;0Za^p^N(6Qx1H;p$bqK8lKe68_8(zS6~1!9&%%Xw~W+z|2? z0JrmqOZd$}R@kNq%B4YZEp&E@E@(JO1|J5*f{d;2FW03Eh(c*;VRj+U3=wXni1;XE zRc1J4J%{I$9>45tfnrDcV#0k{Pyc%*e-KZENEw^qQjKPSrhQ-`CWZdZxIWV2_lxwt z5hVtf*e#6kQ##dJ{#}>uY1T6$w^B#};mE$0D@sed&Hs5)VCx!o6tE<++IiAF71`O) znO|&?6v|R+xT2ke^8oEH}5JeIiJY3*O6c)2i@J2`@Z&-vhvlQ z3k{3{vYwPyBy;}?R;?xO2cz2O9eb7!AbL_fIlS^zCRGLC`pSP3T(0|Ei8~xts+hd) z=o*Jab_Kz1Kqg;)t(eyEFz-&YcDLr=>NY`BY!VY>f zT*3}{GtBanbuDZKl{=L>`wMa3^|@1a!^cDm<_mkx;u@g*+bb)&+YQ{B3sZ}H_s%kc zR75EIdO9B?OGds&0e%E~LLn6i7B6P+@A-b2SoE+-NpkP%q#Cy^q)kML zpdK<$0M>WX(QL9E>1zw*g49l(W}%mBr^z@~t8YnKuxt!_}eFyZ_ zLYuOt-!N|3^P$D>FWauB%})gq5#?|i3PKlb5R#LVx9cx= zMfIoEw9^BFD3Tg z!N|bAuwuq6TIdNx)8)BoBJ+_1RnRM4c`2GNz27U3I&5OgY88qhsM*k{A9pZ4ZgBws zZB7@UyQ#>P`s2Z>fKgrY=f0xXmli)MCB^K?zlNFV27ta%-7Wn_-bWbDFTIV*56S_t zUKieZkpC4dP??h02gICS43S^gH_;TH#DWmheSdl4kcwa;f#H8f$8>MOVNxu1vmrVJLH}0$#7)wd>Y%eVC$W`I=j|YB7 zl!#Q6*6Hx)BzPG3u+Oq0NB?G=?=fstm@{PGL96ou$Wl&~lcJ0w>YFDE(m$5-e6Jb+ zYOg=W5MrM(>6`q?r?EAS2kPgE3k6IT1OV=L)!Jk098)V-cpP%7@JkyZ%x{vcj0vT> zt98e>Vj-IE32QKLka+xhjH;IB$9-_7ucm&!b+jR+Szo2k@B6O*JiFn(QL$irlT?hN z|5GH}=IxwPldxOGA(5Zmkj5oOX^@-~;;ki6^wr$)%xn5BVc%1cE0hN_b1de6P8jkD z_%xbmt*q{blx)9WTSEpLZph@yhzq03-f81gV&tMG$~^U?LTsS#V^0YV&HUR93DNwQ z@K>Ytt~&RB7r3kL$KdgDn)ORD4}5FBKc$$k9rz_tCg)n>8t)c(_kkcUKG21~!diQr ze}~etakt44xZ=!4_ieprA`a;hxb};PS%8EF3aGx61uumi-ugq~CSUnXB&NTqPYs_H&Dz%Tg-h)PI;R(D}dq34rrcu zppfS^7)bLm_WR4Zx?~9~2*BJPSj<)Kv%%X8vQhx*Ns0;FN>{l^IQ%gh zWQ9GVFb=U^Pvlnmb_@J11jz6EYc20A9GQryKNRO zyqq|bUb)2_AtNr``FVjY+)@m13s}Fz`bl5K!cy+t2Uuqv-X>G(c5%3o!}DJuu4Lw) zlRYAXVN)=?`#fZ;`Ia3|b^46TqCPisy8`gYaPucX*k!gX^OAgBeojHcRsVo`w%*|~ zRcbHu^`ghH;p57iKrMaFK!GfcYw*ka4oC#z4wYBAef?de4D3DB z8Lzw2AZGh^Ki^)>45LuZetYLF!+qdrGGXq!7P5W9rmz&iMirvTJE!)C57&-=ZIt_%TnAwBuRL4y zLbExPUCsKZs~9p;^S-^8S}e_qIsuYk#<%sj)~5Z^xu-qsxF7E2Mh$Nie(xDZM(A|$Z0=RR`1@&gI}Ia1oe=d`JEsE%JEF)>LI2kC zd*LbglO~2xb40SKiU(fe$(~ka7SKRdxs~4tLfTHbC6YofM12}fAl3PoBicw->JAY# zsVlIEV_vt>?C4AoA`9$J_O%8mI*0v9TV|0cRtjA7-1yrsum#m^q9~vAc5Wyg?Re5N^CcpZ~sc2K2#Y{yJ;Vvow=;N1}<05qb=Z z#u3$S>SIyr;}kh9wT**W;FX)M)E?g-Q7R4vS-pGPaky$>+3P4TiMb34nIn*Q%P_uo zUKUSWDduz*!=XQcju!=6x!lge*)x`mZx)LM3L5_%j`ic6P&Mepx4yPQ+s!YaRAcujY1)K!#wjFr zaPrr>r_6eP?tcg+|E6C6jYmRqGn9Q9exrSkGawX9Mh`I9gusqY>;T&k`*7j~BFb%$8+`bBaBB|<6siRSIN<+OJC$ZRz)P{}Bfb-jzaY3kexli) zI9Hy$?7?L|~8ZC!D?Xtn>GSEC)Upmd(&!1r{3(kXLkKhr*6YJeSX<$b z;r^lx`6z^o$5suWC3GK&Co;wcabxH#Loe^}&~7SatJkdZTjD$_zs$9=@x&zj*AOd7 z{x}OPMLZsW&YxYdSY}RUk@c1>x_tseWMJ^eXn?rA!< z0vp1tyU6#8+0dC2SjpR4e@pL5Y{nnlrR%?!+UHebwQOe0A#=mq8gJ=78_++N*T zyR7}Ajg{E-;_!;oO)~2djMfJD{cxNe=0CqoeJ3RsXV53{phCvO~+HfP5celfwrcTrwjXDp+wvLoVe`u_GJTXH_GrRSK`QM|@>!;0*BhaOsHND`-D2er2t+t;ypt6H+moscCx;@|iV!Fb=uPOAO1G32O2VnarIhNd?( zxRK#dbzfdDgP{#lU-M|kCJRz>oC{J(rfW0hei6>8KF)spKjIUzFK_m!t1^y2;?;{)}m#J7fv+RP!+FlebME)}_dv z945u}Npo%*jMCqCAGc^HpP~|EpoOtse2I1#|MoFV487jdR#DUeU_$0X2Inxf3E@D= z0KyS=z_lF*7K9xbKny)2nco-6j-hU=1I18hs^Y}HC%#gC1~Q3cgCJ0}Dq|GAH}x(8 zp6f|m{330IymC!Q;TbvnhO)q2lMoxya_boV1lD$us@Oljd+6AJxxF7+X$=Ky{~9`v z&W^+f5C-d8*4!$X>n{6_N0FmdHNIXpjdy#g*yUDCTn0u_Hy)mJzRpk%{9V&L-=K8( z1ixjs7vW#l6CU1>Pk5_#K);Q;dYS!B0@w$AO={@$yI9Rsn5zV&ENpQyrecAe!v+J#AJ9%CFTTQi=t&oqw~hdrUWB=~`4k8!G&~VHdas?o#!CGTbd3kuIL*Z@ z%!6dVKx^`tf0+uqBq^2`tmz1r)})FW1irqQg+(?0rE7s`M#h??3#rl!z;ZykY5C$^ z*qcu`U*;($s1Z$FmcurR(LUzpc^cYhcPbz~XD2eH^hih&AE~F-tK>>@Z!-{cMRo%) z%xM8K$aPtMKrupZWT|#G>fCn*p-u`t>8Ouw-B#;VDpFbQ!n%rT-BS8lr7ZU zKEcBq{z@n?lkx`tp=D3u=NI&%J+7)M#S)6Cnfz-@a8}SlTJCsp%Z0RayY{CBuTNvP zEoI8mk#}V5Z@y*R$tHUS0Qx=kp(HSy3`(01a z)t;{RwHpjXXE&^-FYOsXtWP9PhfXBV4(K||NBYnkY^!Y%y=CSDZ=cd-LCBwhr5;cI zSxq<5=?!G^g&3W<*$Sz}7f~fqk*|DeN=`U*a?Ohp-AhvT9ZKwn0&tPbGM5YpFmHMS zQl&JXOUAr{-h*{M zg5LDu1bRhDF7|Mn3;}9CM_hzm+p$$RqMfWhdGeoZ`_Bu?&O+ug(a?}UBG>pxI6&D9 zk<%3^A+bu=cOW+CeE9jF?4=hWdK0||++IFOCMNe-%A6CZVIry~tR5+IWLZ#$0vVJf zLO~h{%k9uoQ{)LdZ8*~E-t?dUPy5T`gS?@tlJz#3LQo4kd~=6i%js~PC%K&5QnJzP zD>LRN2q|&m+#4zhpKRVbCEynKLh<|$K23mt(F$@0oimg>$ln(jBzq{oDSp-i#q1q~ zvB|9roA#N_zz<^aX^I!bn%S7zlA1eTh#PVq3*+wpUFNNqA*A-_;zGKuYWxK#;2C+? z)7_l6F8y3e?YcMgUTO6iBz5)+d9rToou#n@)P?GXjfYL{wQ+5!na>8?(>(>fUKqCT z@cp|^UObJsd%EVh1^)bEn?lq$0>_GSUUk<$|Pp1H;L zj-W$97LQcb%e%DK?D5L19o_V@*GknwQMd6=MXtW4(aI zR)gF}C^F9pq#%6*uaPvm{?~)5S@HMe$oL|FG7rOGOCd;j;Dt4N%O=h2sQ6OiFu}P7?OEXlXGa`OFCtjyeQibpfqSd|V>X@WTv-f2HM1 zVe|`LW@*PEMq>1wkFN;}IsipIx-Y&91>gHCL|#l4&dAa=Wg#sJxa(*rSd z6ypgD%^;Ee^Hv<+~h<5OOPQz-NCb#mXe%j5-Pt16ce^T ze^C+4-(I~~?W0WzshF6f3lzv%3uEGU5Y0AyV8Ui7nJg(JyilbV;PExAwfI}p;{_K9 z>bpe4&xtxC+I-B7BJwMJ6O2j#K%&w;@)&AsYms%<@iRufsF&&R$Y@U*CZ;5u+OxX> zU!(@2EHn$!F$T$g9a)01046_0jP_vXmWP`gWefpA-P)?$VzkW)%%H;;wA4lXas!?| zFRL4SVz>4ld^nZNWKo4{)kkLw4se(B@gD~D>U(~Q)2kkPax3y9OOi#TC`chzG?T@d zDaM4|d3iw`mvgqCYzvV>ovIDEzKVnGgk-V!AWm-Y29G=iYBcMw-fJFP;bL$S7rQVt zKQYsBCJ((t-v0vW=ZO$9?&$iqd^kVc{`MLKL@JZup*eN?pkO+FQ4t1_l&qwl4{@S! zR1+uJr!S3Xz`fG4^(eP?t-W)_N;{C1H+{+ZQdk!ZwjucakgE4AE#^2X?>uViBkl$d zV6-q9thu`s03Efo$dUzL@zqMmY7HPAe7zfb&yzkUArF9FoUc*6}&i&X8-a#Gf1 z%awM%3CjJVl0nd@#;irVR^7`gB)aCiwWB5&`{M3!NcW|^layKi=zkrPMZ9UHgSiOS zOv`m?(hm-Fn7EIqCQJ%s1Q;kV^)d6l^Lkg%_A~Y#i4u6pe8 literal 0 HcmV?d00001 diff --git a/src/main/java/appeng/client/gui/AEBaseScreen.java b/src/main/java/appeng/client/gui/AEBaseScreen.java index f2ee3e9b716..66ab85b1930 100644 --- a/src/main/java/appeng/client/gui/AEBaseScreen.java +++ b/src/main/java/appeng/client/gui/AEBaseScreen.java @@ -781,17 +781,23 @@ public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX } var bgLayer = new SpriteLayer(); + var panels = new PanelBlitter(); var generatedBackground = style.getGeneratedBackground(); if (generatedBackground != null) { - var blitter = new PanelBlitter(); - blitter.addBounds( + panels.addBounds( 0, 0, generatedBackground.getWidth(), generatedBackground.getHeight()); - blitter.render(bgLayer, 0, 0xffffffff); } + widgets.addBackgroundPanels(panels, getBounds(true)); + + panels.render(bgLayer, 0, 0xffffffff); + + // Allow all composite widgets to participate in the sprite layer + widgets.drawBackgroundSpriteLayer(bgLayer, getBounds(true), new Point(mouseX - leftPos, mouseY - topPos)); + // Group all slots by semantic. We make the assumption here // that visually the slots belonging to the same semantic are contiguous. for (var semantic : menu.getSlotBySemantic().keySet()) { @@ -818,7 +824,7 @@ public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX minX, minY, 1, - maxX - minX , + maxX - minX, maxY - minY, 0xffffffff ); @@ -827,7 +833,7 @@ public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX minX, minY, 2, - maxX - minX , + maxX - minX, maxY - minY, 0xffffffff ); diff --git a/src/main/java/appeng/client/gui/ICompositeWidget.java b/src/main/java/appeng/client/gui/ICompositeWidget.java index dc2723e1390..f5d010a09ee 100644 --- a/src/main/java/appeng/client/gui/ICompositeWidget.java +++ b/src/main/java/appeng/client/gui/ICompositeWidget.java @@ -18,17 +18,17 @@ package appeng.client.gui; -import java.util.List; -import java.util.function.Consumer; - -import org.jetbrains.annotations.Nullable; - +import appeng.client.Point; +import appeng.client.gui.widgets.PanelBlitter; +import appeng.client.gui.widgets.SpriteLayer; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.Rect2i; +import org.jetbrains.annotations.Nullable; -import appeng.client.Point; +import java.util.List; +import java.util.function.Consumer; public interface ICompositeWidget { @@ -60,9 +60,9 @@ default void addExclusionZones(List exclusionZones, Rect2i screenBounds) // Automatically add the bounds if they exceed the screen bounds if (bounds.getX() < 0 - || bounds.getY() < 0 - || bounds.getX() + bounds.getWidth() > screenBounds.getWidth() - || bounds.getY() + bounds.getHeight() > screenBounds.getHeight()) { + || bounds.getY() < 0 + || bounds.getX() + bounds.getWidth() > screenBounds.getWidth() + || bounds.getY() + bounds.getHeight() > screenBounds.getHeight()) { exclusionZones.add(new Rect2i( screenBounds.getX() + bounds.getX(), screenBounds.getY() + bounds.getY(), @@ -71,6 +71,12 @@ default void addExclusionZones(List exclusionZones, Rect2i screenBounds) } } + /** + * Allows the widget to add to the background panels shown on the screen. + */ + default void addBackgroundPanels(PanelBlitter panels, Rect2i screenBounds) { + } + /** * Reinitializes a Vanilla screen and populates it with additional vanilla widgets. *

@@ -95,6 +101,16 @@ default void tick() { default void updateBeforeRender() { } + /** + * Draw this composite widget on the background layer of the screen. + * + * @param layer The sprite layer this composite widget participates in. + * @param bounds The bounds of the current dialog in window coordinates. + * @param mouse The current mouse position relative to the dialogs origin. + */ + default void drawBackgroundSpriteLayer(SpriteLayer layer, Rect2i bounds, Point mouse) { + } + /** * Draw this composite widget on the background layer of the screen. * diff --git a/src/main/java/appeng/client/gui/WidgetContainer.java b/src/main/java/appeng/client/gui/WidgetContainer.java index 511683d58c8..88b9e70be9e 100644 --- a/src/main/java/appeng/client/gui/WidgetContainer.java +++ b/src/main/java/appeng/client/gui/WidgetContainer.java @@ -18,23 +18,6 @@ package appeng.client.gui; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import com.google.common.base.Preconditions; - -import org.jetbrains.annotations.Nullable; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button.OnPress; -import net.minecraft.client.renderer.Rect2i; -import net.minecraft.network.chat.Component; -import net.neoforged.neoforge.network.PacketDistributor; - import appeng.client.Point; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.style.WidgetStyle; @@ -44,12 +27,28 @@ import appeng.client.gui.widgets.BackgroundPanel; import appeng.client.gui.widgets.IResizableWidget; import appeng.client.gui.widgets.NumberEntryWidget; +import appeng.client.gui.widgets.PanelBlitter; import appeng.client.gui.widgets.Scrollbar; +import appeng.client.gui.widgets.SpriteLayer; import appeng.client.gui.widgets.TabButton; import appeng.core.localization.GuiText; import appeng.core.network.ServerboundPacket; import appeng.core.network.serverbound.SwitchGuisPacket; import appeng.menu.implementations.PriorityMenu; +import com.google.common.base.Preconditions; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button.OnPress; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.network.chat.Component; +import net.neoforged.neoforge.network.PacketDistributor; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; /** * This utility class helps with positioning commonly used Minecraft {@link AbstractWidget} instances on a screen @@ -223,6 +222,28 @@ public void updateBeforeRender() { } } + /** + * @see ICompositeWidget#addBackgroundPanels(PanelBlitter, Rect2i) + */ + public void addBackgroundPanels(PanelBlitter panels, Rect2i screenBounds) { + for (var widget : compositeWidgets.values()) { + if (widget.isVisible()) { + widget.addBackgroundPanels(panels, screenBounds); + } + } + } + + /** + * @see ICompositeWidget#drawBackgroundSpriteLayer(SpriteLayer, Rect2i, Point) + */ + public void drawBackgroundSpriteLayer(SpriteLayer spriteLayer, Rect2i bounds, Point mouse) { + for (var widget : compositeWidgets.values()) { + if (widget.isVisible()) { + widget.drawBackgroundSpriteLayer(spriteLayer, bounds, mouse); + } + } + } + /** * @see ICompositeWidget#drawBackgroundLayer(GuiGraphics, Rect2i, Point) */ @@ -251,8 +272,8 @@ public void drawForegroundLayer(GuiGraphics poseStack, Rect2i bounds, Point mous public boolean onMouseDown(Point mousePos, int btn) { for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && (widget.wantsAllMouseDownEvents() || mousePos.isIn(widget.getBounds())) - && widget.onMouseDown(mousePos, btn)) { + && (widget.wantsAllMouseDownEvents() || mousePos.isIn(widget.getBounds())) + && widget.onMouseDown(mousePos, btn)) { return true; } } @@ -266,8 +287,8 @@ public boolean onMouseDown(Point mousePos, int btn) { public boolean onMouseUp(Point mousePos, int btn) { for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && (widget.wantsAllMouseUpEvents() || mousePos.isIn(widget.getBounds())) - && widget.onMouseUp(mousePos, btn)) { + && (widget.wantsAllMouseUpEvents() || mousePos.isIn(widget.getBounds())) + && widget.onMouseUp(mousePos, btn)) { return true; } } @@ -295,8 +316,8 @@ boolean onMouseWheel(Point mousePos, double wheelDelta) { // First pass: dispatch wheel event to widgets the mouse is over for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && mousePos.isIn(widget.getBounds()) - && widget.onMouseWheel(mousePos, wheelDelta)) { + && mousePos.isIn(widget.getBounds()) + && widget.onMouseWheel(mousePos, wheelDelta)) { return true; } } @@ -304,8 +325,8 @@ boolean onMouseWheel(Point mousePos, double wheelDelta) { // Second pass: send the event to capturing widgets for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && widget.wantsAllMouseWheelEvents() - && widget.onMouseWheel(mousePos, wheelDelta)) { + && widget.wantsAllMouseWheelEvents() + && widget.onMouseWheel(mousePos, wheelDelta)) { return true; } } @@ -355,7 +376,7 @@ public Tooltip getTooltip(int mouseX, int mouseY) { Rect2i bounds = c.getBounds(); if (mouseX >= bounds.getX() && mouseX < bounds.getX() + bounds.getWidth() - && mouseY >= bounds.getY() && mouseY < bounds.getY() + bounds.getHeight()) { + && mouseY >= bounds.getY() && mouseY < bounds.getY() + bounds.getHeight()) { Tooltip tooltip = c.getTooltip(mouseX, mouseY); if (tooltip != null) { return tooltip; @@ -388,7 +409,7 @@ public boolean hitTest(Point mousePos) { // rather than less-than. private static boolean contains(Rect2i area, int mouseX, int mouseY) { return mouseX >= area.getX() && mouseX < area.getX() + area.getWidth() - && mouseY >= area.getY() && mouseY < area.getY() + area.getHeight(); + && mouseY >= area.getY() && mouseY < area.getY() + area.getHeight(); } public AETextField addTextField(String id) { diff --git a/src/main/java/appeng/client/gui/assets/GuiAssets.java b/src/main/java/appeng/client/gui/assets/GuiAssets.java index 8f770cf85fc..000a19a9e00 100644 --- a/src/main/java/appeng/client/gui/assets/GuiAssets.java +++ b/src/main/java/appeng/client/gui/assets/GuiAssets.java @@ -1,6 +1,8 @@ package appeng.client.gui.assets; +import appeng.core.AEConfig; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling; import net.minecraft.resources.ResourceLocation; @@ -8,6 +10,11 @@ * Asset management */ public final class GuiAssets { + /** + * @see net.minecraft.client.gui.GuiSpriteManager + */ + public static final ResourceLocation GUI_SPRITE_ATLAS = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png"); + private GuiAssets() { } @@ -33,7 +40,7 @@ public static NineSliceSprite getNineSliceSprite(ResourceLocation id) { return new NineSliceSprite( sprite.atlasLocation(), border, - new float[] { u0, u1, u2, u3, v0, v1, v2, v3 }); + new float[]{u0, u1, u2, u3, v0, v1, v2, v3}); } /** @@ -41,7 +48,7 @@ public static NineSliceSprite getNineSliceSprite(ResourceLocation id) { * These values refer to the atlas. */ public record NineSliceSprite(ResourceLocation atlasLocation, - GuiSpriteScaling.NineSlice.Border border, - float[] uv) { + GuiSpriteScaling.NineSlice.Border border, + float[] uv) { } } diff --git a/src/main/java/appeng/client/gui/util/RectangleMerger.java b/src/main/java/appeng/client/gui/util/RectangleMerger.java new file mode 100644 index 00000000000..88c3ad3cb62 --- /dev/null +++ b/src/main/java/appeng/client/gui/util/RectangleMerger.java @@ -0,0 +1,20 @@ +package appeng.client.gui.util; + +import net.minecraft.client.renderer.Rect2i; + +import java.util.ArrayList; +import java.util.List; + +public final class RectangleMerger { + private RectangleMerger() { + } + + public List merge(List rectangles) { + var result = new ArrayList(rectangles.size()); + for (var rectangle : rectangles) { + + } + + return result; + } +} diff --git a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java index 2ced2c8cd49..2ab693f5ef0 100644 --- a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java +++ b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java @@ -14,7 +14,7 @@ public class PanelBlitter { private static final ResourceLocation WINDOW_SPRITE = AppEng.makeId("window"); private static final ResourceLocation INNER_BORDER_SPRITE = AppEng.makeId("window_inner"); - private final List rects = new ArrayList<>(); + private final List rects = new ArrayList<>(); private final List processedRects = new ArrayList<>(); @@ -56,7 +56,7 @@ public void addBounds(Rect2i rect) { } public void addBounds(int x, int y, int width, int height) { - rects.add(new Rectangle(x, y, width, height)); + rects.add(new Rect2i(x, y, width, height)); processedRects.clear(); } @@ -75,8 +75,9 @@ public void render(SpriteLayer layer, int z, int color) { if (processedRects.size() != rects.size()) { processedRects.clear(); for (var rect : rects) { - processedRects.add(rect.copy()); + processedRects.add(new Rectangle(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight())); } + // Merge/Split Edges with other rectangles for (var rect : processedRects) { for (var otherRect : processedRects) { @@ -443,6 +444,12 @@ public void mergeEdges(Rectangle otherRect, int side) { edges.clear(); edges.addAll(tempEdges); } + + public boolean contains(Rectangle rect) { + var right = rect.x + rect.width; + var bottom = rect.y + rect.height; + return rect.x >= x && rect.y >= y && right <= x + width && bottom <= y + height; + } } private enum EdgeStyle { diff --git a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java index e672fe46feb..1c7a7fa034d 100644 --- a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java +++ b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java @@ -1,5 +1,6 @@ package appeng.client.gui.widgets; +import appeng.client.gui.assets.GuiAssets; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferUploader; @@ -16,15 +17,12 @@ * Helper to build and draw a layer of sprites in a single draw-call. */ public final class SpriteLayer { - /** - * @see net.minecraft.client.gui.GuiSpriteManager - */ - private static final ResourceLocation GUI_SPRITE_ATLAS = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png"); + private final ResourceLocation atlasLocation; private BufferBuilder builder; public SpriteLayer() { - atlasLocation = GUI_SPRITE_ATLAS; + atlasLocation = GuiAssets.GUI_SPRITE_ATLAS; builder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); } diff --git a/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java b/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java index 3cd59e3e484..de33b3cb1ab 100644 --- a/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java +++ b/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java @@ -25,7 +25,6 @@ import org.jetbrains.annotations.Nullable; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; @@ -38,7 +37,6 @@ import appeng.client.gui.ICompositeWidget; import appeng.client.gui.Rects; import appeng.client.gui.Tooltip; -import appeng.client.gui.style.Blitter; import appeng.menu.slot.AppEngSlot; /** @@ -50,9 +48,6 @@ public final class UpgradesPanel implements ICompositeWidget { private static final int PADDING = 5; private static final int MAX_ROWS = 8; - private static final Blitter BACKGROUND = Blitter.texture("guis/extra_panels.png", 128, 128); - private static final Blitter INNER_CORNER = BACKGROUND.copy().src(12, 33, SLOT_SIZE, SLOT_SIZE); - private final List slots; // The screen origin in window space (used to layout slots) @@ -125,80 +120,41 @@ public void updateBeforeRender() { } @Override - public void drawBackgroundLayer(GuiGraphics guiGraphics, Rect2i bounds, Point mouse) { - int slotCount = getUpgradeSlotCount(); - if (slotCount <= 0) { - return; - } - - // This is the absolute x,y coord of the first slot within the panel - int slotOriginX = screenOrigin.getX() + this.x + PADDING; - int slotOriginY = screenOrigin.getY() + this.y + PADDING; - - for (int i = 0; i < slotCount; i++) { - // Unlike other UIs, this is drawn top-to-bottom,left-to-right - int row = i % MAX_ROWS; - int col = i / MAX_ROWS; - - int x = slotOriginX + col * SLOT_SIZE; - int y = slotOriginY + row * SLOT_SIZE; - - boolean borderLeft = col == 0; - boolean borderTop = row == 0; - // The panel can have a "jagged" edge if the number of slots is not divisible by MAX_ROWS - boolean lastSlot = i + 1 >= slotCount; - boolean lastRow = row + 1 >= MAX_ROWS; - boolean borderBottom = lastRow || lastSlot; - boolean borderRight = i >= slotCount - MAX_ROWS; - - drawSlot(guiGraphics, x, y, borderLeft, borderTop, borderRight, borderBottom); - - // Cover up the inner corner when we just drew a rather ugly "inner corner" - if (col > 0 && lastSlot && !lastRow) { - INNER_CORNER.dest(x, y + SLOT_SIZE).blit(guiGraphics); - } - } - // Added border to match the rest of the GUI style - RID - guiGraphics.hLine(slotOriginX - 4, slotOriginX + 11, slotOriginY, 0XFFf2f2f2); - guiGraphics.hLine(slotOriginX - 4, slotOriginX + 11, slotOriginY + (SLOT_SIZE * slotCount) - 1, 0XFFf2f2f2); - guiGraphics.vLine(slotOriginX - 5, slotOriginY - 1, slotOriginY + (SLOT_SIZE * slotCount), 0XFFf2f2f2); - guiGraphics.vLine(slotOriginX + 12, slotOriginY - 1, slotOriginY + (SLOT_SIZE * slotCount), 0XFFf2f2f2); + public void addBackgroundPanels(PanelBlitter panels, Rect2i screenBounds) { + visitBackgroundPanels(panels::addBounds); } @Override public void addExclusionZones(List exclusionZones, Rect2i screenBounds) { - int offsetX = screenBounds.getX(); - int offsetY = screenBounds.getY(); + visitBackgroundPanels(rect -> exclusionZones.add(Rects.move(rect, screenBounds.getX(), screenBounds.getY()))); + } + private void visitBackgroundPanels(Consumer visitor) { int slotCount = getUpgradeSlotCount(); - // Use a bit of a margin around the zone to avoid things looking too cramped - final int margin = 2; - // Add a single bounding rectangle for as many columns as are fully populated int fullCols = slotCount / MAX_ROWS; - int rightEdge = offsetX + x; + int rightEdge = x; if (fullCols > 0) { int fullColWidth = PADDING * 2 + fullCols * SLOT_SIZE; - exclusionZones.add(Rects.expand(new Rect2i( + visitor.accept(new Rect2i( rightEdge, - offsetY + y, + y, fullColWidth, - PADDING * 2 + MAX_ROWS * SLOT_SIZE), margin)); + PADDING * 2 + MAX_ROWS * SLOT_SIZE)); rightEdge += fullColWidth; } // If there's a partially populated row at the end, add a smaller rectangle for it int remaining = slotCount - fullCols * MAX_ROWS; if (remaining > 0) { - exclusionZones.add(Rects.expand(new Rect2i( + visitor.accept(new Rect2i( rightEdge, - offsetY + y, + y, // We need to add padding in case there's no full column that already includes it SLOT_SIZE + (fullCols > 0 ? 0 : PADDING * 2), - PADDING * 2 + remaining * SLOT_SIZE), margin)); + PADDING * 2 + remaining * SLOT_SIZE)); } - } @Nullable @@ -216,35 +172,6 @@ public Tooltip getTooltip(int mouseX, int mouseY) { return new Tooltip(tooltip); } - private static void drawSlot(GuiGraphics guiGraphics, int x, int y, - boolean borderLeft, boolean borderTop, boolean borderRight, boolean borderBottom) { - int srcX = PADDING; - int srcY = PADDING; - int srcWidth = SLOT_SIZE; - int srcHeight = SLOT_SIZE; - - if (borderLeft) { - x -= PADDING; - srcX = 0; - srcWidth += PADDING; - } - if (borderRight) { - srcWidth += PADDING; - } - if (borderTop) { - y -= PADDING; - srcY = 0; - srcHeight += PADDING; - } - if (borderBottom) { - srcHeight += PADDING + 2; - } - - BACKGROUND.src(srcX, srcY, srcWidth, srcHeight) - .dest(x, y) - .blit(guiGraphics); - } - /** * We need this function since the cell workbench can dynamically change how many upgrade slots are active based on * the cell in the workbench. diff --git a/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java b/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java index e2137436ef9..1228c56a5e8 100644 --- a/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java +++ b/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java @@ -18,19 +18,16 @@ package appeng.client.gui.widgets; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import net.minecraft.client.gui.GuiGraphics; +import appeng.client.Point; +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.ICompositeWidget; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.renderer.Rect2i; -import appeng.client.Point; -import appeng.client.gui.AEBaseScreen; -import appeng.client.gui.ICompositeWidget; -import appeng.core.AppEng; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; /** * A stacked button panel on the left or right side of our UIs. @@ -121,13 +118,16 @@ public void populateScreen(Consumer addWidget, Rect2i bounds, AE } @Override - public void drawBackgroundLayer(GuiGraphics guiGraphics, Rect2i bounds, Point mouse) { - guiGraphics.blitSprite( - AppEng.makeId("vertical_buttons_bg"), - bounds.getX() + this.bounds.getX() - 2, - bounds.getY() + this.bounds.getY() - 1, - 1, - this.bounds.getWidth() + 1, - this.bounds.getHeight() + 4); + public void addBackgroundPanels(PanelBlitter panels, Rect2i screenBounds) { + var panelLeft = bounds.getX() - 2; + var panelTop = bounds.getY() - 1; + + panels.addBounds( + panelLeft, + panelTop, + // The panel should be flush with the window + -panelLeft, + bounds.getHeight() + 4 + ); } } diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java index f3d30e8d82b..4fb62a9110c 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java @@ -19,10 +19,8 @@ * Renders a standard Minecraft GUI slot. */ public class LytSlot extends LytBlock implements InteractiveElement { - public static final ResourceLocation SLOT_LIGHT = AppEng.makeId("slot"); - public static final ResourceLocation SLOT_DARK = AppEng.makeId("ae2guide/gui/slot_dark.png"); - public static final ResourceLocation LARGE_SLOT_LIGHT = AppEng.makeId("slot_large"); - public static final ResourceLocation LARGE_SLOT_DARK = AppEng.makeId("ae2guide/gui/large_slot_dark.png"); + public static final ResourceLocation SLOT_BG = AppEng.makeId("slot"); + public static final ResourceLocation SLOT_LARGE_BG = AppEng.makeId("slot_large"); private static final int ITEM_SIZE = 16; private static final int PADDING = 1; @@ -74,13 +72,7 @@ public void render(RenderContext context) { var x = bounds.x(); var y = bounds.y(); - ResourceLocation texture; - if (largeSlot) { - texture = context.isDarkMode() ? LARGE_SLOT_DARK : LARGE_SLOT_LIGHT; - } else { - texture = context.isDarkMode() ? SLOT_DARK : SLOT_LIGHT; - } - context.fillIcon(bounds, texture); + context.fillIcon(bounds, largeSlot ? SLOT_LARGE_BG : SLOT_BG); // Render a border around the slot if we're not contained in a slot grid if (!(parent instanceof LytSlotGrid)) { diff --git a/src/main/java/appeng/core/AEConfig.java b/src/main/java/appeng/core/AEConfig.java index 8efbca6da60..d4825d1ce56 100644 --- a/src/main/java/appeng/core/AEConfig.java +++ b/src/main/java/appeng/core/AEConfig.java @@ -18,19 +18,6 @@ package appeng.core; -import java.util.HashMap; -import java.util.Map; -import java.util.function.DoubleSupplier; - -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.event.config.ModConfigEvent; -import net.neoforged.neoforge.common.ModConfigSpec; -import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; -import net.neoforged.neoforge.common.ModConfigSpec.DoubleValue; -import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; -import net.neoforged.neoforge.common.ModConfigSpec.IntValue; - import appeng.api.config.CondenserOutput; import appeng.api.config.PowerMultiplier; import appeng.api.config.PowerUnit; @@ -40,6 +27,18 @@ import appeng.core.settings.TickRates; import appeng.util.EnumCycler; import appeng.util.Platform; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.common.ModConfigSpec.BooleanValue; +import net.neoforged.neoforge.common.ModConfigSpec.DoubleValue; +import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; +import net.neoforged.neoforge.common.ModConfigSpec.IntValue; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.DoubleSupplier; public final class AEConfig { @@ -86,13 +85,24 @@ public double wireless_getDrainRate(double range) { public double wireless_getMaxRange(int boosters) { return common.wirelessBaseRange.get() - + common.wirelessBoosterRangeMultiplier.get() * Math.pow(boosters, common.wirelessBoosterExp.get()); + + common.wirelessBoosterRangeMultiplier.get() * Math.pow(boosters, common.wirelessBoosterExp.get()); } public double wireless_getPowerDrain(int boosters) { return common.wirelessBaseCost.get() - + common.wirelessCostMultiplier.get() - * Math.pow(boosters, 1 + boosters / common.wirelessHighWirelessCount.get()); + + common.wirelessCostMultiplier.get() + * Math.pow(boosters, 1 + boosters / common.wirelessHighWirelessCount.get()); + } + + public boolean isDarkModeEnabled() { + return client.darkMode.getAsBoolean(); + } + + public void setDarkModeEnabled(boolean enabled) { + if (enabled != client.darkMode.getAsBoolean()) { + client.darkMode.set(enabled); + client.spec.save(); + } } public boolean isSearchModNameInTooltips() { @@ -426,6 +436,7 @@ private static class ClientConfig { private final ModConfigSpec spec; // Misc + public final BooleanValue darkMode; public final BooleanValue enableEffects; public final BooleanValue useLargeFonts; public final BooleanValue useColoredCraftingStatus; @@ -469,6 +480,7 @@ public ClientConfig() { builder.pop(); builder.push("client"); + this.darkMode = define(builder, "darkMode", false); this.enableEffects = define(builder, "enableEffects", true); this.useLargeFonts = define(builder, "useTerminalUseLargeFont", false); this.useColoredCraftingStatus = define(builder, "useColoredCraftingStatus", true); @@ -733,7 +745,7 @@ public void sync() { } private static BooleanValue define(ModConfigSpec.Builder builder, String name, boolean defaultValue, - String comment) { + String comment) { builder.comment(comment); return define(builder, name, defaultValue); } @@ -757,18 +769,18 @@ private static DoubleValue define(ModConfigSpec.Builder builder, String name, do } private static DoubleValue define(ModConfigSpec.Builder builder, String name, double defaultValue, double min, - double max, String comment) { + double max, String comment) { builder.comment(comment); return define(builder, name, defaultValue, min, max); } private static DoubleValue define(ModConfigSpec.Builder builder, String name, double defaultValue, double min, - double max) { + double max) { return builder.defineInRange(name, defaultValue, min, max); } private static IntValue define(ModConfigSpec.Builder builder, String name, int defaultValue, int min, int max, - String comment) { + String comment) { builder.comment(comment); return define(builder, name, defaultValue, min, max); } @@ -782,12 +794,12 @@ private static IntValue define(ModConfigSpec.Builder builder, String name, int d } private static > EnumValue defineEnum(ModConfigSpec.Builder builder, String name, - T defaultValue) { + T defaultValue) { return builder.defineEnum(name, defaultValue); } private static > EnumValue defineEnum(ModConfigSpec.Builder builder, String name, - T defaultValue, String comment) { + T defaultValue, String comment) { builder.comment(comment); return defineEnum(builder, name, defaultValue); } diff --git a/src/main/java/appeng/hooks/DarkModeHook.java b/src/main/java/appeng/hooks/DarkModeHook.java new file mode 100644 index 00000000000..16791ae85d5 --- /dev/null +++ b/src/main/java/appeng/hooks/DarkModeHook.java @@ -0,0 +1,32 @@ +package appeng.hooks; + +import appeng.core.AEConfig; +import appeng.core.AppEng; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.resources.ResourceLocation; + +import java.util.Map; + +public final class DarkModeHook { + private static final String DARKMODE_SUFFIX = "_darkmode"; + + private DarkModeHook() { + } + + public static boolean isDarkModeEnabledNamespace(String id) { + return AppEng.MOD_ID.equals(id); + } + + + public static TextureAtlasSprite getReplacedSprite(ResourceLocation id, + Map textures) { + if (DarkModeHook.isDarkModeEnabledNamespace(id.getNamespace()) + && AEConfig.instance().isDarkModeEnabled() + && !id.getPath().endsWith(DARKMODE_SUFFIX)) { + var alternate = ResourceLocation.fromNamespaceAndPath(id.getNamespace(), id.getPath() + DARKMODE_SUFFIX); + return textures.get(alternate); + } + + return null; + } +} diff --git a/src/main/java/appeng/hooks/GuiGraphicsHooks.java b/src/main/java/appeng/hooks/GuiGraphicsHooks.java index 610bb2ba8b0..702c746e63a 100644 --- a/src/main/java/appeng/hooks/GuiGraphicsHooks.java +++ b/src/main/java/appeng/hooks/GuiGraphicsHooks.java @@ -47,7 +47,7 @@ public static boolean onRenderGuiItem(GuiGraphics guiGraphics, @Nullable LivingE @Nullable Level level, ItemStack stack, int x, int y, int seed, int z) { var minecraft = Minecraft.getInstance(); - if (stack.getItem() instanceof EncodedPatternItem encodedPattern) { + if (stack.getItem() instanceof EncodedPatternItem encodedPattern) { if (OVERRIDING_FOR.get() == stack) { return false; // Don't allow recursive model replacements } diff --git a/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java b/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java new file mode 100644 index 00000000000..5c1369f44d6 --- /dev/null +++ b/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java @@ -0,0 +1,29 @@ +package appeng.mixins; + +import appeng.hooks.DarkModeHook; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.gui.GuiSpriteManager; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.client.resources.TextureAtlasHolder; +import net.minecraft.resources.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(GuiSpriteManager.class) +public class GuiSpriteManagerMixin extends TextureAtlasHolder { + public GuiSpriteManagerMixin(TextureManager textureManager, ResourceLocation textureAtlasLocation, ResourceLocation atlasInfoLocation) { + super(textureManager, textureAtlasLocation, atlasInfoLocation); + } + + @WrapOperation(method = "getSprite", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/TextureAtlasHolder;getSprite(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;")) + private TextureAtlasSprite redirectToDarkModeSprite(GuiSpriteManager manager, ResourceLocation id, Operation original) { + var replacedSprite = DarkModeHook.getReplacedSprite(id, textureAtlas.getTextures()); + if (replacedSprite != null) { + return replacedSprite; + } + + return original.call(manager, id); + } +} diff --git a/src/main/resources/ae2.mixins.json b/src/main/resources/ae2.mixins.json index 740db0916cd..8c96cb8c0fa 100644 --- a/src/main/resources/ae2.mixins.json +++ b/src/main/resources/ae2.mixins.json @@ -24,7 +24,8 @@ "TextureAtlasMixin", "PonderWorldMixin", "ModelBakeryMixin", - "VariantDeserializerMixin" + "VariantDeserializerMixin", + "GuiSpriteManagerMixin" ], "server": [], "injectors": { diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_blank_pattern_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_blank_pattern_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1fe334093ea34890203b06b208cbbd654ad5e6 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`{hlt4Ar*6$PIlxvpuoct{87nP z<45bI>i_@M9OEyAvQ(+`}1PGj(N^>bP0l+XkK!YxY= literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_encoded_pattern_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_encoded_pattern_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbf67e90d1cb603d619902618d5ad980b531a4c GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`U7jwEAr*6$PIlxvpuoctEcjJ% z>GyyClV2^}Ya(=c1t(XNhINkFM~n5p8+)IY9bsUo`xcRv_grFE^h5biywl u6M=hM@2hz2op-j=;?y*sw)vj3PjXjTMPCW~$;AP56@#a%pUXO@geCwdX-nb& literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_fuel_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_fuel_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..1e1670c2f63158261244d2be947d1e6579434325 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`rJgR1Ar*6yLjsNeoOfVMc=+VW zG8cPhZnJ~@^7j{gVExhBY5w5w@$C{)8#)g%2pe(%MGRwFuQJpyOjmS~Za6KlgYn0o z&hrA3Vj6fSq%v&MW;@0j!Fxdb`U?ij1A#$GX9RLI1!HA-5>!%i^b|Gq1uix4F=*)N VM_Tx}8UP)@;OXk;vd$@?2>{SDIHLdn literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_ingot_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_ingot_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..b72a45091ce23253d2ab53af8df3dea757f19f05 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`m7Xq+Ar*6yLjsNeoOfVMc=+VW zG8cPh;f-AOH8rO{JbWDOBQ9`>g_-Hkp#nn*>D7!kOca?J>k>trBRVd&oEH!)Q5WP+ zXcLg_*m2;1bc4B6A!Eb>?ho9?8y2{8EBYGrYUmwEx@irA?Mflxy&8!QepL%B a#2DE1jV8abzc~x&2nJ7AKbLh*2~7awu{qlS literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_plate_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_plate_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..006ae7f8ba869a6bc03a950dbe22518e76b3fd8b GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`37#&FAr*6yLjsNeoOfVMc=+T= zS^^OK`0!EJQ;PLRt0LP0lfx_>tOm9|DlZQ)vE5@3ixGO!*rna`VV6Tg{EUPLm(FqX z@ElpnJBLkRwjP_E)JowFrayNwTkps|7#2d+Qs1M>gTe~DWM4fIr=ki literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_primary_output_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_primary_output_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..89eb402ee37bb5ba8678c2d0a691d10e420c5383 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`_MR?|Ar*6yLjsNeoOfVMc=+T= z+K&$(bt7l7{%Dnqm|(etVa8IQl*nEdH(m+TgaQeM^W0C;G{o50E*T3MF)$P+a_x^3 SxVs%_6oaR$pUXO@geCy+C?ux< literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_singularity_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_singularity_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..8dd26287662a3121221598a9a2a974ac18be9b1a GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%``JOJ0Ar*6yLjsNeoOfVMc=+T= zS^^OK`0!D;tA$~|K_S}#lf~Hw)Y^Cr1XIs0Fl%USWE4tx(Rkv3LyX0S0}?lw1ZTRj zoydwbNSP>d(ByHqVpC$nS?&{U{53ikt}!Gn5>MB8VBE4IcI6wkI5T1`Arm&4GoYR%Q(i!!6WCPk(*IK24v z+vlruLgSPRUstI-S1>)HrnmH$;twV-Pd-(#6WO5;7$*x?I0Pp&>NTqW=df8Ew5U79 z^M}{xUY(T5G2wh!!h8NTO*wL4g@c_sTaaFBQBr4F%H?@Lr!jcC`njxgN@xNAYb{Wg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_spatial_cell_no_shadow_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_spatial_cell_no_shadow_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..02ebc92d9e69705e12b1a71ba95ab868557ba072 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jh-%!Ar*7hPIlxvpuoej{ZZ_} z@+ox-{{Qb?pr7I2XyP_`+B;7J<7XT4eiz0?EcwdCaAM_(?-#kZ#f6?!n6Y<8aG+{q zvx!3cjw$E(4@w7kF-?h3uwTu@zvIpU=2=YKC7d=t-5*3wHPW4=`f%#A-ku_lYg3zV lu^o<|Fv)*^U|GUc>F(!Ao!|ZimjNBa;OXk;vd$@?2>>%iNw5F_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_storage_cell_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_storage_cell_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..678d8c380d9348a6af3329e3758b615e1006adc9 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`lRaG=Ln`Jho$ScfpdjF?UZ|`q zw8v@7`+w*W;{QLdEY{wxJKf)*dG^(lO4+sZrypXRCgA$KG{A#B z!Kvhc!bRb#2`eI<4Gy_`95{Nz&0Tp-pR~j6yUi;a^aTVb9?(bP0l+XkK DB{fZR literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_storage_component_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_storage_component_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..c7ddfcb9f8c9c420056ecbe16f6b1d8351501412 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`6FprVLn`Jh4R+*fP~dUi%>9vp zGv46d-~Y3HzOYtF&D`qKV|#Y3?%Wp!`mZJH9Fpd|I$W2t_1wxk9cyPsG|5IVcIkv~ zSa>L`X^z*4l_9+foV^B&YO>ADwr86)0_Wz2tPe|`bzo9JLqYh9#tqB1^OC;AEb0@`Q<* z;T*OJ%x=5~gnF6&9QvT)(sznq{=Sf($B8xpZZ`-@G?f4V literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_upgrade_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_upgrade_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..e2dd13195c2062c94e3609bed2caa869cd666a05 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`37#&FAr*6yLjsNeoOfVMc=+T= zS^^OK`0!CTP>SV8Yank!rrY!btQW2t2`=UKG&b_x#CU^8`8GpWtU(rol4GJogYLSE zrW5B**udDun~<2$V8MCwbP0l+XkK8!9rW literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_view_cell_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/background_view_cell_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..3a290561a5554bf6e6438cba0aebdc172307aff6 GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vpiiKLn`J>4f5n`P~ch9vf~J| zO3lHF@Bj0SF3zsH7|iL-ULYi-G52M_E#24Ogbw_Tsk8sGc*f%8ndh30?-UlCXsXxM znyw$9Y_z6uQHP$O<+<`n9gi4RY3<~04V@vdE^Oio_La42)2kM3n(eZL?P6ny;7)C; zjKU=s{$J~eYvADb7Fi*Ar*6yLjsNeoOfVMc=+Ur zo8XTRA9b4<_8VMeGYCww4&_O3d{*=0hgj<|)(Lw#%o{}ZE3ZyHsOGumZ3ORuWgPP# zGRt!_=QCV$P!SQX;8jqSUm&4yK;LJ}*@$2z%WcL61_lfaruSriKR>2=9%vmdK II;Vst0K0!R&;S4c literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..ed6b653a4763364c2690923f7c56eda9e8e99964 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4f(Vi}jAr*6yONzezd;j?DnSkKC z2ONA4G@S_yyS%t-VR?S6lj)Dc$G3|}YaP%(a{vTfHhNkdSaEPMTfdY|)`t%!5=(8` vrY;RJ|MC0#`Viw8c8(w4e2iTs$-{8tmVn?33%{E{YZyFT{an^LB{Ts5zs)?R literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_focus_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/horizontal_tab_focus_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..f448e93871947e3f17cc76bf7af663749a08d3c8 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fiJmTwAr*6yONzezd;j?DnSkKC z2ONA4FePu<5_JB-tXFIu2cx&WTRLl@-Mw=>Am9^XEXdmAm9^XEXdm6WPfy{2a4U8UnB}(@h7;gKs@IBY=djQnS;OXk;vd$@?2>`(0 BAK?H1 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/inscriber_buffer_4_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/inscriber_buffer_4_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..855a100f7c7dcd4c301c25de80f5a4c96a40b642 GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`OFUg1Ln`K6oovY0Y{28XfAzd> z=?YHnP_b?E8iK#@g^ICoJ71M9Ff4C8rD~RYJ+-$UE#xndn_su?7f&jd<3$290&*|kNN@k9ED?qivC&HVpcRXOASrkX$e Tz+PMobT5OatDnm{r-UW|+}rLn`JZ&oQm%W>#%@`e&!M z22-1xm5Ss6mx*7S4>zduzcW7Y;mb);ofDG;50?E`ZhHGq_$^cX{V(<>W-VUqEx8DY zKS&;G@@{HuY&>XwV!Gg|Bbf}>3>t2BykU)5cR;$sK>q=A#oi4indXPPwzf-1dVFOv zQV`)T5YM@1GnrM4d&iu&(oeb-kG*(3+b=-S&ftTmBtz|I(|`+$3g!cy$l&Sf=d#Wz Gp$P!<-BL6F literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/s_arrow_down_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/s_arrow_down_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..5e65ba0152ec8240c7f22833ec9ab28f368851c8 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFq{+=$5Ar*6ypF9=)cfNsrg8>k1 zC@x5x`se3kX=Y)&y9L4$K=AJF-Y%tuOV*q{aNt0Lfn!wEL`G@pJSJ;)@ffC?z6Gp1 gWKJ^`>*_Hun7-kzW#)@`4m6s<)78&qol`;+0Hnw&?EnA( literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/s_arrow_up_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/s_arrow_up_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..4648cf260ba26bc1113257b85cb02f5e765f8fbf GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFq9-c0aAr*6ypF9=)cfNsrg8>k1 zC@x5x`se3kX=Y)&yF5HRE4bd>-FvFw{kzaNToY6$+|)5>+pk1 zC@x5x`se3kX=dS`;`i@D*>ZBkB%Gt7CO&+5`FDZYnUEcvC$1@M4qP)~T38AYyKHQl yI6eG_y*0b|;q&tMA6O;6nQy<(QRKmAH3rAsa%v6Jm<)kdF?hQAxvXk1 zC@x5x`se3kX=dS`;`i@D+p5dgAG&F9pkla z7;ZhF^|JUEiwh$&GjsF9wx>LCd-EkgV4G;S!5sdMlmrlPy1sR71WW7r7JngE$NfvS rna#}Z@bW!UD&S-jc z$@_Xx%kpM5w+kaPGqZE+!4$Uj_sn@f;C7d*gq7YCBakwKg|Bag=`la z7;ZhF^|JUE3$yU91rOVv^0Y}$JN(n<#iSPvoU>E*FV!v+PME~^NNGi%%e7XWlLvsn cFVdQ&MBb@0I1poj5 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/s_substitution_enabled_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/s_substitution_enabled_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..3fb747f9b63e63ee5f58050cbce4bcbec584af7d GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqUY;(FAr*6ypF9=)cfNsrg8>jc z$@_Xx%kpM5H?#1rgw}&8ZCuSK3+#kyg_G4E!%CaiQkX89Be*A#SDC`;tC!2*Tly_ua&bIg>(u7)s_bP6+E6;$|oZ{g;T QK({h@y85}Sb4q9e0P@jOE&u=k literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/tab_button_background_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/tab_button_background_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..95074798bf6e1d45e333d94ef3957215f82cde0c GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;Aq^FBxNX4AwCy$=|KJVaq;Lw2s z2NZPld_@o6+gHP7IH9qz@!{|5`XZ9YPM$pslsj<1W#(*ygDV;rv-L??*zmMWH`{Qj uNsFzn;&+!;o1wvlQ>W|y|5-Mxk)eDsukQOZmJflJFnGH9xvX{alug!$6$cly2{S)fDqZ!` zMYMVO*LTa-t>iJ`;d09g_l@6Q+Y;h?PH$q*sc#3bn9Oc$WRyBq&c7}-0SInv$z;3A b!^UuGyNKKCxPz)dhcI}$`njxgN@xNApi@Bs literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..740deab7874ef650b28c480069ad7bf559e21ca9 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9S!3HE7rssMAsbEhR$B>FS$xj|V`F-Aj^}wM6 z2M#P)v&?;>VtIaSlf)4q_ru@c;hT6KK6#Rs00N6nXnG`ESrFLl#=EHB@xvYd(?>-l lUGfB*cvd_!b;}an$}q=)JO6a%OcS8-44$rjF6*2UngD?5HMRf% literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_focus_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/icons/toolbar_button_background_focus_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..35d2d46a398936643a3e01cdba82f65598e8412a GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9S!3HE7rssMAsQ^zG$B>FS$xj|V`F-Aj^}wM6 z2M!cGyzSn_WBA}vyTlO?_XSsF+l`kF8ERc~npoU;rA#xfDEyLOIsbPrdyjdnT_na(f1h)EQ--t+&};@zS3j3^P6FS$xj|V`F-Aj^}wM6 z2M!cGyzSn_WBA}vyTlO?_XSsF+l`kF8ERc~npoU;rA#xfDEyLOIsbPZ0eiqOn44$rjF6*2UngGCjLkIu> literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..69ef7cff9ae7568e38190d8850dcd384c281c18b GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?31We{epSZZGe6twqr zaSXBWPk!_I_3!fztOuBk7Oz;*Avis9zFn2LtK|M1vYcAU&)Z@Z4-Pb kps*(C!%}6pEYTc>_HLeX(LG-4fkrWSy85}Sb4q9e0PIaFRR910 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png.mcmeta b/src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png.mcmeta new file mode 100644 index 00000000000..fba7643157a --- /dev/null +++ b/src/main/resources/assets/ae2/textures/gui/sprites/slot_border_darkmode.png.mcmeta @@ -0,0 +1,15 @@ +{ + "gui": { + "scaling": { + "type": "nine_slice", + "width": 18, + "height": 18, + "border": { + "left": 1, + "top": 1, + "right": 1, + "bottom": 1 + } + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..d836d6d2db06a2ed3794c71c061154c84db765ad GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|&H|6fVg?4jBOuH;Rhv&5DCpto z;uvD#pIlP(?caF^)&opNk1sw>7m-v|*FMT%)-ieVY^7$0Su-`v7_$znaBOidGU;(U t+)==DcE5!f+ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png.mcmeta b/src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png.mcmeta new file mode 100644 index 00000000000..a107393d5f9 --- /dev/null +++ b/src/main/resources/assets/ae2/textures/gui/sprites/slot_darkmode.png.mcmeta @@ -0,0 +1,9 @@ +{ + "gui": { + "scaling": { + "type": "tile", + "width": 18, + "height": 18 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png b/src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png new file mode 100644 index 0000000000000000000000000000000000000000..cd9c5c6f46238fc94c5c5872db3ddbb98ecbd875 GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%oCO|{#S9F5he4R}c>anMpkRon zi(`m~cd||Gvs3kKRt#bjWS{HXFIXAcwu_4=p}U2ZH6u&#M1k6@+YDP1t|)DCt}=Py z*32izw74_T#IQ$=ZPVog85erlWYahrW=9tpZOPALWbj22WQ%mvv4F FO#qjFF=zk) literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png.mcmeta b/src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png.mcmeta new file mode 100644 index 00000000000..d0022acb6e6 --- /dev/null +++ b/src/main/resources/assets/ae2/textures/gui/sprites/slot_large.png.mcmeta @@ -0,0 +1,10 @@ +{ + "gui": { + "scaling": { + "type": "nine_slice", + "width": 26, + "height": 26, + "border": 2 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png b/src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png deleted file mode 100644 index 8f51696ffbe59e014075c24d7c9cf789c6c5ae1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^qChOg!3HEF9L1f16lZ})WHAE+w=f7ZGR&GI0Tc}O zba4#v=u9rz!vFKUL+XJ;2M!$AQ25#AmGmhftKjoBmdKI;Vst E03DAubN~PV diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png.mcmeta b/src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png.mcmeta deleted file mode 100644 index 2223135531c..00000000000 --- a/src/main/resources/assets/ae2/textures/gui/sprites/vertical_buttons_bg.png.mcmeta +++ /dev/null @@ -1,15 +0,0 @@ -{ - "gui": { - "scaling": { - "type": "nine_slice", - "width": 21, - "height": 26, - "border": { - "left": 0, - "top": 2, - "right": 0, - "bottom": 4 - } - } - } -} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/window_darkmode.png b/src/main/resources/assets/ae2/textures/gui/sprites/window_darkmode.png new file mode 100644 index 0000000000000000000000000000000000000000..b61d87cd4207a26abdfb52ce1fb1f0f6d7d80667 GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9F5M?jcysy3fAP%zxn z#WBRfKl#a{C%?}-FeWg*%+1VIINdSp6&u?IMuiQjuj3~1eEe9(wmMzOQ(WxmVzwSO zC5^}f!Ad%*6K8FZaGdEG5i={KHsNc2yxme8&E{B#XZ4F z;uvD#pZw)+=c7!w#@zMXF}TSuAebCBeW6PG%daK35Uu0MZLF5e9Xfgj)g3M*?a zD=7Tbc(^Y~zembIs$)S5i+|^aDXv~wEGtu&m{>D2s7@9LSt-rf2{fI-)78&qol`;+ E08rsGwg3PC literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/window_inner_darkmode.png.mcmeta b/src/main/resources/assets/ae2/textures/gui/sprites/window_inner_darkmode.png.mcmeta new file mode 100644 index 00000000000..0288d0ca0c1 --- /dev/null +++ b/src/main/resources/assets/ae2/textures/gui/sprites/window_inner_darkmode.png.mcmeta @@ -0,0 +1,15 @@ +{ + "gui": { + "scaling": { + "type": "nine_slice", + "width": 16, + "height": 16, + "border": { + "left": 2, + "top": 4, + "right": 2, + "bottom": 2 + } + } + } +} \ No newline at end of file From b2ed6aa5146e4a3e4df983bbbd42db957f16d492 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sun, 11 Aug 2024 02:26:31 +0200 Subject: [PATCH 5/6] Rectangle merging --- .../client/gui/util/RectangleMerger.java | 113 +++++++++++++++++- .../client/gui/widgets/PanelBlitter.java | 10 +- .../client/gui/widgets/UpgradesPanel.java | 32 ++--- .../client/gui/util/RectangleMergerTest.java | 87 ++++++++++++++ 4 files changed, 212 insertions(+), 30 deletions(-) create mode 100644 src/test/java/appeng/client/gui/util/RectangleMergerTest.java diff --git a/src/main/java/appeng/client/gui/util/RectangleMerger.java b/src/main/java/appeng/client/gui/util/RectangleMerger.java index 88c3ad3cb62..4dbeec6d80f 100644 --- a/src/main/java/appeng/client/gui/util/RectangleMerger.java +++ b/src/main/java/appeng/client/gui/util/RectangleMerger.java @@ -1,20 +1,125 @@ package appeng.client.gui.util; -import net.minecraft.client.renderer.Rect2i; +import appeng.client.guidebook.document.LytRect; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +/** + * Remove overlapping rectangles and produce a list of equivalent non-overlapping rectangles. + */ public final class RectangleMerger { private RectangleMerger() { } - public List merge(List rectangles) { - var result = new ArrayList(rectangles.size()); - for (var rectangle : rectangles) { + /** + * This implements a line-sweep algorithm that ensures all rectangles are non-overlapping + * by merging or subdividing overlapping rectangles. + *

+ * What this algorithm ends up doing is essentially cutting up the union of all rectangles into + * vertical stripes by scanning from left-to-right and producing rectangles whenever a rectangle + * ends on the horizontal axis. + */ + public static List merge(List rects) { + if (rects.isEmpty()) { + return List.of(); + } + + List result = new ArrayList<>(rects.size()); + + List xEvents = getSortedXEvents(rects); + List yEvents = getSortedYEvents(rects); + + // For each rect-index, mark if it's currently open (that is we saw an opening event along + // the primary axis, but not yet a closing) + boolean[] currentSet = new boolean[rects.size()]; + + for (int i = 0; i < xEvents.size() - 1; i++) { + var xEvent = xEvents.get(i); + currentSet[xEvent.ind] = xEvent.type == OPENING; + + var nextXEvent = xEvents.get(i + 1); + // For stacked rectangles, only process the last (after adding all to the current set) + if (nextXEvent.x == xEvent.x) { + continue; + } + + int left = xEvent.x; + int right = nextXEvent.x; + int top = 0; + int opened = 0; // Used to find the end of rectangles that overlap on the y-axis + + yEvents: + for (int j = 0; j < yEvents.size(); j++) { + var yEvent = yEvents.get(j); + + // Only process rects for which we are between open/close on the primary axis + if (currentSet[yEvent.ind]) { + if (yEvent.type == OPENING) { + if (++opened == 1) { + // Use the first open after a clear area as the top + // of the rectangle we'll produce + top = yEvent.y; + } + } else { + if (--opened == 0) { + // Try to find the next relevant open event. + // If it is directly adjacent, skip creating a rectangle now, and continue with it + for (int k = j + 1; k < yEvents.size(); k++) { + var nextEvent = yEvents.get(k); + if (currentSet[nextEvent.ind] && nextEvent.type == OPENING) { + if (nextEvent.y == yEvent.y) { + ++opened; + j = k; + // Note that we keep top intact to produce a joined rectangle + continue yEvents; + } + break; + } + } + + // Try to join the emitted rectangle if the next opening rectangle is directly adjacent + result.add(new LytRect(left, top, right - left, yEvent.y - top)); + } + } + } + } } return result; } + + private static List getSortedXEvents(List rects) { + List events = new ArrayList<>(rects.size() * 2); + + for (int i = 0; i < rects.size(); i++) { + var rect = rects.get(i); + events.add(new Event(i, OPENING, rect.x(), rect.y(), rect.width())); + events.add(new Event(i, CLOSING, rect.x() + rect.width(), rect.y(), rect.width())); + } + + events.sort(Comparator.comparingInt((Event e) -> e.x).thenComparingInt(e -> e.y)); + return events; + } + + private static List getSortedYEvents(List rects) { + List events = new ArrayList<>(rects.size() * 2); + + for (int i = 0; i < rects.size(); i++) { + var rect = rects.get(i); + events.add(new Event(i, OPENING, rect.x(), rect.y(), rect.height())); + events.add(new Event(i, CLOSING, rect.x(), rect.y() + rect.height(), rect.height())); + } + + events.sort(Comparator.comparingInt((Event e) -> e.y).thenComparingInt(e -> e.x)); + return events; + } + + private static final int OPENING = 0; + private static final int CLOSING = 1; + + public record Event(int ind, int type, int x, int y, int length) { + } } diff --git a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java index 2ab693f5ef0..bd32fcbc893 100644 --- a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java +++ b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java @@ -1,6 +1,8 @@ package appeng.client.gui.widgets; import appeng.client.gui.assets.GuiAssets; +import appeng.client.gui.util.RectangleMerger; +import appeng.client.guidebook.document.LytRect; import appeng.core.AppEng; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.Rect2i; @@ -14,7 +16,7 @@ public class PanelBlitter { private static final ResourceLocation WINDOW_SPRITE = AppEng.makeId("window"); private static final ResourceLocation INNER_BORDER_SPRITE = AppEng.makeId("window_inner"); - private final List rects = new ArrayList<>(); + private final List rects = new ArrayList<>(); private final List processedRects = new ArrayList<>(); @@ -56,7 +58,7 @@ public void addBounds(Rect2i rect) { } public void addBounds(int x, int y, int width, int height) { - rects.add(new Rect2i(x, y, width, height)); + rects.add(new LytRect(x, y, width, height)); processedRects.clear(); } @@ -74,8 +76,8 @@ public void render(SpriteLayer layer, int z, int color) { // Update processed rectangles lazily if (processedRects.size() != rects.size()) { processedRects.clear(); - for (var rect : rects) { - processedRects.add(new Rectangle(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight())); + for (var rect : RectangleMerger.merge(rects)) { + processedRects.add(new Rectangle(rect.x(), rect.y(), rect.width(), rect.height())); } // Merge/Split Edges with other rectangles diff --git a/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java b/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java index de33b3cb1ab..3861a132707 100644 --- a/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java +++ b/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java @@ -18,26 +18,22 @@ package appeng.client.gui.widgets; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - -import org.jetbrains.annotations.Nullable; - -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.renderer.Rect2i; -import net.minecraft.network.chat.Component; -import net.minecraft.world.inventory.Slot; - import appeng.api.upgrades.IUpgradeableObject; import appeng.api.upgrades.Upgrades; import appeng.client.Point; -import appeng.client.gui.AEBaseScreen; import appeng.client.gui.ICompositeWidget; import appeng.client.gui.Rects; import appeng.client.gui.Tooltip; import appeng.menu.slot.AppEngSlot; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.network.chat.Component; +import net.minecraft.world.inventory.Slot; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; /** * A panel that can draw a dynamic number of upgrade slots in a vertical layout. @@ -45,14 +41,11 @@ public final class UpgradesPanel implements ICompositeWidget { private static final int SLOT_SIZE = 18; - private static final int PADDING = 5; + private static final int PADDING = 3; private static final int MAX_ROWS = 8; private final List slots; - // The screen origin in window space (used to layout slots) - private Point screenOrigin = Point.ZERO; - // Relative to current screen origin (not window) private int x; private int y; @@ -98,11 +91,6 @@ public Rect2i getBounds() { return new Rect2i(x, y, width, height); } - @Override - public void populateScreen(Consumer addWidget, Rect2i bounds, AEBaseScreen screen) { - this.screenOrigin = Point.fromTopLeft(bounds); - } - @Override public void updateBeforeRender() { int slotOriginX = this.x; diff --git a/src/test/java/appeng/client/gui/util/RectangleMergerTest.java b/src/test/java/appeng/client/gui/util/RectangleMergerTest.java new file mode 100644 index 00000000000..b248f53a162 --- /dev/null +++ b/src/test/java/appeng/client/gui/util/RectangleMergerTest.java @@ -0,0 +1,87 @@ +package appeng.client.gui.util; + +import appeng.client.guidebook.document.LytRect; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RectangleMergerTest { + @Test + public void testEmpty() { + assertThat(RectangleMerger.merge(List.of())).isEmpty(); + } + + @Test + public void testSingle() { + var r = new LytRect(5, 10, 20, 50); + assertThat(RectangleMerger.merge(List.of(r))) + .containsOnly(r); + } + + @Test + public void testMergeIdentical() { + var r1 = new LytRect(5, 10, 20, 50); + var r2 = new LytRect(5, 10, 20, 50); + assertThat(RectangleMerger.merge(List.of(r1, r2))) + .containsOnly(r1); + } + + /** + * The algorithm doesn't "look ahead" to merge adjacent rectangles. It only handles overlap. + */ + @Test + public void testDoesNotMergeAdjacentOnPrimaryAxis() { + var r1 = new LytRect(5, 10, 20, 50); + var r2 = new LytRect(25, 10, 20, 50); + assertThat(RectangleMerger.merge(List.of(r1, r2))) + .containsOnly(r1, r2); + } + + /** + * The algorithm does indeed look ahead on the secondary axis to merge adjacent rectangles. + */ + @Test + public void testDoesMergeAdjacentOnSecondaryAxis() { + var r1 = new LytRect(0, 0, 10, 10); + var r2 = new LytRect(0, 10, 10, 10); + assertThat(RectangleMerger.merge(List.of(r1, r2))) + .containsOnly(new LytRect(0, 0, 10, 20)); + } + + /** + * The algorithm doesn't "look ahead" to merge adjacent rectangles. It only handles overlap. + */ + @Test + public void testMergeOverlapping() { + var r1 = new LytRect(5, 10, 20, 50); + var r2 = new LytRect(24, 10, 21, 50); + assertThat(RectangleMerger.merge(List.of(r1, r2))) + .containsOnly( + new LytRect(5, 10, 19, 50), + new LytRect(24, 10, 1, 50), + new LytRect(25, 10, 20, 50) + ); + } + + @Test + public void testCornerOverlaps() { + var centerRect = new LytRect(-10, -10, 20, 20); + var topLeft = new LytRect(-15, -15, 10, 10); + var topRight = new LytRect(5, -15, 10, 10); + var bottomRight = new LytRect(5, 5, 10, 10); + var bottomLeft = new LytRect(-15, 5, 10, 10); + + assertThat(RectangleMerger.merge(List.of(centerRect, topLeft, topRight, bottomRight, bottomLeft))) + .containsOnly( + new LytRect(-15, -15, 5, 10), + new LytRect(-15, 5, 5, 10), + new LytRect(-10, -15, 5, 30), + new LytRect(-5, -10, 10, 20), + new LytRect(5, -15, 5, 30), + new LytRect(10, -15, 5, 10), + new LytRect(10, 5, 5, 10) + ); + } +} From 4c5af88f3855d122cc0044b9a2f08ed3643a491d Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Mon, 12 Aug 2024 02:50:27 +0200 Subject: [PATCH 6/6] WIP --- settings.gradle | 4 +- .../java/appeng/client/gui/AEBaseScreen.java | 229 +++++++++--------- .../appeng/client/gui/ICompositeWidget.java | 20 +- .../appeng/client/gui/WidgetContainer.java | 51 ++-- .../appeng/client/gui/assets/GuiAssets.java | 25 +- .../client/gui/assets/SpritePadding.java | 19 ++ .../implementations/UpgradeableScreen.java | 2 +- .../client/gui/me/common/MEStorageScreen.java | 2 +- .../client/gui/util/RectangleMerger.java | 16 +- .../client/gui/widgets/PanelBlitter.java | 28 +-- .../client/gui/widgets/SpriteLayer.java | 64 ++--- .../client/gui/widgets/ToolboxPanel.java | 17 +- .../client/gui/widgets/UpgradesPanel.java | 45 ++-- .../client/gui/widgets/VerticalButtonBar.java | 16 +- .../guidebook/document/block/LytSlot.java | 17 +- .../guidebook/document/block/LytSlotGrid.java | 7 +- .../guidebook/render/RenderContext.java | 33 +-- src/main/java/appeng/core/AEConfig.java | 43 ++-- src/main/java/appeng/hooks/DarkModeHook.java | 14 +- src/main/java/appeng/menu/AEBaseMenu.java | 1 - .../implementations/CellWorkbenchMenu.java | 4 + .../appeng/menu/slot/CellPartitionSlot.java | 11 - .../java/appeng/menu/slot/IOptionalSlot.java | 16 +- .../slot/MolecularAssemblerPatternSlot.java | 11 - .../appeng/menu/slot/OptionalFakeSlot.java | 17 -- .../appeng/mixins/GuiSpriteManagerMixin.java | 15 +- .../ae2/screens/common/terminal_toolbox.json | 8 - .../assets/ae2/screens/common/toolbox.json | 8 - .../ae2/textures/gui/sprites/slot_invalid.png | Bin 0 -> 91 bytes .../assets/ae2/textures/guis/extra_panels.png | Bin 877 -> 0 bytes .../client/gui/util/RectangleMergerTest.java | 13 +- 31 files changed, 369 insertions(+), 387 deletions(-) create mode 100644 src/main/java/appeng/client/gui/assets/SpritePadding.java create mode 100644 src/main/resources/assets/ae2/textures/gui/sprites/slot_invalid.png delete mode 100644 src/main/resources/assets/ae2/textures/guis/extra_panels.png diff --git a/settings.gradle b/settings.gradle index e171e2bb97c..349b4dcc894 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,8 +7,8 @@ pluginManagement { id 'io.github.goooler.shadow' version '8.1.7' id 'de.undercouch.download' version '5.6.0' // https://projects.neoforged.net/neoforged/ModDevGradle - id 'net.neoforged.moddev' version '1.0.13' - id 'net.neoforged.moddev.repositories' version '1.0.13' + id 'net.neoforged.moddev' version '2.0.9-beta' + id 'net.neoforged.moddev.repositories' version '2.0.9-beta' } } diff --git a/src/main/java/appeng/client/gui/AEBaseScreen.java b/src/main/java/appeng/client/gui/AEBaseScreen.java index 66ab85b1930..0bf00ab58db 100644 --- a/src/main/java/appeng/client/gui/AEBaseScreen.java +++ b/src/main/java/appeng/client/gui/AEBaseScreen.java @@ -18,14 +18,54 @@ package appeng.client.gui; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import com.google.common.base.Stopwatch; +import com.mojang.blaze3d.platform.InputConstants; + +import org.jetbrains.annotations.MustBeInvokedByOverriders; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ComponentRenderUtils; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.util.FastColor; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.network.PacketDistributor; + import appeng.api.behaviors.ContainerItemStrategies; import appeng.api.behaviors.EmptyingAction; import appeng.api.implementations.menuobjects.ItemMenuHost; import appeng.api.parts.IPart; import appeng.api.stacks.GenericStack; import appeng.client.Point; +import appeng.client.gui.assets.GuiAssets; import appeng.client.gui.layout.SlotGridLayout; -import appeng.client.gui.style.Blitter; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.style.SlotPosition; import appeng.client.gui.style.Text; @@ -62,42 +102,6 @@ import appeng.menu.slot.IOptionalSlot; import appeng.menu.slot.ResizableSlot; import appeng.util.ConfigMenuInventory; -import com.google.common.base.Stopwatch; -import com.mojang.blaze3d.platform.InputConstants; -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.ComponentRenderUtils; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.Rect2i; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.chat.Component; -import net.minecraft.util.FormattedCharSequence; -import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; -import net.minecraft.world.inventory.Slot; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.neoforged.neoforge.network.PacketDistributor; -import org.jetbrains.annotations.MustBeInvokedByOverriders; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.glfw.GLFW; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.TimeUnit; public abstract class AEBaseScreen extends AbstractContainerScreen { private static final Logger LOG = LoggerFactory.getLogger(AEBaseScreen.class); @@ -146,7 +150,6 @@ public AEBaseScreen(T menu, Inventory playerInventory, Component title, ScreenSt this.style = Objects.requireNonNull(style, "style"); this.widgets = new WidgetContainer(style); this.verticalToolbar = new VerticalButtonBar(); -// this.widgets.add("verticalToolbar", this.verticalToolbar = new VerticalButtonBar()); // TODO (RID): Added a check if a Screen should have the Vertical Tool Bar. This was added to avoid rendering // the bar from the SkyChestScreen. @@ -346,8 +349,8 @@ private void renderTooltips(GuiGraphics guiGraphics, int mouseX, int mouseY) { var area = tooltipWidget.getTooltipArea(); if (mouseX >= area.getX() && mouseY >= area.getY() && - mouseX < area.getX() + area.getWidth() - && mouseY < area.getY() + area.getHeight()) { + mouseX < area.getX() + area.getWidth() + && mouseY < area.getY() + area.getHeight()) { var tooltip = new Tooltip(tooltipWidget.getTooltipMessage()); if (!tooltip.getContent().isEmpty()) { drawTooltipWithHeader(guiGraphics, tooltip, mouseX, mouseY); @@ -496,32 +499,11 @@ public void drawFG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX @Override protected final void renderBg(GuiGraphics guiGraphics, float f, int x, - int y) { + int y) { this.drawBG(guiGraphics, leftPos, topPos, x, y, f); widgets.drawBackgroundLayer(guiGraphics, getBounds(true), new Point(x - leftPos, y - topPos)); - - for (Slot slot : this.getInventorySlots()) { - if (slot instanceof IOptionalSlot) { - drawOptionalSlotBackground(guiGraphics, (IOptionalSlot) slot, false); - } - } - } - - private void drawOptionalSlotBackground(GuiGraphics guiGraphics, IOptionalSlot slot, boolean alwaysDraw) { - // If a slot is optional and doesn't currently render, we still need to provide a background for it - if (alwaysDraw || slot.isRenderDisabled()) { - // If the slot is disabled, shade the background overlay - float alpha = slot.isSlotEnabled() ? 1.0f : 0.2f; - - Point pos = slot.getBackgroundPos(); - - Blitter.guiSprite(Icon.SLOT_BACKGROUND) - .dest(leftPos + pos.getX(), topPos + pos.getY()) - .color(1, 1, 1, alpha) - .blit(guiGraphics); - } } // Convert global mouse x,y to relative Point @@ -637,8 +619,8 @@ protected void slotClicked(@Nullable Slot slot, int slotIdx, int mouseButton, Cl } if (this.drag_click.size() <= 1 - && mouseButton == InputConstants.MOUSE_BUTTON_RIGHT - && getEmptyingAction(slot, menu.getCarried()) != null) { + && mouseButton == InputConstants.MOUSE_BUTTON_RIGHT + && getEmptyingAction(slot, menu.getCarried()) != null) { var p = new InventoryActionPacket(InventoryAction.EMPTY_ITEM, slotIdx, 0); PacketDistributor.sendToServer(p); return; @@ -669,7 +651,7 @@ && getEmptyingAction(slot, menu.getCarried()) != null) { } if (slot != null && - InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_SPACE)) { + InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_KEY_SPACE)) { int slotNum = slot.index; final InventoryActionPacket p = new InventoryActionPacket(InventoryAction.MOVE_REGION, slotNum, 0); PacketDistributor.sendToServer(p); @@ -680,7 +662,7 @@ && getEmptyingAction(slot, menu.getCarried()) != null) { this.disableShiftClick = true; if (this.dbl_whichItem.isEmpty() || this.bl_clicked != slot - || this.dbl_clickTimer.elapsed(TimeUnit.MILLISECONDS) > 250) { + || this.dbl_clickTimer.elapsed(TimeUnit.MILLISECONDS) > 250) { // some simple double click logic. this.bl_clicked = slot; this.dbl_clickTimer = Stopwatch.createStarted(); @@ -691,8 +673,8 @@ && getEmptyingAction(slot, menu.getCarried()) != null) { final List slots = this.getInventorySlots(); for (Slot inventorySlot : slots) { if (inventorySlot != null && inventorySlot.mayPickup(getPlayer()) && inventorySlot.hasItem() - && isSameInventory(inventorySlot, slot) - && AbstractContainerMenu.canItemQuickReplace(inventorySlot, this.dbl_whichItem, true)) { + && isSameInventory(inventorySlot, slot) + && AbstractContainerMenu.canItemQuickReplace(inventorySlot, this.dbl_whichItem, true)) { this.slotClicked(inventorySlot, inventorySlot.index, 0, ClickType.QUICK_MOVE); } } @@ -737,7 +719,7 @@ protected boolean checkHotbarKeyPressed(int keyCode, int scanCode) { final List slots = this.getInventorySlots(); for (Slot s : slots) { if (s.slot == j && s.container == this.menu.getPlayerInventory() - && !s.mayPickup(this.menu.getPlayerInventory().player)) { + && !s.mayPickup(this.menu.getPlayerInventory().player)) { return false; } } @@ -748,7 +730,7 @@ protected boolean checkHotbarKeyPressed(int keyCode, int scanCode) { } else { for (Slot s : slots) { if (s.slot == j - && s.container == this.menu.getPlayerInventory()) { + && s.container == this.menu.getPlayerInventory()) { ServerboundPacket message = new SwapSlotsPacket(s.index, theSlot.index); PacketDistributor.sendToServer(message); return true; @@ -773,7 +755,7 @@ protected boolean isHovering(Slot slot, double x, double y) { } public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX, int mouseY, - float partialTicks) { + float partialTicks) { var background = style.getBackground(); if (background != null) { @@ -804,44 +786,81 @@ public void drawBG(GuiGraphics guiGraphics, int offsetX, int offsetY, int mouseX var slots = menu.getSlotBySemantic().get(semantic); int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE; for (var slot : slots) { + float opacity = 1; if (!slot.isActive()) { - continue; + // If a slot is optional and doesn't currently render, we still need to provide a background for it + if (slot instanceof IOptionalSlot optionalSlot && optionalSlot.isRenderDisabled()) { + opacity = 0.2f; + } else { + continue; + } } - // Slots are 16x16 but our border is 18x18 - minX = Math.min(minX, slot.x - 1); - minY = Math.min(minY, slot.y - 1); - maxX = Math.max(maxX, slot.x + 17); - maxY = Math.max(maxY, slot.y + 17); + var sprite = GuiAssets.SLOT_BACKGROUND; + var backgroundX = slot.x - 1; + var backgroundY = slot.y - 1; + int backgroundWidth = 18; + int backgroundHeight = 18; + if (slot instanceof ResizableSlot resizableSlot) { + backgroundWidth = resizableSlot.getWidth() + 2; + backgroundHeight = resizableSlot.getHeight() + 2; + sprite = GuiAssets.SLOT_LARGE_BACKGROUND; + } + + var color = FastColor.ARGB32.colorFromFloat(opacity, 1f, 1f, 1f); + bgLayer.fillSprite( + sprite, + backgroundX, + backgroundY, + 1, + backgroundWidth, + backgroundHeight, + color); + + minX = Math.min(minX, backgroundX); + minY = Math.min(minY, backgroundY); + maxX = Math.max(maxX, backgroundX + backgroundWidth); + maxY = Math.max(maxY, backgroundY + backgroundHeight); } - if (minX == Integer.MAX_VALUE) { - continue; // No visible slots found + for (var slot : slots) { + if (slot instanceof AppEngSlot appEngSlot && slot.isActive()) { + renderAppEngSlotSpriteLayer(appEngSlot, bgLayer, 1.1f); + } } - bgLayer.fillSprite( - AppEng.makeId("slot"), - minX, - minY, - 1, - maxX - minX, - maxY - minY, - 0xffffffff - ); - bgLayer.fillSprite( - AppEng.makeId("slot_border"), - minX, - minY, - 2, - maxX - minX, - maxY - minY, - 0xffffffff - ); + if (minX != Integer.MAX_VALUE) { + bgLayer.fillSprite( + GuiAssets.SLOT_BORDER, + minX, + minY, + 2, + maxX - minX, + maxY - minY, + 0xffffffff); + } } bgLayer.render(guiGraphics.pose(), offsetX, offsetY, 0); } + private void renderAppEngSlotSpriteLayer(AppEngSlot s, SpriteLayer bgLayer, float baseZ) { + var is = s.getItem(); + + // If the slot has a background icon, render it, but only if the slot is empty + // or it requests the icon to be always drawn + if ((s.renderIconWithItem() || is.isEmpty()) && s.isSlotEnabled() && s.getIcon() != null) { + var opacity = s.getOpacityOfIcon(); + var color = FastColor.ARGB32.colorFromFloat(opacity, 1, 1, 1); + bgLayer.fillSprite(s.getIcon(), s.x, s.y, baseZ, 16, 16, color); + } + + // Draw a red background for slots that are in an invalid state + if (!s.isValid()) { + bgLayer.fillSprite(AppEng.makeId("slot_invalid"), s.x, s.y, baseZ + 0.1f, 16, 16, -1); + } + } + public void drawItem(GuiGraphics guiGraphics, int x, int y, ItemStack is) { guiGraphics.renderItem(is, x, y); guiGraphics.renderItemDecorations(font, is, x, y); @@ -868,22 +887,6 @@ public void renderSlot(GuiGraphics guiGraphics, Slot s) { } private void renderAppEngSlot(GuiGraphics guiGraphics, AppEngSlot s) { - var is = s.getItem(); - - // If the slot has a background icon, render it, but only if the slot is empty - // or it requests the icon to be always drawn - if ((s.renderIconWithItem() || is.isEmpty()) && s.isSlotEnabled() && s.getIcon() != null) { - Blitter.guiSprite(s.getIcon()) - .dest(s.x, s.y) - .opacity(s.getOpacityOfIcon()) - .blit(guiGraphics); - } - - // Draw a red background for slots that are in an invalid state - if (!s.isValid()) { - guiGraphics.fill(s.x, s.y, 16 + s.x, 16 + s.y, 0x66ff6666); - } - super.renderSlot(guiGraphics, s); } diff --git a/src/main/java/appeng/client/gui/ICompositeWidget.java b/src/main/java/appeng/client/gui/ICompositeWidget.java index f5d010a09ee..1dfe362aa07 100644 --- a/src/main/java/appeng/client/gui/ICompositeWidget.java +++ b/src/main/java/appeng/client/gui/ICompositeWidget.java @@ -18,17 +18,19 @@ package appeng.client.gui; -import appeng.client.Point; -import appeng.client.gui.widgets.PanelBlitter; -import appeng.client.gui.widgets.SpriteLayer; +import java.util.List; +import java.util.function.Consumer; + +import org.jetbrains.annotations.Nullable; + import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.Rect2i; -import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.function.Consumer; +import appeng.client.Point; +import appeng.client.gui.widgets.PanelBlitter; +import appeng.client.gui.widgets.SpriteLayer; public interface ICompositeWidget { @@ -60,9 +62,9 @@ default void addExclusionZones(List exclusionZones, Rect2i screenBounds) // Automatically add the bounds if they exceed the screen bounds if (bounds.getX() < 0 - || bounds.getY() < 0 - || bounds.getX() + bounds.getWidth() > screenBounds.getWidth() - || bounds.getY() + bounds.getHeight() > screenBounds.getHeight()) { + || bounds.getY() < 0 + || bounds.getX() + bounds.getWidth() > screenBounds.getWidth() + || bounds.getY() + bounds.getHeight() > screenBounds.getHeight()) { exclusionZones.add(new Rect2i( screenBounds.getX() + bounds.getX(), screenBounds.getY() + bounds.getY(), diff --git a/src/main/java/appeng/client/gui/WidgetContainer.java b/src/main/java/appeng/client/gui/WidgetContainer.java index 88b9e70be9e..685ede5c832 100644 --- a/src/main/java/appeng/client/gui/WidgetContainer.java +++ b/src/main/java/appeng/client/gui/WidgetContainer.java @@ -18,6 +18,23 @@ package appeng.client.gui; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import com.google.common.base.Preconditions; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button.OnPress; +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.network.chat.Component; +import net.neoforged.neoforge.network.PacketDistributor; + import appeng.client.Point; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.style.WidgetStyle; @@ -35,20 +52,6 @@ import appeng.core.network.ServerboundPacket; import appeng.core.network.serverbound.SwitchGuisPacket; import appeng.menu.implementations.PriorityMenu; -import com.google.common.base.Preconditions; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button.OnPress; -import net.minecraft.client.renderer.Rect2i; -import net.minecraft.network.chat.Component; -import net.neoforged.neoforge.network.PacketDistributor; -import org.jetbrains.annotations.Nullable; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; /** * This utility class helps with positioning commonly used Minecraft {@link AbstractWidget} instances on a screen @@ -272,8 +275,8 @@ public void drawForegroundLayer(GuiGraphics poseStack, Rect2i bounds, Point mous public boolean onMouseDown(Point mousePos, int btn) { for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && (widget.wantsAllMouseDownEvents() || mousePos.isIn(widget.getBounds())) - && widget.onMouseDown(mousePos, btn)) { + && (widget.wantsAllMouseDownEvents() || mousePos.isIn(widget.getBounds())) + && widget.onMouseDown(mousePos, btn)) { return true; } } @@ -287,8 +290,8 @@ public boolean onMouseDown(Point mousePos, int btn) { public boolean onMouseUp(Point mousePos, int btn) { for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && (widget.wantsAllMouseUpEvents() || mousePos.isIn(widget.getBounds())) - && widget.onMouseUp(mousePos, btn)) { + && (widget.wantsAllMouseUpEvents() || mousePos.isIn(widget.getBounds())) + && widget.onMouseUp(mousePos, btn)) { return true; } } @@ -316,8 +319,8 @@ boolean onMouseWheel(Point mousePos, double wheelDelta) { // First pass: dispatch wheel event to widgets the mouse is over for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && mousePos.isIn(widget.getBounds()) - && widget.onMouseWheel(mousePos, wheelDelta)) { + && mousePos.isIn(widget.getBounds()) + && widget.onMouseWheel(mousePos, wheelDelta)) { return true; } } @@ -325,8 +328,8 @@ boolean onMouseWheel(Point mousePos, double wheelDelta) { // Second pass: send the event to capturing widgets for (var widget : compositeWidgets.values()) { if (widget.isVisible() - && widget.wantsAllMouseWheelEvents() - && widget.onMouseWheel(mousePos, wheelDelta)) { + && widget.wantsAllMouseWheelEvents() + && widget.onMouseWheel(mousePos, wheelDelta)) { return true; } } @@ -376,7 +379,7 @@ public Tooltip getTooltip(int mouseX, int mouseY) { Rect2i bounds = c.getBounds(); if (mouseX >= bounds.getX() && mouseX < bounds.getX() + bounds.getWidth() - && mouseY >= bounds.getY() && mouseY < bounds.getY() + bounds.getHeight()) { + && mouseY >= bounds.getY() && mouseY < bounds.getY() + bounds.getHeight()) { Tooltip tooltip = c.getTooltip(mouseX, mouseY); if (tooltip != null) { return tooltip; @@ -409,7 +412,7 @@ public boolean hitTest(Point mousePos) { // rather than less-than. private static boolean contains(Rect2i area, int mouseX, int mouseY) { return mouseX >= area.getX() && mouseX < area.getX() + area.getWidth() - && mouseY >= area.getY() && mouseY < area.getY() + area.getHeight(); + && mouseY >= area.getY() && mouseY < area.getY() + area.getHeight(); } public AETextField addTextField(String id) { diff --git a/src/main/java/appeng/client/gui/assets/GuiAssets.java b/src/main/java/appeng/client/gui/assets/GuiAssets.java index 000a19a9e00..87dd3fed093 100644 --- a/src/main/java/appeng/client/gui/assets/GuiAssets.java +++ b/src/main/java/appeng/client/gui/assets/GuiAssets.java @@ -1,11 +1,11 @@ package appeng.client.gui.assets; -import appeng.core.AEConfig; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling; import net.minecraft.resources.ResourceLocation; +import appeng.core.AppEng; + /** * Asset management */ @@ -13,11 +13,22 @@ public final class GuiAssets { /** * @see net.minecraft.client.gui.GuiSpriteManager */ - public static final ResourceLocation GUI_SPRITE_ATLAS = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png"); + public static final ResourceLocation GUI_SPRITE_ATLAS = ResourceLocation + .withDefaultNamespace("textures/atlas/gui.png"); + + public static final ResourceLocation WINDOW_SPRITE = AppEng.makeId("window"); + public static final ResourceLocation INNER_BORDER_SPRITE = AppEng.makeId("window_inner"); + public static final ResourceLocation SLOT_BACKGROUND = AppEng.makeId("slot"); + public static final ResourceLocation SLOT_LARGE_BACKGROUND = AppEng.makeId("slot_large"); + public static final ResourceLocation SLOT_BORDER = AppEng.makeId("slot_border"); private GuiAssets() { } + public static SpritePadding getWindowPadding() { + return getNineSliceSprite(WINDOW_SPRITE).padding; + } + public static NineSliceSprite getNineSliceSprite(ResourceLocation id) { var guiSprites = Minecraft.getInstance().getGuiSprites(); var sprite = guiSprites.getSprite(id); @@ -39,8 +50,8 @@ public static NineSliceSprite getNineSliceSprite(ResourceLocation id) { return new NineSliceSprite( sprite.atlasLocation(), - border, - new float[]{u0, u1, u2, u3, v0, v1, v2, v3}); + new SpritePadding(border.left(), border.top(), border.right(), border.bottom()), + new float[] { u0, u1, u2, u3, v0, v1, v2, v3 }); } /** @@ -48,7 +59,7 @@ public static NineSliceSprite getNineSliceSprite(ResourceLocation id) { * These values refer to the atlas. */ public record NineSliceSprite(ResourceLocation atlasLocation, - GuiSpriteScaling.NineSlice.Border border, - float[] uv) { + SpritePadding padding, + float[] uv) { } } diff --git a/src/main/java/appeng/client/gui/assets/SpritePadding.java b/src/main/java/appeng/client/gui/assets/SpritePadding.java new file mode 100644 index 00000000000..1a094a9cc2a --- /dev/null +++ b/src/main/java/appeng/client/gui/assets/SpritePadding.java @@ -0,0 +1,19 @@ +package appeng.client.gui.assets; + +public record SpritePadding(int left, int top, int right, int bottom) { + public int height() { + return top + bottom; + } + + public int width() { + return left + right; + } + + public SpritePadding expand(int padding) { + return new SpritePadding(left + padding, top + padding, right + padding, bottom + padding); + } + + public SpritePadding expand(int left, int top, int right, int bottom) { + return new SpritePadding(this.left + left, this.top + top, this.right + right, this.bottom + bottom); + } +} diff --git a/src/main/java/appeng/client/gui/implementations/UpgradeableScreen.java b/src/main/java/appeng/client/gui/implementations/UpgradeableScreen.java index dbfcd511a13..cd5372a40ad 100644 --- a/src/main/java/appeng/client/gui/implementations/UpgradeableScreen.java +++ b/src/main/java/appeng/client/gui/implementations/UpgradeableScreen.java @@ -47,7 +47,7 @@ public UpgradeableScreen(T menu, Inventory playerInventory, Component title, Scr menu.getSlots(SlotSemantics.UPGRADE), this::getCompatibleUpgrades)); if (menu.getToolbox().isPresent()) { - this.widgets.add("toolbox", new ToolboxPanel(style, menu.getToolbox().getName())); + this.widgets.add("toolbox", new ToolboxPanel(menu.getToolbox().getName())); } } diff --git a/src/main/java/appeng/client/gui/me/common/MEStorageScreen.java b/src/main/java/appeng/client/gui/me/common/MEStorageScreen.java index 55a2aa429fb..590b2b4ee32 100644 --- a/src/main/java/appeng/client/gui/me/common/MEStorageScreen.java +++ b/src/main/java/appeng/client/gui/me/common/MEStorageScreen.java @@ -194,7 +194,7 @@ public MEStorageScreen(C menu, Inventory playerInventory, menu.getSlots(SlotSemantics.UPGRADE), menu.getHost())); if (menu.getToolbox().isPresent()) { - this.widgets.add("toolbox", new ToolboxPanel(style, menu.getToolbox().getName())); + this.widgets.add("toolbox", new ToolboxPanel(menu.getToolbox().getName())); } // Restore previous search term diff --git a/src/main/java/appeng/client/gui/util/RectangleMerger.java b/src/main/java/appeng/client/gui/util/RectangleMerger.java index 4dbeec6d80f..5e2c5fc8e52 100644 --- a/src/main/java/appeng/client/gui/util/RectangleMerger.java +++ b/src/main/java/appeng/client/gui/util/RectangleMerger.java @@ -1,11 +1,11 @@ package appeng.client.gui.util; -import appeng.client.guidebook.document.LytRect; - import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import appeng.client.guidebook.document.LytRect; + /** * Remove overlapping rectangles and produce a list of equivalent non-overlapping rectangles. */ @@ -14,12 +14,11 @@ private RectangleMerger() { } /** - * This implements a line-sweep algorithm that ensures all rectangles are non-overlapping - * by merging or subdividing overlapping rectangles. + * This implements a line-sweep algorithm that ensures all rectangles are non-overlapping by merging or subdividing + * overlapping rectangles. *

- * What this algorithm ends up doing is essentially cutting up the union of all rectangles into - * vertical stripes by scanning from left-to-right and producing rectangles whenever a rectangle - * ends on the horizontal axis. + * What this algorithm ends up doing is essentially cutting up the union of all rectangles into vertical stripes by + * scanning from left-to-right and producing rectangles whenever a rectangle ends on the horizontal axis. */ public static List merge(List rects) { if (rects.isEmpty()) { @@ -51,8 +50,7 @@ public static List merge(List rects) { int top = 0; int opened = 0; // Used to find the end of rectangles that overlap on the y-axis - yEvents: - for (int j = 0; j < yEvents.size(); j++) { + yEvents: for (int j = 0; j < yEvents.size(); j++) { var yEvent = yEvents.get(j); // Only process rects for which we are between open/close on the primary axis diff --git a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java index bd32fcbc893..fab3e4a0aff 100644 --- a/src/main/java/appeng/client/gui/widgets/PanelBlitter.java +++ b/src/main/java/appeng/client/gui/widgets/PanelBlitter.java @@ -1,21 +1,17 @@ package appeng.client.gui.widgets; -import appeng.client.gui.assets.GuiAssets; -import appeng.client.gui.util.RectangleMerger; -import appeng.client.guidebook.document.LytRect; -import appeng.core.AppEng; +import java.util.ArrayList; +import java.util.List; + import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.Rect2i; -import net.minecraft.resources.ResourceLocation; -import java.util.ArrayList; -import java.util.List; +import appeng.client.gui.assets.GuiAssets; +import appeng.client.gui.util.RectangleMerger; +import appeng.client.guidebook.document.LytRect; public class PanelBlitter { - private static final ResourceLocation WINDOW_SPRITE = AppEng.makeId("window"); - private static final ResourceLocation INNER_BORDER_SPRITE = AppEng.makeId("window_inner"); - private final List rects = new ArrayList<>(); private final List processedRects = new ArrayList<>(); @@ -35,8 +31,8 @@ public class PanelBlitter { private final SpriteSlice INNER_BOTTOM_LEFT; public PanelBlitter() { - var window = GuiAssets.getNineSliceSprite(WINDOW_SPRITE); - var inner = GuiAssets.getNineSliceSprite(INNER_BORDER_SPRITE); + var window = GuiAssets.getNineSliceSprite(GuiAssets.WINDOW_SPRITE); + var inner = GuiAssets.getNineSliceSprite(GuiAssets.INNER_BORDER_SPRITE); CENTER = new SpriteSlice(window, 4); OUTER_TOP_LEFT = new SpriteSlice(window, 0); @@ -151,7 +147,7 @@ public void render(SpriteLayer layer, int z, int color) { // We must use the width/height the corner would normally be, in case it is filled in // with an edge sprite. - var border = cornerStyle.nineSlice.border(); + var border = cornerStyle.nineSlice.padding(); var width = switch (i) { default -> border.left(); case 1, 2 -> border.right(); @@ -279,7 +275,7 @@ public Edge(int start, int end) { } private final class Rectangle { - SpriteSlice[] corners = new SpriteSlice[]{ + SpriteSlice[] corners = new SpriteSlice[] { OUTER_TOP_LEFT, OUTER_TOP_RIGHT, OUTER_BOTTOM_RIGHT, @@ -468,11 +464,11 @@ private enum EdgeStyle { */ private record SpriteSlice(GuiAssets.NineSliceSprite nineSlice, int slice) { int height() { - return slice / 3 == 2 ? nineSlice.border().bottom() : nineSlice.border().top(); + return slice / 3 == 2 ? nineSlice.padding().bottom() : nineSlice.padding().top(); } int width() { - return slice % 3 == 2 ? nineSlice.border().right() : nineSlice.border().left(); + return slice % 3 == 2 ? nineSlice.padding().right() : nineSlice.padding().left(); } public void addQuad(SpriteLayer layer, float x, float y, float z, float width, float height, int color) { diff --git a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java index 1c7a7fa034d..1dd349c8b9c 100644 --- a/src/main/java/appeng/client/gui/widgets/SpriteLayer.java +++ b/src/main/java/appeng/client/gui/widgets/SpriteLayer.java @@ -1,6 +1,5 @@ package appeng.client.gui.widgets; -import appeng.client.gui.assets.GuiAssets; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.BufferUploader; @@ -8,11 +7,14 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexFormat; + import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling; import net.minecraft.resources.ResourceLocation; +import appeng.client.gui.assets.GuiAssets; + /** * Helper to build and draw a layer of sprites in a single draw-call. */ @@ -26,9 +28,6 @@ public SpriteLayer() { builder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); } - public void addSprite(ResourceLocation sprite, int x, int y) { - } - public void fillSprite(ResourceLocation id, float x, float y, float z, float width, float height, int color) { // Too large values for width / height cause immediate crashes of the VM due to graphics driver bugs // These maximum values are picked without too much thought. @@ -40,13 +39,15 @@ public void fillSprite(ResourceLocation id, float x, float y, float z, float wid var scaling = guiSprites.getSpriteScaling(sprite); switch (scaling) { case GuiSpriteScaling.Tile tiled -> { - fillTiled(x, y, z, width, height, color, tiled.width(), tiled.height(), sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); + fillTiled(x, y, z, width, height, color, tiled.width(), tiled.height(), sprite.getU0(), sprite.getU1(), + sprite.getV0(), sprite.getV1()); } case GuiSpriteScaling.Stretch stretch -> { addQuad(x, y, z, width, height, color, sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); } case GuiSpriteScaling.NineSlice nineSlice -> { - addTiledNineSlice(id, x, y, z, width, height, color, nineSlice.width(), nineSlice.height(), nineSlice.border(), sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); + addTiledNineSlice(id, x, y, z, width, height, color, nineSlice.width(), nineSlice.height(), + nineSlice.border(), sprite.getU0(), sprite.getU1(), sprite.getV0(), sprite.getV1()); } default -> { } @@ -54,19 +55,19 @@ public void fillSprite(ResourceLocation id, float x, float y, float z, float wid } private void addTiledNineSlice(ResourceLocation id, - float x, - float y, - float z, - float width, - float height, - int color, - float nineSliceWidth, - float nineSliceHeight, - GuiSpriteScaling.NineSlice.Border border, - float u0, - float u1, - float v0, - float v1) { + float x, + float y, + float z, + float width, + float height, + int color, + float nineSliceWidth, + float nineSliceHeight, + GuiSpriteScaling.NineSlice.Border border, + float u0, + float u1, + float v0, + float v1) { var leftWidth = Math.min(border.left(), width / 2); var rightWidth = Math.min(border.right(), width / 2); @@ -92,20 +93,27 @@ private void addTiledNineSlice(ResourceLocation id, // Corners are always untiled, but may be cropped addQuad(x, y, z, leftWidth, topHeight, color, u0, leftU, v0, topV); // Top left addQuad(dstInnerRight, y, z, rightWidth, topHeight, color, rightU, u1, v0, topV); // Top right - addQuad(dstInnerRight, dstInnerBottom, z, rightWidth, bottomHeight, color, rightU, u1, bottomV, v1); // Bottom right + addQuad(dstInnerRight, dstInnerBottom, z, rightWidth, bottomHeight, color, rightU, u1, bottomV, v1); // Bottom + // right addQuad(x, dstInnerBottom, z, leftWidth, bottomHeight, color, u0, leftU, bottomV, v1); // Bottom left // The edges are tiled - fillTiled(dstInnerLeft, y, z, dstInnerWidth, topHeight, color, innerWidth, border.top(), leftU, rightU, v0, topV); // Top Edge - fillTiled(dstInnerLeft, dstInnerBottom, z, dstInnerWidth, bottomHeight, color, innerWidth, border.bottom(), leftU, rightU, bottomV, v1); // Bottom Edge - fillTiled(x, dstInnerTop, z, leftWidth, dstInnerHeight, color, border.left(), innerHeight, u0, leftU, topV, bottomV); // Left Edge - fillTiled(dstInnerRight, dstInnerTop, z, rightWidth, dstInnerHeight, color, border.right(), innerHeight, rightU, u1, topV, bottomV); // Right Edge + fillTiled(dstInnerLeft, y, z, dstInnerWidth, topHeight, color, innerWidth, border.top(), leftU, rightU, v0, + topV); // Top Edge + fillTiled(dstInnerLeft, dstInnerBottom, z, dstInnerWidth, bottomHeight, color, innerWidth, border.bottom(), + leftU, rightU, bottomV, v1); // Bottom Edge + fillTiled(x, dstInnerTop, z, leftWidth, dstInnerHeight, color, border.left(), innerHeight, u0, leftU, topV, + bottomV); // Left Edge + fillTiled(dstInnerRight, dstInnerTop, z, rightWidth, dstInnerHeight, color, border.right(), innerHeight, rightU, + u1, topV, bottomV); // Right Edge // The center is tiled too - fillTiled(dstInnerLeft, dstInnerTop, z, dstInnerWidth, dstInnerHeight, color, innerWidth, innerHeight, leftU, rightU, topV, bottomV); + fillTiled(dstInnerLeft, dstInnerTop, z, dstInnerWidth, dstInnerHeight, color, innerWidth, innerHeight, leftU, + rightU, topV, bottomV); } - private void fillTiled(float x, float y, float z, float width, float height, int color, float destTileWidth, float destTileHeight, float u0, float u1, float v0, float v1) { + private void fillTiled(float x, float y, float z, float width, float height, int color, float destTileWidth, + float destTileHeight, float u0, float u1, float v0, float v1) { if (destTileWidth <= 0 || destTileHeight <= 0) { return; } @@ -127,7 +135,8 @@ private void fillTiled(float x, float y, float z, float width, float height, int } } - public void addQuad(float x, float y, float z, float width, float height, int color, float minU, float maxU, float minV, float maxV) { + public void addQuad(float x, float y, float z, float width, float height, int color, float minU, float maxU, + float minV, float maxV) { if (width < 0 || height < 0) { return; } @@ -149,6 +158,7 @@ public void render(PoseStack poseStack, int x, int y, int z) { return; } + RenderSystem.enableBlend(); RenderSystem.setShaderTexture(0, atlasLocation); RenderSystem.setShader(GameRenderer::getPositionTexColorShader); var modelViewStack = RenderSystem.getModelViewStack(); diff --git a/src/main/java/appeng/client/gui/widgets/ToolboxPanel.java b/src/main/java/appeng/client/gui/widgets/ToolboxPanel.java index f8e18a18c57..04f33b30ad6 100644 --- a/src/main/java/appeng/client/gui/widgets/ToolboxPanel.java +++ b/src/main/java/appeng/client/gui/widgets/ToolboxPanel.java @@ -21,15 +21,12 @@ import org.jetbrains.annotations.Nullable; import net.minecraft.ChatFormatting; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.Rect2i; import net.minecraft.network.chat.Component; import appeng.client.Point; import appeng.client.gui.ICompositeWidget; import appeng.client.gui.Tooltip; -import appeng.client.gui.style.Blitter; -import appeng.client.gui.style.ScreenStyle; import appeng.core.localization.GuiText; /** @@ -37,16 +34,12 @@ */ public class ToolboxPanel implements ICompositeWidget { - // Backdrop for the 3x3 toolbox offered by the network-tool - private final Blitter background; - private final Component toolbeltName; // Relative to the origin of the current screen (not window) private Rect2i bounds = new Rect2i(0, 0, 0, 0); - public ToolboxPanel(ScreenStyle style, Component toolbeltName) { - this.background = style.getImage("toolbox"); + public ToolboxPanel(Component toolbeltName) { this.toolbeltName = toolbeltName; } @@ -66,12 +59,8 @@ public Rect2i getBounds() { } @Override - public void drawBackgroundLayer(GuiGraphics guiGraphics, Rect2i bounds, Point mouse) { - background.dest( - bounds.getX() + this.bounds.getX(), - bounds.getY() + this.bounds.getY(), - this.bounds.getWidth(), - this.bounds.getHeight()).blit(guiGraphics); + public void addBackgroundPanels(PanelBlitter panels, Rect2i screenBounds) { + panels.addBounds(bounds); } @Nullable diff --git a/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java b/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java index 3861a132707..833f8d427c2 100644 --- a/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java +++ b/src/main/java/appeng/client/gui/widgets/UpgradesPanel.java @@ -18,22 +18,26 @@ package appeng.client.gui.widgets; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.client.renderer.Rect2i; +import net.minecraft.network.chat.Component; +import net.minecraft.world.inventory.Slot; + import appeng.api.upgrades.IUpgradeableObject; import appeng.api.upgrades.Upgrades; import appeng.client.Point; import appeng.client.gui.ICompositeWidget; import appeng.client.gui.Rects; import appeng.client.gui.Tooltip; +import appeng.client.gui.assets.GuiAssets; +import appeng.client.gui.assets.SpritePadding; import appeng.menu.slot.AppEngSlot; -import net.minecraft.client.renderer.Rect2i; -import net.minecraft.network.chat.Component; -import net.minecraft.world.inventory.Slot; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; /** * A panel that can draw a dynamic number of upgrade slots in a vertical layout. @@ -41,10 +45,10 @@ public final class UpgradesPanel implements ICompositeWidget { private static final int SLOT_SIZE = 18; - private static final int PADDING = 3; private static final int MAX_ROWS = 8; private final List slots; + private final SpritePadding padding; // Relative to current screen origin (not window) private int x; @@ -63,11 +67,14 @@ public UpgradesPanel(List slots, IUpgradeableObject upgradeableObject) { public UpgradesPanel(List slots, Supplier> tooltipSupplier) { this.slots = slots; this.tooltipSupplier = tooltipSupplier; + + // Padding is derived from the GUI sprite for windows + padding = GuiAssets.getWindowPadding().expand(3); } @Override public void setPosition(Point position) { - x = position.getX(); + x = position.getX() - padding.left(); y = position.getY(); } @@ -86,15 +93,15 @@ public void setSize(int width, int height) { public Rect2i getBounds() { int slotCount = getUpgradeSlotCount(); - int height = 2 * PADDING + Math.min(MAX_ROWS, slotCount) * SLOT_SIZE; - int width = 2 * PADDING + (slotCount + MAX_ROWS - 1) / MAX_ROWS * SLOT_SIZE; + int height = padding.height() + Math.min(MAX_ROWS, slotCount) * SLOT_SIZE; + int width = padding.width() + (slotCount + MAX_ROWS - 1) / MAX_ROWS * SLOT_SIZE; return new Rect2i(x, y, width, height); } @Override public void updateBeforeRender() { - int slotOriginX = this.x; - int slotOriginY = this.y + PADDING; + int slotOriginX = this.x + padding.left(); + int slotOriginY = this.y + padding.top(); for (Slot slot : slots) { if (!slot.isActive()) { @@ -124,12 +131,12 @@ private void visitBackgroundPanels(Consumer visitor) { int fullCols = slotCount / MAX_ROWS; int rightEdge = x; if (fullCols > 0) { - int fullColWidth = PADDING * 2 + fullCols * SLOT_SIZE; + int fullColWidth = padding.width() + fullCols * SLOT_SIZE; visitor.accept(new Rect2i( rightEdge, y, fullColWidth, - PADDING * 2 + MAX_ROWS * SLOT_SIZE)); + padding.height() + MAX_ROWS * SLOT_SIZE)); rightEdge += fullColWidth; } @@ -140,8 +147,8 @@ private void visitBackgroundPanels(Consumer visitor) { rightEdge, y, // We need to add padding in case there's no full column that already includes it - SLOT_SIZE + (fullCols > 0 ? 0 : PADDING * 2), - PADDING * 2 + remaining * SLOT_SIZE)); + SLOT_SIZE + (fullCols > 0 ? 0 : padding.width()), + padding.height() + remaining * SLOT_SIZE)); } } diff --git a/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java b/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java index 1228c56a5e8..bed666b1dfd 100644 --- a/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java +++ b/src/main/java/appeng/client/gui/widgets/VerticalButtonBar.java @@ -18,16 +18,17 @@ package appeng.client.gui.widgets; -import appeng.client.Point; -import appeng.client.gui.AEBaseScreen; -import appeng.client.gui.ICompositeWidget; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.Button; import net.minecraft.client.renderer.Rect2i; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; +import appeng.client.Point; +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.ICompositeWidget; /** * A stacked button panel on the left or right side of our UIs. @@ -127,7 +128,6 @@ public void addBackgroundPanels(PanelBlitter panels, Rect2i screenBounds) { panelTop, // The panel should be flush with the window -panelLeft, - bounds.getHeight() + 4 - ); + bounds.getHeight() + 4); } } diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java index 4fb62a9110c..56c35d9c476 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlot.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlot.java @@ -1,5 +1,13 @@ package appeng.client.guidebook.document.block; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + import appeng.client.guidebook.document.LytRect; import appeng.client.guidebook.document.interaction.GuideTooltip; import appeng.client.guidebook.document.interaction.InteractiveElement; @@ -7,13 +15,6 @@ import appeng.client.guidebook.layout.LayoutContext; import appeng.client.guidebook.render.RenderContext; import appeng.core.AppEng; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; /** * Renders a standard Minecraft GUI slot. @@ -38,7 +39,7 @@ public LytSlot(Ingredient ingredient) { } public LytSlot(ItemStack stack) { - this.stacks = new ItemStack[]{stack}; + this.stacks = new ItemStack[] { stack }; } public boolean isLargeSlot() { diff --git a/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java b/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java index 2d8b8149746..04c51d977bd 100644 --- a/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java +++ b/src/main/java/appeng/client/guidebook/document/block/LytSlotGrid.java @@ -1,14 +1,15 @@ package appeng.client.guidebook.document.block; +import java.util.List; + +import net.minecraft.world.item.crafting.Ingredient; + import appeng.client.gui.Icon; import appeng.client.guidebook.color.ConstantColor; import appeng.client.guidebook.document.LytRect; import appeng.client.guidebook.layout.LayoutContext; import appeng.client.guidebook.render.RenderContext; import appeng.core.AppEng; -import net.minecraft.world.item.crafting.Ingredient; - -import java.util.List; public class LytSlotGrid extends LytBox { private final int width; diff --git a/src/main/java/appeng/client/guidebook/render/RenderContext.java b/src/main/java/appeng/client/guidebook/render/RenderContext.java index 07823d5f3fa..5a21dbb8252 100644 --- a/src/main/java/appeng/client/guidebook/render/RenderContext.java +++ b/src/main/java/appeng/client/guidebook/render/RenderContext.java @@ -1,17 +1,11 @@ package appeng.client.guidebook.render; -import appeng.api.stacks.AEFluidKey; -import appeng.client.gui.style.FluidBlitter; -import appeng.client.gui.widgets.PanelBlitter; -import appeng.client.gui.widgets.SpriteLayer; -import appeng.client.guidebook.color.ColorValue; -import appeng.client.guidebook.color.ConstantColor; -import appeng.client.guidebook.color.LightDarkMode; -import appeng.client.guidebook.document.LytRect; -import appeng.client.guidebook.layout.MinecraftFontMetrics; -import appeng.client.guidebook.style.ResolvedTextStyle; import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; + +import org.joml.Matrix4f; +import org.joml.Vector3f; + import net.minecraft.client.Minecraft; import net.minecraft.client.StringSplitter; import net.minecraft.client.gui.Font; @@ -27,8 +21,17 @@ import net.minecraft.world.level.material.Fluid; import net.minecraft.world.phys.Vec2; import net.neoforged.neoforge.fluids.FluidStack; -import org.joml.Matrix4f; -import org.joml.Vector3f; + +import appeng.api.stacks.AEFluidKey; +import appeng.client.gui.style.FluidBlitter; +import appeng.client.gui.widgets.PanelBlitter; +import appeng.client.gui.widgets.SpriteLayer; +import appeng.client.guidebook.color.ColorValue; +import appeng.client.guidebook.color.ConstantColor; +import appeng.client.guidebook.color.LightDarkMode; +import appeng.client.guidebook.document.LytRect; +import appeng.client.guidebook.layout.MinecraftFontMetrics; +import appeng.client.guidebook.style.ResolvedTextStyle; public interface RenderContext { @@ -51,13 +54,13 @@ default PoseStack poseStack() { void fillRect(LytRect rect, ColorValue topLeft, ColorValue topRight, ColorValue bottomRight, ColorValue bottomLeft); default void fillTexturedRect(LytRect rect, AbstractTexture texture, ColorValue topLeft, ColorValue topRight, - ColorValue bottomRight, ColorValue bottomLeft) { + ColorValue bottomRight, ColorValue bottomLeft) { // Just use the entire texture by default fillTexturedRect(rect, texture, topLeft, topRight, bottomRight, bottomLeft, 0, 0, 1, 1); } void fillTexturedRect(LytRect rect, AbstractTexture texture, ColorValue topLeft, ColorValue topRight, - ColorValue bottomRight, ColorValue bottomLeft, float u0, float v0, float u1, float v1); + ColorValue bottomRight, ColorValue bottomLeft, float u0, float v0, float u1, float v1); default void fillTexturedRect(LytRect rect, GuidePageTexture texture) { fillTexturedRect(rect, texture.use(), ConstantColor.WHITE); @@ -148,7 +151,7 @@ default void renderTextCenteredIn(String text, ResolvedTextStyle style, LytRect var lineHeight = fontMetrics.getLineHeight(style); var overallHeight = splitLines.size() * lineHeight; var overallWidth = (int) (splitLines.stream().mapToDouble(splitter::stringWidth).max().orElse(0f) - * style.fontScale()); + * style.fontScale()); var textRect = new LytRect(0, 0, overallWidth, overallHeight); textRect = textRect.centerIn(rect); diff --git a/src/main/java/appeng/core/AEConfig.java b/src/main/java/appeng/core/AEConfig.java index d4825d1ce56..3b7ac316470 100644 --- a/src/main/java/appeng/core/AEConfig.java +++ b/src/main/java/appeng/core/AEConfig.java @@ -18,15 +18,10 @@ package appeng.core; -import appeng.api.config.CondenserOutput; -import appeng.api.config.PowerMultiplier; -import appeng.api.config.PowerUnit; -import appeng.api.config.Settings; -import appeng.api.config.TerminalStyle; -import appeng.api.networking.pathing.ChannelMode; -import appeng.core.settings.TickRates; -import appeng.util.EnumCycler; -import appeng.util.Platform; +import java.util.HashMap; +import java.util.Map; +import java.util.function.DoubleSupplier; + import net.neoforged.fml.ModContainer; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.event.config.ModConfigEvent; @@ -36,9 +31,15 @@ import net.neoforged.neoforge.common.ModConfigSpec.EnumValue; import net.neoforged.neoforge.common.ModConfigSpec.IntValue; -import java.util.HashMap; -import java.util.Map; -import java.util.function.DoubleSupplier; +import appeng.api.config.CondenserOutput; +import appeng.api.config.PowerMultiplier; +import appeng.api.config.PowerUnit; +import appeng.api.config.Settings; +import appeng.api.config.TerminalStyle; +import appeng.api.networking.pathing.ChannelMode; +import appeng.core.settings.TickRates; +import appeng.util.EnumCycler; +import appeng.util.Platform; public final class AEConfig { @@ -85,13 +86,13 @@ public double wireless_getDrainRate(double range) { public double wireless_getMaxRange(int boosters) { return common.wirelessBaseRange.get() - + common.wirelessBoosterRangeMultiplier.get() * Math.pow(boosters, common.wirelessBoosterExp.get()); + + common.wirelessBoosterRangeMultiplier.get() * Math.pow(boosters, common.wirelessBoosterExp.get()); } public double wireless_getPowerDrain(int boosters) { return common.wirelessBaseCost.get() - + common.wirelessCostMultiplier.get() - * Math.pow(boosters, 1 + boosters / common.wirelessHighWirelessCount.get()); + + common.wirelessCostMultiplier.get() + * Math.pow(boosters, 1 + boosters / common.wirelessHighWirelessCount.get()); } public boolean isDarkModeEnabled() { @@ -745,7 +746,7 @@ public void sync() { } private static BooleanValue define(ModConfigSpec.Builder builder, String name, boolean defaultValue, - String comment) { + String comment) { builder.comment(comment); return define(builder, name, defaultValue); } @@ -769,18 +770,18 @@ private static DoubleValue define(ModConfigSpec.Builder builder, String name, do } private static DoubleValue define(ModConfigSpec.Builder builder, String name, double defaultValue, double min, - double max, String comment) { + double max, String comment) { builder.comment(comment); return define(builder, name, defaultValue, min, max); } private static DoubleValue define(ModConfigSpec.Builder builder, String name, double defaultValue, double min, - double max) { + double max) { return builder.defineInRange(name, defaultValue, min, max); } private static IntValue define(ModConfigSpec.Builder builder, String name, int defaultValue, int min, int max, - String comment) { + String comment) { builder.comment(comment); return define(builder, name, defaultValue, min, max); } @@ -794,12 +795,12 @@ private static IntValue define(ModConfigSpec.Builder builder, String name, int d } private static > EnumValue defineEnum(ModConfigSpec.Builder builder, String name, - T defaultValue) { + T defaultValue) { return builder.defineEnum(name, defaultValue); } private static > EnumValue defineEnum(ModConfigSpec.Builder builder, String name, - T defaultValue, String comment) { + T defaultValue, String comment) { builder.comment(comment); return defineEnum(builder, name, defaultValue); } diff --git a/src/main/java/appeng/hooks/DarkModeHook.java b/src/main/java/appeng/hooks/DarkModeHook.java index 16791ae85d5..7dfaccc7d18 100644 --- a/src/main/java/appeng/hooks/DarkModeHook.java +++ b/src/main/java/appeng/hooks/DarkModeHook.java @@ -1,11 +1,12 @@ package appeng.hooks; -import appeng.core.AEConfig; -import appeng.core.AppEng; +import java.util.Map; + import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.resources.ResourceLocation; -import java.util.Map; +import appeng.core.AEConfig; +import appeng.core.AppEng; public final class DarkModeHook { private static final String DARKMODE_SUFFIX = "_darkmode"; @@ -17,12 +18,11 @@ public static boolean isDarkModeEnabledNamespace(String id) { return AppEng.MOD_ID.equals(id); } - public static TextureAtlasSprite getReplacedSprite(ResourceLocation id, - Map textures) { + Map textures) { if (DarkModeHook.isDarkModeEnabledNamespace(id.getNamespace()) - && AEConfig.instance().isDarkModeEnabled() - && !id.getPath().endsWith(DARKMODE_SUFFIX)) { + && AEConfig.instance().isDarkModeEnabled() + && !id.getPath().endsWith(DARKMODE_SUFFIX)) { var alternate = ResourceLocation.fromNamespaceAndPath(id.getNamespace(), id.getPath() + DARKMODE_SUFFIX); return textures.get(alternate); } diff --git a/src/main/java/appeng/menu/AEBaseMenu.java b/src/main/java/appeng/menu/AEBaseMenu.java index 6dae1a31bb6..568d794cd94 100644 --- a/src/main/java/appeng/menu/AEBaseMenu.java +++ b/src/main/java/appeng/menu/AEBaseMenu.java @@ -30,7 +30,6 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; -import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/src/main/java/appeng/menu/implementations/CellWorkbenchMenu.java b/src/main/java/appeng/menu/implementations/CellWorkbenchMenu.java index 9c2edd504ad..c28b570cea3 100644 --- a/src/main/java/appeng/menu/implementations/CellWorkbenchMenu.java +++ b/src/main/java/appeng/menu/implementations/CellWorkbenchMenu.java @@ -150,6 +150,10 @@ public boolean isSlotEnabled(int idx) { @Override public boolean isPartitionSlotEnabled(int idx) { + if (idx >= 9) { + return false; + } + var cwi = getHost().getCell(); if (cwi != null && getCopyMode() == CopyMode.CLEAR_ON_REMOVE) { return idx < cwi.getConfigInventory(getWorkbenchItem()).size(); diff --git a/src/main/java/appeng/menu/slot/CellPartitionSlot.java b/src/main/java/appeng/menu/slot/CellPartitionSlot.java index e97bd3f89d9..b1dc55f8db3 100644 --- a/src/main/java/appeng/menu/slot/CellPartitionSlot.java +++ b/src/main/java/appeng/menu/slot/CellPartitionSlot.java @@ -9,7 +9,6 @@ import appeng.api.inventories.InternalInventory; import appeng.api.storage.StorageCells; -import appeng.client.Point; import appeng.core.localization.GuiText; import appeng.core.localization.Tooltips; @@ -45,16 +44,6 @@ public void set(ItemStack is) { } } - @Override - public boolean isRenderDisabled() { - return true; - } - - @Override - public Point getBackgroundPos() { - return new Point(x - 1, y - 1); - } - @Override public @Nullable List getCustomTooltip(ItemStack carriedItem) { if (!canFitInsideCell(carriedItem)) { diff --git a/src/main/java/appeng/menu/slot/IOptionalSlot.java b/src/main/java/appeng/menu/slot/IOptionalSlot.java index 85f69c26894..ce2a43da365 100644 --- a/src/main/java/appeng/menu/slot/IOptionalSlot.java +++ b/src/main/java/appeng/menu/slot/IOptionalSlot.java @@ -18,24 +18,10 @@ package appeng.menu.slot; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.api.distmarker.OnlyIn; - -import appeng.client.Point; - -/** - * @author BrockWS - * @version rv6 - 2/05/2018 - * @since rv6 2/05/2018 - */ public interface IOptionalSlot { default boolean isRenderDisabled() { - return false; + return true; } boolean isSlotEnabled(); - - @OnlyIn(Dist.CLIENT) - Point getBackgroundPos(); - } diff --git a/src/main/java/appeng/menu/slot/MolecularAssemblerPatternSlot.java b/src/main/java/appeng/menu/slot/MolecularAssemblerPatternSlot.java index 2c984725745..3596c665ed8 100644 --- a/src/main/java/appeng/menu/slot/MolecularAssemblerPatternSlot.java +++ b/src/main/java/appeng/menu/slot/MolecularAssemblerPatternSlot.java @@ -21,7 +21,6 @@ import net.minecraft.world.item.ItemStack; import appeng.api.inventories.InternalInventory; -import appeng.client.Point; import appeng.menu.implementations.MolecularAssemblerMenu; public class MolecularAssemblerPatternSlot extends AppEngSlot implements IOptionalSlot { @@ -45,11 +44,6 @@ protected boolean getCurrentValidationState() { return stack.isEmpty() || mayPlace(stack); } - @Override - public boolean isRenderDisabled() { - return true; // The background image does not include a slot background - } - @Override public boolean isSlotEnabled() { // Always enabled when there's an item in the inventory (otherwise you can't take it out...) @@ -60,9 +54,4 @@ public boolean isSlotEnabled() { var pattern = mac.getHost().getCurrentPattern(); return slot >= 0 && slot < 9 && pattern != null && pattern.isSlotEnabled(slot); } - - @Override - public Point getBackgroundPos() { - return new Point(x - 1, y - 1); - } } diff --git a/src/main/java/appeng/menu/slot/OptionalFakeSlot.java b/src/main/java/appeng/menu/slot/OptionalFakeSlot.java index 5e5cc9e4da8..9ab86ce6d3d 100644 --- a/src/main/java/appeng/menu/slot/OptionalFakeSlot.java +++ b/src/main/java/appeng/menu/slot/OptionalFakeSlot.java @@ -21,13 +21,10 @@ import net.minecraft.world.item.ItemStack; import appeng.api.inventories.InternalInventory; -import appeng.client.Point; public class OptionalFakeSlot extends FakeSlot implements IOptionalSlot { - private final int groupNum; private final IOptionalSlotHost host; - private boolean renderDisabled = true; public OptionalFakeSlot(InternalInventory inv, IOptionalSlotHost containerBus, int invSlot, int groupNum) { @@ -53,18 +50,4 @@ public boolean isSlotEnabled() { return this.host.isSlotEnabled(this.groupNum); } - - @Override - public boolean isRenderDisabled() { - return this.renderDisabled; - } - - public void setRenderDisabled(boolean renderDisabled) { - this.renderDisabled = renderDisabled; - } - - @Override - public Point getBackgroundPos() { - return new Point(x - 1, y - 1); - } } diff --git a/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java b/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java index 5c1369f44d6..43c7b4f6e0c 100644 --- a/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java +++ b/src/main/java/appeng/mixins/GuiSpriteManagerMixin.java @@ -1,24 +1,29 @@ package appeng.mixins; -import appeng.hooks.DarkModeHook; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + import net.minecraft.client.gui.GuiSpriteManager; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.resources.TextureAtlasHolder; import net.minecraft.resources.ResourceLocation; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; + +import appeng.hooks.DarkModeHook; @Mixin(GuiSpriteManager.class) public class GuiSpriteManagerMixin extends TextureAtlasHolder { - public GuiSpriteManagerMixin(TextureManager textureManager, ResourceLocation textureAtlasLocation, ResourceLocation atlasInfoLocation) { + public GuiSpriteManagerMixin(TextureManager textureManager, ResourceLocation textureAtlasLocation, + ResourceLocation atlasInfoLocation) { super(textureManager, textureAtlasLocation, atlasInfoLocation); } @WrapOperation(method = "getSprite", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/TextureAtlasHolder;getSprite(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;")) - private TextureAtlasSprite redirectToDarkModeSprite(GuiSpriteManager manager, ResourceLocation id, Operation original) { + private TextureAtlasSprite redirectToDarkModeSprite(GuiSpriteManager manager, ResourceLocation id, + Operation original) { var replacedSprite = DarkModeHook.getReplacedSprite(id, textureAtlas.getTextures()); if (replacedSprite != null) { return replacedSprite; diff --git a/src/main/resources/assets/ae2/screens/common/terminal_toolbox.json b/src/main/resources/assets/ae2/screens/common/terminal_toolbox.json index cd6831843f3..1dd9b99f520 100644 --- a/src/main/resources/assets/ae2/screens/common/terminal_toolbox.json +++ b/src/main/resources/assets/ae2/screens/common/terminal_toolbox.json @@ -8,14 +8,6 @@ "grid": "BREAK_AFTER_3COLS" } }, - "images": { - "toolbox": { - "texture": "guis/extra_panels.png", - "textureWidth": 128, - "textureHeight": 128, - "srcRect": [69, 62, 59, 66] - } - }, "widgets": { "toolbox": { "$comment": "The toolbox found on some screens", diff --git a/src/main/resources/assets/ae2/screens/common/toolbox.json b/src/main/resources/assets/ae2/screens/common/toolbox.json index 851bdc324f6..2c011724ea9 100644 --- a/src/main/resources/assets/ae2/screens/common/toolbox.json +++ b/src/main/resources/assets/ae2/screens/common/toolbox.json @@ -8,14 +8,6 @@ "grid": "BREAK_AFTER_3COLS" } }, - "images": { - "toolbox": { - "texture": "guis/extra_panels.png", - "textureWidth": 128, - "textureHeight": 128, - "srcRect": [69, 62, 59, 66] - } - }, "widgets": { "toolbox": { "$comment": "The toolbox found on some screens", diff --git a/src/main/resources/assets/ae2/textures/gui/sprites/slot_invalid.png b/src/main/resources/assets/ae2/textures/gui/sprites/slot_invalid.png new file mode 100644 index 0000000000000000000000000000000000000000..119bc2980a2b839e5b4ec88438ab55ba1e22fa75 GIT binary patch literal 91 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k8}bl&H|6fVg?31We{epSZZGe6y)`E kaSY*zPW~}}+B7x>wp1pg6-!oy0VNnbUHx3vIVCg!0KZQY3;+NC literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2/textures/guis/extra_panels.png b/src/main/resources/assets/ae2/textures/guis/extra_panels.png deleted file mode 100644 index 5f47d4f02412df7877bb66a9f1b876f44055e6f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 877 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9GG!XV7ZFl&wk0|T?X zr;B4q#hkZy?XyK3W!gX9KeeZ!V=areE+6a8*DSs4w^~2PAFx_~?3~E5o~0a$PoC@& zQS4Znq$n13a-z7v4Do_6^_WeEWw#q!KdzhaUwPzYgMXTN`R18fX0vY=WT&Tb&Y340 zc6wjhj;)g)MJKzSJM-r9liDwh?zeYeJnmBS?)CQh7wdn$zjLq0Rz+y%v}&dk-3&(r z8!Xb(zWqHu->v!1{%Y+*ArqR7jdvH;Dt|T+n>$bT**qt0uOFf_yc;Z-1l$=OaX82@ zb|^CxvMBU{^`Z!#JCpPIncK5W1@SPDigQorIm{I*JGG*qxo-WZd+L=&98ZhW(tfi~ zzVq2ZZS8&L4SS{^ojG%EPk-@w=9w1n_uAHet9co`=XJ&A?}|q^baaS1#FU!+n^em5 z{@%OPzyDtS+snVd#Xoz-~YzP>H*UEtlimv!ajWY^XOD|bKT{@ zi__c~ea`D=y_b#aku#AL)9w0{ctNY4mnZd+9MlI_AG@zFI?pJ%kqM`NP(0c7hgr~Y zZSkL;OU$}!7#>;eXIkU@^n#Xg7(?Y62F8_sA^N_4hApuNQPUaD4Ik-hY4pxO|OJ-)t`X{OjrVt?Pvy9M%Y5^_G5a z^o|h}Ehs^Y;uH>tJBOAx#@^&x`z66WxJE`s;5E>)V>^3*u@u(O5LEW*h?M%4v%L&V zUbjyk?o&{4BoJ{x)BBs5p^6W32?U(-lPhCM4Y~!Q6gim$wij?dXymd5`Wtrwc>MT5 zgZkm>z+;`I456!<*KjOwyZbUK_NXc-z(E0U;eF`6-{J diff --git a/src/test/java/appeng/client/gui/util/RectangleMergerTest.java b/src/test/java/appeng/client/gui/util/RectangleMergerTest.java index b248f53a162..91f0270b74d 100644 --- a/src/test/java/appeng/client/gui/util/RectangleMergerTest.java +++ b/src/test/java/appeng/client/gui/util/RectangleMergerTest.java @@ -1,11 +1,12 @@ package appeng.client.gui.util; -import appeng.client.guidebook.document.LytRect; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.List; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; + +import appeng.client.guidebook.document.LytRect; public class RectangleMergerTest { @Test @@ -61,8 +62,7 @@ public void testMergeOverlapping() { .containsOnly( new LytRect(5, 10, 19, 50), new LytRect(24, 10, 1, 50), - new LytRect(25, 10, 20, 50) - ); + new LytRect(25, 10, 20, 50)); } @Test @@ -81,7 +81,6 @@ public void testCornerOverlaps() { new LytRect(-5, -10, 10, 20), new LytRect(5, -15, 5, 30), new LytRect(10, -15, 5, 10), - new LytRect(10, 5, 5, 10) - ); + new LytRect(10, 5, 5, 10)); } }