diff --git a/src/main/java/mcp/mobius/waila/api/IWailaDataProvider.java b/src/main/java/mcp/mobius/waila/api/IWailaDataProvider.java index dca63ce6..53b3467e 100644 --- a/src/main/java/mcp/mobius/waila/api/IWailaDataProvider.java +++ b/src/main/java/mcp/mobius/waila/api/IWailaDataProvider.java @@ -107,6 +107,25 @@ default List getWailaAdvancedBody(ItemStack itemStack, List curr List getWailaTail(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor, IWailaConfigHandler config); + /** + * Callback used to add icons to the bottom right of the tooltip.
+ * Works exactly like the Head/Body/Tail callbacks but with {@link IWailaInfoIcon} objects instead of strings. Will + * be used if the implementing class is registered via {@link IWailaRegistrar}.{@link registerInfoIconProvider} + * client side.
+ * You are supposed to always return the modified input currentIcons.
+ * + * @param itemStack Current block scanned, in ItemStack form. + * @param currentIcons Current list of tooltip icons (might have been processed by other providers and might be + * processed by other providers). + * @param accessor Contains most of the relevant information about the current environment. + * @param config Current configuration of Waila. + * @return Modified input currentIcons + */ + default List getWailaInfoIcon(ItemStack itemStack, List currentIcons, + IWailaDataAccessor accessor, IWailaConfigHandler config) { + return currentIcons; + } + /** * Callback used server side to return a custom synchronization NBTTagCompound.
* Will be used if the implementing class is registered via {@link IWailaRegistrar}.{@link registerNBTProvider} diff --git a/src/main/java/mcp/mobius/waila/api/IWailaEntityProvider.java b/src/main/java/mcp/mobius/waila/api/IWailaEntityProvider.java index fb4774c0..41e778f6 100644 --- a/src/main/java/mcp/mobius/waila/api/IWailaEntityProvider.java +++ b/src/main/java/mcp/mobius/waila/api/IWailaEntityProvider.java @@ -77,6 +77,25 @@ List getWailaBody(Entity entity, List currenttip, IWailaEntityAc List getWailaTail(Entity entity, List currenttip, IWailaEntityAccessor accessor, IWailaConfigHandler config); + /** + * Callback used to add icons to the bottom right of the tooltip.
+ * Works exactly like the Head/Body/Tail callbacks but with {@link IWailaInfoIcon} objects instead of strings. Will + * be used if the implementing class is registered via {@link IWailaRegistrar}.{@link registerInfoIconProvider} + * client side.
+ * You are supposed to always return the modified input currentIcons.
+ * + * @param itemStack Current block scanned, in ItemStack form. + * @param currentIcons Current list of tooltip icons (might have been processed by other providers and might be + * processed by other providers). + * @param accessor Contains most of the relevant information about the current environment. + * @param config Current configuration of Waila. + * @return Modified input currentIcons + */ + default List getWailaInfoIcon(Entity entity, List currentIcons, + IWailaDataAccessor accessor, IWailaConfigHandler config) { + return currentIcons; + } + /** * Callback used server side to return a custom synchronization NBTTagCompound.
* Will be used if the implementing class is registered via {@link IWailaRegistrar}.{@link registerNBTProvider} diff --git a/src/main/java/mcp/mobius/waila/api/IWailaInfoIcon.java b/src/main/java/mcp/mobius/waila/api/IWailaInfoIcon.java new file mode 100644 index 00000000..7d7e6c51 --- /dev/null +++ b/src/main/java/mcp/mobius/waila/api/IWailaInfoIcon.java @@ -0,0 +1,31 @@ +package mcp.mobius.waila.api; + +/** + * Represents a single icon in the bottom right of the waila tooltip and is the icon equivalent of the line strings + * returned by the getWailaBody/Head/Tail callbacks.
+ * + * Info icons mirror line strings as much as possible and {@link IWailaDataProvider}s can provide them by overriding the + * getWailaInfoIcon method. + * + * @author SuperSouper + * + */ +public interface IWailaInfoIcon { + + /** + * Returns the width of the icon.
+ * No padding is added if returns 0. + * + * @param accessor A global accessor for TileEntities and Entities + * @return The width in pixels. + */ + int getWidth(IWailaCommonAccessor accessor); + + /** + * Draws the icon onto the tooltip.
+ * Position is already translated so that 0,0 is the top-left position of the icon. + * + * @param accessor A global accessor for TileEntities and Entities + */ + void draw(IWailaCommonAccessor accessor); +} diff --git a/src/main/java/mcp/mobius/waila/api/IWailaRegistrar.java b/src/main/java/mcp/mobius/waila/api/IWailaRegistrar.java index 66874a1f..e7bad4e7 100644 --- a/src/main/java/mcp/mobius/waila/api/IWailaRegistrar.java +++ b/src/main/java/mcp/mobius/waila/api/IWailaRegistrar.java @@ -39,6 +39,8 @@ public interface IWailaRegistrar { void addConfigRemote(String modname, String keyname, boolean defvalue); + void registerInfoIconProvider(IWailaDataProvider dataProvider, Class block); + /* Register a stack overrider for the given blockID */ void registerStackProvider(IWailaDataProvider dataProvider, Class block); @@ -61,6 +63,8 @@ public interface IWailaRegistrar { void registerOverrideEntityProvider(IWailaEntityProvider dataProvider, Class entity); + void registerInfoIconProvider(IWailaEntityProvider dataProvider, Class entity); + /* Registering an NBT Provider provides a way to override the default "writeToNBT" way of doing things. */ void registerNBTProvider(IWailaEntityProvider dataProvider, Class entity); diff --git a/src/main/java/mcp/mobius/waila/api/impl/ConfigHandler.java b/src/main/java/mcp/mobius/waila/api/impl/ConfigHandler.java index 304c7c21..0ae78bd2 100644 --- a/src/main/java/mcp/mobius/waila/api/impl/ConfigHandler.java +++ b/src/main/java/mcp/mobius/waila/api/impl/ConfigHandler.java @@ -36,6 +36,8 @@ public static ConfigHandler instance() { public String fluidUnit; + public static int infoIconHeight = 8; + public void addModule(String modName, HashMap options) { this.addModule(modName, new ConfigModule(modName, options)); } diff --git a/src/main/java/mcp/mobius/waila/api/impl/MetaDataProvider.java b/src/main/java/mcp/mobius/waila/api/impl/MetaDataProvider.java index 2ea627d0..0859b82a 100644 --- a/src/main/java/mcp/mobius/waila/api/impl/MetaDataProvider.java +++ b/src/main/java/mcp/mobius/waila/api/impl/MetaDataProvider.java @@ -18,12 +18,14 @@ import mcp.mobius.waila.api.IWailaBlock; import mcp.mobius.waila.api.IWailaDataProvider; import mcp.mobius.waila.api.IWailaEntityProvider; +import mcp.mobius.waila.api.IWailaInfoIcon; import mcp.mobius.waila.cbcore.LangUtil; import mcp.mobius.waila.cbcore.Layout; import mcp.mobius.waila.client.KeyEvent; import mcp.mobius.waila.network.Message0x01TERequest; import mcp.mobius.waila.network.Message0x03EntRequest; import mcp.mobius.waila.network.WailaPacketHandler; +import mcp.mobius.waila.overlay.infoicons.StringInfoIcon; import mcp.mobius.waila.utils.WailaExceptionHandler; public class MetaDataProvider { @@ -31,10 +33,12 @@ public class MetaDataProvider { private final Map> headBlockProviders = new TreeMap<>(); private final Map> bodyBlockProviders = new TreeMap<>(); private Map> tailBlockProviders = new TreeMap<>(); + private final Map> infoIconBlockProviders = new TreeMap<>(); private final Map> headEntityProviders = new TreeMap<>(); private final Map> bodyEntityProviders = new TreeMap<>(); private final Map> tailEntityProviders = new TreeMap<>(); + private final Map> infoIconEntityProviders = new TreeMap<>(); public ItemStack identifyBlockHighlight(World world, EntityPlayer player, MovingObjectPosition mop, DataAccessorCommon accessor) { @@ -248,4 +252,52 @@ else if (layout == Layout.FOOTER && ModuleRegistrar.instance().hasTailEntityProv return currenttip; } + + public List handleBlockInfoIconData(ItemStack itemStack, World world, EntityPlayer player, + MovingObjectPosition mop, DataAccessorCommon accessor, List currentIcons) { + // NBT networking stuff already handled in handleBlockTextData + + Block block = accessor.getBlock(); + infoIconBlockProviders.clear(); + + if (ModuleRegistrar.instance().hasInfoIconProviders(block)) + infoIconBlockProviders.putAll(ModuleRegistrar.instance().getInfoIconProviders(block)); + + if (ModuleRegistrar.instance().hasInfoIconProviders(accessor.getTileEntity())) + infoIconBlockProviders.putAll(ModuleRegistrar.instance().getInfoIconProviders(accessor.getTileEntity())); + + for (List providersList : infoIconBlockProviders.values()) { + for (IWailaDataProvider dataProvider : providersList) try { + currentIcons = dataProvider + .getWailaInfoIcon(itemStack, currentIcons, accessor, ConfigHandler.instance()); + } catch (Throwable e) { + WailaExceptionHandler.handleErr(e, dataProvider.getClass().toString(), null); + currentIcons.add(new StringInfoIcon("")); + } + } + return currentIcons; + } + + public List handleEntityInfoIconData(Entity entity, World world, EntityPlayer player, + MovingObjectPosition mop, DataAccessorCommon accessor, List currentIcons) { + // NBT networking stuff already handled in handleBlockTextData + + infoIconEntityProviders.clear(); + + if (ModuleRegistrar.instance().hasInfoIconProviders(entity)) + infoIconEntityProviders.putAll(ModuleRegistrar.instance().getInfoIconEntityProviders(entity)); + + if (ModuleRegistrar.instance().hasInfoIconProviders(accessor.getTileEntity())) infoIconEntityProviders + .putAll(ModuleRegistrar.instance().getInfoIconEntityProviders(accessor.getTileEntity())); + + for (List providersList : infoIconEntityProviders.values()) { + for (IWailaEntityProvider dataProvider : providersList) try { + currentIcons = dataProvider.getWailaInfoIcon(entity, currentIcons, accessor, ConfigHandler.instance()); + } catch (Throwable e) { + WailaExceptionHandler.handleErr(e, dataProvider.getClass().toString(), null); + currentIcons.add(new StringInfoIcon("")); + } + } + return currentIcons; + } } diff --git a/src/main/java/mcp/mobius/waila/api/impl/ModuleRegistrar.java b/src/main/java/mcp/mobius/waila/api/impl/ModuleRegistrar.java index 9ad57c09..db5287d8 100644 --- a/src/main/java/mcp/mobius/waila/api/impl/ModuleRegistrar.java +++ b/src/main/java/mcp/mobius/waila/api/impl/ModuleRegistrar.java @@ -32,6 +32,7 @@ public class ModuleRegistrar implements IWailaRegistrar { public LinkedHashMap> headBlockProviders = new LinkedHashMap<>(); public LinkedHashMap> bodyBlockProviders = new LinkedHashMap<>(); public LinkedHashMap> tailBlockProviders = new LinkedHashMap<>(); + public LinkedHashMap> infoIconBlockProviders = new LinkedHashMap<>(); public LinkedHashMap> stackBlockProviders = new LinkedHashMap<>(); public LinkedHashMap> NBTDataProviders = new LinkedHashMap<>(); @@ -40,6 +41,7 @@ public class ModuleRegistrar implements IWailaRegistrar { public LinkedHashMap> headEntityProviders = new LinkedHashMap<>(); public LinkedHashMap> bodyEntityProviders = new LinkedHashMap<>(); public LinkedHashMap> tailEntityProviders = new LinkedHashMap<>(); + public LinkedHashMap> infoIconEntityProviders = new LinkedHashMap<>(); public LinkedHashMap> overrideEntityProviders = new LinkedHashMap<>(); public LinkedHashMap> NBTEntityProviders = new LinkedHashMap<>(); @@ -129,6 +131,11 @@ public void registerTailProvider(IWailaDataProvider dataProvider, Class block) { this.registerProvider(dataProvider, block, this.tailBlockProviders); } + @Override + public void registerInfoIconProvider(IWailaDataProvider dataProvider, Class block) { + this.registerProvider(dataProvider, block, this.infoIconBlockProviders); + } + @Override public void registerStackProvider(IWailaDataProvider dataProvider, Class block) { this.registerProvider(dataProvider, block, this.stackBlockProviders); @@ -154,6 +161,11 @@ public void registerTailProvider(IWailaEntityProvider dataProvider, Class entity this.registerProvider(dataProvider, entity, this.tailEntityProviders); } + @Override + public void registerInfoIconProvider(IWailaEntityProvider dataProvider, Class entity) { + this.registerProvider(dataProvider, entity, this.infoIconEntityProviders); + } + @Override public void registerNBTProvider(IWailaEntityProvider dataProvider, Class entity) { this.registerProvider(dataProvider, entity, this.NBTEntityProviders); @@ -241,6 +253,10 @@ public Map> getTailProviders(Object block) { return getProviders(block, this.tailBlockProviders); } + public Map> getInfoIconProviders(Object block) { + return getProviders(block, this.infoIconBlockProviders); + } + public Map> getStackProviders(Object block) { return getProviders(block, this.stackBlockProviders); } @@ -261,6 +277,10 @@ public Map> getTailEntityProviders(Object en return getProviders(entity, this.tailEntityProviders); } + public Map> getInfoIconEntityProviders(Object entity) { + return getProviders(entity, this.infoIconEntityProviders); + } + public Map> getOverrideEntityProviders(Object entity) { return getProviders(entity, this.overrideEntityProviders); } @@ -343,6 +363,10 @@ public boolean hasTailProviders(Object block) { return hasProviders(block, this.tailBlockProviders); } + public boolean hasInfoIconProviders(Object block) { + return hasProviders(block, this.infoIconBlockProviders); + } + public boolean hasNBTProviders(Object block) { return hasProviders(block, this.NBTDataProviders); } @@ -359,6 +383,10 @@ public boolean hasTailEntityProviders(Object entity) { return hasProviders(entity, this.tailEntityProviders); } + public boolean hasInfoIconEntityProviders(Object entity) { + return hasProviders(entity, this.infoIconEntityProviders); + } + public boolean hasOverrideEntityProviders(Object entity) { return hasProviders(entity, this.overrideEntityProviders); } diff --git a/src/main/java/mcp/mobius/waila/client/ProxyClient.java b/src/main/java/mcp/mobius/waila/client/ProxyClient.java index 9aee88b3..fa300bdb 100644 --- a/src/main/java/mcp/mobius/waila/client/ProxyClient.java +++ b/src/main/java/mcp/mobius/waila/client/ProxyClient.java @@ -43,10 +43,12 @@ public void registerHandlers() { ModuleRegistrar.instance().registerHeadProvider(new HUDHandlerBlocks(), Block.class); ModuleRegistrar.instance().registerTailProvider(new HUDHandlerBlocks(), Block.class); + ModuleRegistrar.instance().registerInfoIconProvider(new HUDHandlerBlocks(), Block.class); ModuleRegistrar.instance().registerHeadProvider(new HUDHandlerEntities(), Entity.class); ModuleRegistrar.instance().registerBodyProvider(new HUDHandlerEntities(), Entity.class); ModuleRegistrar.instance().registerTailProvider(new HUDHandlerEntities(), Entity.class); + ModuleRegistrar.instance().registerInfoIconProvider(new HUDHandlerEntities(), Entity.class); ModuleRegistrar.instance().addConfig("General", "general.showents"); ModuleRegistrar.instance().addConfig("General", "general.showhp"); diff --git a/src/main/java/mcp/mobius/waila/overlay/OverlayRenderer.java b/src/main/java/mcp/mobius/waila/overlay/OverlayRenderer.java index 86a9b945..3d61cd64 100644 --- a/src/main/java/mcp/mobius/waila/overlay/OverlayRenderer.java +++ b/src/main/java/mcp/mobius/waila/overlay/OverlayRenderer.java @@ -63,6 +63,8 @@ private static void doRenderOverlay(Tooltip tooltip) { tooltip.draw2nd(); + tooltip.drawInfoIcons(); + if (tooltip.hasIcon) RenderHelper.enableGUIStandardItemLighting(); GL11.glEnable(GL12.GL_RESCALE_NORMAL); @@ -92,11 +94,11 @@ private static void loadGLState() { } private static void drawTooltipBox(int x, int y, int w, int h, int bg, int grad1, int grad2) { - DisplayUtil.drawGradientRect(x + 1, y, w - 1, 1, bg, bg); - DisplayUtil.drawGradientRect(x + 1, y + h, w - 1, 1, bg, bg); - DisplayUtil.drawGradientRect(x + 1, y + 1, w - 1, h - 1, bg, bg);// center - DisplayUtil.drawGradientRect(x, y + 1, 1, h - 1, bg, bg); - DisplayUtil.drawGradientRect(x + w, y + 1, 1, h - 1, bg, bg); + DisplayUtil.drawGradientRect(x + 1, y, w - 1, 1, bg, bg); // top outer line + DisplayUtil.drawGradientRect(x + 1, y + h, w - 1, 1, bg, bg); // bottom outer line + DisplayUtil.drawGradientRect(x + 1, y + 1, w - 1, h - 1, bg, bg); // fill + DisplayUtil.drawGradientRect(x, y + 1, 1, h - 1, bg, bg); // left outer line + DisplayUtil.drawGradientRect(x + w, y + 1, 1, h - 1, bg, bg); // right outer line DisplayUtil.drawGradientRect(x + 1, y + 2, 1, h - 3, grad1, grad2); DisplayUtil.drawGradientRect(x + w - 1, y + 2, 1, h - 3, grad1, grad2); DisplayUtil.drawGradientRect(x + 1, y + 1, w - 1, 1, grad1, grad1); diff --git a/src/main/java/mcp/mobius/waila/overlay/Tooltip.java b/src/main/java/mcp/mobius/waila/overlay/Tooltip.java index 1c12ca5d..e281619b 100644 --- a/src/main/java/mcp/mobius/waila/overlay/Tooltip.java +++ b/src/main/java/mcp/mobius/waila/overlay/Tooltip.java @@ -20,6 +20,7 @@ import org.lwjgl.opengl.GL11; import mcp.mobius.waila.api.IWailaCommonAccessor; +import mcp.mobius.waila.api.IWailaInfoIcon; import mcp.mobius.waila.api.IWailaTooltipRenderer; import mcp.mobius.waila.api.IWailaVariableWidthTooltipRenderer; import mcp.mobius.waila.api.SpecialChars; @@ -42,6 +43,7 @@ public class Tooltip { ArrayList elements = new ArrayList<>(); ArrayList elements2nd = new ArrayList<>(); + ArrayList infoIcons = new ArrayList<>(); int w, h, x, y, ty; int offsetX; @@ -102,11 +104,19 @@ public String toString() { //////////////////////////////////////////////////////////////////////////// public Tooltip(List textData, ItemStack stack) { - this(textData, true); + this(textData, stack, null); + } + + public Tooltip(List textData, ItemStack stack, List infoIcons) { + this(textData, infoIcons, true); this.stack = stack; } public Tooltip(List textData, boolean hasIcon) { + this(textData, null, hasIcon); + } + + public Tooltip(List textData, List infoIcons, boolean hasIcon) { if (hasIcon) hasIcon = ConfigHandler.instance().showIcon(); @@ -153,8 +163,33 @@ public Tooltip(List textData, boolean hasIcon) { tmp += TabSpacing * (columnsWidth.size() - 1); maxStringW = Math.max(maxStringW, tmp); + if (infoIcons != null) { + tmp = 0; + for (IWailaInfoIcon icon : infoIcons) { + tmp += icon.getWidth(accessor); + } + maxStringW = Math.max( + maxStringW, + DisplayUtil.getDisplayWidth(textData.get(textData.size() - 1)) + tmp + infoIcons.size()); + } + this.computeRenderables(); this.computePositionAndSize(hasIcon); + this.computeInfoIconRenderables(infoIcons); + } + + private void computeInfoIconRenderables(List infoIcons) { + if (infoIcons == null) return; + + int xOffset = -(hasIcon ? 26 : 8); + int yOffset = -(ConfigHandler.infoIconHeight + 8); + for (IWailaInfoIcon icon : infoIcons) { + int width = icon.getWidth(accessor); + if (width > 0) { + xOffset -= width + 1; + this.infoIcons.add(new TooltipInfoIconRenderable(icon, new Point(w + xOffset, h + yOffset))); + } + } } private void computeRenderables() { @@ -250,4 +285,8 @@ public void draw() { public void draw2nd() { for (Renderable r : this.elements2nd) r.draw(accessor, x + offsetX, y + ty); } + + public void drawInfoIcons() { + for (TooltipInfoIconRenderable r : this.infoIcons) r.draw(accessor, x + offsetX, y + ty); + } } diff --git a/src/main/java/mcp/mobius/waila/overlay/TooltipInfoIconRenderable.java b/src/main/java/mcp/mobius/waila/overlay/TooltipInfoIconRenderable.java new file mode 100644 index 00000000..e9061fe1 --- /dev/null +++ b/src/main/java/mcp/mobius/waila/overlay/TooltipInfoIconRenderable.java @@ -0,0 +1,52 @@ +package mcp.mobius.waila.overlay; + +import java.awt.*; + +import org.lwjgl.opengl.GL11; + +import mcp.mobius.waila.api.IWailaCommonAccessor; +import mcp.mobius.waila.api.IWailaInfoIcon; +import mcp.mobius.waila.utils.WailaExceptionHandler; + +// This is the icon equivalent to the private ToolTip.Renderable class. +// Holds the icon's position and renders it with translation. +class TooltipInfoIconRenderable { + + final IWailaInfoIcon icon; + final Point pos; + + public TooltipInfoIconRenderable(IWailaInfoIcon icon, Point pos) { + this.icon = icon; + this.pos = pos; + } + + public Point getPos() { + return this.pos; + } + + public int getWidth(IWailaCommonAccessor accessor) { + int width = 0; + try { + width = this.icon.getWidth(accessor); + } catch (Throwable e) { + WailaExceptionHandler.handleErr(e, this.icon.getClass().getName() + ".getWidth()", null); + } + return width; + } + + public void draw(IWailaCommonAccessor accessor, int x, int y) { + GL11.glPushMatrix(); + GL11.glTranslatef(x + this.pos.x, y + this.pos.y, 0); + try { + this.icon.draw(accessor); + } catch (Throwable e) { + WailaExceptionHandler.handleErr(e, this.icon.getClass().getName() + ".draw()", null); + } + GL11.glPopMatrix(); + } + + @Override + public String toString() { + return String.format("TooltipInfoIconRenderable@[%d,%d] | %s", pos.x, pos.y, icon); + } +} diff --git a/src/main/java/mcp/mobius/waila/overlay/WailaTickHandler.java b/src/main/java/mcp/mobius/waila/overlay/WailaTickHandler.java index ba7733bc..8caaf25d 100644 --- a/src/main/java/mcp/mobius/waila/overlay/WailaTickHandler.java +++ b/src/main/java/mcp/mobius/waila/overlay/WailaTickHandler.java @@ -2,6 +2,7 @@ import static mcp.mobius.waila.api.SpecialChars.ITALIC; +import java.util.ArrayList; import java.util.List; import net.minecraft.client.Minecraft; @@ -14,6 +15,7 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; +import mcp.mobius.waila.api.IWailaInfoIcon; import mcp.mobius.waila.api.impl.ConfigHandler; import mcp.mobius.waila.api.impl.DataAccessorCommon; import mcp.mobius.waila.api.impl.MetaDataProvider; @@ -54,6 +56,7 @@ public void tickClient(TickEvent.ClientTickEvent event) { List currenttipHead; List currenttipBody; List currenttipTail; + List currentTipInfoIcons; if (target != null && target.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { DataAccessorCommon accessor = DataAccessorCommon.instance; accessor.set(world, player, target); @@ -97,7 +100,11 @@ public void tickClient(TickEvent.ClientTickEvent event) { currenttip.addAll(currenttipBody); currenttip.addAll(currenttipTail); - this.tooltip = new Tooltip(currenttip, targetStack); + currentTipInfoIcons = new ArrayList(); + currentTipInfoIcons = handler + .handleBlockInfoIconData(targetStack, world, player, target, accessor, currentTipInfoIcons); + + this.tooltip = new Tooltip(currenttip, targetStack, currentTipInfoIcons); } } else if (target != null && target.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY) { DataAccessorCommon accessor = DataAccessorCommon.instance; diff --git a/src/main/java/mcp/mobius/waila/overlay/infoicons/BlockInfoIcon.java b/src/main/java/mcp/mobius/waila/overlay/infoicons/BlockInfoIcon.java new file mode 100644 index 00000000..a47bda06 --- /dev/null +++ b/src/main/java/mcp/mobius/waila/overlay/infoicons/BlockInfoIcon.java @@ -0,0 +1,363 @@ +package mcp.mobius.waila.overlay.infoicons; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.util.ForgeDirection; + +import org.lwjgl.opengl.GL11; + +import mcp.mobius.waila.api.IWailaCommonAccessor; +import mcp.mobius.waila.api.IWailaInfoIcon; +import mcp.mobius.waila.api.impl.ConfigHandler; + +/** + * Draws a fake semi-transparent block that is facing the exact same direction as the block that the player is looking + * at.
+ * Texture and UV can be specified in the constructor(s).
+ * Overlays can be added with the withSideOverlay method.
+ *
+ * + * Example usage:
+ * currentIcons.add(new BlockInfoIcon(new ResourceLocation("textures/blocks/stone_slab_top.png"), 0d, 1d, 0d, 1d) + * .withSideOverlay(ForgeDirection.EAST, new ResourceLocation("waila", "textures/o.png")) + * .withSideOverlay(ForgeDirection.NORTH, new ResourceLocation("waila", "textures/x.png")));
+ *
+ * + * Intended to be as simple visually as possible so it can be read while making it very small. Only supports using the + * same texture for all the faces of the block.
+ *
+ * + * Made specifically for GregTech to show where machines are facing. + * + * @author SuperSouper + * + */ +public class BlockInfoIcon implements IWailaInfoIcon { + + private final ResourceLocation texture; + + private final double minU; + private final double maxU; + private final double minV; + private final double maxV; + + protected List sideOverlays = new ArrayList<>(); + + float size = (float) (3.3 * ConfigHandler.infoIconHeight) / 8; + + public BlockInfoIcon(Block block) { + this(block, 0, 0); + } + + public BlockInfoIcon(Block block, int side, int meta) { + this( + TextureMap.locationBlocksTexture, + block.getIcon(side, meta).getMinU(), + block.getIcon(side, meta).getMaxU(), + block.getIcon(side, meta).getMinV(), + block.getIcon(side, meta).getMaxV()); + } + + public BlockInfoIcon(IIcon iIcon) { + this(TextureMap.locationBlocksTexture, iIcon.getMinU(), iIcon.getMaxU(), iIcon.getMinV(), iIcon.getMaxV()); + } + + public BlockInfoIcon(ResourceLocation texture, double minU, double maxU, double minV, double maxV) { + this.texture = texture; + this.minU = minU; + this.maxU = maxU; + this.minV = minV; + this.maxV = maxV; + } + + @FunctionalInterface + public interface FaceOverlayDrawer { + + void draw(double size, double minU, double maxU, double minV, double maxV); + } + + private class SideOverlay { + + private final ResourceLocation overlayTexture; + private final FaceOverlayDrawer overlayDrawerFront; + private final FaceOverlayDrawer overlayDrawerBack; + private final double size; + private final double overlayMinU; + private final double overlayMaxU; + private final double overlayMinV; + private final double overlayMaxV; + + SideOverlay(ResourceLocation overlayTexture, double size, FaceOverlayDrawer overlayDrawerFront, + FaceOverlayDrawer overlayDrawerBack, double overlayMinU, double overlayMaxU, double overlayMinV, + double overlayMaxV) { + this.overlayTexture = overlayTexture; + this.overlayDrawerFront = overlayDrawerFront; + this.overlayDrawerBack = overlayDrawerBack; + this.size = size; + this.overlayMinU = overlayMinU; + this.overlayMaxU = overlayMaxU; + this.overlayMinV = overlayMinV; + this.overlayMaxV = overlayMaxV; + } + + protected void drawFront() { + Minecraft.getMinecraft().renderEngine.bindTexture(overlayTexture); + GL11.glColor4f(1F, 1F, 1F, 1F); + tessellator.startDrawingQuads(); + overlayDrawerFront.draw(size, overlayMinU, overlayMaxU, overlayMinV, overlayMaxV); + tessellator.draw(); + } + + protected void drawBack() { + Minecraft.getMinecraft().renderEngine.bindTexture(overlayTexture); + GL11.glColor4f(1F, 1F, 1F, 0.25F); + tessellator.startDrawingQuads(); + overlayDrawerBack.draw(size, overlayMinU, overlayMaxU, overlayMinV, overlayMaxV); + tessellator.draw(); + } + } + + public BlockInfoIcon withSideOverlay(ForgeDirection side, ResourceLocation texture) { + return withSideOverlay(side, texture, 0d, 1d, 0d, 1d); + } + + public BlockInfoIcon withSideOverlay(ForgeDirection side, ResourceLocation texture, double overlayMinU, + double overlayMaxU, double overlayMinV, double overlayMaxV) { + FaceOverlayDrawer overlayDrawerFront; + FaceOverlayDrawer overlayDrawerBack; + switch (side) { + case DOWN: + overlayDrawerFront = this::drawBottomFaceFront; + overlayDrawerBack = this::drawBottomFaceBack; + break; + case UP: + overlayDrawerFront = this::drawTopFaceFront; + overlayDrawerBack = this::drawTopFaceBack; + break; + case NORTH: + overlayDrawerFront = this::drawNorthFaceFront; + overlayDrawerBack = this::drawNorthFaceBack; + break; + case SOUTH: + overlayDrawerFront = this::drawSouthFaceFront; + overlayDrawerBack = this::drawSouthFaceBack; + break; + case WEST: + overlayDrawerFront = this::drawWestFaceFront; + overlayDrawerBack = this::drawWestFaceBack; + break; + case EAST: + overlayDrawerFront = this::drawEastFaceFront; + overlayDrawerBack = this::drawEastFaceBack; + break; + default: + throw new IllegalArgumentException("Invalid side " + side); + } + + sideOverlays.add( + new SideOverlay( + texture, + size, + overlayDrawerFront, + overlayDrawerBack, + overlayMinU, + overlayMaxU, + overlayMinV, + overlayMaxV)); + + return this; + } + + @Override + public int getWidth(IWailaCommonAccessor accessor) { + return (int) Math.ceil(ConfigHandler.infoIconHeight * 1.33); + } + + @Override + public void draw(IWailaCommonAccessor accessor) { + Minecraft mc = Minecraft.getMinecraft(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); + GL11.glEnable(GL11.GL_POLYGON_SMOOTH); + + GL11.glTranslatef((float) getWidth(accessor) / 2, (float) ConfigHandler.infoIconHeight / 2, 0); + + // Calculate the rotation + float x1 = (float) mc.thePlayer.posX; + float y1 = (float) mc.thePlayer.posY; + float z1 = (float) mc.thePlayer.posZ; + float x2 = accessor.getPosition().blockX + 0.5f; + float y2 = accessor.getPosition().blockY + 0.5f; + float z2 = accessor.getPosition().blockZ + 0.5f; + + float hx = x2 - x1; + float hy = y2 - y1; + float hz = z2 - z1; + + // Don't ask me how this math works, chatgpt wrote it. + double deg = Math.atan2(hx, hz); + double deg2 = Math.acos(Math.max(-1.0, Math.min(1.0, hy / Math.sqrt(hx * hx + hy * hy + hz * hz)))) + + Math.PI * 0.55; + // Convert Euler angles to quaternion + float cy = (float) Math.cos(deg * 0.5); // Z rotation + float sy = (float) Math.sin(deg * 0.5); + float cr = (float) Math.cos(-deg2 * 0.5); // Y rotation + float sr = (float) Math.sin(-deg2 * 0.5); + + float w = cy * cr; + float x = cy * sr; + float y = sy * cr; + float z = sy * sr; + + // Convert quaternion to rotation matrix and apply + float angle = (float) (2.0 * Math.acos(w)); + float scale = (float) Math.sqrt(1.0 - w * w); + // DisplayUtil.drawString(mc.thePlayer.rotationYaw + "", 0, 100, 1, true); + if (scale < 0.001f) { + GL11.glRotatef(angle * 57.2958f, 1.0F, 0.0F, 0.0F); + } else { + GL11.glRotatef(angle * 57.2958f, x / scale, y / scale, z / scale); + } + + GL11.glColor4f(1F, 1F, 1F, 1F); + mc.renderEngine.bindTexture(texture); + tessellator.startDrawingQuads(); + tessellator.setColorRGBA(255, 255, 255, (int) (255 * 0.85)); + drawTopFaceFront(size, minU, maxU, minV, maxV); + drawNorthFaceFront(size, minU, maxU, minV, maxV); + drawBottomFaceFront(size, minU, maxU, minV, maxV); + drawSouthFaceFront(size, minU, maxU, minV, maxV); + drawEastFaceFront(size, minU, maxU, minV, maxV); + drawWestFaceFront(size, minU, maxU, minV, maxV); + + tessellator.setColorRGBA(255, 255, 255, (int) (255 * 0.25)); + drawTopFaceBack(size, minU, maxU, minV, maxV); + drawBottomFaceBack(size, minU, maxU, minV, maxV); + drawNorthFaceBack(size, minU, maxU, minV, maxV); + drawSouthFaceBack(size, minU, maxU, minV, maxV); + drawEastFaceBack(size, minU, maxU, minV, maxV); + drawWestFaceBack(size, minU, maxU, minV, maxV); + tessellator.draw(); + + for (SideOverlay sideOverlay : sideOverlays) { + sideOverlay.drawBack(); + } + + for (SideOverlay sideOverlay : sideOverlays) { + sideOverlay.drawFront(); + } + + GL11.glDisable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_POLYGON_SMOOTH); + + } + + Tessellator tessellator = Tessellator.instance; + + // bottom face + protected void drawBottomFaceFront(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(size, -size, size, maxU, maxV); + tessellator.addVertexWithUV(size, -size, -size, maxU, minV); + tessellator.addVertexWithUV(-size, -size, -size, minU, minV); + tessellator.addVertexWithUV(-size, -size, size, minU, maxV); + } + + // top face + protected void drawTopFaceFront(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, size, size, minU, maxV); + tessellator.addVertexWithUV(-size, size, -size, minU, minV); + tessellator.addVertexWithUV(size, size, -size, maxU, minV); + tessellator.addVertexWithUV(size, size, size, maxU, maxV); + } + + // north (z-negative) + protected void drawNorthFaceFront(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, -size, -size, maxU, maxV); + tessellator.addVertexWithUV(size, -size, -size, minU, maxV); + tessellator.addVertexWithUV(size, size, -size, minU, minV); + tessellator.addVertexWithUV(-size, size, -size, maxU, minV); + } + + // south (z-positive) + protected void drawSouthFaceFront(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(size, size, size, maxU, minV); + tessellator.addVertexWithUV(size, -size, size, maxU, maxV); + tessellator.addVertexWithUV(-size, -size, size, minU, maxV); + tessellator.addVertexWithUV(-size, size, size, minU, minV); + } + + // west (x-negative) face + protected void drawEastFaceFront(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, -size, size, maxU, maxV); + tessellator.addVertexWithUV(-size, -size, -size, minU, maxV); + tessellator.addVertexWithUV(-size, size, -size, minU, minV); + tessellator.addVertexWithUV(-size, size, size, maxU, minV); + } + + // east (x-positive) face + protected void drawWestFaceFront(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(size, size, size, minU, minV); + tessellator.addVertexWithUV(size, size, -size, maxU, minV); + tessellator.addVertexWithUV(size, -size, -size, maxU, maxV); + tessellator.addVertexWithUV(size, -size, size, minU, maxV); + } + + // bottom face + protected void drawBottomFaceBack(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, -size, size, minU, maxV); + tessellator.addVertexWithUV(-size, -size, -size, minU, minV); + tessellator.addVertexWithUV(size, -size, -size, maxU, minV); + tessellator.addVertexWithUV(size, -size, size, maxU, maxV); + } + + // top face + protected void drawTopFaceBack(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(size, size, size, maxU, maxV); + tessellator.addVertexWithUV(size, size, -size, maxU, minV); + tessellator.addVertexWithUV(-size, size, -size, minU, minV); + tessellator.addVertexWithUV(-size, size, size, minU, maxV); + } + + // north (z-negative) + protected void drawNorthFaceBack(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, size, -size, maxU, minV); + tessellator.addVertexWithUV(size, size, -size, minU, minV); + tessellator.addVertexWithUV(size, -size, -size, minU, maxV); + tessellator.addVertexWithUV(-size, -size, -size, maxU, maxV); + } + + // south (z-positive) + protected void drawSouthFaceBack(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, size, size, minU, minV); + tessellator.addVertexWithUV(-size, -size, size, minU, maxV); + tessellator.addVertexWithUV(size, -size, size, maxU, maxV); + tessellator.addVertexWithUV(size, size, size, maxU, minV); + } + + // west (x-negative) face + protected void drawEastFaceBack(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(-size, size, size, maxU, minV); + tessellator.addVertexWithUV(-size, size, -size, minU, minV); + tessellator.addVertexWithUV(-size, -size, -size, minU, maxV); + tessellator.addVertexWithUV(-size, -size, size, maxU, maxV); + } + + // east (x-positive) face + protected void drawWestFaceBack(double size, double minU, double maxU, double minV, double maxV) { + tessellator.addVertexWithUV(size, -size, size, minU, maxV); + tessellator.addVertexWithUV(size, -size, -size, maxU, maxV); + tessellator.addVertexWithUV(size, size, -size, maxU, minV); + tessellator.addVertexWithUV(size, size, size, minU, minV); + } + +} diff --git a/src/main/java/mcp/mobius/waila/overlay/infoicons/SimpleInfoIcon.java b/src/main/java/mcp/mobius/waila/overlay/infoicons/SimpleInfoIcon.java new file mode 100644 index 00000000..764d6ec2 --- /dev/null +++ b/src/main/java/mcp/mobius/waila/overlay/infoicons/SimpleInfoIcon.java @@ -0,0 +1,41 @@ +package mcp.mobius.waila.overlay.infoicons; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.ResourceLocation; + +import org.lwjgl.opengl.GL11; + +import mcp.mobius.waila.api.IWailaCommonAccessor; +import mcp.mobius.waila.api.IWailaInfoIcon; +import mcp.mobius.waila.api.impl.ConfigHandler; + +public class SimpleInfoIcon implements IWailaInfoIcon { + + private ResourceLocation texture; + + public SimpleInfoIcon(ResourceLocation t) { + texture = t; + } + + @Override + public int getWidth(IWailaCommonAccessor accessor) { + return ConfigHandler.infoIconHeight; + } + + @Override + public void draw(IWailaCommonAccessor accessor) { + Minecraft mc = Minecraft.getMinecraft(); + mc.renderEngine.bindTexture(texture); + GL11.glColor4f(1f, 1f, 1f, 1f); + + Tessellator tessellator = Tessellator.instance; + + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(0, ConfigHandler.infoIconHeight, 0, 0.0D, 1.0D); + tessellator.addVertexWithUV(ConfigHandler.infoIconHeight, ConfigHandler.infoIconHeight, 0, 1.0D, 1.0D); + tessellator.addVertexWithUV(ConfigHandler.infoIconHeight, 0, 0, 1.0D, 0.0D); + tessellator.addVertexWithUV(0, 0, 0, 0.0D, 0.0D); + tessellator.draw(); + } +} diff --git a/src/main/java/mcp/mobius/waila/overlay/infoicons/StringInfoIcon.java b/src/main/java/mcp/mobius/waila/overlay/infoicons/StringInfoIcon.java new file mode 100644 index 00000000..63325e67 --- /dev/null +++ b/src/main/java/mcp/mobius/waila/overlay/infoicons/StringInfoIcon.java @@ -0,0 +1,26 @@ +package mcp.mobius.waila.overlay.infoicons; + +import mcp.mobius.waila.api.IWailaCommonAccessor; +import mcp.mobius.waila.api.IWailaInfoIcon; +import mcp.mobius.waila.api.impl.ConfigHandler; +import mcp.mobius.waila.overlay.DisplayUtil; +import mcp.mobius.waila.overlay.OverlayConfig; + +public class StringInfoIcon implements IWailaInfoIcon { + + private String text; + + public StringInfoIcon(String s) { + text = s; + } + + @Override + public int getWidth(IWailaCommonAccessor accessor) { + return DisplayUtil.getDisplayWidth(text); + } + + @Override + public void draw(IWailaCommonAccessor accessor) { + DisplayUtil.drawString(text, 0, ConfigHandler.infoIconHeight - 8, OverlayConfig.fontcolor, true); + } +} diff --git a/src/main/resources/assets/waila/textures/o.png b/src/main/resources/assets/waila/textures/o.png new file mode 100644 index 00000000..b52d2a58 Binary files /dev/null and b/src/main/resources/assets/waila/textures/o.png differ diff --git a/src/main/resources/assets/waila/textures/x.png b/src/main/resources/assets/waila/textures/x.png new file mode 100644 index 00000000..2ba73431 Binary files /dev/null and b/src/main/resources/assets/waila/textures/x.png differ