aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmpa <42304516+ItsEmpa@users.noreply.github.com>2024-06-09 19:41:41 +0200
committerGitHub <noreply@github.com>2024-06-09 19:41:41 +0200
commitcc629382945460d48fc9fa6472106df9fcbb589d (patch)
tree64caa8a0cee2d321f700b7d97fb13754319f82d4
parentfc8e81a9f88b01ec63d8fa1d7f0d6ebbdc51d836 (diff)
downloadskyhanni-cc629382945460d48fc9fa6472106df9fcbb589d.tar.gz
skyhanni-cc629382945460d48fc9fa6472106df9fcbb589d.tar.bz2
skyhanni-cc629382945460d48fc9fa6472106df9fcbb589d.zip
Feature: Custom Wardrobe (#2039)
Co-authored-by: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Co-authored-by: Cal <cwolfson58@gmail.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java27
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/ColorConfig.java44
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/CustomWardrobeConfig.java54
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/SpacingConfig.java145
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java15
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt595
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/EstimatedWardrobePrice.kt41
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeAPI.kt207
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeSlot.kt42
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedWardrobePrice.kt74
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/AccessorGuiContainer.java6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt18
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt24
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt17
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt115
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt9
20 files changed, 1348 insertions, 109 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java
index 05f701059..f20643cc3 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java
@@ -3,6 +3,7 @@ package at.hannibal2.skyhanni.config.features.inventory;
import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.HasLegacyId;
import at.hannibal2.skyhanni.config.features.inventory.chocolatefactory.ChocolateFactoryConfig;
+import at.hannibal2.skyhanni.config.features.inventory.customwardrobe.CustomWardrobeConfig;
import at.hannibal2.skyhanni.config.features.inventory.helper.HelperConfig;
import at.hannibal2.skyhanni.config.features.itemability.ItemAbilityConfig;
import at.hannibal2.skyhanni.config.features.misc.EstimatedItemValueConfig;
@@ -50,6 +51,10 @@ public class InventoryConfig {
public ItemAbilityConfig itemAbilities = new ItemAbilityConfig();
@Expose
+ @Category(name = "Custom Wardrobe", desc = "New Wardrobe Look.")
+ public CustomWardrobeConfig customWardrobe = new CustomWardrobeConfig();
+
+ @Expose
@Category(name = "Chocolate Factory", desc = "Features to help you master the Chocolate Factory idle game.")
public ChocolateFactoryConfig chocolateFactory = new ChocolateFactoryConfig();
@@ -93,16 +98,13 @@ public class InventoryConfig {
@Accordion
public GetFromSackConfig gfs = new GetFromSackConfig();
+ @Expose
@ConfigOption(name = "Pocket Sack-In-A-Sack", desc = "")
@Accordion
- @Expose
public PocketSackInASackConfig pocketSackInASack = new PocketSackInASackConfig();
@Expose
- @ConfigOption(
- name = "Item Number",
- desc = "Showing the item number as a stack size for these items."
- )
+ @ConfigOption(name = "Item Number", desc = "Showing the item number as a stack size for these items.")
@ConfigEditorDraggableList
public List<ItemNumberEntry> itemNumberAsStackSize = new ArrayList<>(Arrays.asList(
NEW_YEAR_CAKE,
@@ -168,10 +170,10 @@ public class InventoryConfig {
public boolean vacuumBagCap = true;
@Expose
- @ConfigOption(
- name = "Quick Craft Confirmation",
+ @ConfigOption(name = "Quick Craft Confirmation",
desc = "Require Ctrl+Click to craft items that aren't often quick crafted " +
- "(e.g. armor, weapons, accessories). Sack items can be crafted normally."
+ "(e.g. armor, weapons, accessories). " +
+ "Sack items can be crafted normally."
)
@ConfigEditorBoolean
@FeatureToggle
@@ -190,23 +192,20 @@ public class InventoryConfig {
public boolean anvilCombineHelper = false;
@Expose
- @ConfigOption(name = "Item Stars",
- desc = "Show a compact star count in the item name for all items.")
+ @ConfigOption(name = "Item Stars", desc = "Show a compact star count in the item name for all items.")
@ConfigEditorBoolean
@FeatureToggle
public boolean itemStars = false;
@Expose
- @ConfigOption(name = "Missing Tasks",
- desc = "Highlight missing tasks in the SkyBlock Level Guide inventory.")
+ @ConfigOption(name = "Missing Tasks", desc = "Highlight missing tasks in the SkyBlock Level Guide inventory.")
// TODO move( , "inventory.highlightMissingSkyBlockLevelGuide", "inventory.skyblockGuideConfig.highlightMissingSkyBlockLevelGuide")
@ConfigEditorBoolean
@FeatureToggle
public boolean highlightMissingSkyBlockLevelGuide = true;
@Expose
- @ConfigOption(name = "Power Stone Guide",
- desc = "Highlight missing power stones, show their total bazaar price, and allows to open the bazaar when clicking on the items in the Power Stone Guide.")
+ @ConfigOption(name = "Power Stone Guide", desc = "Highlight missing power stones, show their total bazaar price, and allows to open the bazaar when clicking on the items in the Power Stone Guide.")
// TODO move( , "inventory.powerStoneGuide", "inventory.skyblockGuideConfig.powerStoneGuide")
@ConfigEditorBoolean
@FeatureToggle
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/ColorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/ColorConfig.java
new file mode 100644
index 000000000..20f928254
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/ColorConfig.java
@@ -0,0 +1,44 @@
+package at.hannibal2.skyhanni.config.features.inventory.customwardrobe;
+
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class ColorConfig {
+
+ @Expose
+ @ConfigOption(name = "Background", desc = "Color of the GUI background.")
+ @ConfigEditorColour
+ public String backgroundColor = "0:127:0:0:0";
+
+ @Expose
+ @ConfigOption(name = "Equipped", desc = "Color of the currently equipped wardrobe slot.")
+ @ConfigEditorColour
+ public String equippedColor = "0:127:85:255:85";
+
+ @Expose
+ @ConfigOption(name = "Favorite", desc = "Color of the wardrobe slots that have been added as favorites.")
+ @ConfigEditorColour
+ public String favoriteColor = "0:127:255:85:85";
+
+ @Expose
+ @ConfigOption(name = "Same Page", desc = "Color of wardrobe slots in the same page.")
+ @ConfigEditorColour
+ public String samePageColor = "0:127:94:108:255";
+
+ @Expose
+ @ConfigOption(name = "Other Page", desc = "Color of wardrobe slots in another page.")
+ @ConfigEditorColour
+ public String otherPageColor = "0:127:0:0:0";
+
+ @Expose
+ @ConfigOption(name = "Top Outline", desc = "Color of the top of the outline when hovered.")
+ @ConfigEditorColour
+ public String topBorderColor = "0:255:255:200:0";
+
+ @Expose
+ @ConfigOption(name = "Bottom Outline", desc = "Color of the bottom of the outline when hovered.")
+ @ConfigEditorColour
+ public String bottomBorderColor = "0:255:255:0:0";
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/CustomWardrobeConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/CustomWardrobeConfig.java
new file mode 100644
index 000000000..a4ea69bad
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/CustomWardrobeConfig.java
@@ -0,0 +1,54 @@
+package at.hannibal2.skyhanni.config.features.inventory.customwardrobe;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.Accordion;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class CustomWardrobeConfig {
+
+ @Expose
+ @ConfigOption(name = "Enable", desc = "Enables the Custom Wardrobe GUI.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Follow mouse", desc = "Players follow the movement of the mouse.")
+ @ConfigEditorBoolean
+ public boolean eyesFollowMouse = true;
+
+ @Expose
+ @ConfigOption(name = "Hide Empty Slots", desc = "Hides wardrobe slots with no armor.")
+ @ConfigEditorBoolean
+ public boolean hideEmptySlots = false;
+
+ @Expose
+ @ConfigOption(name = "Hide Locked Slots", desc = "Hides locked wardrobe slots.")
+ @ConfigEditorBoolean
+ public boolean hideLockedSlots = false;
+
+ @Expose
+ public boolean onlyFavorites = false;
+
+ @Expose
+ @ConfigOption(name = "Estimated Value", desc = "Show a §2$ §7sign you can hover to see the wardrobe slot value.")
+ @ConfigEditorBoolean
+ public boolean estimatedValue = true;
+
+ @Expose
+ @ConfigOption(name = "Loading text", desc = "Shows a \"§cLoading...\" §7text when the wardrobe page hasn't fully loaded in yet.")
+ @ConfigEditorBoolean
+ public boolean loadingText = true;
+
+ @Expose
+ @ConfigOption(name = "Colors", desc = "Change the color settings.")
+ @Accordion
+ public ColorConfig color = new ColorConfig();
+
+ @Expose
+ @ConfigOption(name = "Spacing", desc = "")
+ @Accordion
+ public SpacingConfig spacing = new SpacingConfig();
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/SpacingConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/SpacingConfig.java
new file mode 100644
index 000000000..363a9a6a7
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/customwardrobe/SpacingConfig.java
@@ -0,0 +1,145 @@
+package at.hannibal2.skyhanni.config.features.inventory.customwardrobe;
+
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+import io.github.notenoughupdates.moulconfig.observer.Property;
+
+public class SpacingConfig {
+
+ @Expose
+ @ConfigOption(name = "Global Scale", desc = "Controls the scale of the entirety of the wardrobe.")
+ @ConfigEditorSlider(
+ minValue = 30,
+ maxValue = 200,
+ minStep = 1
+ )
+ public Property<Integer> globalScale = Property.of(100);
+
+ @Expose
+ @ConfigOption(name = "Outline Thickness", desc = "How thick the outline of the hovered slot is.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 15,
+ minStep = 1
+ )
+ public Property<Integer> outlineThickness = Property.of(5);
+
+ @Expose
+ @ConfigOption(name = "Outline Blur", desc = "Amount of blur of the outline.")
+ @ConfigEditorSlider(
+ minValue = 0f,
+ maxValue = 1f,
+ minStep = 0.1f
+ )
+ public Property<Float> outlineBlur = Property.of(0.5f);
+
+ @Expose
+ @ConfigOption(name = "Slot Width", desc = "Width of the wardrobe slots.")
+ @ConfigEditorSlider(
+ minValue = 30,
+ maxValue = 100,
+ minStep = 1
+ )
+ public Property<Integer> slotWidth = Property.of(75);
+
+ @Expose
+ @ConfigOption(name = "Slot Height", desc = "Height of the wardrobe slots.")
+ @ConfigEditorSlider(
+ minValue = 60,
+ maxValue = 200,
+ minStep = 1
+ )
+ public Property<Integer> slotHeight = Property.of(140);
+
+ @Expose
+ @ConfigOption(name = "Player Scale", desc = "Scale of the players.")
+ @ConfigEditorSlider(
+ minValue = 0,
+ maxValue = 100,
+ minStep = 1
+ )
+ public Property<Integer> playerScale = Property.of(75);
+
+ @Expose
+ @ConfigOption(name = "Slots per Row", desc = "Max amount of wardrobe slots per row.")
+ @ConfigEditorSlider(
+ minValue = 5,
+ maxValue = 18,
+ minStep = 1
+ )
+ public Property<Integer> maxPlayersPerRow = Property.of(9);
+
+ @Expose
+ @ConfigOption(name = "Slots Horizontal Spacing", desc = "How much space horizontally between wardrobe slots.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 20,
+ minStep = 1
+ )
+ public Property<Integer> horizontalSpacing = Property.of(3);
+
+ @Expose
+ @ConfigOption(name = "Slots Vertical Spacing", desc = "How much space vertically between wardrobe slots.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 20,
+ minStep = 1
+ )
+ public Property<Integer> verticalSpacing = Property.of(3);
+
+ @Expose
+ @ConfigOption(name = "Slots & Buttons Spacing", desc = "How much vertical space there is between wardrobe slots and the buttons.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 40,
+ minStep = 1
+ )
+ public Property<Integer> buttonSlotsVerticalSpacing = Property.of(10);
+
+ @Expose
+ @ConfigOption(name = "Button Horizontal Spacing", desc = "How much space horizontally between buttons.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 40,
+ minStep = 1
+ )
+ public Property<Integer> buttonHorizontalSpacing = Property.of(10);
+
+ @Expose
+ @ConfigOption(name = "Button Vertical Spacing", desc = "How much space vertically between buttons.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 40,
+ minStep = 1
+ )
+ public Property<Integer> buttonVerticalSpacing = Property.of(10);
+
+ @Expose
+ @ConfigOption(name = "Button Width", desc = "Width of the buttons.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 60,
+ minStep = 1
+ )
+ public Property<Integer> buttonWidth = Property.of(50);
+
+ @Expose
+ @ConfigOption(name = "Button Height", desc = "Height of the buttons.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 60,
+ minStep = 1
+ )
+ public Property<Integer> buttonHeight = Property.of(20);
+
+ @Expose
+ @ConfigOption(name = "Background Padding", desc = "Space between the edges of the background and the slots.")
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 20,
+ minStep = 1
+ )
+ public Property<Integer> backgroundPadding = Property.of(10);
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
index 9d0ca44f9..e037b80a6 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
@@ -28,6 +28,7 @@ import at.hannibal2.skyhanni.features.garden.fortuneguide.FarmingItems;
import at.hannibal2.skyhanni.features.garden.pests.PestProfitTracker;
import at.hannibal2.skyhanni.features.garden.pests.VinylType;
import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward;
+import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI;
import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker;
import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker;
import at.hannibal2.skyhanni.features.misc.trevor.TrevorTracker;
@@ -618,7 +619,7 @@ public class ProfileSpecificStorage {
public DianaProfitTracker.Data dianaProfitTracker = new DianaProfitTracker.Data();
@Expose
- // TODO renmae
+ // TODO rename
public MythologicalCreatureTracker.Data mythologicalMobTracker = new MythologicalCreatureTracker.Data();
}
@@ -626,5 +627,17 @@ public class ProfileSpecificStorage {
public Map<SkillType, SkillAPI.SkillInfo> skillData = new HashMap<>();
@Expose
+ public WardrobeStorage wardrobe = new WardrobeStorage();
+
+ public static class WardrobeStorage {
+ @Expose
+ public Map<Integer, WardrobeAPI.WardrobeData> data = new HashMap<>();
+
+ @Expose
+ @Nullable
+ public Integer currentSlot = null;
+ }
+
+ @Expose
public UpgradeReminder.CommunityShopUpgrade communityShopProfileUpgrade = null;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt
new file mode 100644
index 000000000..243b3132f
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/CustomWardrobe.kt
@@ -0,0 +1,595 @@
+package at.hannibal2.skyhanni.features.inventory.wardrobe
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.core.config.Position
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
+import at.hannibal2.skyhanni.events.GuiContainerEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.InventoryUpdatedEvent
+import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI.MAX_PAGES
+import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI.MAX_SLOT_PER_PAGE
+import at.hannibal2.skyhanni.mixins.transformers.gui.AccessorGuiContainer
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ColorUtils.addAlpha
+import at.hannibal2.skyhanni.utils.ColorUtils.darker
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColorInt
+import at.hannibal2.skyhanni.utils.ColorUtils.withAlpha
+import at.hannibal2.skyhanni.utils.ConditionalUtils
+import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf
+import at.hannibal2.skyhanni.utils.ConfigUtils.jumpToEditor
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.EntityUtils.getFakePlayer
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.removeEnchants
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment
+import at.hannibal2.skyhanni.utils.RenderUtils.VerticalAlignment
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.inventory.GuiContainer
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+import kotlin.math.min
+import kotlin.time.Duration.Companion.milliseconds
+
+@SkyHanniModule
+object CustomWardrobe {
+
+ val config get() = SkyHanniMod.feature.inventory.customWardrobe
+
+ private var displayRenderable: Renderable? = null
+ private var inventoryButton: Renderable? = null
+ private var editMode = false
+ private var waitingForInventoryUpdate = false
+
+ private var activeScale: Int = 100
+ private var currentMaxSize: Pair<Int, Int>? = null
+ private var lastScreenSize: Pair<Int, Int>? = null
+ private var guiName = "Custom Wardrobe"
+
+ @SubscribeEvent
+ fun onGuiRender(event: GuiContainerEvent.BeforeDraw) {
+ if (!isEnabled() || editMode) return
+ val renderable = displayRenderable ?: run {
+ update()
+ displayRenderable ?: return
+ }
+
+ val gui = event.gui
+ val screenSize = gui.width to gui.height
+
+ if (screenSize != lastScreenSize) {
+ lastScreenSize = screenSize
+ val shouldUpdate = updateScreenSize(screenSize)
+ if (shouldUpdate) {
+ update()
+ return
+ }
+ }
+
+ val (width, height) = renderable.width to renderable.height
+ val pos = Position((gui.width - width) / 2, (gui.height - height) / 2)
+ if (waitingForInventoryUpdate && config.loadingText) {
+ val loadingRenderable = Renderable.string(
+ "§cLoading...",
+ scale = activeScale / 100.0
+ )
+ val loadingPos =
+ Position(pos.rawX + (width - loadingRenderable.width) / 2, pos.rawY - loadingRenderable.height)
+ loadingPos.renderRenderable(loadingRenderable, posLabel = guiName, addToGuiManager = false)
+ }
+
+ GlStateManager.translate(0f, 0f, 100f)
+ pos.renderRenderable(renderable, posLabel = guiName, addToGuiManager = false)
+ GlStateManager.translate(0f, 0f, -100f)
+ event.cancel()
+ }
+
+ // Edit button in normal wardrobe while in edit mode
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) {
+ if (!isEnabled()) return
+ if (!editMode) return
+ val gui = Minecraft.getMinecraft().currentScreen as? GuiContainer ?: return
+ val renderable = inventoryButton ?: addReEnableButton().also { inventoryButton = it }
+ val accessorGui = gui as AccessorGuiContainer
+ val posX = accessorGui.guiLeft + (1.05 * accessorGui.width).toInt()
+ val posY = accessorGui.guiTop + (accessorGui.height - renderable.height) / 2
+ Position(posX, posY).renderRenderable(renderable, posLabel = guiName, addToGuiManager = false)
+ }
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ waitingForInventoryUpdate = false
+ if (!isEnabled()) return
+ DelayedRun.runDelayed(250.milliseconds) {
+ if (!WardrobeAPI.inWardrobe()) {
+ reset()
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onConfigUpdate(event: ConfigLoadEvent) {
+ with(config.spacing) {
+ ConditionalUtils.onToggle(
+ globalScale, outlineThickness, outlineBlur,
+ slotWidth, slotHeight, playerScale,
+ maxPlayersPerRow, horizontalSpacing, verticalSpacing,
+ buttonSlotsVerticalSpacing, buttonHorizontalSpacing, buttonVerticalSpacing,
+ buttonWidth, buttonHeight, backgroundPadding,
+ ) {
+ currentMaxSize = null
+ lastScreenSize = null
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryUpdate(event: InventoryUpdatedEvent) {
+ if (!isEnabled() || editMode) return
+ update()
+ }
+
+ private fun update() {
+ displayRenderable = createRenderables()
+ }
+
+ private fun updateScreenSize(gui: Pair<Int, Int>): Boolean {
+ val renderable = currentMaxSize ?: run {
+ activeScale = config.spacing.globalScale.get()
+ update()
+ return true
+ }
+ val previousActiveScale = activeScale
+ val unscaledRenderableWidth = renderable.first / activeScale
+ val unscaledRenderableHeight = renderable.second / activeScale
+ val autoScaleWidth = 0.95 * gui.first / unscaledRenderableWidth
+ val autoScaleHeight = 0.95 * gui.second / unscaledRenderableHeight
+ val maxScale = min(autoScaleWidth, autoScaleHeight).toInt()
+
+ activeScale = config.spacing.globalScale.get().coerceAtMost(maxScale)
+
+ return activeScale != previousActiveScale
+ }
+
+ private fun createWarning(list: List<WardrobeSlot>): Pair<String?, List<WardrobeSlot>> {
+ var wardrobeWarning: String? = null
+ var wardrobeSlots = list
+
+ if (wardrobeSlots.isEmpty()) wardrobeWarning = "§cYour wardrobe is empty :("
+
+ if (config.hideLockedSlots) {
+ wardrobeSlots = wardrobeSlots.filter { !it.locked }
+ if (wardrobeSlots.isEmpty()) wardrobeWarning = "§cAll your slots are locked? Somehow"
+ }
+
+ if (config.hideEmptySlots) {
+ wardrobeSlots = wardrobeSlots.filter { !it.isEmpty() }
+ if (wardrobeSlots.isEmpty()) wardrobeWarning = "§cAll slots are empty :("
+ }
+ if (config.onlyFavorites) {
+ wardrobeSlots = wardrobeSlots.filter { it.favorite || it.isCurrentSlot() }
+ if (wardrobeSlots.isEmpty()) wardrobeWarning = "§cDidn't set any favorites"
+ }
+
+ return wardrobeWarning to wardrobeSlots
+ }
+
+ private fun createArmorTooltipRenderable(
+ slot: WardrobeSlot,
+ containerHeight: Int,
+ containerWidth: Int,
+ ): Renderable {
+ val loreList = mutableListOf<Renderable>()
+ val height = containerHeight - 3
+
+ // This is needed to keep the background size the same as the player renderable size
+ val hoverableSizes = MutableList(4) { height / 4 }.apply {
+ for (k in 0 until height % 4) this[k]++
+ }
+
+ for (armorIndex in 0 until 4) {
+ val stack = slot.armor[armorIndex]?.copy()
+ if (stack == null) {
+ loreList.add(Renderable.placeholder(containerWidth, hoverableSizes[armorIndex]))
+ } else {
+ loreList.add(
+ Renderable.hoverable(
+ Renderable.hoverTips(
+ Renderable.placeholder(containerWidth, hoverableSizes[armorIndex]),
+ stack.getTooltip(Minecraft.getMinecraft().thePlayer, false)
+ ),
+ Renderable.placeholder(containerWidth, hoverableSizes[armorIndex]),
+ bypassChecks = true
+ )
+ )
+ }
+ }
+ return Renderable.verticalContainer(loreList, spacing = 1)
+ }
+
+ private fun createFakePlayerRenderable(
+ slot: WardrobeSlot,
+ playerWidth: Double,
+ containerHeight: Int,
+ containerWidth: Int,
+ ): Renderable {
+ val fakePlayer = getFakePlayer()
+ var scale = playerWidth
+
+ fakePlayer.inventory.armorInventory =
+ slot.armor.map { it?.copy()?.removeEnchants() }.reversed().toTypedArray()
+
+ val playerColor = if (!slot.isInCurrentPage()) {
+ scale *= 0.9
+ Color.GRAY.withAlpha(100)
+ } else null
+
+ return Renderable.fakePlayer(
+ fakePlayer,
+ followMouse = config.eyesFollowMouse,
+ width = containerWidth,
+ height = containerHeight,
+ entityScale = scale.toInt(),
+ padding = 0,
+ color = playerColor,
+ )
+ }
+
+ private fun createRenderables(): Renderable {
+ val (wardrobeWarning, list) = createWarning(WardrobeAPI.slots)
+
+ val maxPlayersPerRow = config.spacing.maxPlayersPerRow.get().coerceAtLeast(1)
+ val maxPlayersRows = ((MAX_SLOT_PER_PAGE * MAX_PAGES - 1) / maxPlayersPerRow) + 1
+ val containerWidth = (config.spacing.slotWidth.get() * (activeScale / 100.0)).toInt()
+ val containerHeight = (config.spacing.slotHeight.get() * (activeScale / 100.0)).toInt()
+ val playerWidth = (containerWidth * (config.spacing.playerScale.get() / 100.0))
+ val horizontalSpacing = (config.spacing.horizontalSpacing.get() * (activeScale / 100.0)).toInt()
+ val verticalSpacing = (config.spacing.verticalSpacing.get() * (activeScale / 100.0)).toInt()
+ val backgroundPadding = (config.spacing.backgroundPadding.get() * (activeScale / 100.0)).toInt()
+ val buttonVerticalSpacing = (config.spacing.buttonVerticalSpacing.get() * (activeScale / 100.0)).toInt()
+
+ var maxRenderableWidth = maxPlayersPerRow * containerWidth + (maxPlayersPerRow - 1) * horizontalSpacing
+ var maxRenderableHeight = maxPlayersRows * containerHeight + (maxPlayersRows - 1) * verticalSpacing
+
+ val button = addButtons()
+
+ if (button.width > maxRenderableWidth) maxRenderableWidth = button.width
+ maxRenderableHeight += button.height + buttonVerticalSpacing
+
+ maxRenderableWidth += 2 * backgroundPadding
+ maxRenderableHeight += 2 * backgroundPadding
+ currentMaxSize = maxRenderableWidth to maxRenderableHeight
+
+ wardrobeWarning?.let { text ->
+ val warningRenderable = Renderable.wrappedString(
+ text,
+ maxRenderableWidth,
+ 3.0 * (activeScale / 100.0),
+ horizontalAlign = HorizontalAlignment.CENTER
+ )
+ val withButtons = Renderable.verticalContainer(
+ listOf(warningRenderable, button),
+ buttonVerticalSpacing,
+ horizontalAlign = HorizontalAlignment.CENTER
+ )
+ return addGuiBackground(withButtons, backgroundPadding)
+ }
+
+ val chunkedList = list.chunked(maxPlayersPerRow)
+
+ val rowsRenderables = chunkedList.map { row ->
+ val slotsRenderables = row.map { slot ->
+ val armorTooltipRenderable = createArmorTooltipRenderable(slot, containerHeight, containerWidth)
+
+ val playerBackground = createHoverableRenderable(
+ armorTooltipRenderable,
+ topLayerRenderable = addSlotHoverableButtons(slot),
+ hoveredColor = slot.getSlotColor(),
+ borderOutlineThickness = config.spacing.outlineThickness.get(),
+ borderOutlineBlur = config.spacing.outlineBlur.get(),
+ onClick = { slot.clickSlot() }
+ )
+
+ val playerRenderable = createFakePlayerRenderable(slot, playerWidth, containerHeight, containerWidth)
+
+ Renderable.doubleLayered(playerBackground, playerRenderable, false)
+ }
+ Renderable.horizontalContainer(slotsRenderables, horizontalSpacing)
+ }
+
+ val allSlotsRenderable = Renderable.verticalContainer(
+ rowsRenderables,
+ verticalSpacing,
+ horizontalAlign = HorizontalAlignment.CENTER
+ )
+
+ val withButtons = Renderable.verticalContainer(
+ listOf(allSlotsRenderable, button),
+ buttonVerticalSpacing,
+ horizontalAlign = HorizontalAlignment.CENTER
+ )
+
+ return addGuiBackground(withButtons, backgroundPadding)
+ }
+
+ private fun addGuiBackground(renderable: Renderable, borderPadding: Int) =
+ Renderable.drawInsideRoundedRect(
+ Renderable.doubleLayered(
+ renderable,
+ Renderable.clickable(
+ Renderable.string(
+ "§7SkyHanni",
+ horizontalAlign = HorizontalAlignment.RIGHT,
+ verticalAlign = VerticalAlignment.BOTTOM,
+ scale = 1.0 * (activeScale / 100.0)
+ ).let { Renderable.hoverable(hovered = Renderable.underlined(it), unhovered = it) },
+ onClick = {
+ config::enabled.jumpToEditor()
+ reset()
+ WardrobeAPI.currentPage = null
+ }
+ ),
+ blockBottomHover = false
+ ),
+ config.color.backgroundColor.toChromaColor(),
+ padding = borderPadding
+ )
+
+ private fun reset() {
+ WardrobeAPI.inCustomWardrobe = false
+ editMode = false
+ displayRenderable = null
+ inventoryButton = null
+ }
+
+ private fun addButtons(): Renderable {
+ val (horizontalSpacing, verticalSpacing) = with(config.spacing) {
+ buttonHorizontalSpacing.get() * (activeScale / 100.0) to buttonVerticalSpacing.get() * (activeScale / 100.0)
+ }
+
+ val backButton = createLabeledButton(
+ "§aBack",
+ onClick = {
+ InventoryUtils.clickSlot(48)
+ reset()
+ WardrobeAPI.currentPage = null
+ }
+ )
+ val exitButton = createLabeledButton(
+ "§cClose",
+ onClick = {
+ InventoryUtils.clickSlot(49)
+ reset()
+ WardrobeAPI.currentPage = null
+ }
+ )
+
+ val greenColor = Color(85, 255, 85, 200)
+ val redColor = Color(255, 85, 85, 200)
+
+ val onlyFavoriteButton = createLabeledButton(
+ "§eFavorite",
+ hoveredColor = if (config.onlyFavorites) greenColor else redColor,
+ onClick = {
+ config.onlyFavorites = !config.onlyFavorites
+ update()
+ }
+ )
+
+ val editButton = createLabeledButton(
+ "§bEdit",
+ onClick = {
+ DelayedRun.runNextTick {
+ reset()
+ editMode = true
+ }
+ }
+ )
+
+ val row = Renderable.horizontalContainer(
+ listOf(backButton, exitButton, onlyFavoriteButton),
+ horizontalSpacing.toInt(),
+ horizontalAlign = HorizontalAlignment.CENTER,
+ )
+
+ val total = Renderable.verticalContainer(
+ listOf(row, editButton),
+ verticalSpacing.toInt(),
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER
+ )
+
+ return total
+ }
+
+ private fun addReEnableButton(): Renderable {
+ val color = Color(116, 150, 255, 200)
+ return createLabeledButton(
+ "§bEdit",
+ hoveredColor = color,
+ unhoveredColor = color.darker(0.8),
+ onClick = {
+ WardrobeAPI.inCustomWardrobe = false
+ editMode = false
+ update()
+ }
+ )
+ }
+
+ private fun addSlotHoverableButtons(wardrobeSlot: WardrobeSlot): Renderable {
+ val list = mutableListOf<Renderable>()
+ val textScale = 1.5 * (activeScale / 100.0)
+ list.add(
+ Renderable.clickable(
+ Renderable.hoverable(
+ Renderable.string(
+ (if (wardrobeSlot.favorite) "§c" else "§7") + "❤",
+ scale = textScale,
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER
+ ),
+ Renderable.string(
+ (if (wardrobeSlot.favorite) "§4" else "§8") + "❤",
+ scale = textScale,
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER
+ )
+ ),
+ onClick = {
+ wardrobeSlot.favorite = !wardrobeSlot.favorite
+ update()
+ }
+ )
+ )
+
+ if (config.estimatedValue && !wardrobeSlot.isEmpty()) {
+ val lore = WardrobeAPI.createPriceLore(wardrobeSlot)
+ list.add(
+ Renderable.hoverTips(
+ Renderable.string(
+ "§2$",
+ scale = textScale,
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER,
+ ),
+ lore,
+ ),
+ )
+ }
+
+ return Renderable.verticalContainer(list, 1, HorizontalAlignment.RIGHT)
+ }
+
+ private fun createLabeledButton(
+ text: String,
+ hoveredColor: Color = Color(130, 130, 130, 200),
+ unhoveredColor: Color = hoveredColor.darker(0.57),
+ onClick: () -> Unit,
+ ): Renderable {
+ val buttonWidth = (config.spacing.buttonWidth.get() * (activeScale / 100.0)).toInt()
+ val buttonHeight = (config.spacing.buttonHeight.get() * (activeScale / 100.0)).toInt()
+ val textScale = (activeScale / 100.0)
+
+ val renderable = Renderable.hoverable(
+ Renderable.drawInsideRoundedRectWithOutline(
+ Renderable.doubleLayered(
+ Renderable.clickable(
+ Renderable.placeholder(buttonWidth, buttonHeight),
+ onClick
+ ),
+ Renderable.string(
+ text,
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER,
+ scale = textScale
+ ),
+ false,
+ ),
+ hoveredColor,
+ padding = 0,
+ topOutlineColor = config.color.topBorderColor.toChromaColorInt(),
+ bottomOutlineColor = config.color.bottomBorderColor.toChromaColorInt(),
+ borderOutlineThickness = 2,
+ horizontalAlign = HorizontalAlignment.CENTER
+ ),
+ Renderable.drawInsideRoundedRect(
+ Renderable.doubleLayered(
+ Renderable.placeholder(buttonWidth, buttonHeight),
+ Renderable.string(
+ text,
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER,
+ scale = textScale
+ ),
+ ),
+ unhoveredColor.darker(0.57),
+ padding = 0,
+ horizontalAlign = HorizontalAlignment.CENTER
+ )
+ )
+
+ return renderable
+ }
+
+ private fun createHoverableRenderable(
+ hoveredRenderable: Renderable,
+ unhoveredRenderable: Renderable = Renderable.placeholder(hoveredRenderable.width, hoveredRenderable.height),
+ topLayerRenderable: Renderable = Renderable.placeholder(0, 0),
+ padding: Int = 0,
+ horizontalAlignment: HorizontalAlignment = HorizontalAlignment.CENTER,
+ verticalAlignment: VerticalAlignment = VerticalAlignment.CENTER,
+ hoveredColor: Color,
+ unHoveredColor: Color = hoveredColor,
+ borderOutlineThickness: Int,
+ borderOutlineBlur: Float = 0.5f,
+ onClick: () -> Unit,
+ onHover: () -> Unit = {},
+ ): Renderable =
+ Renderable.hoverable(
+ Renderable.drawInsideRoundedRectWithOutline(
+ Renderable.doubleLayered(
+ Renderable.clickable(
+ hoveredRenderable,
+ onClick,
+ ),
+ topLayerRenderable,
+ ),
+ hoveredColor,
+ padding = padding,
+ topOutlineColor = config.color.topBorderColor.toChromaColorInt(),
+ bottomOutlineColor = config.color.bottomBorderColor.toChromaColorInt(),
+ borderOutlineThickness = borderOutlineThickness,
+ blur = borderOutlineBlur,
+ horizontalAlign = horizontalAlignment,
+ verticalAlign = verticalAlignment,
+ ),
+ Renderable.drawInsideRoundedRect(
+ unhoveredRenderable,
+ unHoveredColor,
+ padding = padding,
+ horizontalAlign = horizontalAlignment,
+ verticalAlign = verticalAlignment
+ ),
+ onHover = { onHover() },
+ )
+
+ private fun WardrobeSlot.clickSlot() {
+ val previousPageSlot = 45
+ val nextPageSlot = 53
+ val wardrobePage = WardrobeAPI.currentPage ?: return
+ if (isInCurrentPage()) {
+ if (isEmpty() || locked || waitingForInventoryUpdate) return
+ WardrobeAPI.currentSlot = if (isCurrentSlot()) null else id
+ InventoryUtils.clickSlot(inventorySlot)
+ } else {
+ if (page < wardrobePage) {
+ WardrobeAPI.currentPage = wardrobePage - 1
+ waitingForInventoryUpdate = true
+ InventoryUtils.clickSlot(previousPageSlot)
+ } else if (page > wardrobePage) {
+ WardrobeAPI.currentPage = wardrobePage + 1
+ waitingForInventoryUpdate = true
+ InventoryUtils.clickSlot(nextPageSlot)
+ }
+ }
+ update()
+ }
+
+ private fun WardrobeSlot.getSlotColor(): Color = with(config.color) {
+ when {
+ isCurrentSlot() -> equippedColor
+ favorite -> favoriteColor
+ else -> null
+ }?.toChromaColor()?.transformIf({ isInCurrentPage() }) { darker() }
+ ?: (if (isInCurrentPage()) samePageColor else otherPageColor).toChromaColor()
+ .transformIf({ locked || isEmpty() }) { darker(0.2) }.addAlpha(100)
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled && WardrobeAPI.inWardrobe()
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/EstimatedWardrobePrice.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/EstimatedWardrobePrice.kt
new file mode 100644
index 000000000..d55b30cf1
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/EstimatedWardrobePrice.kt
@@ -0,0 +1,41 @@
+package at.hannibal2.skyhanni.features.inventory.wardrobe
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
+import at.hannibal2.skyhanni.events.LorenzToolTipEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+@SkyHanniModule
+object EstimatedWardrobePrice {
+
+ private val config get() = SkyHanniMod.feature.inventory.estimatedItemValues
+
+ @SubscribeEvent
+ fun onTooltip(event: LorenzToolTipEvent) {
+ if (!isEnabled()) return
+
+ val slot = WardrobeAPI.slots.firstOrNull {
+ event.slot.slotNumber == it.inventorySlot && it.isInCurrentPage()
+ } ?: return
+
+
+ val lore = WardrobeAPI.createPriceLore(slot)
+ if (lore.isEmpty()) return
+
+ val tooltip = event.toolTip
+ var index = 3
+
+ tooltip.add(index++, "")
+ tooltip.addAll(index, lore)
+ }
+
+ private fun isEnabled() =
+ LorenzUtils.inSkyBlock && config.armor && WardrobeAPI.inWardrobe() && !WardrobeAPI.inCustomWardrobe
+
+ @SubscribeEvent
+ fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
+ event.move(3, "misc.estimatedIemValueArmor", "misc.estimatedItemValues.armor")
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeAPI.kt
new file mode 100644
index 000000000..013aa1866
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeAPI.kt
@@ -0,0 +1,207 @@
+package at.hannibal2.skyhanni.features.inventory.wardrobe
+
+import at.hannibal2.skyhanni.data.ProfileStorageData
+import at.hannibal2.skyhanni.events.DebugDataCollectEvent
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.InventoryOpenEvent
+import at.hannibal2.skyhanni.events.InventoryUpdatedEvent
+import at.hannibal2.skyhanni.features.misc.items.EstimatedItemValueCalculator
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil
+import at.hannibal2.skyhanni.utils.NumberUtil.formatInt
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import com.google.gson.annotations.Expose
+import net.minecraft.init.Blocks
+import net.minecraft.init.Items
+import net.minecraft.item.EnumDyeColor
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.milliseconds
+
+@SkyHanniModule
+object WardrobeAPI {
+
+ val storage get() = ProfileStorageData.profileSpecific?.wardrobe
+
+ private val repoGroup = RepoPattern.group("inventory.wardrobe")
+ private val inventoryPattern by repoGroup.pattern(
+ "inventory.name",
+ "Wardrobe \\((?<currentPage>\\d+)/\\d+\\)"
+ )
+
+ /**
+ * REGEX-TEST: §7Slot 4: §aEquipped
+ */
+ private val equippedSlotPattern by repoGroup.pattern(
+ "equippedslot",
+ "§7Slot \\d+: §aEquipped"
+ )
+
+ private const val FIRST_SLOT = 36
+ private const val FIRST_HELMET_SLOT = 0
+ private const val FIRST_CHESTPLATE_SLOT = 9
+ private const val FIRST_LEGGINGS_SLOT = 18
+ private const val FIRST_BOOTS_SLOT = 27
+ const val MAX_SLOT_PER_PAGE = 9
+ const val MAX_PAGES = 2
+
+ var slots = listOf<WardrobeSlot>()
+ var inCustomWardrobe = false
+
+ internal fun emptyArmor(): List<ItemStack?> = listOf(null, null, null, null)
+
+ var currentSlot: Int?
+ get() = storage?.currentSlot
+ set(value) {
+ storage?.currentSlot = value
+ }
+
+ var currentPage: Int? = null
+ private var inWardrobe = false
+
+ init {
+ val list = mutableListOf<WardrobeSlot>()
+ var id = 0
+
+ for (page in 1..MAX_PAGES) {
+ for (slot in 0 until MAX_SLOT_PER_PAGE) {
+ val inventorySlot = FIRST_SLOT + slot
+ val helmetSlot = FIRST_HELMET_SLOT + slot
+ val chestplateSlot = FIRST_CHESTPLATE_SLOT + slot
+ val leggingsSlot = FIRST_LEGGINGS_SLOT + slot
+ val bootsSlot = FIRST_BOOTS_SLOT + slot
+ list.add(WardrobeSlot(++id, page, inventorySlot, helmetSlot, chestplateSlot, leggingsSlot, bootsSlot))
+ }
+ }
+ slots = list
+ }
+
+ private fun getWardrobeItem(itemStack: ItemStack?) =
+ if (itemStack?.item == ItemStack(Blocks.stained_glass_pane).item || itemStack == null) null else itemStack
+
+ private fun getWardrobeSlotFromId(id: Int?) = slots.find { it.id == id }
+
+ fun inWardrobe() = InventoryUtils.inInventory() && inWardrobe
+
+ fun createPriceLore(slot: WardrobeSlot) = buildList {
+ if (slot.isEmpty()) return@buildList
+ add("§aEstimated Armor Value:")
+ var totalPrice = 0.0
+ for (stack in slot.armor.filterNotNull()) {
+ val price = EstimatedItemValueCalculator.getTotalPrice(stack)
+ add(" §7- ${stack.name}: §6${NumberUtil.format(price)}")
+ totalPrice += price
+ }
+ if (totalPrice != 0.0) add(" §aTotal Value: §6§l${NumberUtil.format(totalPrice)} coins")
+ }
+
+ @SubscribeEvent
+ fun onInventoryOpen(event: InventoryOpenEvent) {
+ inventoryPattern.matches(event.inventoryName).let {
+ inWardrobe = it
+ if (CustomWardrobe.config.enabled) inCustomWardrobe = it
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryUpdate(event: InventoryUpdatedEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+
+ inventoryPattern.matchMatcher(event.inventoryName) {
+ inWardrobe = true
+ currentPage = group("currentPage").formatInt()
+ } ?: return
+
+ val itemsList = event.inventoryItems
+
+ val allGrayDye = slots.all {
+ itemsList[it.inventorySlot]?.itemDamage == EnumDyeColor.GRAY.dyeDamage || !it.isInCurrentPage()
+ }
+
+ if (allGrayDye) {
+ val allSlotsEmpty = slots.filter { it.isInCurrentPage() }.all { slot ->
+ (slot.inventorySlots.all { getWardrobeItem(itemsList[it]) == null })
+ }
+ if (allSlotsEmpty) {
+ for (slot in slots.filter { it.isInCurrentPage() }) {
+ slot.getData()?.armor = emptyArmor()
+ }
+ } else return
+ }
+
+ val foundCurrentSlot = processSlots(slots, itemsList)
+ if (!foundCurrentSlot && getWardrobeSlotFromId(currentSlot)?.page == currentPage) {
+ currentSlot = null
+ }
+ }
+
+ private fun processSlots(slots: List<WardrobeSlot>, itemsList: Map<Int, ItemStack>): Boolean {
+ var foundCurrentSlot = false
+
+ for (slot in slots.filter { it.isInCurrentPage() }) {
+ slot.getData()?.armor = listOf(
+ getWardrobeItem(itemsList[slot.helmetSlot]),
+ getWardrobeItem(itemsList[slot.chestplateSlot]),
+ getWardrobeItem(itemsList[slot.leggingsSlot]),
+ getWardrobeItem(itemsList[slot.bootsSlot]),
+ )
+ if (equippedSlotPattern.matches(itemsList[slot.inventorySlot]?.name)) {
+ currentSlot = slot.id
+ foundCurrentSlot = true
+ }
+ slot.locked = (itemsList[slot.inventorySlot] == ItemStack(Items.dye, EnumDyeColor.RED.dyeDamage))
+ if (slot.locked) slots.forEach { if (it.id > slot.id) it.locked = true }
+ }
+
+ return foundCurrentSlot
+ }
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ if (!inWardrobe) return
+ DelayedRun.runDelayed(250.milliseconds) {
+ if (!inventoryPattern.matches(InventoryUtils.openInventoryName())) {
+ inWardrobe = false
+ currentPage = null
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onDebugCollect(event: DebugDataCollectEvent) {
+ event.title("Wardrobe")
+ event.addIrrelevant {
+ for (slot in slots) {
+ val slotInfo = buildString {
+ append("Slot ${slot.id}")
+ if (slot.favorite) append(" - Favorite: true")
+ }
+ if (slot.locked) {
+ add("$slotInfo is locked")
+ } else if (slot.isEmpty()) {
+ add("$slotInfo is empty")
+ } else {
+ add(slotInfo)
+ setOf("Helmet", "Chestplate", "Leggings", "Boots").forEachIndexed { id, armourName ->
+ slot.getData()?.armor?.get(id)?.name?.let { name ->
+ add(" $armourName: $name")
+ }
+ }
+ }
+ }
+ }
+ }
+
+ class WardrobeData(
+ @Expose val id: Int,
+ @Expose var armor: List<ItemStack?>,
+ @Expose var locked: Boolean,
+ @Expose var favorite: Boolean,
+ )
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeSlot.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeSlot.kt
new file mode 100644
index 000000000..e500887f9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/wardrobe/WardrobeSlot.kt
@@ -0,0 +1,42 @@
+package at.hannibal2.skyhanni.features.inventory.wardrobe
+
+class WardrobeSlot(
+ val id: Int,
+ val page: Int,
+ val inventorySlot: Int,
+ val helmetSlot: Int,
+ val chestplateSlot: Int,
+ val leggingsSlot: Int,
+ val bootsSlot: Int,
+) {
+ fun getData() = WardrobeAPI.storage?.data?.getOrPut(id) {
+ WardrobeAPI.WardrobeData(
+ id,
+ armor = WardrobeAPI.emptyArmor(),
+ locked = true,
+ favorite = false,
+ )
+ }
+
+ var locked: Boolean
+ get() = getData()?.locked ?: true
+ set(value) {
+ getData()?.locked = value
+ }
+
+ var favorite: Boolean
+ get() = getData()?.favorite ?: false
+ set(value) {
+ getData()?.favorite = value
+ }
+
+ val armor get() = getData()?.armor ?: WardrobeAPI.emptyArmor()
+
+ val inventorySlots = listOf(helmetSlot, chestplateSlot, leggingsSlot, bootsSlot)
+
+ fun isEmpty(): Boolean = armor.all { it == null }
+
+ fun isCurrentSlot() = getData()?.id == WardrobeAPI.currentSlot
+
+ fun isInCurrentPage() = (WardrobeAPI.currentPage == null && page == 1) || (page == WardrobeAPI.currentPage)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedWardrobePrice.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedWardrobePrice.kt
deleted file mode 100644
index 5e536ad01..000000000
--- a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedWardrobePrice.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package at.hannibal2.skyhanni.features.misc.items
-
-import at.hannibal2.skyhanni.SkyHanniMod
-import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
-import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
-import at.hannibal2.skyhanni.events.LorenzToolTipEvent
-import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.InventoryUtils
-import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull
-import at.hannibal2.skyhanni.utils.ItemUtils.name
-import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.NumberUtil
-import net.minecraft.item.ItemStack
-import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
-
-@SkyHanniModule
-object EstimatedWardrobePrice {
-
- private val config get() = SkyHanniMod.feature.inventory.estimatedItemValues
- var data = mutableMapOf<Int, MutableList<ItemStack>>()
-
- @SubscribeEvent
- fun onTooltip(event: LorenzToolTipEvent) {
- if (!LorenzUtils.inSkyBlock) return
- if (!config.armor) return
- if (!InventoryUtils.openInventoryName().contains("Wardrobe")) return
-
- val slot = event.slot.slotNumber
- val id = slot % 9
- // Only showing in the armor select line
- if (slot - id != 36) return
- val items = data[id] ?: return
-
- var index = 3
- val toolTip = event.toolTip
- if (toolTip.size < 4) return
- toolTip.add(index++, "")
- toolTip.add(index++, "§aEstimated Armor Value:")
-
- var totalPrice = 0.0
- for (item in items) {
-
- val price = EstimatedItemValueCalculator.getTotalPrice(item)
- totalPrice += price
-
- toolTip.add(index++, " §7- ${item.name}: §6${NumberUtil.format(price)}")
- }
- toolTip.add(index, " §aTotal Value: §6§l${NumberUtil.format(totalPrice)} coins")
- }
-
- @SubscribeEvent
- fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
- if (!LorenzUtils.inSkyBlock) return
- if (!config.armor) return
- if (!event.inventoryName.startsWith("Wardrobe")) return
-
- val map = mutableMapOf<Int, MutableList<ItemStack>>()
-
- for ((slot, item) in event.inventoryItems) {
- item.getInternalNameOrNull() ?: continue
- val price = EstimatedItemValueCalculator.getTotalPrice(item)
- if (price == 0.0) continue
- val id = slot % 9
- val list = map.getOrPut(id) { mutableListOf() }
- list.add(item)
- }
- data = map
- }
-
- @SubscribeEvent
- fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
- event.move(3, "misc.estimatedIemValueArmor", "misc.estimatedItemValues.armor")
- }
-}
diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt
index 2572b969f..041457d25 100644
--- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt
+++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/GuiContainerHook.kt
@@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.GuiContainerEvent
import at.hannibal2.skyhanni.events.GuiContainerEvent.CloseWindowEvent
import at.hannibal2.skyhanni.events.GuiContainerEvent.SlotClickEvent
import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests
+import at.hannibal2.skyhanni.utils.DelayedRun
import io.github.moulberry.notenoughupdates.NEUApi
import net.minecraft.client.gui.inventory.GuiContainer
import net.minecraft.inventory.Slot
@@ -40,7 +41,9 @@ class GuiContainerHook(guiAny: Any) {
GuiData.preDrawEventCancelled = true
ci.cancel()
} else {
- GuiData.preDrawEventCancelled = false
+ DelayedRun.runNextTick {
+ GuiData.preDrawEventCancelled = false
+ }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/AccessorGuiContainer.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/AccessorGuiContainer.java
index 5d9d6146d..8a4485694 100644
--- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/AccessorGuiContainer.java
+++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/AccessorGuiContainer.java
@@ -20,4 +20,10 @@ public interface AccessorGuiContainer {
@Invoker("drawGuiContainerBackgroundLayer")
void invokeDrawGuiContainerBackgroundLayer_skyhanni(float f, int i, int mouseY);
+
+ @Accessor("xSize")
+ int getWidth();
+
+ @Accessor("ySize")
+ int getHeight();
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt
index 52656f6ad..ed0a9740e 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt
@@ -1,11 +1,10 @@
package at.hannibal2.skyhanni.utils
import java.awt.Color
-import kotlin.math.max
object ColorUtils {
- /** Transfer string colors from the config to java.awt.Color */
+ /** Transfer string colors from the config to [Color] */
fun String.toChromaColor() = Color(toChromaColorInt(), true)
fun String.toChromaColorInt() = SpecialColour.specialToChromaRGB(this)
@@ -25,14 +24,13 @@ object ColorUtils {
(start.blue * (1 - percent) + end.blue * percent).toInt()
)
- fun Color.darker(factor: Double): Color {
- return Color(
- max((red * factor).toInt(), 0),
- max((green * factor).toInt(), 0),
- max((blue * factor).toInt(), 0),
- alpha
- )
- }
+ /** Darkens a color by a [factor]. The lower the [factor], the darker the color. */
+ fun Color.darker(factor: Double = 0.7) = Color(
+ (red * factor).toInt().coerceIn(0, 255),
+ (green * factor).toInt().coerceIn(0, 255),
+ (blue * factor).toInt().coerceIn(0, 255),
+ alpha
+ )
fun Color.withAlpha(alpha: Int): Int = (alpha.coerceIn(0, 255) shl 24) or (this.rgb and 0x00ffffff)
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt
index 00eb2e046..430310709 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt
@@ -14,13 +14,16 @@ import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import net.minecraft.block.state.IBlockState
import net.minecraft.client.Minecraft
import net.minecraft.client.entity.EntityOtherPlayerMP
+import net.minecraft.client.resources.DefaultPlayerSkin
import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.item.EntityArmorStand
import net.minecraft.entity.monster.EntityEnderman
import net.minecraft.entity.player.EntityPlayer
+import net.minecraft.entity.player.EnumPlayerModelParts
import net.minecraft.item.ItemStack
import net.minecraft.potion.Potion
+import net.minecraft.scoreboard.ScorePlayerTeam
import net.minecraft.util.AxisAlignedBB
import net.minecraftforge.client.event.RenderLivingEvent
import net.minecraftforge.fml.common.eventhandler.Event
@@ -190,6 +193,27 @@ object EntityUtils {
fun EntityLivingBase.isRunicAndCorrupt() = baseMaxHealth == health.toInt().derpy() * 3 * 4
fun Entity.cleanName() = this.name.removeColor()
+
+ /**
+ * @return a fake player with the same skin as the real player
+ */
+ fun getFakePlayer(): EntityOtherPlayerMP {
+ val mc = Minecraft.getMinecraft()
+ return object : EntityOtherPlayerMP(
+ mc.theWorld,
+ mc.thePlayer.gameProfile
+ ) {
+ override fun getLocationSkin() =
+ mc.thePlayer.locationSkin ?: DefaultPlayerSkin.getDefaultSkin(mc.thePlayer.uniqueID)
+
+ override fun getTeam() = object : ScorePlayerTeam(null, null) {
+ override fun getNameTagVisibility() = EnumVisible.NEVER
+ }
+
+ override fun isWearing(part: EnumPlayerModelParts?) =
+ mc.thePlayer.isWearing(part) && part != EnumPlayerModelParts.CAPE
+ }
+ }
}
private fun Event.cancel() {
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt
index e3f06b83b..53ee6cf7a 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/InventoryUtils.kt
@@ -34,8 +34,12 @@ object InventoryUtils {
} else ""
}
+ fun inInventory() = Minecraft.getMinecraft().currentScreen is GuiChest
+
fun ContainerChest.getInventoryName() = this.lowerChestInventory.displayName.unformattedText.trim()
+ fun getWindowId(): Int? = (Minecraft.getMinecraft().currentScreen as? GuiChest)?.inventorySlots?.windowId
+
fun getItemsInOwnInventory() =
getItemsInOwnInventoryWithNull()?.filterNotNull() ?: emptyList()
@@ -112,4 +116,10 @@ object InventoryUtils {
}
fun NEUInternalName.getAmountInInventory(): Int = countItemsInLowerInventory { it.getInternalNameOrNull() == this }
+
+ fun clickSlot(slot: Int) {
+ val windowId = getWindowId() ?: return
+ val controller = Minecraft.getMinecraft().playerController
+ controller.windowClick(windowId, slot, 0, 0, Minecraft.getMinecraft().thePlayer)
+ }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt
index d093dcfaf..6af19e382 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt
@@ -123,6 +123,13 @@ object ItemUtils {
// Checks for hypixel enchantments in the attributes
fun ItemStack.hasEnchantments() = getEnchantments()?.isNotEmpty() ?: false
+ fun ItemStack.removeEnchants(): ItemStack = apply {
+ val tag = tagCompound ?: NBTTagCompound()
+ tag.removeTag("ench")
+ tag.removeTag("StoredEnchantments")
+ tagCompound = tag
+ }
+
fun ItemStack.getSkullTexture(): String? {
if (item != Items.skull) return null
if (tagCompound == null) return null
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
index 40e8ddab9..1506882cd 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
@@ -559,6 +559,7 @@ object RenderUtils {
renderables: List<Renderable>,
extraSpace: Int = 0,
posLabel: String,
+ addToGuiManager: Boolean = true,
) {
if (renderables.isEmpty()) return
var longestY = 0
@@ -575,7 +576,21 @@ object RenderUtils {
GlStateManager.popMatrix()
}
- GuiEditManager.add(this, posLabel, longestX, longestY)
+ if (addToGuiManager) GuiEditManager.add(this, posLabel, longestX, longestY)
+ }
+
+ fun Position.renderRenderable(
+ renderable: Renderable,
+ posLabel: String,
+ addToGuiManager: Boolean = true,
+ ) {
+ GlStateManager.pushMatrix()
+ val (x, y) = transform()
+ Renderable.withMousePosition(x, y) {
+ renderable.render(0, 0)
+ }
+ GlStateManager.popMatrix()
+ if (addToGuiManager) GuiEditManager.add(this, posLabel, renderable.width, 0)
}
/**
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
index 0d830e5c1..870a3a2f2 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
@@ -8,8 +8,10 @@ import at.hannibal2.skyhanni.data.ToolTipData
import at.hannibal2.skyhanni.features.chroma.ChromaShaderManager
import at.hannibal2.skyhanni.features.chroma.ChromaType
import at.hannibal2.skyhanni.features.misc.DarkenShader
+import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper
import at.hannibal2.skyhanni.utils.CollectionUtils.contains
import at.hannibal2.skyhanni.utils.ColorUtils
+import at.hannibal2.skyhanni.utils.ColorUtils.addAlpha
import at.hannibal2.skyhanni.utils.ColorUtils.darker
import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked
import at.hannibal2.skyhanni.utils.LorenzColor
@@ -31,7 +33,9 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Gui
import net.minecraft.client.gui.GuiIngameMenu
import net.minecraft.client.gui.inventory.GuiEditSign
+import net.minecraft.client.gui.inventory.GuiInventory.drawEntityOnScreen
import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.util.ResourceLocation
import java.awt.Color
@@ -292,10 +296,10 @@ interface Renderable {
bypassChecks: Boolean = false,
condition: () -> Boolean = { true },
highlightsOnHoverSlots: List<Int> = emptyList(),
+ onHover: () -> Unit = {},
) = object : Renderable {
- override val width: Int
- get() = max(hovered.width, unhovered.width)
- override val height = 10
+ override val width = max(hovered.width, unhovered.width)
+ override val height = max(hovered.height, unhovered.height)
override val horizontalAlign get() = if (isHovered) hovered.horizontalAlign else unhovered.horizontalAlign
override val verticalAlign get() = if (isHovered) hovered.verticalAlign else unhovered.verticalAlign
@@ -304,6 +308,7 @@ interface Renderable {
override fun render(posX: Int, posY: Int) {
val pair = Pair(posX, posY)
isHovered = if (isHovered(posX, posY) && condition() && shouldAllowLink(true, bypassChecks)) {
+ onHover()
hovered.render(posX, posY)
HighlightOnHoverSlot.currentSlots[pair] = highlightsOnHoverSlots
true
@@ -315,6 +320,28 @@ interface Renderable {
}
}
+ /** Bottom Layer must be bigger then the top layer */
+ fun doubleLayered(
+ bottomLayer: Renderable,
+ topLayer: Renderable,
+ blockBottomHover: Boolean = true,
+ ) = object : Renderable {
+ override val width = bottomLayer.width
+ override val height = bottomLayer.height
+ override val horizontalAlign = bottomLayer.horizontalAlign
+ override val verticalAlign = bottomLayer.verticalAlign
+
+ override fun render(posX: Int, posY: Int) {
+ val (x, y) = topLayer.renderXYAligned(posX, posY, width, height)
+ val (posX, posY) = if (topLayer.isHovered(posX + x, posY + y) && blockBottomHover) {
+ bottomLayer.width + 1 to bottomLayer.height + 1
+ } else {
+ posX to posY
+ }
+ bottomLayer.render(posX, posY)
+ }
+ }
+
fun itemStackWithTip(
item: ItemStack,
scale: Double = NEUItems.itemFontSize,
@@ -562,7 +589,7 @@ interface Renderable {
}
// TODO use this to render current boosted crop in next jacob contest crops
- fun Renderable.renderBounds(color: Color = LorenzColor.GREEN.toColor()) = object : Renderable {
+ fun Renderable.renderBounds(color: Color = LorenzColor.GREEN.toColor().addAlpha(100)) = object : Renderable {
override val width = this@renderBounds.width
override val height = this@renderBounds.height
override val horizontalAlign = this@renderBounds.horizontalAlign
@@ -846,5 +873,85 @@ interface Renderable {
GlStateManager.translate(-padding.toFloat(), -padding.toFloat(), 0f)
}
}
+
+ fun drawInsideRoundedRectWithOutline(
+ input: Renderable,
+ color: Color,
+ padding: Int = 2,
+ radius: Int = 10,
+ smoothness: Int = 2,
+ topOutlineColor: Int,
+ bottomOutlineColor: Int,
+ borderOutlineThickness: Int,
+ blur: Float = 0.7f,
+ horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT,
+ verticalAlign: VerticalAlignment = VerticalAlignment.TOP,
+ ) = object : Renderable {
+ override val width = input.width + padding * 2
+ override val height = input.height + padding * 2
+ override val horizontalAlign = horizontalAlign
+ override val verticalAlign = verticalAlign
+
+ override fun render(posX: Int, posY: Int) {
+ RenderUtils.drawRoundRect(0, 0, width, height, color.rgb, radius, smoothness)
+ RenderUtils.drawRoundRectOutline(
+ 0,
+ 0,
+ width,
+ height,
+ topOutlineColor,
+ bottomOutlineColor,
+ borderOutlineThickness,
+ radius,
+ blur
+ )
+
+ GlStateManager.translate(padding.toFloat(), padding.toFloat(), 0f)
+ input.render(posX + padding, posY + padding)
+ GlStateManager.translate(-padding.toFloat(), -padding.toFloat(), 0f)
+ }
+ }
+
+ fun fakePlayer(
+ player: EntityPlayer,
+ followMouse: Boolean = false,
+ eyesX: Float = 0f,
+ eyesY: Float = 0f,
+ width: Int = 50,
+ height: Int = 100,
+ entityScale: Int = 30,
+ padding: Int = 5,
+ color: Int? = null,
+ colorCondition: () -> Boolean = { true },
+ ) = object : Renderable {
+ override val width = width + 2 * padding
+ override val height = height + 2 * padding
+ override val horizontalAlign = HorizontalAlignment.LEFT
+ override val verticalAlign = VerticalAlignment.TOP
+ val playerWidth = entityScale
+ val playerHeight = entityScale * 2
+ val playerX = width / 2 + padding
+ val playerY = height / 2 + playerHeight / 2 + padding
+
+ override fun render(posX: Int, posY: Int) {
+ GlStateManager.color(1f, 1f, 1f, 1f)
+ if (color != null) RenderLivingEntityHelper.setEntityColor(player, color, colorCondition)
+ val mouse = currentRenderPassMousePosition ?: return
+ val mouseXRelativeToPlayer =
+ if (followMouse) (posX + playerX - mouse.first).toFloat() else eyesX
+ val mouseYRelativeToPlayer =
+ if (followMouse) (posY + playerY - mouse.second - 1.62 * entityScale).toFloat() else eyesY
+ GlStateManager.translate(0f, 0f, 100f)
+ drawEntityOnScreen(
+ playerX,
+ playerY,
+ entityScale,
+ mouseXRelativeToPlayer,
+ mouseYRelativeToPlayer,
+ player
+ )
+ GlStateManager.translate(0f, 0f, -100f)
+ }
+ }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt
index 7fdaba165..0d2c88785 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt
@@ -75,7 +75,7 @@ object RenderableTooltips {
RenderHelper.disableStandardItemLighting()
GlStateManager.enableDepth()
- val zLevel = 300f
+ val zLevel = 400f
GlStateManager.translate(tooltipX.toFloat(), tooltipY.toFloat(), zLevel)
RenderUtils.drawGradientRect(
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt
index cf81fc9ff..d1a8aae02 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt
@@ -48,25 +48,28 @@ internal object RenderableUtils {
else -> 0
}
- fun Renderable.renderXYAligned(posX: Int, posY: Int, xSpace: Int, ySpace: Int) {
+ fun Renderable.renderXYAligned(posX: Int, posY: Int, xSpace: Int, ySpace: Int): Pair<Int, Int> {
val xOffset = calculateAlignmentXOffset(this, xSpace)
val yOffset = calculateAlignmentYOffset(this, ySpace)
GlStateManager.translate(xOffset.toFloat(), yOffset.toFloat(), 0f)
this.render(posX + xOffset, posY + yOffset)
GlStateManager.translate(-xOffset.toFloat(), -yOffset.toFloat(), 0f)
+ return xOffset to yOffset
}
- fun Renderable.renderXAligned(posX: Int, posY: Int, xSpace: Int) {
+ fun Renderable.renderXAligned(posX: Int, posY: Int, xSpace: Int): Int {
val xOffset = calculateAlignmentXOffset(this, xSpace)
GlStateManager.translate(xOffset.toFloat(), 0f, 0f)
this.render(posX + xOffset, posY)
GlStateManager.translate(-xOffset.toFloat(), 0f, 0f)
+ return xOffset
}
- fun Renderable.renderYAligned(posX: Int, posY: Int, ySpace: Int) {
+ fun Renderable.renderYAligned(posX: Int, posY: Int, ySpace: Int): Int {
val yOffset = calculateAlignmentYOffset(this, ySpace)
GlStateManager.translate(0f, yOffset.toFloat(), 0f)
this.render(posX, posY + yOffset)
GlStateManager.translate(0f, -yOffset.toFloat(), 0f)
+ return yOffset
}
}