diff options
author | Yasin <a.piri@hotmail.de> | 2023-10-09 12:58:02 +0200 |
---|---|---|
committer | Yasin <a.piri@hotmail.de> | 2023-10-09 12:58:02 +0200 |
commit | bd3f0329d0e391bd84b5f9e3ff207d9dd9815853 (patch) | |
tree | 2fd1d1ef625f57acc2e4916c967d8d2393844798 /src/main/java/de/hysky/skyblocker/skyblock/tabhud | |
parent | 2315b90da8117f28f66348927afdb621ee4fc815 (diff) | |
download | Skyblocker-bd3f0329d0e391bd84b5f9e3ff207d9dd9815853.tar.gz Skyblocker-bd3f0329d0e391bd84b5f9e3ff207d9dd9815853.tar.bz2 Skyblocker-bd3f0329d0e391bd84b5f9e3ff207d9dd9815853.zip |
new pr because fixing merge conflict would take too long
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock/tabhud')
67 files changed, 4197 insertions, 0 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java new file mode 100644 index 00000000..f226f371 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.tabhud; + +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; + +public class TabHud { + + public static KeyBinding toggleB; + public static KeyBinding toggleA; + // public static KeyBinding mapTgl; + public static KeyBinding defaultTgl; + + public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Tab HUD"); + + public static void init() { + + toggleB = KeyBindingHelper.registerKeyBinding( + new KeyBinding("key.skyblocker.toggleB", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_B, + "key.categories.skyblocker")); + toggleA = KeyBindingHelper.registerKeyBinding( + new KeyBinding("key.skyblocker.toggleA", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_N, + "key.categories.skyblocker")); + defaultTgl = KeyBindingHelper.registerKeyBinding( + new KeyBinding("key.skyblocker.defaultTgl", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_M, + "key.categories.skyblocker")); + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java new file mode 100644 index 00000000..ceeaa365 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java @@ -0,0 +1,179 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder; + +import java.io.BufferedReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.NoSuchElementException; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.AlignStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.CollideStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PipelineStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PlaceStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.StackStage; +import de.hysky.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget; +import de.hysky.skyblocker.skyblock.tabhud.widget.ErrorWidget; +import de.hysky.skyblocker.skyblock.tabhud.widget.EventWidget; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.util.Identifier; + +public class ScreenBuilder { + + // layout pipeline + private final ArrayList<PipelineStage> layoutPipeline = new ArrayList<>(); + + // all widget instances this builder knows + private final ArrayList<Widget> instances = new ArrayList<>(); + // maps alias -> widget instance + private final HashMap<String, Widget> objectMap = new HashMap<>(); + + private final String builderName; + + /** + * Create a ScreenBuilder from a json. + */ + public ScreenBuilder(Identifier ident) { + + try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager().openAsReader(ident)) { + this.builderName = ident.getPath(); + + JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); + + JsonArray widgets = json.getAsJsonArray("widgets"); + JsonArray layout = json.getAsJsonArray("layout"); + + for (JsonElement w : widgets) { + JsonObject widget = w.getAsJsonObject(); + String name = widget.get("name").getAsString(); + String alias = widget.get("alias").getAsString(); + + Widget wid = instanceFrom(name, widget); + objectMap.put(alias, wid); + instances.add(wid); + } + + for (JsonElement l : layout) { + PipelineStage ps = createStage(l.getAsJsonObject()); + layoutPipeline.add(ps); + } + } catch (Exception ex) { + // rethrow as unchecked exception so that I don't have to catch anything in the ScreenMaster + throw new IllegalStateException("Failed to load file " + ident + ". Reason: " + ex.getMessage()); + } + } + + /** + * Try to find a class in the widget package that has the supplied name and + * call it's constructor. Manual work is required if the class has arguments. + */ + public Widget instanceFrom(String name, JsonObject widget) { + + // do widgets that require args the normal way + JsonElement arg; + switch (name) { + case "EventWidget" -> { + return new EventWidget(widget.get("inGarden").getAsBoolean()); + } + case "DungeonPlayerWidget" -> { + return new DungeonPlayerWidget(widget.get("player").getAsInt()); + } + case "ErrorWidget" -> { + arg = widget.get("text"); + if (arg == null) { + return new ErrorWidget(); + } else { + return new ErrorWidget(arg.getAsString()); + } + } + case "Widget" -> + // clown case sanity check. don't instantiate the superclass >:| + throw new NoSuchElementException(builderName + "[ERROR]: No such Widget type \"Widget\"!"); + } + + // reflect something together for the "normal" ones. + + // list all packages that might contain widget classes + // using Package isn't reliable, as some classes might not be loaded yet, + // causing the packages not to show. + String packbase = "de.hysky.skyblocker.skyblock.tabhud.widget"; + String[] packnames = { + packbase, + packbase + ".rift" + }; + + // construct the full class name and try to load. + Class<?> clazz = null; + for (String pn : packnames) { + try { + clazz = Class.forName(pn + "." + name); + } catch (LinkageError | ClassNotFoundException ex) { + continue; + } + } + + // load failed. + if (clazz == null) { + throw new NoSuchElementException(builderName + "/[ERROR]: No such Widget type \"" + name + "\"!"); + } + + // return instance of that class. + try { + Constructor<?> ctor = clazz.getConstructor(); + return (Widget) ctor.newInstance(); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException | SecurityException ex) { + throw new IllegalStateException(builderName + "/" + name + ": Internal error..."); + } + } + + /** + * Create a PipelineStage from a json object. + */ + public PipelineStage createStage(JsonObject descr) throws NoSuchElementException { + + String op = descr.get("op").getAsString(); + + return switch (op) { + case "place" -> new PlaceStage(this, descr); + case "stack" -> new StackStage(this, descr); + case "align" -> new AlignStage(this, descr); + case "collideAgainst" -> new CollideStage(this, descr); + default -> throw new NoSuchElementException("No such op " + op + " as requested by " + this.builderName); + }; + } + + /** + * Lookup Widget instance from alias name + */ + public Widget getInstance(String name) { + if (!this.objectMap.containsKey(name)) { + throw new NoSuchElementException("No widget with alias " + name + " in screen " + builderName); + } + return this.objectMap.get(name); + } + + /** + * Run the pipeline to build a Screen + */ + public void run(DrawContext context, int screenW, int screenH) { + + for (Widget w : instances) { + w.update(); + } + for (PipelineStage ps : layoutPipeline) { + ps.run(screenW, screenH); + } + for (Widget w : instances) { + w.render(context); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java new file mode 100644 index 00000000..210d8001 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java @@ -0,0 +1,144 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder; + +import java.io.BufferedReader; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import de.hysky.skyblocker.skyblock.tabhud.TabHud; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerLocator; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.fabric.api.resource.ResourcePackActivationType; +import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourceType; +import net.minecraft.util.Identifier; + +public class ScreenMaster { + + private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker"); + + private static final int VERSION = 1; + + private static final HashMap<String, ScreenBuilder> standardMap = new HashMap<>(); + private static final HashMap<String, ScreenBuilder> screenAMap = new HashMap<>(); + private static final HashMap<String, ScreenBuilder> screenBMap = new HashMap<>(); + + /** + * Load a screen mapping from an identifier + */ + public static void load(Identifier ident) { + + String path = ident.getPath(); + String[] parts = path.split("/"); + String screenType = parts[parts.length - 2]; + String location = parts[parts.length - 1]; + location = location.replace(".json", ""); + + ScreenBuilder sb = new ScreenBuilder(ident); + switch (screenType) { + case "standard" -> standardMap.put(location, sb); + case "screen_a" -> screenAMap.put(location, sb); + case "screen_b" -> screenBMap.put(location, sb); + } + } + + /** + * Top level render method. + * Calls the appropriate ScreenBuilder with the screen's dimensions + */ + public static void render(DrawContext context, int w, int h) { + String location = PlayerLocator.getPlayerLocation().internal; + HashMap<String, ScreenBuilder> lookup; + if (TabHud.toggleA.isPressed()) { + lookup = screenAMap; + } else if (TabHud.toggleB.isPressed()) { + lookup = screenBMap; + } else { + lookup = standardMap; + } + + ScreenBuilder sb = lookup.get(location); + // seems suboptimal, maybe load the default first into all possible values + // and then override? + if (sb == null) { + sb = lookup.get("default"); + } + + sb.run(context, w, h); + + } + + public static void init() { + + // WHY MUST IT ALWAYS BE SUCH NESTED GARBAGE MINECRAFT KEEP THAT IN DFU FFS + + FabricLoader.getInstance() + .getModContainer("skyblocker") + .ifPresent(container -> ResourceManagerHelper.registerBuiltinResourcePack( + new Identifier("skyblocker", "top_aligned"), + container, + ResourcePackActivationType.NORMAL)); + + ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener( + // ...why are we instantiating an interface again? + new SimpleSynchronousResourceReloadListener() { + @Override + public Identifier getFabricId() { + return new Identifier("skyblocker", "tabhud"); + } + + @Override + public void reload(ResourceManager manager) { + + standardMap.clear(); + screenAMap.clear(); + screenBMap.clear(); + + int excnt = 0; + + for (Map.Entry<Identifier, Resource> entry : manager + .findResources("tabhud", path -> path.getPath().endsWith("version.json")) + .entrySet()) { + + try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager() + .openAsReader(entry.getKey())) { + JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); + if (json.get("format_version").getAsInt() != VERSION) { + throw new IllegalStateException(String.format("Resource pack isn't compatible! Expected version %d, got %d", VERSION, json.get("format_version").getAsInt())); + } + + } catch (Exception ex) { + throw new IllegalStateException( + "Rejected this resource pack. Reason: " + ex.getMessage()); + } + } + + for (Map.Entry<Identifier, Resource> entry : manager + .findResources("tabhud", path -> path.getPath().endsWith(".json") && !path.getPath().endsWith("version.json")) + .entrySet()) { + try { + + load(entry.getKey()); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + excnt++; + } + } + if (excnt > 0) { + throw new IllegalStateException("This screen definition isn't valid, see above"); + } + } + }); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java new file mode 100644 index 00000000..7c01a6db --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java @@ -0,0 +1,83 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class AlignStage extends PipelineStage { + + private enum AlignReference { + HORICENT("horizontalCenter"), + VERTCENT("verticalCenter"), + LEFTCENT("leftOfCenter"), + RIGHTCENT("rightOfCenter"), + TOPCENT("topOfCenter"), + BOTCENT("botOfCenter"), + TOP("top"), + BOT("bot"), + LEFT("left"), + RIGHT("right"); + + private final String str; + + AlignReference(String d) { + this.str = d; + } + + public static AlignReference parse(String s) throws NoSuchElementException { + for (AlignReference d : AlignReference.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid reference for an align op!"); + } + } + + private final AlignReference reference; + + public AlignStage(ScreenBuilder builder, JsonObject descr) { + this.reference = AlignReference.parse(descr.get("reference").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + } + + public void run(int screenW, int screenH) { + int wHalf, hHalf; + for (Widget wid : primary) { + switch (this.reference) { + case HORICENT -> wid.setX((screenW - wid.getWidth()) / 2); + case VERTCENT -> wid.setY((screenH - wid.getHeight()) / 2); + case LEFTCENT -> { + wHalf = screenW / 2; + wid.setX(wHalf - ScreenConst.WIDGET_PAD_HALF - wid.getWidth()); + } + case RIGHTCENT -> { + wHalf = screenW / 2; + wid.setX(wHalf + ScreenConst.WIDGET_PAD_HALF); + } + case TOPCENT -> { + hHalf = screenH / 2; + wid.setY(hHalf - ScreenConst.WIDGET_PAD_HALF - wid.getHeight()); + } + case BOTCENT -> { + hHalf = screenH / 2; + wid.setY(hHalf + ScreenConst.WIDGET_PAD_HALF); + } + case TOP -> wid.setY(ScreenConst.getScreenPad()); + case BOT -> wid.setY(screenH - wid.getHeight() - ScreenConst.getScreenPad()); + case LEFT -> wid.setX(ScreenConst.getScreenPad()); + case RIGHT -> wid.setX(screenW - wid.getWidth() - ScreenConst.getScreenPad()); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java new file mode 100644 index 00000000..d100a52e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java @@ -0,0 +1,153 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class CollideStage extends PipelineStage { + + private enum CollideDirection { + LEFT("left"), + RIGHT("right"), + TOP("top"), + BOT("bot"); + + private final String str; + + CollideDirection(String d) { + this.str = d; + } + + public static CollideDirection parse(String s) throws NoSuchElementException { + for (CollideDirection d : CollideDirection.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a collide op!"); + } + } + + private final CollideDirection direction; + + public CollideStage(ScreenBuilder builder, JsonObject descr) { + this.direction = CollideDirection.parse(descr.get("direction").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("widgets") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + this.secondary = new ArrayList<>(descr.getAsJsonArray("colliders") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + } + + public void run(int screenW, int screenH) { + switch (this.direction) { + case LEFT -> primary.forEach(w -> collideAgainstL(screenW, w)); + case RIGHT -> primary.forEach(w -> collideAgainstR(screenW, w)); + case TOP -> primary.forEach(w -> collideAgainstT(screenH, w)); + case BOT -> primary.forEach(w -> collideAgainstB(screenH, w)); + } + } + + public void collideAgainstL(int screenW, Widget w) { + int yMin = w.getY(); + int yMax = w.getY() + w.getHeight(); + + int xCor = screenW; + + for (Widget other : secondary) { + if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) { + // too high, next one + continue; + } + + if (other.getY() - ScreenConst.WIDGET_PAD > yMax) { + // too low, next + continue; + } + + int xPos = other.getX() - ScreenConst.WIDGET_PAD - w.getWidth(); + xCor = Math.min(xCor, xPos); + } + w.setX(xCor); + } + + public void collideAgainstR(int screenW, Widget w) { + int yMin = w.getY(); + int yMax = w.getY() + w.getHeight(); + + int xCor = 0; + + for (Widget other : secondary) { + if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) { + // too high, next one + continue; + } + + if (other.getY() - ScreenConst.WIDGET_PAD > yMax) { + // too low, next + continue; + } + + int xPos = other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD; + xCor = Math.max(xCor, xPos); + } + w.setX(xCor); + } + + public void collideAgainstT(int screenH, Widget w) { + int xMin = w.getX(); + int xMax = w.getX() + w.getWidth(); + + int yCor = screenH; + + for (Widget other : secondary) { + if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) { + // too far left, next one + continue; + } + + if (other.getX() - ScreenConst.WIDGET_PAD > xMax) { + // too far right, next + continue; + } + + int yPos = other.getY() - ScreenConst.WIDGET_PAD - w.getHeight(); + yCor = Math.min(yCor, yPos); + } + w.setY(yCor); + } + + public void collideAgainstB(int screenH, Widget w) { + int xMin = w.getX(); + int xMax = w.getX() + w.getWidth(); + + int yCor = 0; + + for (Widget other : secondary) { + if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) { + // too far left, next one + continue; + } + + if (other.getX() - ScreenConst.WIDGET_PAD > xMax) { + // too far right, next + continue; + } + + int yPos = other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD; + yCor = Math.max(yCor, yPos); + } + w.setY(yCor); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java new file mode 100644 index 00000000..20e4859e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; + +public abstract class PipelineStage { + + protected ArrayList<Widget> primary = null; + protected ArrayList<Widget> secondary = null; + + public abstract void run(int screenW, int screenH); + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java new file mode 100644 index 00000000..7d57305b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java @@ -0,0 +1,94 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class PlaceStage extends PipelineStage { + + private enum PlaceLocation { + CENTER("center"), + TOPCENT("centerTop"), + BOTCENT("centerBot"), + LEFTCENT("centerLeft"), + RIGHTCENT("centerRight"), + TRCORNER("cornerTopRight"), + TLCORNER("cornerTopLeft"), + BRCORNER("cornerBotRight"), + BLCORNER("cornerBotLeft"); + + private final String str; + + PlaceLocation(String d) { + this.str = d; + } + + public static PlaceLocation parse(String s) throws NoSuchElementException { + for (PlaceLocation d : PlaceLocation.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid location for a place op!"); + } + } + + private final PlaceLocation where; + + public PlaceStage(ScreenBuilder builder, JsonObject descr) { + this.where = PlaceLocation.parse(descr.get("where").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .limit(1) + .toList()); + } + + public void run(int screenW, int screenH) { + Widget wid = primary.get(0); + switch (where) { + case CENTER -> { + wid.setX((screenW - wid.getWidth()) / 2); + wid.setY((screenH - wid.getHeight()) / 2); + } + case TOPCENT -> { + wid.setX((screenW - wid.getWidth()) / 2); + wid.setY(ScreenConst.getScreenPad()); + } + case BOTCENT -> { + wid.setX((screenW - wid.getWidth()) / 2); + wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); + } + case LEFTCENT -> { + wid.setX(ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) / 2); + } + case RIGHTCENT -> { + wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) / 2); + } + case TLCORNER -> { + wid.setX(ScreenConst.getScreenPad()); + wid.setY(ScreenConst.getScreenPad()); + } + case TRCORNER -> { + wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); + wid.setY(ScreenConst.getScreenPad()); + } + case BLCORNER -> { + wid.setX(ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); + } + case BRCORNER -> { + wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); + } + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java new file mode 100644 index 00000000..f4fe07e5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java @@ -0,0 +1,114 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class StackStage extends PipelineStage { + + private enum StackDirection { + HORIZONTAL("horizontal"), + VERTICAL("vertical"); + + private final String str; + + StackDirection(String d) { + this.str = d; + } + + public static StackDirection parse(String s) throws NoSuchElementException { + for (StackDirection d : StackDirection.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a stack op!"); + } + } + + private enum StackAlign { + TOP("top"), + BOT("bot"), + LEFT("left"), + RIGHT("right"), + CENTER("center"); + + private final String str; + + StackAlign(String d) { + this.str = d; + } + + public static StackAlign parse(String s) throws NoSuchElementException { + for (StackAlign d : StackAlign.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid alignment for a stack op!"); + } + } + + private final StackDirection direction; + private final StackAlign align; + + public StackStage(ScreenBuilder builder, JsonObject descr) { + this.direction = StackDirection.parse(descr.get("direction").getAsString()); + this.align = StackAlign.parse(descr.get("align").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + } + + public void run(int screenW, int screenH) { + switch (this.direction) { + case HORIZONTAL -> stackWidgetsHoriz(screenW); + case VERTICAL -> stackWidgetsVert(screenH); + } + } + + public void stackWidgetsVert(int screenH) { + int compHeight = -ScreenConst.WIDGET_PAD; + for (Widget wid : primary) { + compHeight += wid.getHeight() + 5; + } + + int y = switch (this.align) { + + case TOP -> ScreenConst.getScreenPad(); + case BOT -> (screenH - compHeight) - ScreenConst.getScreenPad(); + default -> (screenH - compHeight) / 2; + }; + + for (Widget wid : primary) { + wid.setY(y); + y += wid.getHeight() + ScreenConst.WIDGET_PAD; + } + } + + public void stackWidgetsHoriz(int screenW) { + int compWidth = -ScreenConst.WIDGET_PAD; + for (Widget wid : primary) { + compWidth += wid.getWidth() + ScreenConst.WIDGET_PAD; + } + + int x = switch (this.align) { + + case LEFT -> ScreenConst.getScreenPad(); + case RIGHT -> (screenW - compWidth) - ScreenConst.getScreenPad(); + default -> (screenW - compWidth) / 2; + }; + + for (Widget wid : primary) { + wid.setX(x); + x += wid.getWidth() + ScreenConst.WIDGET_PAD; + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java new file mode 100644 index 00000000..24883d77 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java @@ -0,0 +1,60 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +/** + * Stores convenient shorthands for common ItemStack definitions + */ +public class Ico { + public static final ItemStack MAP = new ItemStack(Items.FILLED_MAP); + public static final ItemStack NTAG = new ItemStack(Items.NAME_TAG); + public static final ItemStack EMERALD = new ItemStack(Items.EMERALD); + public static final ItemStack CLOCK = new ItemStack(Items.CLOCK); + public static final ItemStack DIASWORD = new ItemStack(Items.DIAMOND_SWORD); + public static final ItemStack DBUSH = new ItemStack(Items.DEAD_BUSH); + public static final ItemStack VILLAGER = new ItemStack(Items.VILLAGER_SPAWN_EGG); + public static final ItemStack MOREGOLD = new ItemStack(Items.GOLDEN_APPLE); + public static final ItemStack COMPASS = new ItemStack(Items.COMPASS); + public static final ItemStack SUGAR = new ItemStack(Items.SUGAR); + public static final ItemStack HOE = new ItemStack(Items.IRON_HOE); + public static final ItemStack GOLD = new ItemStack(Items.GOLD_INGOT); + public static final ItemStack BONE = new ItemStack(Items.BONE); + public static final ItemStack SIGN = new ItemStack(Items.OAK_SIGN); + public static final ItemStack FISH_ROD = new ItemStack(Items.FISHING_ROD); + public static final ItemStack SWORD = new ItemStack(Items.IRON_SWORD); + public static final ItemStack LANTERN = new ItemStack(Items.LANTERN); + public static final ItemStack COOKIE = new ItemStack(Items.COOKIE); + public static final ItemStack POTION = new ItemStack(Items.POTION); + public static final ItemStack BARRIER = new ItemStack(Items.BARRIER); + public static final ItemStack PLAYER = new ItemStack(Items.PLAYER_HEAD); + public static final ItemStack WATER = new ItemStack(Items.WATER_BUCKET); + public static final ItemStack LEATHER = new ItemStack(Items.LEATHER); + public static final ItemStack MITHRIL = new ItemStack(Items.PRISMARINE_CRYSTALS); + public static final ItemStack REDSTONE = new ItemStack(Items.REDSTONE); + public static final ItemStack FIRE = new ItemStack(Items.CAMPFIRE); + public static final ItemStack STRING = new ItemStack(Items.STRING); + public static final ItemStack WITHER = new ItemStack(Items.WITHER_SKELETON_SKULL); + public static final ItemStack FLESH = new ItemStack(Items.ROTTEN_FLESH); + public static final ItemStack DRAGON = new ItemStack(Items.DRAGON_HEAD); + public static final ItemStack DIAMOND = new ItemStack(Items.DIAMOND); + public static final ItemStack ICE = new ItemStack(Items.ICE); + public static final ItemStack CHEST = new ItemStack(Items.CHEST); + public static final ItemStack COMMAND = new ItemStack(Items.COMMAND_BLOCK); + public static final ItemStack SKULL = new ItemStack(Items.SKELETON_SKULL); + public static final ItemStack BOOK = new ItemStack(Items.WRITABLE_BOOK); + public static final ItemStack FURNACE = new ItemStack(Items.FURNACE); + public static final ItemStack CHESTPLATE = new ItemStack(Items.IRON_CHESTPLATE); + public static final ItemStack B_ROD = new ItemStack(Items.BLAZE_ROD); + public static final ItemStack BOW = new ItemStack(Items.BOW); + public static final ItemStack COPPER = new ItemStack(Items.COPPER_INGOT); + public static final ItemStack COMPOSTER = new ItemStack(Items.COMPOSTER); + public static final ItemStack SAPLING = new ItemStack(Items.OAK_SAPLING); + public static final ItemStack MILESTONE = new ItemStack(Items.LODESTONE); + public static final ItemStack PICKAXE = new ItemStack(Items.IRON_PICKAXE); + public static final ItemStack NETHER_STAR = new ItemStack(Items.NETHER_STAR); + public static final ItemStack HEART_OF_THE_SEA = new ItemStack(Items.HEART_OF_THE_SEA); + public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE); + public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE); + public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java new file mode 100644 index 00000000..f577f2d3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java @@ -0,0 +1,171 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.mixin.accessor.PlayerListHudAccessor; +import de.hysky.skyblocker.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; + +/** + * This class may be used to get data from the player list. It doesn't get its + * data every frame, instead, a scheduler is used to update the data this class + * is holding periodically. The list is sorted like in the vanilla game. + */ +public class PlayerListMgr { + + public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Regex"); + + private static List<PlayerListEntry> playerList; + private static String footer; + + public static void updateList() { + + if (!Utils.isOnSkyblock()) { + return; + } + + ClientPlayNetworkHandler cpnwh = MinecraftClient.getInstance().getNetworkHandler(); + + // check is needed, else game crash on server leave + if (cpnwh != null) { + playerList = cpnwh.getPlayerList().stream().sorted(PlayerListHudAccessor.getOrdering()).toList(); + } + } + + public static void updateFooter(Text f) { + if (f == null) { + footer = null; + } else { + footer = f.getString(); + } + } + + public static String getFooter() { + return footer; + } + + /** + * Get the display name at some index of the player list and apply a pattern to + * it + * + * @return the matcher if p fully matches, else null + */ + public static Matcher regexAt(int idx, Pattern p) { + + String str = PlayerListMgr.strAt(idx); + + if (str == null) { + return null; + } + + Matcher m = p.matcher(str); + if (!m.matches()) { + LOGGER.error("no match: \"{}\" against \"{}\"", str, p); + return null; + } else { + return m; + } + } + + /** + * Get the display name at some index of the player list as string + * + * @return the string or null, if the display name is null, empty or whitespace + * only + */ + public static String strAt(int idx) { + + if (playerList == null) { + return null; + } + + if (playerList.size() <= idx) { + return null; + } + + Text txt = playerList.get(idx).getDisplayName(); + if (txt == null) { + return null; + } + String str = txt.getString().trim(); + if (str.isEmpty()) { + return null; + } + return str; + } + + /** + * Gets the display name at some index of the player list + * + * @return the text or null, if the display name is null + * + * @implNote currently designed specifically for crimson isles faction quests + * widget and the rift widgets, might not work correctly without + * modification for other stuff. you've been warned! + */ + public static Text textAt(int idx) { + + if (playerList == null) { + return null; + } + + if (playerList.size() <= idx) { + return null; + } + + Text txt = playerList.get(idx).getDisplayName(); + if (txt == null) { + return null; + } + + // Rebuild the text object to remove leading space thats in all faction quest + // stuff (also removes trailing space just in case) + MutableText newText = Text.empty(); + int size = txt.getSiblings().size(); + + for (int i = 0; i < size; i++) { + Text current = txt.getSiblings().get(i); + String textToAppend = current.getString(); + + // Trim leading & trailing space - this can only be done at the start and end + // otherwise it'll produce malformed results + if (i == 0) + textToAppend = textToAppend.stripLeading(); + if (i == size - 1) + textToAppend = textToAppend.stripTrailing(); + + newText.append(Text.literal(textToAppend).setStyle(current.getStyle())); + } + + // Avoid returning an empty component - Rift advertisements needed this + if (newText.getString().isEmpty()) { + return null; + } + + return newText; + } + + /** + * Get the display name at some index of the player list as Text as seen in the + * game + * + * @return the PlayerListEntry at that index + */ + public static PlayerListEntry getRaw(int idx) { + return playerList.get(idx); + } + + public static int getSize() { + return playerList.size(); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java new file mode 100644 index 00000000..e5f5bfc8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java @@ -0,0 +1,87 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import de.hysky.skyblocker.utils.Utils; + +/** + * Uses data from the player list to determine the area the player is in. + */ +public class PlayerLocator { + + public enum Location { + DUNGEON("dungeon"), + GUEST_ISLAND("guest_island"), + HOME_ISLAND("home_island"), + CRIMSON_ISLE("crimson_isle"), + DUNGEON_HUB("dungeon_hub"), + FARMING_ISLAND("farming_island"), + PARK("park"), + DWARVEN_MINES("dwarven_mines"), + CRYSTAL_HOLLOWS("crystal_hollows"), + END("end"), + GOLD_MINE("gold_mine"), + DEEP_CAVERNS("deep_caverns"), + HUB("hub"), + SPIDER_DEN("spider_den"), + JERRY("jerry_workshop"), + GARDEN("garden"), + INSTANCED("kuudra"), + THE_RIFT("rift"), + DARK_AUCTION("dark_auction"), + UNKNOWN("unknown"); + + public final String internal; + + Location(String i) { + // as used internally by the mod, e.g. in the json + this.internal = i; + } + + } + + public static Location getPlayerLocation() { + + if (!Utils.isOnSkyblock()) { + return Location.UNKNOWN; + } + + String areaDescriptor = PlayerListMgr.strAt(41); + + if (areaDescriptor == null || areaDescriptor.length() < 6) { + return Location.UNKNOWN; + } + + if (areaDescriptor.startsWith("Dungeon")) { + return Location.DUNGEON; + } + + return switch (areaDescriptor.substring(6)) { + case "Private Island" -> { + String islandType = PlayerListMgr.strAt(44); + if (islandType == null) { + yield Location.UNKNOWN; + } else if (islandType.endsWith("Guest")) { + yield Location.GUEST_ISLAND; + } else { + yield Location.HOME_ISLAND; + } + } + case "Crimson Isle" -> Location.CRIMSON_ISLE; + case "Dungeon Hub" -> Location.DUNGEON_HUB; + case "The Farming Islands" -> Location.FARMING_ISLAND; + case "The Park" -> Location.PARK; + case "Dwarven Mines" -> Location.DWARVEN_MINES; + case "Crystal Hollows" -> Location.CRYSTAL_HOLLOWS; + case "The End" -> Location.END; + case "Gold Mine" -> Location.GOLD_MINE; + case "Deep Caverns" -> Location.DEEP_CAVERNS; + case "Hub" -> Location.HUB; + case "Spider's Den" -> Location.SPIDER_DEN; + case "Jerry's Workshop" -> Location.JERRY; + case "Garden" -> Location.GARDEN; + case "Instanced" -> Location.INSTANCED; + case "The Rift" -> Location.THE_RIFT; + case "Dark Auction" -> Location.DARK_AUCTION; + default -> Location.UNKNOWN; + }; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java new file mode 100644 index 00000000..6a4d96d3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java @@ -0,0 +1,13 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; + +public class ScreenConst { + public static final int WIDGET_PAD = 5; + public static final int WIDGET_PAD_HALF = 3; + private static final int SCREEN_PAD_BASE = 20; + + public static int getScreenPad() { + return (int) ((1f/((float)SkyblockerConfigManager.get().general.tabHud.tabHudScale/100f) * SCREEN_PAD_BASE)); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java new file mode 100644 index 00000000..9cff3d32 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java @@ -0,0 +1,37 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +public class CameraPositionWidget extends Widget { + private static final MutableText TITLE = Text.literal("Camera Pos").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + + public CameraPositionWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + double yaw = CLIENT.getCameraEntity().getYaw(); + double pitch = CLIENT.getCameraEntity().getPitch(); + + this.addComponent( + new PlainTextComponent(Text.literal("Yaw: " + roundToDecimalPlaces(MathHelper.wrapDegrees(yaw), 3)))); + this.addComponent(new PlainTextComponent( + Text.literal("Pitch: " + roundToDecimalPlaces(MathHelper.wrapDegrees(pitch), 3)))); + + } + + // https://stackoverflow.com/a/33889423 + private static double roundToDecimalPlaces(double value, int decimalPlaces) { + double shift = Math.pow(10, decimalPlaces); + + return Math.round(value * shift) / shift; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java new file mode 100644 index 00000000..e8bf91ab --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java @@ -0,0 +1,63 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +// this widget shows the status of the king's commissions. +// (dwarven mines and crystal hollows) + +public class CommsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + // match a comm + // group 1: comm name + // group 2: comm progress (without "%" for comms that show a percentage) + private static final Pattern COMM_PATTERN = Pattern.compile("(?<name>.*): (?<progress>.*)%?"); + + public CommsWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + for (int i = 50; i <= 53; i++) { + Matcher m = PlayerListMgr.regexAt(i, COMM_PATTERN); + // end of comms found? + if (m == null) { + if (i == 50) { + this.addComponent(new IcoTextComponent()); + } + break; + } + + ProgressComponent pc; + + String name = m.group("name"); + String progress = m.group("progress"); + + if (progress.equals("DONE")) { + pc = new ProgressComponent(Ico.BOOK, Text.of(name), Text.of(progress), 100f, pcntToCol(100)); + } else { + float pcnt = Float.parseFloat(progress.substring(0, progress.length() - 1)); + pc = new ProgressComponent(Ico.BOOK, Text.of(name), pcnt, pcntToCol(pcnt)); + } + this.addComponent(pc); + } + } + + private int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java new file mode 100644 index 00000000..fbeb5ae5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the garden's composter + +public class ComposterWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Composter").formatted(Formatting.GREEN, + Formatting.BOLD); + + public ComposterWidget() { + super(TITLE, Formatting.GREEN.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.SAPLING, "Organic Matter:", Formatting.YELLOW, 48); + this.addSimpleIcoText(Ico.FURNACE, "Fuel:", Formatting.BLUE, 49); + this.addSimpleIcoText(Ico.CLOCK, "Time Left:", Formatting.RED, 50); + this.addSimpleIcoText(Ico.COMPOSTER, "Stored Compost:", Formatting.DARK_GREEN, 51); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java new file mode 100644 index 00000000..a5883e7e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java @@ -0,0 +1,50 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about active super cookies +// or not, if you're unwilling to buy one + +public class CookieWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Cookie Info").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + private static final Pattern COOKIE_PATTERN = Pattern.compile(".*\\nCookie Buff\\n(?<buff>.*)\\n"); + + public CookieWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + String footertext = PlayerListMgr.getFooter(); + if (footertext == null || !footertext.contains("Cookie Buff")) { + this.addComponent(new IcoTextComponent()); + return; + } + + Matcher m = COOKIE_PATTERN.matcher(footertext); + if (!m.find() || m.group("buff") == null) { + this.addComponent(new IcoTextComponent()); + return; + } + + String buff = m.group("buff"); + if (buff.startsWith("Not")) { + this.addComponent(new IcoTextComponent(Ico.COOKIE, Text.of("Not active"))); + } else { + Text cookie = Text.literal("Time Left: ").append(buff); + this.addComponent(new IcoTextComponent(Ico.COOKIE, cookie)); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java new file mode 100644 index 00000000..fd896796 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java @@ -0,0 +1,68 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.Arrays; +import java.util.Comparator; + +// this widget shows a list of obtained dungeon buffs + +public class DungeonBuffWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Dungeon Buffs").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public DungeonBuffWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + + String footertext = PlayerListMgr.getFooter(); + + if (footertext == null || !footertext.contains("Dungeon Buffs")) { + this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY))); + return; + } + + String interesting = footertext.split("Dungeon Buffs")[1]; + String[] lines = interesting.split("\n"); + + if (!lines[1].startsWith("Blessing")) { + this.addComponent(new PlainTextComponent(Text.literal("No buffs found!").formatted(Formatting.GRAY))); + return; + } + + //Filter out text unrelated to blessings + lines = Arrays.stream(lines).filter(s -> s.contains("Blessing")).toArray(String[]::new); + + //Alphabetically sort the blessings + Arrays.sort(lines, Comparator.comparing(String::toLowerCase)); + + for (String line : lines) { + if (line.length() < 3) { // empty line is §s + break; + } + int color = getBlessingColor(line); + this.addComponent(new PlainTextComponent(Text.literal(line).styled(style -> style.withColor(color)))); + } + + } + + @SuppressWarnings("DataFlowIssue") + public int getBlessingColor(String blessing) { + if (blessing.contains("Life")) return Formatting.LIGHT_PURPLE.getColorValue(); + if (blessing.contains("Power")) return Formatting.RED.getColorValue(); + if (blessing.contains("Stone")) return Formatting.GREEN.getColorValue(); + if (blessing.contains("Time")) return 0xafb8c1; + if (blessing.contains("Wisdom")) return Formatting.AQUA.getColorValue(); + + return 0xffffff; + } + +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java new file mode 100644 index 00000000..9c299210 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows various dungeon info +// deaths, healing, dmg taken, milestones + +public class DungeonDeathWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Death").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match the deaths entry + // group 1: amount of deaths + private static final Pattern DEATH_PATTERN = Pattern.compile("Team Deaths: (?<deathnum>\\d+).*"); + + public DungeonDeathWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + Matcher m = PlayerListMgr.regexAt(25, DEATH_PATTERN); + if (m == null) { + this.addComponent(new IcoTextComponent()); + } else { + Formatting f = (m.group("deathnum").equals("0")) ? Formatting.GREEN : Formatting.RED; + Text d = Widget.simpleEntryText(m.group("deathnum"), "Deaths: ", f); + IcoTextComponent deaths = new IcoTextComponent(Ico.SKULL, d); + this.addComponent(deaths); + } + + this.addSimpleIcoText(Ico.SWORD, "Damage Dealt:", Formatting.RED, 26); + this.addSimpleIcoText(Ico.POTION, "Healing Done:", Formatting.RED, 27); + this.addSimpleIcoText(Ico.NTAG, "Milestone:", Formatting.YELLOW, 28); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java new file mode 100644 index 00000000..9a8de0eb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java @@ -0,0 +1,44 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about... something? +// related to downed people in dungeons, not sure what this is supposed to show + +public class DungeonDownedWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Downed").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public DungeonDownedWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + String down = PlayerListMgr.strAt(21); + if (down == null) { + this.addComponent(new IcoTextComponent()); + } else { + + Formatting format = Formatting.RED; + if (down.endsWith("NONE")) { + format = Formatting.GRAY; + } + int idx = down.indexOf(": "); + Text downed = (idx == -1) ? null + : Widget.simpleEntryText(down.substring(idx + 2), "Downed: ", format); + IcoTextComponent d = new IcoTextComponent(Ico.SKULL, downed); + this.addComponent(d); + } + + this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GRAY, 22); + this.addSimpleIcoText(Ico.POTION, "Revive:", Formatting.GRAY, 23); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java new file mode 100644 index 00000000..be1a3c6e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java @@ -0,0 +1,103 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about a player in the current dungeon group + +public class DungeonPlayerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Player").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match a player entry + // group 1: name + // group 2: class (or literal "EMPTY" pre dungeon start) + // group 3: level (or nothing, if pre dungeon start) + // this regex filters out the ironman icon as well as rank prefixes and emblems + // \[\d*\] (?:\[[A-Za-z]+\] )?(?<name>[A-Za-z0-9_]*) (?:.* )?\((?<class>\S*) ?(?<level>[LXVI]*)\) + private static final Pattern PLAYER_PATTERN = Pattern + .compile("\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?(?<name>[A-Za-z0-9_]*) (?:.* )?\\((?<class>\\S*) ?(?<level>[LXVI]*)\\)"); + + private static final HashMap<String, ItemStack> ICOS = new HashMap<>(); + private static final ArrayList<String> MSGS = new ArrayList<>(); + static { + ICOS.put("Tank", Ico.CHESTPLATE); + ICOS.put("Mage", Ico.B_ROD); + ICOS.put("Berserk", Ico.DIASWORD); + ICOS.put("Archer", Ico.BOW); + ICOS.put("Healer", Ico.POTION); + + MSGS.add("PRESS A TO JOIN"); + MSGS.add("Invite a friend!"); + MSGS.add("But nobody came."); + MSGS.add("More is better!"); + } + + private final int player; + + // title needs to be changeable here + public DungeonPlayerWidget(int player) { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + this.player = player; + } + + @Override + public void updateContent() { + int start = 1 + (player - 1) * 4; + + if (PlayerListMgr.strAt(start) == null) { + int idx = player - 2; + IcoTextComponent noplayer = new IcoTextComponent(Ico.SIGN, + Text.literal(MSGS.get(idx)).formatted(Formatting.GRAY)); + this.addComponent(noplayer); + return; + } + Matcher m = PlayerListMgr.regexAt(start, PLAYER_PATTERN); + if (m == null) { + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + } else { + + Text name = Text.literal("Name: ").append(Text.literal(m.group("name")).formatted(Formatting.YELLOW)); + this.addComponent(new IcoTextComponent(Ico.PLAYER, name)); + + String cl = m.group("class"); + String level = m.group("level"); + + if (level == null) { + PlainTextComponent ptc = new PlainTextComponent( + Text.literal("Player is dead").formatted(Formatting.RED)); + this.addComponent(ptc); + } else { + + Formatting clf = Formatting.GRAY; + ItemStack cli = Ico.BARRIER; + if (!cl.equals("EMPTY")) { + cli = ICOS.get(cl); + clf = Formatting.LIGHT_PURPLE; + cl += " " + m.group("level"); + } + + Text clazz = Text.literal("Class: ").append(Text.literal(cl).formatted(clf)); + IcoTextComponent itclass = new IcoTextComponent(cli, clazz); + this.addComponent(itclass); + } + } + + this.addSimpleIcoText(Ico.CLOCK, "Ult Cooldown:", Formatting.GOLD, start + 1); + this.addSimpleIcoText(Ico.POTION, "Revives:", Formatting.DARK_PURPLE, start + 2); + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java new file mode 100644 index 00000000..1b3b8644 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java @@ -0,0 +1,57 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about all puzzeles in the dungeon (name and status) + +public class DungeonPuzzleWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Puzzles").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match a puzzle entry + // group 1: name + // group 2: status + // " ?.*" to diescard the solver's name if present + // the teleport maze has a trailing whitespace that messes with the regex + private static final Pattern PUZZLE_PATTERN = Pattern.compile("(?<name>.*): \\[(?<status>.*)\\] ?.*"); + + public DungeonPuzzleWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + int pos = 48; + + while (pos < 60) { + Matcher m = PlayerListMgr.regexAt(pos, PUZZLE_PATTERN); + if (m == null) { + break; + } + Text t = Text.literal(m.group("name") + ": ") + .append(Text.literal("[").formatted(Formatting.GRAY)) + .append(m.group("status")) + .append(Text.literal("]").formatted(Formatting.GRAY)); + IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, t); + this.addComponent(itc); + pos++; + // code points for puzzle status chars unsolved and solved: 10022, 10004 + // not sure which one is which + // still need to find out codepoint for the puzzle failed char + } + if (pos == 48) { + this.addComponent( + new IcoTextComponent(Ico.BARRIER, Text.literal("No puzzles!").formatted(Formatting.GRAY))); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java new file mode 100644 index 00000000..6f40f5a8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java @@ -0,0 +1,26 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the secrets of the dungeon + +public class DungeonSecretWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public DungeonSecretWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31); + this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java new file mode 100644 index 00000000..569987e8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java @@ -0,0 +1,48 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows broad info about the current dungeon +// opened/completed rooms, % of secrets found and time taken + +public class DungeonServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Dungeon Info").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match the secrets text + // group 1: % of secrets found (without "%") + private static final Pattern SECRET_PATTERN = Pattern.compile("Secrets Found: (?<secnum>.*)%"); + + public DungeonServerWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.AQUA, 41); + this.addSimpleIcoText(Ico.SIGN, "Rooms Visited:", Formatting.DARK_PURPLE, 42); + this.addSimpleIcoText(Ico.SIGN, "Rooms Completed:", Formatting.LIGHT_PURPLE, 43); + + Matcher m = PlayerListMgr.regexAt(44, SECRET_PATTERN); + if (m == null) { + this.addComponent(new ProgressComponent()); + } else { + ProgressComponent scp = new ProgressComponent(Ico.CHEST, Text.of("Secrets found:"), + Float.parseFloat(m.group("secnum")), + Formatting.DARK_PURPLE.getColorValue()); + this.addComponent(scp); + } + + this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GOLD, 45); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java new file mode 100644 index 00000000..5ec3faf1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java @@ -0,0 +1,67 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widgte shows, how many active effects you have. +// it also shows one of those in detail. +// the parsing is super suspect and should be replaced by some regexes sometime later + +public class EffectWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Effect Info").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public EffectWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + + String footertext = PlayerListMgr.getFooter(); + + if (footertext == null || !footertext.contains("Active Effects")) { + this.addComponent(new IcoTextComponent()); + return; + + } + + String[] lines = footertext.split("Active Effects")[1].split("\n"); + if (lines.length < 2) { + this.addComponent(new IcoTextComponent()); + return; + } + + if (lines[1].startsWith("No")) { + Text txt = Text.literal("No effects active").formatted(Formatting.GRAY); + this.addComponent(new IcoTextComponent(Ico.POTION, txt)); + } else if (lines[1].contains("God")) { + String timeleft = lines[1].split("! ")[1]; + Text godpot = Text.literal("God potion!").formatted(Formatting.RED); + Text txttleft = Text.literal(timeleft).formatted(Formatting.LIGHT_PURPLE); + IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, godpot, txttleft); + this.addComponent(iftc); + } else { + String number = lines[1].substring("You have ".length()); + int idx = number.indexOf(' '); + if (idx == -1 || lines.length < 4) { + this.addComponent(new IcoFatTextComponent()); + return; + } + number = number.substring(0, idx); + Text active = Text.literal("Active Effects: ") + .append(Text.literal(number).formatted(Formatting.YELLOW)); + + IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, active, + Text.literal(lines[3]).formatted(Formatting.AQUA)); + this.addComponent(iftc); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java new file mode 100644 index 00000000..ec935faf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java @@ -0,0 +1,104 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows the status or results of the current election + +public class ElectionWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Election Info").formatted(Formatting.YELLOW, + Formatting.BOLD); + + private static final HashMap<String, ItemStack> MAYOR_DATA = new HashMap<>(); + + private static final Text EL_OVER = Text.literal("Election ") + .append(Text.literal("over!").formatted(Formatting.RED)); + + // pattern matching a candidate while people are voting + // group 1: name + // group 2: % of votes + private static final Pattern VOTE_PATTERN = Pattern.compile("(?<mayor>\\S*): \\|+ \\((?<pcnt>\\d*)%\\)"); + + static { + MAYOR_DATA.put("Aatrox", Ico.DIASWORD); + MAYOR_DATA.put("Cole", Ico.PICKAXE); + MAYOR_DATA.put("Diana", Ico.BONE); + MAYOR_DATA.put("Diaz", Ico.GOLD); + MAYOR_DATA.put("Finnegan", Ico.HOE); + MAYOR_DATA.put("Foxy", Ico.SUGAR); + MAYOR_DATA.put("Paul", Ico.COMPASS); + MAYOR_DATA.put("Scorpius", Ico.MOREGOLD); + MAYOR_DATA.put("Jerry", Ico.VILLAGER); + MAYOR_DATA.put("Derpy", Ico.DBUSH); + MAYOR_DATA.put("Marina", Ico.FISH_ROD); + } + + private static final Formatting[] COLS = { Formatting.GOLD, Formatting.RED, Formatting.LIGHT_PURPLE }; + + public ElectionWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + } + + @Override + public void updateContent() { + String status = PlayerListMgr.strAt(76); + if (status == null) { + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + return; + } + + if (status.contains("Over!")) { + // election is over + IcoTextComponent over = new IcoTextComponent(Ico.BARRIER, EL_OVER); + this.addComponent(over); + + String win = PlayerListMgr.strAt(77); + if (win == null || !win.contains(": ")) { + this.addComponent(new IcoTextComponent()); + } else { + String winnername = win.split(": ")[1]; + Text winnertext = Widget.simpleEntryText(winnername, "Winner: ", Formatting.GREEN); + IcoTextComponent winner = new IcoTextComponent(MAYOR_DATA.get(winnername), winnertext); + this.addComponent(winner); + } + + this.addSimpleIcoText(Ico.PLAYER, "Participants:", Formatting.AQUA, 78); + this.addSimpleIcoText(Ico.SIGN, "Year:", Formatting.LIGHT_PURPLE, 79); + + } else { + // election is going on + this.addSimpleIcoText(Ico.CLOCK, "End in:", Formatting.GOLD, 76); + + for (int i = 77; i <= 79; i++) { + Matcher m = PlayerListMgr.regexAt(i, VOTE_PATTERN); + if (m == null) { + this.addComponent(new ProgressComponent()); + } else { + + String mayorname = m.group("mayor"); + String pcntstr = m.group("pcnt"); + float pcnt = Float.parseFloat(pcntstr); + Text candidate = Text.literal(mayorname).formatted(COLS[i - 77]); + ProgressComponent pc = new ProgressComponent(MAYOR_DATA.get(mayorname), candidate, pcnt, + COLS[i - 77].getColorValue()); + this.addComponent(pc); + } + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java new file mode 100644 index 00000000..85019dbf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java @@ -0,0 +1,32 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// empty widget for when nothing can be shown + +public class ErrorWidget extends Widget { + private static final MutableText TITLE = Text.literal("Error").formatted(Formatting.RED, + Formatting.BOLD); + + Text error = Text.of("No info available!"); + + public ErrorWidget() { + super(TITLE, Formatting.RED.getColorValue()); + } + + public ErrorWidget(String error) { + super(TITLE, Formatting.RED.getColorValue()); + this.error = Text.of(error); + } + + @Override + public void updateContent() { + PlainTextComponent inf = new PlainTextComponent(this.error); + this.addComponent(inf); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java new file mode 100644 index 00000000..d171b753 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows your dungeon essences (dungeon hub only) + +public class EssenceWidget extends Widget { + + private Text undead, wither, diamond, gold, dragon, spider, ice, crimson; + + private static final MutableText TITLE = Text.literal("Essences").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public EssenceWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + wither = Widget.simpleEntryText(46, "Wither:", Formatting.DARK_PURPLE); + spider = Widget.simpleEntryText(47, "Spider:", Formatting.DARK_PURPLE); + undead = Widget.simpleEntryText(48, "Undead:", Formatting.DARK_PURPLE); + dragon = Widget.simpleEntryText(49, "Dragon:", Formatting.DARK_PURPLE); + gold = Widget.simpleEntryText(50, "Gold:", Formatting.DARK_PURPLE); + diamond = Widget.simpleEntryText(51, "Diamond:", Formatting.DARK_PURPLE); + ice = Widget.simpleEntryText(52, "Ice:", Formatting.DARK_PURPLE); + crimson = Widget.simpleEntryText(53, "Crimson:", Formatting.DARK_PURPLE); + + TableComponent tc = new TableComponent(2, 4, Formatting.DARK_AQUA.getColorValue()); + + tc.addToCell(0, 0, new IcoTextComponent(Ico.WITHER, wither)); + tc.addToCell(0, 1, new IcoTextComponent(Ico.STRING, spider)); + tc.addToCell(0, 2, new IcoTextComponent(Ico.FLESH, undead)); + tc.addToCell(0, 3, new IcoTextComponent(Ico.DRAGON, dragon)); + tc.addToCell(1, 0, new IcoTextComponent(Ico.GOLD, gold)); + tc.addToCell(1, 1, new IcoTextComponent(Ico.DIAMOND, diamond)); + tc.addToCell(1, 2, new IcoTextComponent(Ico.ICE, ice)); + tc.addToCell(1, 3, new IcoTextComponent(Ico.REDSTONE, crimson)); + this.addComponent(tc); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java new file mode 100644 index 00000000..5a1e4239 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about ongoing events (e.g. election) + +public class EventWidget extends Widget { + private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD); + + private final boolean isInGarden; + + public EventWidget(boolean isInGarden) { + super(TITLE, Formatting.YELLOW.getColorValue()); + this.isInGarden = isInGarden; + } + + @Override + public void updateContent() { + // hypixel devs carefully inserting the most random edge cases #317: + // the event info is placed a bit differently when in the garden. + int offset = (isInGarden) ? -1 : 0; + + this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.YELLOW, 73 + offset); + + // this could look better + Text time = Widget.plainEntryText(74 + offset); + IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time); + this.addComponent(t); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java new file mode 100644 index 00000000..0211cbd6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java @@ -0,0 +1,68 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.Formatting; + +// this widget shows info about fire sales when in the hub. +// or not, if there isn't one going on + +public class FireSaleWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Fire Sale").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + // matches a fire sale item + // group 1: item name + // group 2: # items available + // group 3: # items available in total (1 digit + "k") + private static final Pattern FIRE_PATTERN = Pattern.compile("(?<item>.*): (?<avail>\\d*)/(?<total>[0-9.]*)k"); + + public FireSaleWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + String event = PlayerListMgr.strAt(46); + + if (event == null) { + this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY))); + return; + } + + if (event.contains("Starts In")) { + this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.DARK_AQUA, 46); + return; + } + + for (int i = 46;; i++) { + Matcher m = PlayerListMgr.regexAt( i, FIRE_PATTERN); + if (m == null) { + break; + } + String avail = m.group("avail"); + Text itemTxt = Text.literal(m.group("item")); + float total = Float.parseFloat(m.group("total")) * 1000; + Text prgressTxt = Text.literal(String.format("%s/%.0f", avail, total)); + float pcnt = (Float.parseFloat(avail) / (total)) * 100f; + ProgressComponent pc = new ProgressComponent(Ico.GOLD, itemTxt, prgressTxt, pcnt, pcntToCol(pcnt)); + this.addComponent(pc); + } + + } + + private int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb( pcnt / 300f, 0.9f, 0.9f); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java new file mode 100644 index 00000000..1a4683f5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java @@ -0,0 +1,81 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows what you're forging right now. +// for locked slots, the unlock requirement is shown + +public class ForgeWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Forge Status").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public ForgeWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + int forgestart = 54; + // why is it forges and not looms >:( + String pos = PlayerListMgr.strAt(53); + if (pos == null) { + this.addComponent(new IcoTextComponent()); + return; + } + + if (!pos.startsWith("Forges")) { + forgestart += 2; + } + + for (int i = forgestart, slot = 1; i < forgestart + 5 && i < 60; i++, slot++) { + String fstr = PlayerListMgr.strAt(i); + if (fstr == null || fstr.length() < 3) { + if (i == forgestart) { + this.addComponent(new IcoTextComponent()); + } + break; + } + Component c; + Text l1, l2; + + switch (fstr.substring(3)) { + case "LOCKED" -> { + l1 = Text.literal("Locked").formatted(Formatting.RED); + l2 = switch (slot) { + case 3 -> Text.literal("Needs HotM 3").formatted(Formatting.GRAY); + case 4 -> Text.literal("Needs HotM 4").formatted(Formatting.GRAY); + case 5 -> Text.literal("Needs PotM 2").formatted(Formatting.GRAY); + default -> + Text.literal("This message should not appear").formatted(Formatting.RED, Formatting.BOLD); + }; + c = new IcoFatTextComponent(Ico.BARRIER, l1, l2); + } + case "EMPTY" -> { + l1 = Text.literal("Empty").formatted(Formatting.GRAY); + c = new IcoTextComponent(Ico.FURNACE, l1); + } + default -> { + String[] parts = fstr.split(": "); + if (parts.length != 2) { + c = new IcoFatTextComponent(); + } else { + l1 = Text.literal(parts[0].substring(3)).formatted(Formatting.YELLOW); + l2 = Text.literal("Done in: ").formatted(Formatting.GRAY).append(Text.literal(parts[1]).formatted(Formatting.WHITE)); + c = new IcoFatTextComponent(Ico.FIRE, l1, l2); + } + } + } + this.addComponent(c); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java new file mode 100644 index 00000000..221f8b08 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java @@ -0,0 +1,54 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the garden server + +public class GardenServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + // match the next visitor in the garden + // group 1: visitor name + private static final Pattern VISITOR_PATTERN = Pattern.compile("Next Visitor: (?<vis>.*)"); + + public GardenServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + this.addSimpleIcoText(Ico.COPPER, "Copper:", Formatting.GOLD, 44); + + Matcher m = PlayerListMgr.regexAt(45, VISITOR_PATTERN); + if (m == null ) { + this.addComponent(new IcoTextComponent()); + return; + } + + String vis = m.group("vis"); + Formatting col; + if (vis.equals("Not Unlocked!")) { + col = Formatting.RED; + } else { + col = Formatting.GREEN; + } + Text visitor = Widget.simpleEntryText(vis, "Next Visitor: ", col); + IcoTextComponent v = new IcoTextComponent(Ico.PLAYER, visitor); + this.addComponent(v); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java new file mode 100644 index 00000000..e7058fd6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java @@ -0,0 +1,80 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about your skills while in the garden + +public class GardenSkillsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW, + Formatting.BOLD); + + // match the skill entry + // group 1: skill name and level + // group 2: progress to next level (without "%") + private static final Pattern SKILL_PATTERN = Pattern + .compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%"); + // same, but with leading space + private static final Pattern MS_PATTERN = Pattern.compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%"); + + public GardenSkillsWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + } + + @Override + public void updateContent() { + ProgressComponent pc; + Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN); + if (m == null) { + pc = new ProgressComponent(); + } else { + + String strpcnt = m.group("progress"); + String skill = m.group("skill"); + + float pcnt = Float.parseFloat(strpcnt); + pc = new ProgressComponent(Ico.LANTERN, Text.of(skill), pcnt, + Formatting.GOLD.getColorValue()); + } + + this.addComponent(pc); + + Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE); + IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); + Text farmfort = Widget.simpleEntryText(68, "FFO", Formatting.GOLD); + IcoTextComponent ffo = new IcoTextComponent(Ico.HOE, farmfort); + + TableComponent tc = new TableComponent(2, 1, Formatting.YELLOW.getColorValue()); + tc.addToCell(0, 0, spd); + tc.addToCell(1, 0, ffo); + this.addComponent(tc); + + ProgressComponent pc2; + m = PlayerListMgr.regexAt(69, MS_PATTERN); + if (m == null) { + pc2 = new ProgressComponent(); + } else { + String strpcnt = m.group("progress"); + String skill = m.group("skill"); + + float pcnt = Float.parseFloat(strpcnt); + pc2 = new ProgressComponent(Ico.MILESTONE, Text.of(skill), pcnt, + Formatting.GREEN.getColorValue()); + + } + this.addComponent(pc2); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java new file mode 100644 index 00000000..cfbd6cd0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class GardenVisitorsWidget extends Widget { + private static final MutableText TITLE = Text.literal("Visitors").formatted(Formatting.DARK_GREEN, Formatting.BOLD); + + public GardenVisitorsWidget() { + super(TITLE, Formatting.DARK_GREEN.getColorValue()); + } + + @Override + public void updateContent() { + if (PlayerListMgr.textAt(54) == null) { + this.addComponent(new PlainTextComponent(Text.literal("No visitors!").formatted(Formatting.GRAY))); + return; + } + + for (int i = 54; i < 59; i++) { + String text = PlayerListMgr.strAt(i); + if (text != null) + this.addComponent(new PlainTextComponent(Text.literal(text))); + } + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java new file mode 100644 index 00000000..bbd97fb5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the private island you're visiting + +public class GuestServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public GuestServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.SIGN, "Owner:", Formatting.GREEN, 43); + this.addSimpleIcoText(Ico.SIGN, "Status:", Formatting.BLUE, 44); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java new file mode 100644 index 00000000..b527dc78 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows a list of all people visiting the same private island as you + +public class IslandGuestsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Guests").formatted(Formatting.AQUA, + Formatting.BOLD); + + // matches a player entry, removing their level and the hand icon + // group 1: player name + private static final Pattern GUEST_PATTERN = Pattern.compile("\\[\\d*\\] (.*) \\[.\\]"); + + public IslandGuestsWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + } + + @Override + public void updateContent() { + for (int i = 21; i < 40; i++) { + String str = PlayerListMgr.strAt(i); + if (str == null) { + if (i == 21) { + this.addComponent(new PlainTextComponent(Text.literal("No Visitors!").formatted(Formatting.GRAY))); + } + break; + } + Matcher m = PlayerListMgr.regexAt( i, GUEST_PATTERN); + if (m == null) { + this.addComponent(new PlainTextComponent(Text.of("???"))); + } else { + this.addComponent(new PlainTextComponent(Text.of(m.group(1)))); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java new file mode 100644 index 00000000..cde1fa38 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java @@ -0,0 +1,66 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows a list of the owners of a home island while guesting + +public class IslandOwnersWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // matches an owner + // group 1: player name + // group 2: last seen, if owner not online + // ^(?<nameA>.*) \((?<lastseen>.*)\)$|^\[\d*\] (?:\[[A-Za-z]+\] )?(?<nameB>[A-Za-z0-9_]*)(?: .*)?$|^(?<nameC>.*)$ + private static final Pattern OWNER_PATTERN = Pattern + .compile("^(?<nameA>.*) \\((?<lastseen>.*)\\)$|^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?(?<nameB>[A-Za-z0-9_]*)(?: .*)?$|^(?<nameC>.*)$"); + + public IslandOwnersWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + + for (int i = 1; i < 20; i++) { + Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN); + if (m == null) { + break; + } + + String name, lastseen; + Formatting format; + if (m.group("nameA") != null) { + name = m.group("nameA"); + lastseen = m.group("lastseen"); + format = Formatting.GRAY; + } else if (m.group("nameB")!=null){ + name = m.group("nameB"); + lastseen = "Online"; + format = Formatting.WHITE; + } else { + name = m.group("nameC"); + lastseen = "Online"; + format = Formatting.WHITE; + } + + Text entry = Text.literal(name) + .append( + Text.literal(" (" + lastseen + ")") + .formatted(format)); + PlainTextComponent ptc = new PlainTextComponent(entry); + this.addComponent(ptc); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java new file mode 100644 index 00000000..31ad66f7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows a list of the owners while on your home island + +public class IslandSelfWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // matches an owner + // group 1: player name, optionally offline time + // ^\[\d*\] (?:\[[A-Za-z]+\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$ + private static final Pattern OWNER_PATTERN = Pattern + .compile("^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$"); + + public IslandSelfWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + for (int i = 1; i < 20; i++) { + Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN); + if (m == null) { + break; + } + + Text entry = (m.group(1) != null) ? Text.of(m.group(1)) : Text.of(m.group(2)); + this.addComponent(new PlainTextComponent(entry)); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java new file mode 100644 index 00000000..53dc11a6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java @@ -0,0 +1,32 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about your home island + +public class IslandServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public IslandServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Crystals:", Formatting.DARK_PURPLE, 43); + this.addSimpleIcoText(Ico.CHEST, "Stash:", Formatting.GREEN, 44); + this.addSimpleIcoText(Ico.COMMAND, "Minions:", Formatting.BLUE, 45); + + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java new file mode 100644 index 00000000..5ae0bd3d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java @@ -0,0 +1,62 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the current jacob's contest (garden only) + +public class JacobsContestWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Jacob's Contest").formatted(Formatting.YELLOW, + Formatting.BOLD); + + private static final HashMap<String, ItemStack> FARM_DATA = new HashMap<>(); + + // again, there HAS to be a better way to do this + static { + FARM_DATA.put("Wheat", new ItemStack(Items.WHEAT)); + FARM_DATA.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); + FARM_DATA.put("Carrot", new ItemStack(Items.CARROT)); + FARM_DATA.put("Potato", new ItemStack(Items.POTATO)); + FARM_DATA.put("Melon", new ItemStack(Items.MELON_SLICE)); + FARM_DATA.put("Pumpkin", new ItemStack(Items.PUMPKIN)); + FARM_DATA.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); + FARM_DATA.put("Nether Wart", new ItemStack(Items.NETHER_WART)); + FARM_DATA.put("Cactus", new ItemStack(Items.CACTUS)); + FARM_DATA.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); + } + + public JacobsContestWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.GOLD, 76); + + TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue()); + + for (int i = 77; i < 80; i++) { + String item = PlayerListMgr.strAt(i); + IcoTextComponent itc; + if (item == null) { + itc = new IcoTextComponent(); + } else { + itc = new IcoTextComponent(FARM_DATA.get(item), Text.of(item)); + } + tc.addToCell(0, i - 77, itc); + } + this.addComponent(tc); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java new file mode 100644 index 00000000..35b9a0c6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java @@ -0,0 +1,151 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about minions placed on the home island + +public class MinionWidget extends Widget { + private static final MutableText TITLE = Text.literal("Minions").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + private static final HashMap<String, ItemStack> MIN_ICOS = new HashMap<>(); + + // hmm... + static { + MIN_ICOS.put("Blaze", new ItemStack(Items.BLAZE_ROD)); + MIN_ICOS.put("Cave Spider", new ItemStack(Items.SPIDER_EYE)); + MIN_ICOS.put("Creeper", new ItemStack(Items.GUNPOWDER)); + MIN_ICOS.put("Enderman", new ItemStack(Items.ENDER_PEARL)); + MIN_ICOS.put("Ghast", new ItemStack(Items.GHAST_TEAR)); + MIN_ICOS.put("Magma Cube", new ItemStack(Items.MAGMA_CREAM)); + MIN_ICOS.put("Skeleton", new ItemStack(Items.BONE)); + MIN_ICOS.put("Slime", new ItemStack(Items.SLIME_BALL)); + MIN_ICOS.put("Spider", new ItemStack(Items.STRING)); + MIN_ICOS.put("Zombie", new ItemStack(Items.ROTTEN_FLESH)); + MIN_ICOS.put("Cactus", new ItemStack(Items.CACTUS)); + MIN_ICOS.put("Carrot", new ItemStack(Items.CARROT)); + MIN_ICOS.put("Chicken", new ItemStack(Items.CHICKEN)); + MIN_ICOS.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); + MIN_ICOS.put("Cow", new ItemStack(Items.BEEF)); + MIN_ICOS.put("Melon", new ItemStack(Items.MELON_SLICE)); + MIN_ICOS.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); + MIN_ICOS.put("Nether Wart", new ItemStack(Items.NETHER_WART)); + MIN_ICOS.put("Pig", new ItemStack(Items.PORKCHOP)); + MIN_ICOS.put("Potato", new ItemStack(Items.POTATO)); + MIN_ICOS.put("Pumpkin", new ItemStack(Items.PUMPKIN)); + MIN_ICOS.put("Rabbit", new ItemStack(Items.RABBIT)); + MIN_ICOS.put("Sheep", new ItemStack(Items.WHITE_WOOL)); + MIN_ICOS.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); + MIN_ICOS.put("Wheat", new ItemStack(Items.WHEAT)); + MIN_ICOS.put("Clay", new ItemStack(Items.CLAY)); + MIN_ICOS.put("Fishing", new ItemStack(Items.FISHING_ROD)); + MIN_ICOS.put("Coal", new ItemStack(Items.COAL)); + MIN_ICOS.put("Cobblestone", new ItemStack(Items.COBBLESTONE)); + MIN_ICOS.put("Diamond", new ItemStack(Items.DIAMOND)); + MIN_ICOS.put("Emerald", new ItemStack(Items.EMERALD)); + MIN_ICOS.put("End Stone", new ItemStack(Items.END_STONE)); + MIN_ICOS.put("Glowstone", new ItemStack(Items.GLOWSTONE_DUST)); + MIN_ICOS.put("Gold", new ItemStack(Items.GOLD_INGOT)); + MIN_ICOS.put("Gravel", new ItemStack(Items.GRAVEL)); + MIN_ICOS.put("Hard Stone", new ItemStack(Items.STONE)); + MIN_ICOS.put("Ice", new ItemStack(Items.ICE)); + MIN_ICOS.put("Iron", new ItemStack(Items.IRON_INGOT)); + MIN_ICOS.put("Lapis", new ItemStack(Items.LAPIS_LAZULI)); + MIN_ICOS.put("Mithril", new ItemStack(Items.PRISMARINE_CRYSTALS)); + MIN_ICOS.put("Mycelium", new ItemStack(Items.MYCELIUM)); + MIN_ICOS.put("Obsidian", new ItemStack(Items.OBSIDIAN)); + MIN_ICOS.put("Quartz", new ItemStack(Items.QUARTZ)); + MIN_ICOS.put("Red Sand", new ItemStack(Items.RED_SAND)); + MIN_ICOS.put("Redstone", new ItemStack(Items.REDSTONE)); + MIN_ICOS.put("Sand", new ItemStack(Items.SAND)); + MIN_ICOS.put("Snow", new ItemStack(Items.SNOWBALL)); + MIN_ICOS.put("Inferno", new ItemStack(Items.BLAZE_SPAWN_EGG)); + MIN_ICOS.put("Revenant", new ItemStack(Items.ZOMBIE_SPAWN_EGG)); + MIN_ICOS.put("Tarantula", new ItemStack(Items.SPIDER_SPAWN_EGG)); + MIN_ICOS.put("Vampire", new ItemStack(Items.REDSTONE)); + MIN_ICOS.put("Voidling", new ItemStack(Items.ENDERMAN_SPAWN_EGG)); + MIN_ICOS.put("Acacia", new ItemStack(Items.ACACIA_LOG)); + MIN_ICOS.put("Birch", new ItemStack(Items.BIRCH_LOG)); + MIN_ICOS.put("Dark Oak", new ItemStack(Items.DARK_OAK_LOG)); + MIN_ICOS.put("Flower", new ItemStack(Items.POPPY)); + MIN_ICOS.put("Jungle", new ItemStack(Items.JUNGLE_LOG)); + MIN_ICOS.put("Oak", new ItemStack(Items.OAK_LOG)); + MIN_ICOS.put("Spruce", new ItemStack(Items.SPRUCE_LOG)); + } + + // matches a minion entry + // group 1: name + // group 2: level + // group 3: status + public static final Pattern MINION_PATTERN = Pattern.compile("(?<name>.*) (?<level>[XVI]*) \\[(?<status>.*)\\]"); + + public MinionWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + + // this looks a bit weird because if we used regex mismatch as a stop condition, + // it'd spam the chat. + // not sure if not having that debug output is worth the cleaner solution here... + + for (int i = 48; i < 59; i++) { + if (!this.addMinionComponent(i)) { + break; + } + } + + // if more minions are placed than the tab menu can display, + // a "And X more..." text is shown + // look for that and add it to the widget + String more = PlayerListMgr.strAt(59); + if (more == null) { + return; + } else if (more.startsWith("And ")) { + this.addComponent(new PlainTextComponent(Text.of(more))); + } else { + this.addMinionComponent(59); + } + } + + public boolean addMinionComponent(int i) { + Matcher m = PlayerListMgr.regexAt(i, MINION_PATTERN); + if (m != null) { + + String min = m.group("name"); + String lvl = m.group("level"); + String stat = m.group("status"); + + MutableText mt = Text.literal(min + " " + lvl).append(Text.literal(": ")); + + Formatting format = Formatting.RED; + if (stat.equals("ACTIVE")) { + format = Formatting.GREEN; + } else if (stat.equals("SLOW")) { + format = Formatting.YELLOW; + } + // makes "BLOCKED" also red. in reality, it's some kind of crimson + mt.append(Text.literal(stat).formatted(format)); + + IcoTextComponent itc = new IcoTextComponent(MIN_ICOS.get(min), mt); + this.addComponent(itc); + return true; + } else { + return false; + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java new file mode 100644 index 00000000..c781a1bc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the park server + +public class ParkServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public ParkServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + this.addSimpleIcoText(Ico.WATER, "Rain:", Formatting.BLUE, 44); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java new file mode 100644 index 00000000..ba178a5e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java @@ -0,0 +1,71 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlayerComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.Comparator; + +// this widget shows a list of players with their skins. +// responsible for non-private-island areas + +public class PlayerListWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Players").formatted(Formatting.GREEN, + Formatting.BOLD); + + public PlayerListWidget() { + super(TITLE, Formatting.GREEN.getColorValue()); + + } + + @Override + public void updateContent() { + ArrayList<PlayerListEntry> list = new ArrayList<>(); + + // hard cap to 4x20 entries. + // 5x20 is too wide (and not possible in theory. in reality however...) + int listlen = Math.min(PlayerListMgr.getSize(), 160); + + // list isn't fully loaded, so our hack won't work... + if (listlen < 80) { + this.addComponent(new PlainTextComponent(Text.literal("List loading...").formatted(Formatting.GRAY))); + return; + } + + // unintuitive int ceil division stolen from + // https://stackoverflow.com/questions/7139382/java-rounding-up-to-an-int-using-math-ceil#21830188 + int tblW = ((listlen - 80) - 1) / 20 + 1; + + TableComponent tc = new TableComponent(tblW, Math.min(listlen - 80, 20), Formatting.GREEN.getColorValue()); + + for (int i = 80; i < listlen; i++) { + list.add(PlayerListMgr.getRaw(i)); + } + + if (SkyblockerConfigManager.get().general.tabHud.nameSorting == SkyblockerConfig.NameSorting.ALPHABETICAL) { + list.sort(Comparator.comparing(o -> o.getProfile().getName().toLowerCase())); + } + + int x = 0, y = 0; + + for (PlayerListEntry ple : list) { + tc.addToCell(x, y, new PlayerComponent(ple)); + y++; + if (y >= 20) { + y = 0; + x++; + } + } + + this.addComponent(tc); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java new file mode 100644 index 00000000..44635fbe --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows how much mithril and gemstone powder you have +// (dwarven mines and crystal hollows) + +public class PowderWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Powders").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public PowderWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MITHRIL, "Mithril:", Formatting.AQUA, 46); + this.addSimpleIcoText(Ico.EMERALD, "Gemstone:", Formatting.DARK_PURPLE, 47); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java new file mode 100644 index 00000000..de2ea0c6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java @@ -0,0 +1,28 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about your profile and bank + +public class ProfileWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.YELLOW, Formatting.BOLD); + + public ProfileWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61); + this.addSimpleIcoText(Ico.BONE, "Pet Sitter:", Formatting.AQUA, 62); + this.addSimpleIcoText(Ico.EMERALD, "Balance:", Formatting.GOLD, 63); + this.addSimpleIcoText(Ico.CLOCK, "Interest in:", Formatting.GOLD, 64); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java new file mode 100644 index 00000000..3c3d3c92 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java @@ -0,0 +1,33 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows your crimson isle faction quests + +public class QuestWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Faction Quests").formatted(Formatting.AQUA, + Formatting.BOLD); + + public QuestWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + + } + + @Override + public void updateContent() { + for (int i = 51; i < 56; i++) { + Text q = PlayerListMgr.textAt(i); + IcoTextComponent itc = new IcoTextComponent(Ico.BOOK, q); + this.addComponent(itc); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java new file mode 100644 index 00000000..3c218fb1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows your faction status (crimson isle) + +public class ReputationWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Faction Status").formatted(Formatting.AQUA, + Formatting.BOLD); + + // matches your faction alignment progress + // group 1: percentage to next alignment level + private static final Pattern PROGRESS_PATTERN = Pattern.compile("\\|+ \\((?<prog>[0-9.]*)%\\)"); + + // matches alignment level names + // group 1: left level name + // group 2: right level name + private static final Pattern STATE_PATTERN = Pattern.compile("(?<from>\\S*) *(?<to>\\S*)"); + + public ReputationWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + } + + @Override + public void updateContent() { + String fracstr = PlayerListMgr.strAt(45); + + int spaceidx; + IcoTextComponent faction; + if (fracstr == null || (spaceidx = fracstr.indexOf(' ')) == -1) { + faction = new IcoTextComponent(); + } else { + String fname = fracstr.substring(0, spaceidx); + if (fname.equals("Mage")) { + faction = new IcoTextComponent(Ico.POTION, Text.literal(fname).formatted(Formatting.DARK_AQUA)); + } else { + faction = new IcoTextComponent(Ico.SWORD, Text.literal(fname).formatted(Formatting.RED)); + } + } + this.addComponent(faction); + + Text rep = Widget.plainEntryText(46); + Matcher prog = PlayerListMgr.regexAt(47, PROGRESS_PATTERN); + Matcher state = PlayerListMgr.regexAt(48, STATE_PATTERN); + + if (prog == null || state == null) { + this.addComponent(new ProgressComponent()); + } else { + float pcnt = Float.parseFloat(prog.group("prog")); + Text reputationText = state.group("from").equals("Max") ? Text.literal("Max Reputation") : Text.literal(state.group("from") + " -> " + state.group("to")); + ProgressComponent pc = new ProgressComponent(Ico.LANTERN, + reputationText, rep, pcnt, + Formatting.AQUA.getColorValue()); + this.addComponent(pc); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java new file mode 100644 index 00000000..475cb038 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about "generic" servers. +// a server is "generic", when only name, server ID and gems are shown +// in the third column of the tab HUD + +public class ServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public ServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java new file mode 100644 index 00000000..379fbb62 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java @@ -0,0 +1,78 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about a skill and some stats, +// as seen in the rightmost column of the default HUD + +public class SkillsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW, + Formatting.BOLD); + + // match the skill entry + // group 1: skill name and level + // group 2: progress to next level (without "%") + private static final Pattern SKILL_PATTERN = Pattern.compile("\\S*: ([A-Za-z]* [0-9]*): ([0-9.MAX]*)%?"); + + public SkillsWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + + } + + @Override + public void updateContent() { + Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN); + Component progress; + if (m == null) { + progress = new ProgressComponent(); + } else { + + String skill = m.group(1); + String pcntStr = m.group(2); + + if (!pcntStr.equals("MAX")) { + float pcnt = Float.parseFloat(pcntStr); + progress = new ProgressComponent(Ico.LANTERN, Text.of(skill), + Text.of(pcntStr + "%"), pcnt, Formatting.GOLD.getColorValue()); + } else { + progress = new IcoFatTextComponent(Ico.LANTERN, Text.of(skill), + Text.literal(pcntStr).formatted(Formatting.RED)); + } + } + + this.addComponent(progress); + + Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE); + IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); + Text strength = Widget.simpleEntryText(68, "STR", Formatting.RED); + IcoTextComponent str = new IcoTextComponent(Ico.SWORD, strength); + Text critDmg = Widget.simpleEntryText(69, "CCH", Formatting.BLUE); + IcoTextComponent cdg = new IcoTextComponent(Ico.SWORD, critDmg); + Text critCh = Widget.simpleEntryText(70, "CDG", Formatting.BLUE); + IcoTextComponent cch = new IcoTextComponent(Ico.SWORD, critCh); + Text aSpeed = Widget.simpleEntryText(71, "ASP", Formatting.YELLOW); + IcoTextComponent asp = new IcoTextComponent(Ico.HOE, aSpeed); + + TableComponent tc = new TableComponent(2, 3, Formatting.YELLOW.getColorValue()); + tc.addToCell(0, 0, spd); + tc.addToCell(0, 1, str); + tc.addToCell(0, 2, asp); + tc.addToCell(1, 0, cdg); + tc.addToCell(1, 1, cch); + this.addComponent(tc); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java new file mode 100644 index 00000000..74bba632 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java @@ -0,0 +1,25 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows how meny pelts you have (farming island) + +public class TrapperWidget extends Widget { + private static final MutableText TITLE = Text.literal("Trapper").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public TrapperWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.LEATHER, "Pelts:", Formatting.AQUA, 46); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java new file mode 100644 index 00000000..a245cbe9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java @@ -0,0 +1,51 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about ongoing profile/account upgrades +// or not, if there aren't any +// TODO: not very pretty atm + +public class UpgradeWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Upgrade Info").formatted(Formatting.GOLD, + Formatting.BOLD); + + public UpgradeWidget() { + super(TITLE, Formatting.GOLD.getColorValue()); + } + + @Override + public void updateContent() { + String footertext = PlayerListMgr.getFooter(); + + if (footertext == null) { + this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY))); + return; + } + + if (!footertext.contains("Upgrades")) { + this.addComponent(new PlainTextComponent(Text.of("Currently no upgrades..."))); + return; + } + + String interesting = footertext.split("Upgrades")[1]; + String[] lines = interesting.split("\n"); + + for (int i = 1; i < lines.length; i++) { + if (lines[i].trim().length() < 3) { // empty line is §s + break; + } + IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, Text.of(lines[i])); + this.addComponent(itc); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java new file mode 100644 index 00000000..8dacfb3a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java @@ -0,0 +1,59 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Pair; + +// shows the volcano status (crimson isle) + +public class VolcanoWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Volcano Status").formatted(Formatting.AQUA, + Formatting.BOLD); + + private static final HashMap<String, Pair<ItemStack, Formatting>> BOOM_TYPE = new HashMap<>(); + + static { + BOOM_TYPE.put("INACTIVE", + new Pair<>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY)); + BOOM_TYPE.put("CHILL", + new Pair<>(new ItemStack(Items.ICE), Formatting.AQUA)); + BOOM_TYPE.put("LOW", + new Pair<>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY)); + BOOM_TYPE.put("DISRUPTIVE", + new Pair<>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE)); + BOOM_TYPE.put("MEDIUM", + new Pair<>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW)); + BOOM_TYPE.put("HIGH", + new Pair<>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD)); + BOOM_TYPE.put("EXPLOSIVE", + new Pair<>(new ItemStack(Items.TNT), Formatting.RED)); + BOOM_TYPE.put("CATACLYSMIC", + new Pair<>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED)); + } + + public VolcanoWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + + } + + @Override + public void updateContent() { + String s = PlayerListMgr.strAt(58); + if (s == null) { + this.addComponent(new IcoTextComponent()); + } else { + Pair<ItemStack, Formatting> p = BOOM_TYPE.get(s); + this.addComponent(new IcoTextComponent(p.getLeft(), Text.literal(s).formatted(p.getRight()))); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java new file mode 100644 index 00000000..5f0d2c3c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java @@ -0,0 +1,216 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.ArrayList; + +import com.mojang.blaze3d.systems.RenderSystem; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Abstract base class for a Widget. + * Widgets are containers for components with a border and a title. + * Their size is dependent on the components inside, + * the position may be changed after construction. + */ +public abstract class Widget { + + private final ArrayList<Component> components = new ArrayList<>(); + private int w = 0, h = 0; + private int x = 0, y = 0; + private final int color; + private final Text title; + + private static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer; + + static final int BORDER_SZE_N = txtRend.fontHeight + 4; + static final int BORDER_SZE_S = 4; + static final int BORDER_SZE_W = 4; + static final int BORDER_SZE_E = 4; + static final int COL_BG_BOX = 0xc00c0c0c; + + public Widget(MutableText title, Integer colorValue) { + this.title = title; + this.color = 0xff000000 | colorValue; + } + + public final void addComponent(Component c) { + this.components.add(c); + } + + public final void update() { + this.components.clear(); + this.updateContent(); + this.pack(); + } + + public abstract void updateContent(); + + /** + * Shorthand function for simple components. + * If the entry at idx has the format "<textA>: <textB>", an IcoTextComponent is + * added as such: + * [ico] [string] [textB.formatted(fmt)] + */ + public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, int idx) { + Text txt = Widget.simpleEntryText(idx, string, fmt); + this.addComponent(new IcoTextComponent(ico, txt)); + } + + /** + * Calculate the size of this widget. + * <b>Must be called before returning from the widget constructor and after all + * components are added!</b> + */ + private void pack() { + h = 0; + w = 0; + for (Component c : components) { + h += c.getHeight() + Component.PAD_L; + w = Math.max(w, c.getWidth() + Component.PAD_S); + } + + h -= Component.PAD_L / 2; // less padding after lowest/last component + h += BORDER_SZE_N + BORDER_SZE_S - 2; + w += BORDER_SZE_E + BORDER_SZE_W; + + // min width is dependent on title + w = Math.max(w, BORDER_SZE_W + BORDER_SZE_E + Widget.txtRend.getWidth(title) + 4 + 4 + 1); + } + + public final void setX(int x) { + this.x = x; + } + + public final int getY() { + return this.y; + } + + public final int getX() { + return this.x; + } + + public final void setY(int y) { + this.y = y; + } + + public final int getWidth() { + return this.w; + } + + public final int getHeight() { + return this.h; + } + + /** + * Draw this widget with a background + */ + public final void render(DrawContext context) { + this.render(context, true); + } + + /** + * Draw this widget, possibly with a background + */ + public final void render(DrawContext context, boolean hasBG) { + MatrixStack ms = context.getMatrices(); + + // not sure if this is the way to go, but it fixes Z-layer issues + // like blocks being rendered behind the BG and the hotbar clipping into things + RenderSystem.enableDepthTest(); + ms.push(); + + float scale = SkyblockerConfigManager.get().general.tabHud.tabHudScale / 100f; + ms.scale(scale, scale, 1); + + // move above other UI elements + ms.translate(0, 0, 200); + if (hasBG) { + context.fill(x + 1, y, x + w - 1, y + h, COL_BG_BOX); + context.fill(x, y + 1, x + 1, y + h - 1, COL_BG_BOX); + context.fill(x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX); + } + // move above background (if exists) + ms.translate(0, 0, 100); + + int strHeightHalf = Widget.txtRend.fontHeight / 2; + int strAreaWidth = Widget.txtRend.getWidth(title) + 4; + + context.drawText(txtRend, title, x + 8, y + 2, this.color, false); + + this.drawHLine(context, x + 2, y + 1 + strHeightHalf, 4); + this.drawHLine(context, x + 2 + strAreaWidth + 4, y + 1 + strHeightHalf, w - 4 - 4 - strAreaWidth); + this.drawHLine(context, x + 2, y + h - 2, w - 4); + + this.drawVLine(context, x + 1, y + 2 + strHeightHalf, h - 4 - strHeightHalf); + this.drawVLine(context, x + w - 2, y + 2 + strHeightHalf, h - 4 - strHeightHalf); + + int yOffs = y + BORDER_SZE_N; + + for (Component c : components) { + c.render(context, x + BORDER_SZE_W, yOffs); + yOffs += c.getHeight() + Component.PAD_L; + } + // pop manipulations above + ms.pop(); + RenderSystem.disableDepthTest(); + } + + private void drawHLine(DrawContext context, int xpos, int ypos, int width) { + context.fill(xpos, ypos, xpos + width, ypos + 1, this.color); + } + + private void drawVLine(DrawContext context, int xpos, int ypos, int height) { + context.fill(xpos, ypos, xpos + 1, ypos + height, this.color); + } + + /** + * If the entry at idx has the format "[textA]: [textB]", the following is + * returned: + * [entryName] [textB.formatted(contentFmt)] + */ + public static Text simpleEntryText(int idx, String entryName, Formatting contentFmt) { + + String src = PlayerListMgr.strAt(idx); + + if (src == null) { + return null; + } + + int cidx = src.indexOf(':'); + if (cidx == -1) { + return null; + } + + src = src.substring(src.indexOf(':') + 1); + return Widget.simpleEntryText(src, entryName, contentFmt); + } + + /** + * @return [entryName] [entryContent.formatted(contentFmt)] + */ + public static Text simpleEntryText(String entryContent, String entryName, Formatting contentFmt) { + return Text.literal(entryName).append(Text.literal(entryContent).formatted(contentFmt)); + } + + /** + * @return the entry at idx as unformatted Text + */ + public static Text plainEntryText(int idx) { + String str = PlayerListMgr.strAt(idx); + if (str == null) { + return null; + } + return Text.of(str); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java new file mode 100644 index 00000000..3c987068 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java @@ -0,0 +1,31 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; + +/** + * Abstract base class for a component that may be added to a Widget. + */ +public abstract class Component { + + static final int ICO_DIM = 16; + public static final int PAD_S = 2; + public static final int PAD_L = 4; + + static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer; + + // these should always be the content dimensions without any padding. + int width, height; + + public abstract void render(DrawContext context, int x, int y); + + public int getWidth() { + return this.width; + } + + public int getHeight() { + return this.height; + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java new file mode 100644 index 00000000..def60d4d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java @@ -0,0 +1,45 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Component that consists of an icon and two lines of text + */ +public class IcoFatTextComponent extends Component { + + private static final int ICO_OFFS = 1; + + private ItemStack ico; + private Text line1, line2; + + public IcoFatTextComponent(ItemStack ico, Text l1, Text l2) { + this.ico = (ico == null) ? Ico.BARRIER : ico; + this.line1 = l1; + this.line2 = l2; + + if (l1 == null || l2 == null) { + this.ico = Ico.BARRIER; + this.line1 = Text.literal("No data").formatted(Formatting.GRAY); + this.line2 = Text.literal("No data").formatted(Formatting.GRAY); + } + + this.width = ICO_DIM + PAD_L + Math.max(txtRend.getWidth(this.line1), txtRend.getWidth(this.line2)); + this.height = txtRend.fontHeight + PAD_S + txtRend.fontHeight; + } + + public IcoFatTextComponent() { + this(null, null, null); + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawItem(ico, x, y + ICO_OFFS); + context.drawText(txtRend, line1, x + ICO_DIM + PAD_L, y, 0xffffffff, false); + context.drawText(txtRend, line2, x + ICO_DIM + PAD_L, y + txtRend.fontHeight + PAD_S, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java new file mode 100644 index 00000000..903a1fa3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Component that consists of an icon and a line of text. + */ +public class IcoTextComponent extends Component { + + private ItemStack ico; + private Text text; + + public IcoTextComponent(ItemStack ico, Text txt) { + this.ico = (ico == null) ? Ico.BARRIER : ico; + this.text = txt; + + if (txt == null) { + this.ico = Ico.BARRIER; + this.text = Text.literal("No data").formatted(Formatting.GRAY); + } + + this.width = ICO_DIM + PAD_L + txtRend.getWidth(this.text); + this.height = ICO_DIM; + } + + public IcoTextComponent() { + this(null, null); + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawItem(ico, x, y); + context.drawText(txtRend, text, x + ICO_DIM + PAD_L, y + 5, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java new file mode 100644 index 00000000..59e82e4e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Component that consists of a line of text. + */ +public class PlainTextComponent extends Component { + + private Text text; + + public PlainTextComponent(Text txt) { + this.text = txt; + + if (txt == null) { + this.text = Text.literal("No data").formatted(Formatting.GRAY); + } + + this.width = PAD_S + txtRend.getWidth(this.text); // looks off without padding + this.height = txtRend.fontHeight; + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawText(txtRend, text, x + PAD_S, y, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java new file mode 100644 index 00000000..ab79bb31 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.PlayerSkinDrawer; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.scoreboard.Team; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +/** + * Component that consists of a player's skin icon and their name + */ +public class PlayerComponent extends Component { + + private static final int SKIN_ICO_DIM = 8; + + private final Text name; + private final Identifier tex; + + public PlayerComponent(PlayerListEntry ple) { + + boolean plainNames = SkyblockerConfigManager.get().general.tabHud.plainPlayerNames; + Team team = ple.getScoreboardTeam(); + String username = ple.getProfile().getName(); + name = (team != null && !plainNames) ? Text.empty().append(team.getPrefix()).append(Text.literal(username).formatted(team.getColor())).append(team.getSuffix()) : Text.of(username); + tex = ple.getSkinTextures().texture(); + + this.width = SKIN_ICO_DIM + PAD_S + txtRend.getWidth(name); + this.height = txtRend.fontHeight; + } + + @Override + public void render(DrawContext context, int x, int y) { + PlayerSkinDrawer.draw(context, tex, x, y, SKIN_ICO_DIM); + context.drawText(txtRend, name, x + SKIN_ICO_DIM + PAD_S, y, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java new file mode 100644 index 00000000..fa839dbe --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + + +/** + * Component that consists of an icon, some text and a progress bar. + * The progress bar either shows the fill percentage or custom text. + * NOTICE: pcnt is 0-100, not 0-1! + */ +public class ProgressComponent extends Component { + + private static final int BAR_WIDTH = 100; + private static final int BAR_HEIGHT = txtRend.fontHeight + 3; + private static final int ICO_OFFS = 4; + private static final int COL_BG_BAR = 0xf0101010; + + private final ItemStack ico; + private final Text desc, bar; + private final float pcnt; + private final int color; + private final int barW; + + public ProgressComponent(ItemStack ico, Text d, Text b, float pcnt, int color) { + if (d == null || b == null) { + this.ico = Ico.BARRIER; + this.desc = Text.literal("No data").formatted(Formatting.GRAY); + this.bar = Text.literal("---").formatted(Formatting.GRAY); + this.pcnt = 100f; + this.color = 0xff000000 | Formatting.DARK_GRAY.getColorValue(); + } else { + this.ico = (ico == null) ? Ico.BARRIER : ico; + this.desc = d; + this.bar = b; + this.pcnt = pcnt; + this.color = 0xff000000 | color; + } + + this.barW = BAR_WIDTH; + this.width = ICO_DIM + PAD_L + Math.max(this.barW, txtRend.getWidth(this.desc)); + this.height = txtRend.fontHeight + PAD_S + 2 + txtRend.fontHeight + 2; + } + + public ProgressComponent(ItemStack ico, Text text, float pcnt, int color) { + this(ico, text, Text.of(pcnt + "%"), pcnt, color); + } + + public ProgressComponent() { + this(null, null, null, 100, 0); + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawItem(ico, x, y + ICO_OFFS); + context.drawText(txtRend, desc, x + ICO_DIM + PAD_L, y, 0xffffffff, false); + + int barX = x + ICO_DIM + PAD_L; + int barY = y + txtRend.fontHeight + PAD_S; + int endOffsX = ((int) (this.barW * (this.pcnt / 100f))); + context.fill(barX + endOffsX, barY, barX + this.barW, barY + BAR_HEIGHT, COL_BG_BAR); + context.fill(barX, barY, barX + endOffsX, barY + BAR_HEIGHT, + this.color); + context.drawTextWithShadow(txtRend, bar, barX + 3, barY + 2, 0xffffffff); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java new file mode 100644 index 00000000..dbc0bf55 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java @@ -0,0 +1,58 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import net.minecraft.client.gui.DrawContext; + +/** + * Meta-Component that consists of a grid of other components + * Grid cols are separated by lines. + */ +public class TableComponent extends Component { + + private final Component[][] comps; + private final int color; + private final int cols, rows; + private int cellW, cellH; + + public TableComponent(int w, int h, int col) { + comps = new Component[w][h]; + color = 0xff000000 | col; + cols = w; + rows = h; + } + + public void addToCell(int x, int y, Component c) { + this.comps[x][y] = c; + + // pad extra to add a vertical line later + this.cellW = Math.max(this.cellW, c.width + PAD_S + PAD_L); + + // assume all rows are equally high so overwriting doesn't matter + // if this wasn't the case, drawing would need more math + // not doing any of that if it's not needed + this.cellH = c.height + PAD_S; + + this.width = this.cellW * this.cols; + this.height = (this.cellH * this.rows) - PAD_S / 2; + + } + + @Override + public void render(DrawContext context, int xpos, int ypos) { + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + if (comps[x][y] != null) { + comps[x][y].render(context, xpos + (x * cellW), ypos + y * cellH); + } + } + // add a line before the col if we're not drawing the first one + if (x != 0) { + int lineX1 = xpos + (x * cellW) - PAD_S - 1; + int lineX2 = xpos + (x * cellW) - PAD_S; + int lineY1 = ypos + 1; + int lineY2 = ypos + this.height - PAD_S - 1; // not sure why but it looks correct + context.fill(lineX1, lineY1, lineX2, lineY2, this.color); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java new file mode 100644 index 00000000..6aa363c9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java @@ -0,0 +1,73 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.hud; + +import java.util.List; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud.Commission; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +// this widget shows the status of the king's commissions. +// (dwarven mines and crystal hollows) +// USE ONLY WITH THE DWARVEN HUD! + +public class HudCommsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + private List<Commission> commissions; + private boolean isFancy; + + // disgusting hack to get around text renderer issues. + // the ctor eventually tries to get the font's height, which doesn't work + // when called before the client window is created (roughly). + // the rebdering god 2 from the fabricord explained that detail, thanks! + public static final HudCommsWidget INSTANCE = new HudCommsWidget(); + public static final HudCommsWidget INSTANCE_CFG = new HudCommsWidget(); + + // another repulsive hack to make this widget-like hud element work with the new widget class + // DON'T USE WITH THE WIDGET SYSTEM, ONLY USE FOR DWARVENHUD! + public HudCommsWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + public void updateData(List<Commission> commissions, boolean isFancy) { + this.commissions = commissions; + this.isFancy = isFancy; + } + + @Override + public void updateContent() { + for (Commission comm : commissions) { + + Text c = Text.literal(comm.commission()); + + float p = 100f; + if (!comm.progression().contains("DONE")) { + p = Float.parseFloat(comm.progression().substring(0, comm.progression().length() - 1)); + } + + Component comp; + if (isFancy) { + comp = new ProgressComponent(Ico.BOOK, c, p, pcntToCol(p)); + } else { + comp = new PlainTextComponent( + Text.literal(comm.commission() + ": ") + .append(Text.literal(comm.progression()).formatted(Formatting.GREEN))); + } + this.addComponent(comp); + } + } + + private int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java new file mode 100644 index 00000000..3499ce39 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class AdvertisementWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Advertisement").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public AdvertisementWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + boolean added = false; + for (int i = 73; i < 80; i++) { + Text text = PlayerListMgr.textAt(i); + if (text != null) { + this.addComponent(new PlainTextComponent(text)); + added = true; + } + } + + if (!added) { + this.addComponent(new PlainTextComponent(Text.literal("No Advertisements").formatted(Formatting.GRAY))); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java new file mode 100644 index 00000000..3a5da142 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class GoodToKnowWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Good To Know").formatted(Formatting.BLUE, Formatting.BOLD); + + public GoodToKnowWidget() { + super(TITLE, Formatting.BLUE.getColorValue()); + } + + @Override + public void updateContent() { + // After you progress further the tab adds more info so we need to be careful of + // that + // In beginning it only shows montezuma, then timecharms and enigma souls are + // added + + int headerPos = 0; + // this seems suboptimal, but I'm not sure if there's a way to do it better. + // search for the GTK header and offset the rest accordingly. + for (int i = 45; i <= 49; i++) { + String str = PlayerListMgr.strAt(i); + if (str != null && str.startsWith("Good to")) { + headerPos = i; + break; + } + } + + Text posA = PlayerListMgr.textAt(headerPos + 2); // Can be times visited rift + Text posB = PlayerListMgr.textAt(headerPos + 4); // Can be lifetime motes or visited rift + Text posC = PlayerListMgr.textAt(headerPos + 6); // Can be lifetime motes + + int visitedRiftPos = 0; + int lifetimeMotesPos = 0; + + // Check each position to see what is or isn't there so we don't try adding + // invalid components + if (posA != null && posA.getString().contains("times")) + visitedRiftPos = headerPos + 2; + if (posB != null && posB.getString().contains("Motes")) + lifetimeMotesPos = headerPos + 4; + if (posB != null && posB.getString().contains("times")) + visitedRiftPos = headerPos + 4; + if (posC != null && posC.getString().contains("Motes")) + lifetimeMotesPos = headerPos + 6; + + Text timesVisitedRift = (visitedRiftPos == headerPos + 4) ? posB : (visitedRiftPos == headerPos + 2) ? posA : Text.literal("No Data").formatted(Formatting.GRAY); + Text lifetimeMotesEarned = (lifetimeMotesPos == headerPos + 6) ? posC : (lifetimeMotesPos == headerPos + 4) ? posB : Text.literal("No Data").formatted(Formatting.GRAY); + + if (visitedRiftPos != 0) { + this.addComponent(new IcoTextComponent(Ico.EXPERIENCE_BOTTLE, + Text.literal("Visited Rift: ").append(timesVisitedRift))); + } + + if (lifetimeMotesPos != 0) { + this.addComponent( + new IcoTextComponent(Ico.PINK_DYE, Text.literal("Lifetime Earned: ").append(lifetimeMotesEarned))); + } + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java new file mode 100644 index 00000000..178bf142 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class RiftProfileWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + public RiftProfileWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java new file mode 100644 index 00000000..93ade5cb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java @@ -0,0 +1,123 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +public class RiftProgressWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Rift Progress").formatted(Formatting.BLUE, Formatting.BOLD); + + private static final Pattern TIMECHARMS_PATTERN = Pattern.compile("Timecharms: (?<current>[0-9]+)\\/(?<total>[0-9]+)"); + private static final Pattern ENIGMA_SOULS_PATTERN = Pattern.compile("Enigma Souls: (?<current>[0-9]+)\\/(?<total>[0-9]+)"); + private static final Pattern MONTEZUMA_PATTERN = Pattern.compile("Montezuma: (?<current>[0-9]+)\\/(?<total>[0-9]+)"); + + public RiftProgressWidget() { + super(TITLE, Formatting.BLUE.getColorValue()); + } + + @Override + public void updateContent() { + // After you progress further, the tab adds more info so we need to be careful + // of that. + // In beginning it only shows montezuma, then timecharms and enigma souls are + // added. + + String pos44 = PlayerListMgr.strAt(44); + + // LHS short-circuits, so the RHS won't be evaluated on pos44 == null + if (pos44 == null || !pos44.contains("Rift Progress")) { + this.addComponent(new PlainTextComponent(Text.literal("No Progress").formatted(Formatting.GRAY))); + return; + } + + // let's try to be clever by assuming what progress item may appear where and + // when to skip testing every slot for every thing. + + // always non-null, as this holds the topmost item. + // if there is none, there shouldn't be a header. + String pos45 = PlayerListMgr.strAt(45); + + // Can be Montezuma, Enigma Souls or Timecharms. + // assume timecharms can only appear here and that they're the last thing to + // appear, so if this exists, we know the rest. + if (pos45.contains("Timecharms")) { + addTimecharmsComponent(45); + addEnigmaSoulsComponent(46); + addMontezumaComponent(47); + return; + } + + // timecharms didn't appear at the top, so there's two or one entries. + // assume that if there's two, souls is always top. + String pos46 = PlayerListMgr.strAt(46); + + if (pos45.contains("Enigma Souls")) { + addEnigmaSoulsComponent(45); + if (pos46 != null) { + // souls might appear alone. + // if there's a second entry, it has to be montezuma + addMontezumaComponent(46); + } + } else { + // first entry isn't souls, so it's just montezuma and nothing else. + addMontezumaComponent(45); + } + + } + + private static int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); + } + + private void addTimecharmsComponent(int pos) { + Matcher m = PlayerListMgr.regexAt(pos, TIMECHARMS_PATTERN); + + int current = Integer.parseInt(m.group("current")); + int total = Integer.parseInt(m.group("total")); + float pcnt = ((float) current / (float) total) * 100f; + Text progressText = Text.literal(current + "/" + total); + + ProgressComponent pc = new ProgressComponent(Ico.NETHER_STAR, Text.literal("Timecharms"), progressText, + pcnt, pcntToCol(pcnt)); + + this.addComponent(pc); + } + + private void addEnigmaSoulsComponent(int pos) { + Matcher m = PlayerListMgr.regexAt(pos, ENIGMA_SOULS_PATTERN); + + int current = Integer.parseInt(m.group("current")); + int total = Integer.parseInt(m.group("total")); + float pcnt = ((float) current / (float) total) * 100f; + Text progressText = Text.literal(current + "/" + total); + + ProgressComponent pc = new ProgressComponent(Ico.HEART_OF_THE_SEA, Text.literal("Enigma Souls"), + progressText, pcnt, pcntToCol(pcnt)); + + this.addComponent(pc); + } + + private void addMontezumaComponent(int pos) { + Matcher m = PlayerListMgr.regexAt(pos, MONTEZUMA_PATTERN); + + int current = Integer.parseInt(m.group("current")); + int total = Integer.parseInt(m.group("total")); + float pcnt = ((float) current / (float) total) * 100f; + Text progressText = Text.literal(current + "/" + total); + + ProgressComponent pc = new ProgressComponent(Ico.BONE, Text.literal("Montezuma"), progressText, pcnt, + pcntToCol(pcnt)); + + this.addComponent(pc); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java new file mode 100644 index 00000000..89530e2f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Special version of the server info widget for the rift! + * + */ +public class RiftServerInfoWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD); + + public RiftServerInfoWidget() { + super(TITLE, Formatting.LIGHT_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.LIGHT_PURPLE, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java new file mode 100644 index 00000000..0e2f323d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class RiftStatsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Stats").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + public RiftStatsWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + Text riftDamage = Widget.simpleEntryText(64, "RDG", Formatting.DARK_PURPLE); + IcoTextComponent rdg = new IcoTextComponent(Ico.DIASWORD, riftDamage); + + Text speed = Widget.simpleEntryText(65, "SPD", Formatting.WHITE); + IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); + + Text intelligence = Widget.simpleEntryText(66, "INT", Formatting.AQUA); + IcoTextComponent intel = new IcoTextComponent(Ico.ENCHANTED_BOOK, intelligence); + + Text manaRegen = Widget.simpleEntryText(67, "MRG", Formatting.AQUA); + IcoTextComponent mrg = new IcoTextComponent(Ico.DIAMOND, manaRegen); + + TableComponent tc = new TableComponent(2, 2, Formatting.AQUA.getColorValue()); + tc.addToCell(0, 0, rdg); + tc.addToCell(0, 1, spd); + tc.addToCell(1, 0, intel); + tc.addToCell(1, 1, mrg); + + this.addComponent(tc); + } + +}
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java new file mode 100644 index 00000000..2827400e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class ShenWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Shen's Countdown").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + public ShenWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addComponent(new PlainTextComponent(Text.literal(PlayerListMgr.strAt(70)))); + } +} |