/*
* Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
* Copyright (C) 2021 cyoung06
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package kr.syeyoung.dungeonsguide.config.guiconfig;
import kr.syeyoung.dungeonsguide.config.types.GUIRectangle;
import kr.syeyoung.dungeonsguide.features.GuiFeature;
import kr.syeyoung.dungeonsguide.gui.MPanel;
import kr.syeyoung.dungeonsguide.utils.RenderUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.Tuple;
import net.minecraft.util.Vec3;
import org.lwjgl.opengl.GL11;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.stream.Stream;
public class PanelDelegate extends MPanel {
private final GuiFeature guiFeature;
private boolean draggable = false;
private GuiGuiLocationConfig guiGuiLocationConfig;
private Set markerSet = new HashSet<>();
public PanelDelegate(GuiFeature guiFeature, boolean draggable, GuiGuiLocationConfig guiGuiLocationConfig) {
this.guiFeature = guiFeature;
this.draggable = draggable;
this.guiGuiLocationConfig = guiGuiLocationConfig;
}
public void rebuildMarker() {
internallyThinking = guiFeature.getFeatureRect().getRectangleNoScale();
applyConstraint();
}
@Override
public Rectangle getBounds() {
Rectangle rectangle = guiFeature.getFeatureRect().getRectangle();
return new Rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
@Override
public void render(int absMousex, int absMousey, int relMouseX, int relMouseY, float partialTicks, Rectangle scissor) {
GlStateManager.pushMatrix();
guiFeature.drawDemo(partialTicks);
GlStateManager.popMatrix();
if (!draggable) return;
Gui.drawRect(0,0, 4, 4, 0xFFBBBBBB);
Gui.drawRect(0, getBounds().height - 4, 4, getBounds().height, 0xFFBBBBBB);
Gui.drawRect(getBounds().width - 4,0, getBounds().width, 4, 0xFFBBBBBB);
Gui.drawRect(getBounds().width - 4,getBounds().height - 4, getBounds().width, getBounds().height, 0xFFBBBBBB);
if (lastAbsClip.contains(absMousex, absMousey)) {
if (relMouseX < 4 && relMouseY < 4) {
Gui.drawRect(0,0, 4, 4, 0x55FFFFFF);
} else if (relMouseX < 4 && relMouseY > getBounds().height - 4) {
Gui.drawRect(0, getBounds().height - 4, 4, getBounds().height, 0x55FFFFFF);
} else if (relMouseX > getBounds().width - 4 && relMouseY > getBounds().height - 4) {
Gui.drawRect(getBounds().width - 4,getBounds().height - 4, getBounds().width, getBounds().height, 0x55FFFFFF);
} else if (relMouseX > getBounds().width - 4 && relMouseY < 4) {
Gui.drawRect(getBounds().width - 4,0, getBounds().width, 4, 0x55FFFFFF);
} else if (selectedPart == -2){
Gui.drawRect(0,0, getBounds().width, getBounds().height, 0x55FFFFFF);
}
}
GlStateManager.enableBlend();
}
@Override
public void render0(ScaledResolution resolution, Point parentPoint, Rectangle parentClip, int absMousex, int absMousey, int relMousex0, int relMousey0, float partialTicks) {
if (selectedPart != -2) {
Gui.drawRect(internallyThinking.x, internallyThinking.y, internallyThinking.x + internallyThinking.width, internallyThinking.y + internallyThinking.height, 0xFF000000);
FontRenderer fontRenderer = Minecraft.getMinecraft().fontRendererObj;
fontRenderer.drawString(internallyThinking.width + "x"+internallyThinking.height, internallyThinking.x, internallyThinking.y, 0xFFFFFFFF);
}
GlStateManager.pushMatrix();
super.render0(resolution, parentPoint, parentClip, absMousex, absMousey, relMousex0, relMousey0, partialTicks);
GlStateManager.popMatrix();
if (snapped != null && selectedPart != -2) {
Tessellator tessellator = Tessellator.getInstance();
GlStateManager.disableTexture2D();
WorldRenderer worldRenderer = tessellator.getWorldRenderer();
worldRenderer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);
GL11.glLineWidth(1);
for (Tuple markerAxisTuple : snapped) {
// worldRenderer.pos(markerAxisTuple.getFirst()[0].getX(), markerAxisTuple.getFirst()[0].getY(), 0).color(255,255,255,255).endVertex();
// worldRenderer.pos(markerAxisTuple.getFirst()[1].getX(), markerAxisTuple.getFirst()[1].getY(), 0).color(255,255,255,255).endVertex();
if (markerAxisTuple.getSecond() == EnumFacing.Axis.X) {
worldRenderer.pos(markerAxisTuple.getFirst()[0].getX(), 0, 0).color(0,255,0,255).endVertex();
worldRenderer.pos(markerAxisTuple.getFirst()[0].getX(), Minecraft.getMinecraft().displayHeight, 0).color(0,255,0,255).endVertex();
} else {
worldRenderer.pos(0, markerAxisTuple.getFirst()[0].getY(), 0).color(0,255,0,255).endVertex();
worldRenderer.pos(Minecraft.getMinecraft().displayWidth, markerAxisTuple.getFirst()[0].getY(), 0).color(0,255,0,255).endVertex();
}
}
tessellator.draw();
for (Marker marker : guiGuiLocationConfig.getMarkerSet()) {
Gui.drawRect(marker.getX(),marker.getY(), marker.getX()+1, marker.getY()+1, 0xFFFF0000);
}
}
}
private int selectedPart = -2;
private int lastX = 0;
private int lastY = 0;
private Rectangle internallyThinking;
private Rectangle constraintApplied;
private Set> snapped = new HashSet<>();
public void applyConstraint() {
constraintApplied = internallyThinking.getBounds();
// SNAP Moving Point.
snapped.clear();
int scailingThreshold = 5;
if (selectedPart == 0){
Point snapPt = new Point(constraintApplied.x +constraintApplied.width, constraintApplied.y + constraintApplied.height);
Optional snapX, snapY;
SortedMap> markerSortedMap = guiGuiLocationConfig.getMarkerTreeMapByX().subMap(snapPt.x-scailingThreshold, snapPt.x +scailingThreshold);
snapX = markerSortedMap.values().stream()
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(a -> a.getParent() != this)
.min(Comparator.comparingInt(a -> (int) snapPt.distanceSq(a.getX(), a.getY())));
markerSortedMap = guiGuiLocationConfig.getMarkerTreeMapByY().subMap(snapPt.y-scailingThreshold, snapPt.y +scailingThreshold);
snapY = markerSortedMap.values().stream()
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(a -> a.getParent() != this)
.min(Comparator.comparingInt(a -> (int) snapPt.distanceSq(a.getX(), a.getY())));
snapX.ifPresent(a -> {
snapPt.x = a.getX();
});
snapY.ifPresent(a -> {
snapPt.y = a.getY();
});
constraintApplied = new Rectangle(constraintApplied.x, constraintApplied.y, snapPt.x - constraintApplied.x, snapPt.y - constraintApplied.y);
int minWidth;
int minHeight;
if (guiFeature.isKeepRatio()) {
if (guiFeature.getDefaultRatio() >= 1) {
minHeight = constraintApplied.height < 0 ? -8 : 8;
minWidth = (int) (guiFeature.getDefaultRatio() * minHeight);
} else {
minWidth = constraintApplied.width < 0 ? -8 : 8;
minHeight = (int) (minWidth / guiFeature.getDefaultRatio());
}
} else {
minWidth = constraintApplied.width < 0 ? -8 : 8;
minHeight = constraintApplied.height < 0 ? -8 : 8;
}
constraintApplied.width = Math.abs(constraintApplied.width) > Math.abs(minWidth) ? constraintApplied.width :
Math.abs(internallyThinking.width) > Math.abs(minWidth) ? internallyThinking.width : minWidth;
constraintApplied.height = Math.abs(constraintApplied.height) > Math.abs(minHeight) ? constraintApplied.height :
Math.abs(internallyThinking.height) > Math.abs(minHeight) ? internallyThinking.height : minHeight;
if (guiFeature.isKeepRatio()) {
double ratio = guiFeature.getDefaultRatio();
int heightWhenWidthFix = (int) Math.abs(constraintApplied.width / ratio);
int widthWhenHeightFix = (int) Math.abs(ratio * constraintApplied.height);
if (Math.abs(heightWhenWidthFix) <= Math.abs(constraintApplied.height)) {
constraintApplied.height = constraintApplied.height < 0 ? -heightWhenWidthFix : heightWhenWidthFix;
} else if (Math.abs(widthWhenHeightFix) <= Math.abs(constraintApplied.width)) {
constraintApplied.width =constraintApplied.width < 0 ? - widthWhenHeightFix : widthWhenHeightFix;
}
}
snapX.ifPresent(a -> {
if (snapPt.x - constraintApplied.x == constraintApplied.width) {
Marker m = new Marker((int) (GuiGuiLocationConfig.facing[3].xCoord * constraintApplied.width) + constraintApplied.x, (int) (GuiGuiLocationConfig.facing[3].yCoord * constraintApplied.height) + constraintApplied.y, (int) GuiGuiLocationConfig.facing[3].zCoord, this);
snapped.add(new Tuple<>(new Marker[]{a, m}, EnumFacing.Axis.X));
}
});
snapY.ifPresent(a -> {
if (snapPt.y - constraintApplied.y == constraintApplied.height) {
Marker m = new Marker((int) (GuiGuiLocationConfig.facing[2].xCoord * constraintApplied.width) + constraintApplied.x, (int) (GuiGuiLocationConfig.facing[2].yCoord * constraintApplied.height) + constraintApplied.y, (int) GuiGuiLocationConfig.facing[2].zCoord, this);
snapped.add(new Tuple<>(new Marker[]{a, m}, EnumFacing.Axis.Y));
}
});
if (constraintApplied.height < 0) {
constraintApplied.height = -constraintApplied.height;
constraintApplied.y -= constraintApplied.height;
}
if (constraintApplied.width < 0) {
constraintApplied.width = -constraintApplied.width;
constraintApplied.x -= constraintApplied.width;
}
} else if (selectedPart == -1) {
for (int i : Arrays.asList(0,3,1,2)) {
Vec3 pt = GuiGuiLocationConfig.facing[i];
Marker m = new Marker((int) (pt.xCoord * constraintApplied.width) + constraintApplied.x, (int) (pt.yCoord * constraintApplied.height) + constraintApplied.y, (int) pt.zCoord, this);
Optional result = guiGuiLocationConfig.getMarkerTreeMapByX().subMap(m.getX()-scailingThreshold, m.getX() +scailingThreshold).values().stream()
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(a -> a.getParent() != this)
.filter(a -> Math.abs(a.getX() - m.getX()) < scailingThreshold)
.filter(a -> ((a.getX() - pt.xCoord * constraintApplied.width) >= 0
&& (a.getX() - pt.xCoord * constraintApplied.width + constraintApplied.width) <= Minecraft.getMinecraft().displayWidth))
.min(Comparator.comparingInt(a -> a.distanceSQ(m)));
if (result.isPresent()) {
int x = result.get().getX();
constraintApplied.x = (int) (x - pt.xCoord * constraintApplied.width);
snapped.add(new Tuple<>(new Marker[] {result.get(), m}, EnumFacing.Axis.X));
break;
}
}
for (int i : Arrays.asList(1,2,0,3)) {
Vec3 pt = GuiGuiLocationConfig.facing[i];
Marker m = new Marker((int) (pt.xCoord * constraintApplied.width) + constraintApplied.x, (int) (pt.yCoord * constraintApplied.height) + constraintApplied.y, (int) pt.zCoord, this);
Optional result = guiGuiLocationConfig.getMarkerTreeMapByY().subMap(m.getY()-scailingThreshold, m.getY() +scailingThreshold).values().stream()
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(a -> a.getParent() != this)
.filter(a -> Math.abs(a.getY() - m.getY()) < scailingThreshold)
.filter(a -> ((a.getY() - pt.yCoord * constraintApplied.height) >= 0
&& (a.getY() - pt.yCoord * constraintApplied.height+ constraintApplied.height) <= Minecraft.getMinecraft().displayHeight))
.min(Comparator.comparingInt(a -> a.distanceSQ(m)));
if (result.isPresent()) {
int y = result.get().getY();
constraintApplied.y = (int) (y - pt.yCoord * constraintApplied.height);
snapped.add(new Tuple<>(new Marker[] {result.get(), m}, EnumFacing.Axis.Y));
break;
}
}
}
if (constraintApplied.x < 0) constraintApplied.x = 0;
if (constraintApplied.y < 0) constraintApplied.y = 0;
if (constraintApplied.x + constraintApplied.width + 1 >=Minecraft.getMinecraft().displayWidth) constraintApplied.x = Minecraft.getMinecraft().displayWidth - constraintApplied.width - 1;
if (constraintApplied.y + constraintApplied.height + 1>= Minecraft.getMinecraft().displayHeight) constraintApplied.y = Minecraft.getMinecraft().displayHeight - constraintApplied.height - 1;
setupMarkers();
}
Marker[] markers = new Marker[4];
public void setupMarkers() {
for (int i1 = 0; i1 < markers.length; i1++) {
Marker orig = markers[i1];
Vec3 pt = GuiGuiLocationConfig.facing[i1];
markers[i1] = new Marker((int) (pt.xCoord * constraintApplied.width) + constraintApplied.x, (int) (pt.yCoord * constraintApplied.height) + constraintApplied.y, (int) pt.zCoord, this);
guiGuiLocationConfig.removeAndAddMarker(orig, markers[i1]);
}
}
@Override
public void mouseClicked(int absMouseX, int absMouseY, int relMouseX, int relMouseY, int mouseButton) {
if (!draggable) return;
if (!lastAbsClip.contains(absMouseX, absMouseY)) return;
if (mouseButton == 0) {
internallyThinking = guiFeature.getFeatureRect().getRectangleNoScale();
if (relMouseX < 4 && relMouseY < 4) { // TL
selectedPart = 0;
internallyThinking.y += internallyThinking.height;
internallyThinking.height = -internallyThinking.height;
internallyThinking.x += internallyThinking.width;
internallyThinking.width = -internallyThinking.width;
} else if (relMouseX < 4 && relMouseY > getBounds().height - 4) { // BL
selectedPart = 0;
internallyThinking.x += internallyThinking.width;
internallyThinking.width = -internallyThinking.width;
} else if (relMouseX > getBounds().width - 4 && relMouseY > getBounds().height - 4) { // BR
selectedPart = 0;
} else if (relMouseX > getBounds().width - 4 && relMouseY < 4) { // TR
selectedPart = 0;
internallyThinking.y += internallyThinking.height;
internallyThinking.height = -internallyThinking.height;
} else {
selectedPart = -1;
}
lastX = absMouseX;
lastY = absMouseY;
applyConstraint();
}
throw new IllegalArgumentException("bruh, a hack to stop event progress");
}
@Override
public void mouseReleased(int absMouseX, int absMouseY, int relMouseX, int relMouseY, int state) {
if (!draggable) return;
if (selectedPart >= -1) {
guiFeature.setFeatureRect(new GUIRectangle(constraintApplied));
}
selectedPart = -2;
}
@Override
public void mouseClickMove(int absMouseX, int absMouseY, int relMouseX, int relMouseY, int clickedMouseButton, long timeSinceLastClick) {
if (!draggable) return;
int dx = (absMouseX - lastX);
int dy = (absMouseY - lastY);
if (selectedPart >= 0) {
Rectangle rectangle = internallyThinking;
int prevWidth = rectangle.width;
int prevHeight= rectangle.height;
rectangle.width = prevWidth + dx;
rectangle.height = prevHeight + dy;
if (rectangle.height * prevHeight <= 0 && prevHeight != rectangle.height) {
rectangle.height += prevHeight < 0 ? 4 : -4;
}
if (rectangle.width * prevWidth <= 0 && prevWidth != rectangle.width) {
rectangle.width += prevWidth < 0 ? 4 : -4;
}
applyConstraint();
guiFeature.setFeatureRect(new GUIRectangle(constraintApplied));
lastX = absMouseX;
lastY = absMouseY;
throw new IllegalArgumentException("bruh, a hack to stop event progress");
} else if (selectedPart == -1){
Rectangle rectangle = internallyThinking;
rectangle.translate(dx, dy);
applyConstraint();
guiFeature.setFeatureRect(new GUIRectangle(constraintApplied));
lastX = absMouseX;
lastY = absMouseY;
}
}
}