aboutsummaryrefslogtreecommitdiff
path: root/mod/src
diff options
context:
space:
mode:
authorsyeyoung <cyoung06@naver.com>2023-01-20 01:50:26 +0900
committersyeyoung <cyoung06@naver.com>2023-01-20 01:50:26 +0900
commite1cdaf7b769abc115f2c2eb569c2bae775b2ede9 (patch)
treed1817169177940a427b5a9b477babab0dbf0832a /mod/src
parent6a45cec52f9219de8fefb012e144f445024f4850 (diff)
downloadSkyblock-Dungeons-Guide-e1cdaf7b769abc115f2c2eb569c2bae775b2ede9.tar.gz
Skyblock-Dungeons-Guide-e1cdaf7b769abc115f2c2eb569c2bae775b2ede9.tar.bz2
Skyblock-Dungeons-Guide-e1cdaf7b769abc115f2c2eb569c2bae775b2ede9.zip
- RichText
mojang pls don't sue me Signed-off-by: syeyoung <cyoung06@naver.com>
Diffstat (limited to 'mod/src')
-rwxr-xr-xmod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java2
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BreakWord.java23
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BrokenWordData.java31
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/FlatTextSpan.java180
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichLine.java33
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichText.java206
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextSpan.java38
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextStyle.java48
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/DefaultFontRenderer.java300
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/FontRenderer.java30
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/Shader.java24
-rw-r--r--mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/SingleColorShader.java40
12 files changed, 955 insertions, 0 deletions
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java
index ad27c7c2..f09ba949 100755
--- a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/DungeonsGuide.java
@@ -35,6 +35,7 @@ import kr.syeyoung.dungeonsguide.mod.events.annotations.EventHandlerRegistry;
import kr.syeyoung.dungeonsguide.mod.events.listener.DungeonListener;
import kr.syeyoung.dungeonsguide.mod.events.listener.PacketListener;
import kr.syeyoung.dungeonsguide.mod.features.FeatureRegistry;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.fonts.DefaultFontRenderer;
import kr.syeyoung.dungeonsguide.mod.overlay.OverlayManager;
import kr.syeyoung.dungeonsguide.mod.party.PartyManager;
import kr.syeyoung.dungeonsguide.mod.resources.DGTexturePack;
@@ -408,6 +409,7 @@ public class DungeonsGuide implements DGInterface {
@Override
public void onResourceReload(IResourceManager a) {
GLCursors.setupCursors();
+ DefaultFontRenderer.DEFAULT_RENDERER.onResourceManagerReload();
}
private boolean showedStartUpGuide;
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BreakWord.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BreakWord.java
new file mode 100644
index 00000000..54f058a6
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BreakWord.java
@@ -0,0 +1,23 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+public enum BreakWord {
+ ALL, WORD
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BrokenWordData.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BrokenWordData.java
new file mode 100644
index 00000000..0f9a7e32
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/BrokenWordData.java
@@ -0,0 +1,31 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data @AllArgsConstructor
+public class BrokenWordData {
+ private FlatTextSpan first;
+ private FlatTextSpan second;
+ private boolean broken;
+
+ private double firstWidth;
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/FlatTextSpan.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/FlatTextSpan.java
new file mode 100644
index 00000000..f4ba08c8
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/FlatTextSpan.java
@@ -0,0 +1,180 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+public class FlatTextSpan {
+ public final TextStyle textStyle;
+
+ public final char[] value;
+
+ public FlatTextSpan(TextStyle textStyle, char[] value) {
+ this.textStyle = textStyle;
+ this.value = value;
+ }
+
+ public double getWidth() {
+ double sum =0;
+ for (char c : value) {
+ sum += textStyle.getFontRenderer().getWidth(c, textStyle);
+ }
+ return sum;
+ }
+ public double getHeight() {
+ return textStyle.getSize() * textStyle.getLineHeight();
+ }
+ public double getBaseline() {
+ return textStyle.getSize() * (textStyle.getLineHeight()-1) + textStyle.getFontRenderer().getBaselineHeight(textStyle);
+ }
+
+ public BrokenWordData breakWord(double remainingWidth, double nextLineWidth, BreakWord breakWord) {
+ if (breakWord == BreakWord.WORD) {
+ // prefer to break on words
+ double scaledRemainingWidth = remainingWidth;
+ double scaledNextLineWidth = nextLineWidth;
+
+ double totalWidth = 0;
+ double wordWidth = 0;
+ double charWidth = 0;
+ int wordStart = 0;
+ int potentialBreak = -1;
+
+ int endIdx = value.length;
+ for (int i = 0; i < value.length; i++) {
+ char character = value[i];
+ charWidth = textStyle.getFontRenderer().getWidth(character, textStyle);
+
+ totalWidth += charWidth;
+ wordWidth += charWidth;
+
+ if (character == ' ' || character == '\n') {
+ if (potentialBreak == -1) {
+ } else if (scaledNextLineWidth < wordWidth) {
+ // Break at potential, word is greater than next line
+ endIdx = potentialBreak;
+ break;
+ } else {
+ // Delegate to next line.
+ endIdx = wordStart;
+ break;
+ }
+
+ // Force break.
+ if (character == '\n') {
+ endIdx = i;
+ break;
+ }
+
+ // Since adding space exceeded remaining width, break without adding space.
+ if (totalWidth > scaledRemainingWidth) {
+ endIdx = i;
+ break;
+ }
+
+ // reset states
+ wordStart = i + 1;
+ potentialBreak = -1;
+ wordWidth = 0;
+ }
+
+ if (totalWidth > scaledRemainingWidth && potentialBreak == -1) {
+ potentialBreak = i;
+ }
+ }
+
+ char[] first = new char[endIdx];
+ System.arraycopy(value, 0, first, 0, endIdx);
+
+ char[] second = null;
+ if (endIdx != value.length) {
+ int startRealWord = -1;
+ for (int i = endIdx; i< value.length; i++) {
+ if (value[i] == ' ' || value[i] == '\n') continue;
+ startRealWord = i;
+ }
+ if (startRealWord != -1) {
+ second = new char[value.length - startRealWord];
+ System.arraycopy(value, startRealWord, second, 0, second.length);
+ }
+ }
+
+ FlatTextSpan flatTextSpan = new FlatTextSpan(textStyle, first);
+ FlatTextSpan secondSpan = null;
+ if (second != null)
+ secondSpan = new FlatTextSpan(textStyle, second);
+
+
+ double resultingWidth = 0;
+ for (char c : first) {
+ resultingWidth += textStyle.getFontRenderer().getWidth(c, textStyle);
+ }
+
+ return new BrokenWordData(flatTextSpan, secondSpan, endIdx != value.length, resultingWidth);
+ } else {
+ // break anywhere
+ double scaledRemainingWidth = remainingWidth;
+
+ double totalWidth = 0;
+ double effectiveWidth = 0;
+
+ int endIdx = value.length;
+ for (int i = 0; i < value.length; i++) {
+ char character = value[i];
+ double charWidth = textStyle.getFontRenderer().getWidth(character, textStyle);
+
+ totalWidth += charWidth;
+
+ if (character == '\n') {
+ // Force break.
+ endIdx = i;
+ break;
+ }
+
+ if (totalWidth > scaledRemainingWidth) {
+ // break here.
+ endIdx = i;
+ break;
+ }
+ effectiveWidth += charWidth;
+ }
+
+ char[] first = new char[endIdx];
+ System.arraycopy(value, 0, first, 0, endIdx);
+
+ char[] second = null;
+ if (endIdx != value.length) {
+ int startRealWord = -1;
+ for (int i = endIdx; i< value.length; i++) {
+ if (value[i] == ' ' || value[i] == '\n') continue;
+ startRealWord = i;
+ }
+ if (startRealWord != -1) {
+ second = new char[value.length - startRealWord];
+ System.arraycopy(value, startRealWord, second, 0, second.length);
+ }
+ }
+
+ FlatTextSpan flatTextSpan = new FlatTextSpan(textStyle, first);
+ FlatTextSpan secondSpan = null;
+ if (second != null)
+ secondSpan = new FlatTextSpan(textStyle, second);
+
+ return new BrokenWordData(flatTextSpan, secondSpan, endIdx != value.length, effectiveWidth);
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichLine.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichLine.java
new file mode 100644
index 00000000..334bc749
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichLine.java
@@ -0,0 +1,33 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.List;
+
+@Data @AllArgsConstructor
+public class RichLine {
+ private List<FlatTextSpan> lineElements;
+
+ private double width;
+ private double height;
+ private double baseline;
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichText.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichText.java
new file mode 100644
index 00000000..6822a27d
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/RichText.java
@@ -0,0 +1,206 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.BindableAttribute;
+import kr.syeyoung.dungeonsguide.mod.guiv2.DomElement;
+import kr.syeyoung.dungeonsguide.mod.guiv2.Widget;
+import kr.syeyoung.dungeonsguide.mod.guiv2.layouter.Layouter;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.ConstraintBox;
+import kr.syeyoung.dungeonsguide.mod.guiv2.primitive.Size;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.Renderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.renderer.RenderingContext;
+import lombok.Setter;
+
+import java.util.*;
+
+public class RichText extends Widget implements Layouter, Renderer {
+ private TextSpan rootSpan;
+ private BreakWord breakWord;
+ private boolean takeAllSpace = false;
+ @Setter
+ private TextAlign align;
+
+
+ private List<RichLine> richLines = new LinkedList<>();
+ public static enum TextAlign {
+ LEFT, CENTER, RIGHT
+ }
+
+ public RichText(TextSpan textSpan, BreakWord breakWord, boolean takeAllSpace, TextAlign align) {
+ this.rootSpan = textSpan;
+ this.breakWord = breakWord;
+ this.takeAllSpace = takeAllSpace;
+ this.align = align;
+ }
+
+ public void setRootSpan(TextSpan rootSpan) {
+ this.rootSpan = rootSpan;
+ this.getDomElement().requestRelayout();
+ }
+
+ public void setBreakWord(BreakWord breakWord) {
+ this.breakWord = breakWord;
+ this.getDomElement().requestRelayout();
+ }
+
+ public void setTakeAllSpace(boolean takeAllSpace) {
+ this.takeAllSpace = takeAllSpace;
+ this.getDomElement().requestRelayout();
+ }
+
+ @Override
+ public Size layout(DomElement buildContext, ConstraintBox constraintBox) {
+ LinkedList<FlatTextSpan> flatTextSpans = new LinkedList<>();
+ rootSpan.flattenTextSpan(flatTextSpans::add);
+
+ LinkedList<RichLine> lines = new LinkedList<>();
+ LinkedList<FlatTextSpan> line = new LinkedList<>();
+ double remaining = constraintBox.getMaxWidth();
+ double usedUp = 0;
+ double maxHeight = 0;
+ double maxBaseline = 0;
+
+ double sumHeight = 0;
+ double maxWidth = 0;
+
+ while (!flatTextSpans.isEmpty()) {
+ FlatTextSpan first = flatTextSpans.pollFirst();
+
+ BrokenWordData brokenWordData = first.breakWord(remaining, constraintBox.getMaxWidth(), breakWord);
+ remaining -= brokenWordData.getFirstWidth();
+ usedUp += brokenWordData.getFirstWidth();
+ line.add(brokenWordData.getFirst());
+
+ maxHeight = Math.max(maxHeight, first.getHeight());
+ maxBaseline = Math.max(maxBaseline, first.getBaseline());
+
+ if (brokenWordData.getSecond() != null) {
+ flatTextSpans.addFirst(brokenWordData.getSecond());
+ }
+
+ if (brokenWordData.isBroken()) {
+ lines.add(new RichLine(line, usedUp, maxHeight, maxBaseline));
+ line.clear();
+ maxWidth = Math.max(maxWidth, usedUp);
+ sumHeight += maxHeight;
+
+ remaining = constraintBox.getMaxWidth();
+ usedUp = 0;
+ maxHeight = 0;
+ maxBaseline = 0;
+ }
+ }
+ if (!line.isEmpty()) {
+ lines.add(new RichLine(line, usedUp, maxHeight, maxBaseline));
+ maxWidth = Math.max(maxWidth, usedUp);
+ sumHeight += maxHeight;
+ }
+
+ richLines = lines;
+
+
+ return new Size(takeAllSpace ? constraintBox.getMaxWidth() : maxWidth, sumHeight);
+ }
+
+ @Override
+ public double getMaxIntrinsicWidth(DomElement buildContext, double height) {
+ LinkedList<FlatTextSpan> flatTextSpans = new LinkedList<>();
+ rootSpan.flattenTextSpan(flatTextSpans::add);
+
+ LinkedList<FlatTextSpan> line = new LinkedList<>();
+ double usedUp = 0;
+
+ double maxWidth = 0;
+ while (!flatTextSpans.isEmpty()) {
+ FlatTextSpan first = flatTextSpans.pollFirst();
+ BrokenWordData brokenWordData = first.breakWord(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, breakWord);
+ usedUp += brokenWordData.getFirstWidth();
+ line.add(brokenWordData.getFirst());
+
+ if (brokenWordData.getSecond() != null) {
+ flatTextSpans.addFirst(brokenWordData.getSecond());
+ }
+
+ if (brokenWordData.isBroken()) {
+ maxWidth = Math.max(maxWidth, usedUp);
+ usedUp = 0;
+ }
+ }
+ if (!line.isEmpty()) {
+ maxWidth = Math.max(maxWidth, usedUp);
+ }
+
+ return maxWidth;
+ }
+
+ @Override
+ public double getMaxIntrinsicHeight(DomElement buildContext, double width) {
+ LinkedList<FlatTextSpan> flatTextSpans = new LinkedList<>();
+ rootSpan.flattenTextSpan(flatTextSpans::add);
+ double remaining = width;
+ double maxHeight = 0;
+ double sumHeight = 0;
+ while (!flatTextSpans.isEmpty()) {
+ FlatTextSpan first = flatTextSpans.pollFirst();
+
+ BrokenWordData brokenWordData = first.breakWord(remaining, width, breakWord);
+ remaining -= brokenWordData.getFirstWidth();
+
+ maxHeight = Math.max(maxHeight, first.getHeight());
+
+ if (brokenWordData.getSecond() != null) {
+ flatTextSpans.addFirst(brokenWordData.getSecond());
+ }
+
+ if (brokenWordData.isBroken()) {
+ remaining = width;
+ sumHeight += maxHeight;
+ maxHeight = 0;
+ }
+ }
+ sumHeight += maxHeight;
+ return sumHeight;
+ }
+
+ @Override
+ public List<Widget> build(DomElement buildContext) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) {
+ double x = 0;
+ double y = 0;
+ double width = buildContext.getSize().getWidth();
+ double currentScale = buildContext.getAbsBounds().getWidth() / width;
+ for (RichLine richLine : richLines) {
+ if (align == TextAlign.RIGHT)
+ x = width - richLine.getWidth();
+ else if (align == TextAlign.CENTER)
+ x = (width - richLine.getWidth()) / 2;
+ for (FlatTextSpan lineElement : richLine.getLineElements()) {
+ lineElement.textStyle.getFontRenderer()
+ .render(lineElement, x, y + richLine.getBaseline() - lineElement.getBaseline(), currentScale);
+ x += lineElement.getWidth();
+ }
+ y += richLine.getHeight();
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextSpan.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextSpan.java
new file mode 100644
index 00000000..fde3e4d0
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextSpan.java
@@ -0,0 +1,38 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class TextSpan {
+ private TextStyle textStyle;
+ private String text;
+ private List<TextSpan> children = new ArrayList<>();
+ public TextSpan(TextStyle textStyle, String text) {
+ this.textStyle = textStyle;
+ this.text = text;
+ }
+
+ public void flattenTextSpan(Consumer<FlatTextSpan> appender) {
+ appender.accept(new FlatTextSpan(textStyle, text.toCharArray()));
+ children.forEach(a -> a.flattenTextSpan(appender));
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextStyle.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextStyle.java
new file mode 100644
index 00000000..eb49baa2
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/TextStyle.java
@@ -0,0 +1,48 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.fonts.FontRenderer;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader;
+import lombok.Data;
+
+@Data
+public class TextStyle {
+ public Double size;
+ public Double lineHeight;
+
+ public boolean bold;
+ public boolean italics;
+ public boolean strikeThrough;
+ public boolean underline;
+ public boolean outline;
+ public boolean shadow;
+
+ public Shader backgroundShader;
+ public Shader textShader;
+ public Shader strikeThroughShader;
+ public Shader underlineShader;
+ public Shader outlineShader;
+ public Shader shadowShader;
+
+
+
+ public TextStyle parent;
+ public FontRenderer fontRenderer;
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/DefaultFontRenderer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/DefaultFontRenderer.java
new file mode 100644
index 00000000..245fc6d2
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/DefaultFontRenderer.java
@@ -0,0 +1,300 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.fonts;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.FlatTextSpan;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.TextStyle;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.WorldRenderer;
+import net.minecraft.client.renderer.texture.TextureUtil;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+import net.minecraft.client.renderer.vertex.VertexFormat;
+import net.minecraft.client.renderer.vertex.VertexFormatElement;
+import net.minecraft.util.ResourceLocation;
+import org.apache.commons.io.IOUtils;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class DefaultFontRenderer implements FontRenderer {
+ public static DefaultFontRenderer DEFAULT_RENDERER = new DefaultFontRenderer();
+
+ private static final ResourceLocation[] unicodePageLocations = new ResourceLocation[256];
+ protected int[] charWidth = new int[256];
+ public int FONT_HEIGHT = 9;
+ protected byte[] glyphData = new byte[65536];
+ protected final ResourceLocation locationFontTexture = new ResourceLocation("textures/font/ascii.png");
+
+ public DefaultFontRenderer() {
+ readGlyphSizes();
+ }
+
+ public void onResourceManagerReload() {
+ this.readFontTexture();
+ this.readGlyphSizes();
+ }
+
+ private void readFontTexture() {
+ BufferedImage bufferedimage;
+ try {
+ bufferedimage = TextureUtil.readBufferedImage(
+ Minecraft.getMinecraft().getResourceManager().getResource(this.locationFontTexture).getInputStream());
+ } catch (IOException var17) {
+ throw new RuntimeException(var17);
+ }
+
+ int i = bufferedimage.getWidth();
+ int j = bufferedimage.getHeight();
+ int[] aint = new int[i * j];
+ bufferedimage.getRGB(0, 0, i, j, aint, 0, i);
+ int k = j / 16;
+ int l = i / 16;
+ int i1 = 1;
+ float f = 8.0F / (float)l;
+
+ for(int j1 = 0; j1 < 256; ++j1) {
+ int k1 = j1 % 16;
+ int l1 = j1 / 16;
+ if (j1 == 32) {
+ this.charWidth[j1] = 3 + i1;
+ }
+
+ int i2;
+ for(i2 = l - 1; i2 >= 0; --i2) {
+ int j2 = k1 * l + i2;
+ boolean flag = true;
+
+ for(int k2 = 0; k2 < k && flag; ++k2) {
+ int l2 = (l1 * l + k2) * i;
+ if ((aint[j2 + l2] >> 24 & 255) != 0) {
+ flag = false;
+ }
+ }
+
+ if (!flag) {
+ break;
+ }
+ }
+
+ ++i2;
+ this.charWidth[j1] = (int)(0.5 + (double)((float)i2 * f)) + i1;
+ }
+
+ }
+
+ private void readGlyphSizes() {
+ InputStream inputstream = null;
+
+ try {
+ inputstream = Minecraft.getMinecraft().getResourceManager().getResource(new ResourceLocation("font/glyph_sizes.bin")).getInputStream();
+ inputstream.read(this.glyphData);
+ } catch (IOException var6) {
+ throw new RuntimeException(var6);
+ } finally {
+ IOUtils.closeQuietly(inputstream);
+ }
+
+ }
+
+ private ResourceLocation getUnicodePageLocation(int page) {
+ if (unicodePageLocations[page] == null) {
+ unicodePageLocations[page] = new ResourceLocation(String.format("textures/font/unicode_page_%02x.png", page));
+ }
+
+ return unicodePageLocations[page];
+ }
+
+ @Override
+ public double getWidth(char text, TextStyle textStyle) {
+ double val;
+ if (text == ' ') {
+ val = 4;
+ } else {
+ int i = "ÀÁÂÈÊËÍÓÔÕÚßãõğİıŒœŞşŴŵžȇ\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αβΓπΣσμτΦΘΩδ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u0000".indexOf(text);
+ if (text > 0 && i != -1) {
+ val = this.charWidth[i];
+ } else if (this.glyphData[text] != 0) {
+ int texStart = this.glyphData[text] >>> 4;
+ int texEnd = this.glyphData[text] & 15 + 1;
+ val = (texEnd - texStart) / 2.0 + 1;
+ } else {
+ val = 0;
+ }
+ }
+ return (val + (textStyle.isBold() ? 1 : 0)) * textStyle.getSize() / 8;
+ }
+
+ @Override
+ public double getBaselineHeight(TextStyle textStyle) {
+ return 7 * textStyle.getSize() / 8.0;
+ }
+
+ public void render(WorldRenderer worldRenderer, FlatTextSpan lineElement, double x, double y, boolean isShadow) {
+ double startX = x;
+ if (isShadow) lineElement.textStyle.getShadowShader().useShader();
+
+ if (!isShadow) lineElement.textStyle.getTextShader().useShader();
+ GlStateManager.enableTexture2D();
+ worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
+ for (char c : lineElement.value) {
+ x += renderChar(worldRenderer, x, y+1, c, lineElement.textStyle);
+ }
+ if (!isShadow) lineElement.textStyle.getTextShader().freeShader();
+ draw(worldRenderer);
+
+ GlStateManager.disableTexture2D();
+ if (lineElement.textStyle.isStrikeThrough()) {
+ worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION);
+ if (!isShadow) lineElement.textStyle.getStrikeThroughShader().useShader();
+ worldRenderer.pos(startX, y + lineElement.textStyle.getSize()/2, 0);
+ worldRenderer.pos(x, y + lineElement.textStyle.getSize()/2, 0);
+ worldRenderer.pos(x, y + lineElement.textStyle.getSize()/2+1, 0);
+ worldRenderer.pos(startX, y + lineElement.textStyle.getSize()/2+1, 0);
+ if (!isShadow) lineElement.textStyle.getStrikeThroughShader().freeShader();
+ draw(worldRenderer);
+ }
+ if (lineElement.textStyle.isUnderline()) {
+ worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION);
+ if (!isShadow) lineElement.textStyle.getUnderlineShader().useShader();
+ worldRenderer.pos(startX, y + lineElement.textStyle.getSize(), 0);
+ worldRenderer.pos(x, y + lineElement.textStyle.getSize(), 0);
+ worldRenderer.pos(x, y + lineElement.textStyle.getSize()+1, 0);
+ worldRenderer.pos(startX, y + lineElement.textStyle.getSize()+1, 0);
+ if (!isShadow) lineElement.textStyle.getUnderlineShader().freeShader();
+ draw(worldRenderer);
+ }
+
+ if (isShadow) lineElement.textStyle.getShadowShader().freeShader();
+
+ }
+
+ @Override
+ public void render(FlatTextSpan lineElement, double x, double y, double currentScale) {
+ WorldRenderer worldRenderer = Tessellator.getInstance().getWorldRenderer();
+ GlStateManager.disableTexture2D();
+ if (lineElement.textStyle.getBackgroundShader() != null) {
+ lineElement.textStyle.getBackgroundShader().useShader();
+ worldRenderer.pos(x, y, 0);
+ worldRenderer.pos(x + lineElement.getWidth(), y, 0);
+ worldRenderer.pos(x + lineElement.getWidth(), y + lineElement.textStyle.getSize()+1, 0);
+ worldRenderer.pos(x, y + lineElement.textStyle.getSize()+1, 0);
+ lineElement.textStyle.getBackgroundShader().freeShader();
+ draw(worldRenderer);
+ }
+
+ if (lineElement.textStyle.isShadow())
+ render(worldRenderer, lineElement, x+1,y+1, true);
+ render(worldRenderer, lineElement, x,y, false);
+ }
+
+
+
+ private double renderChar(WorldRenderer worldRenderer, double x, double y, char ch, TextStyle textStyle) {
+ if (ch == ' ') {
+ return 4.0F;
+ } else {
+ int i = "ÀÁÂÈÊËÍÓÔÕÚßãõğİıŒœŞşŴŵžȇ\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αβΓπΣσμτΦΘΩδ∞∅∈∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■\u0000".indexOf(ch);
+ return i != -1 ? this.renderDefaultChar(worldRenderer, x, y, i, textStyle) : this.renderUnicodeChar(worldRenderer, x, y, ch, textStyle);
+ }
+ }
+
+
+ private void draw(WorldRenderer renderer) {
+ renderer.finishDrawing();
+ if (renderer.getVertexCount() > 0) {
+ VertexFormat vertexformat = renderer.getVertexFormat();
+ int i = vertexformat.getNextOffset();
+ ByteBuffer bytebuffer = renderer.getByteBuffer();
+ List<VertexFormatElement> list = vertexformat.getElements();
+
+ int i1;
+ for(i1 = 0; i1 < list.size(); ++i1) {
+ VertexFormatElement vertexformatelement = (VertexFormatElement)list.get(i1);
+ vertexformatelement.getUsage().preDraw(vertexformat, i1, i, bytebuffer);
+ }
+
+ GL11.glDrawArrays(renderer.getDrawMode(), 0, renderer.getVertexCount());
+ i1 = 0;
+
+ for(int j1 = list.size(); i1 < j1; ++i1) {
+ VertexFormatElement vertexformatelement1 = (VertexFormatElement)list.get(i1);
+ vertexformatelement1.getUsage().postDraw(vertexformat, i1, i, bytebuffer);
+ }
+ }
+
+ renderer.reset();
+ }
+
+ public void bindTexture(WorldRenderer worldRenderer, ResourceLocation resourceLocation) {
+ draw(worldRenderer);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(resourceLocation);
+ }
+
+ private double renderDefaultChar(WorldRenderer worldRenderer, double posX, double posY, int ch, TextStyle textStyle) {
+ int texX = ch % 16 * 8;
+ int texY = ch / 16 * 8;
+ int italicsAddition = textStyle.italics ? 1 : 0;
+ bindTexture(worldRenderer, this.locationFontTexture);
+ double charWidth = (this.charWidth[ch] - 1.0F) * textStyle.getSize() / 8.0;
+ double charHeight = textStyle.getSize();
+ // char width contains the gap between next char
+
+
+ worldRenderer.pos(posX + (float)italicsAddition, posY, 0.0F).tex((float)texX / 128.0F, (float)texY / 128.0F);
+ worldRenderer.pos(posX - (float)italicsAddition, posY + charHeight - 0.01F, 0.0F).tex((float)texX / 128.0F, ((float)texY + 7.99F) / 128.0F);
+ worldRenderer.pos(posX + charWidth+ (float)italicsAddition - 0.01F , posY, 0.0F).tex(((float)texX + charWidth - 1.01F) / 128.0F, (float)texY / 128.0F);
+ worldRenderer.pos(posX + charWidth - (float)italicsAddition - 0.01F, posY + charHeight - 0.01F, 0.0F).tex(((float)texX + charWidth - 1.01F) / 128.0F, ((float)texY + 7.99F) / 128.0F);
+
+ return charWidth + textStyle.getSize() / 8.0;
+ }
+ private double renderUnicodeChar(WorldRenderer worldRenderer, double posX, double posY, char ch, TextStyle textStyle) {
+ if (this.glyphData[ch] == 0) {
+ return 0.0F;
+ } else {
+ int i = ch / 256;
+ bindTexture(worldRenderer, this.getUnicodePageLocation(i));
+ float xStart = (float)(this.glyphData[ch] >>> 4);
+ float xEnd = (float)(this.glyphData[ch] & 15 + 1);
+
+ float texX = (float)(ch % 16 * 16) + xStart;
+ float texY = (float)((ch & 255) / 16 * 16);
+ float texWidth = xEnd - xStart - 0.02F;
+ float italicSlope = textStyle.italics ? 1.0F : 0.0F;
+
+ double charWidth = texWidth * textStyle.getSize() / 16.0;
+ double charHeight = textStyle.getSize();
+
+ worldRenderer.pos(posX + italicSlope, posY, 0.0F)
+ .tex(texX / 256.0F, texY / 256.0F);
+ worldRenderer.pos(posX - italicSlope, posY + charHeight - 0.01F, 0.0F)
+ .tex(texX / 256.0F, (texY + 15.98F) / 256.0F);
+ worldRenderer.pos(posX + charWidth + italicSlope, posY, 0.0F)
+ .tex((texX + texWidth) / 256.0F, texY / 256.0F);
+ worldRenderer.pos(posX + charWidth - italicSlope, posY + charHeight - 0.01F, 0.0F)
+ .tex((texX + texWidth) / 256.0F, (texY + 15.98F) / 256.0F);
+ return charWidth + textStyle.getSize() / 8.0;
+ }
+ }
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/FontRenderer.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/FontRenderer.java
new file mode 100644
index 00000000..b9f52c06
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/fonts/FontRenderer.java
@@ -0,0 +1,30 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.fonts;
+
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.FlatTextSpan;
+import kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.TextStyle;
+
+public interface FontRenderer {
+ double getWidth(char text, TextStyle textStyle);
+ // from y 0 going downward
+ double getBaselineHeight(TextStyle textStyle);
+
+ void render(FlatTextSpan lineElement, double x, double v, double currentScale);
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/Shader.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/Shader.java
new file mode 100644
index 00000000..4f14562d
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/Shader.java
@@ -0,0 +1,24 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders;
+
+public interface Shader {
+ public void useShader();
+ public void freeShader();
+}
diff --git a/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/SingleColorShader.java b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/SingleColorShader.java
new file mode 100644
index 00000000..bd686968
--- /dev/null
+++ b/mod/src/main/java/kr/syeyoung/dungeonsguide/mod/guiv2/elements/richtext/shaders/SingleColorShader.java
@@ -0,0 +1,40 @@
+/*
+ * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod
+ * Copyright (C) 2023 cyoung06 (syeyoung)
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders;
+
+import net.minecraft.client.renderer.GlStateManager;
+
+public class SingleColorShader implements Shader{
+ private float r, g, b, a;
+
+ // argb
+ public SingleColorShader(int color) {
+ r = ((color >> 16) & 0xFF) / 255.0f;
+ g = ((color >> 8) & 0xFF) / 255.0f;
+ b = ((color) & 0xFF) / 255.0f;
+ r = ((color >> 24) & 0xFF) / 255.0f;
+ }
+ @Override
+ public void useShader() {
+ GlStateManager.color(r,g,b,a);
+ }
+
+ @Override
+ public void freeShader() {}
+}