diff --git a/src/main/java/com/gtnewhorizons/wdmla/config/General.java b/src/main/java/com/gtnewhorizons/wdmla/config/General.java index f063a8d8..615f6753 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/config/General.java +++ b/src/main/java/com/gtnewhorizons/wdmla/config/General.java @@ -19,6 +19,8 @@ public class General { public static TextColor textColor = new TextColor(); + public static ModName modName = new ModName(); + public static ProgressColor progressColor = new ProgressColor(); public static BreakProgress breakProgress = new BreakProgress(); @@ -53,6 +55,12 @@ public class General { @Config.RangeInt(min = 1, max = 10000) public static int maxNameLengthPixel; + @Config.LangKey("option.wdmla.general.mobhudrange") + @Config.DefaultInt(64) + @Config.RangeInt(min = 1, max = 256) + @Config.Comment("Maximum distance in blocks at which entities can show WDMla HUD. Only affects entities, not blocks.") + public static int mobHudRange; + @Config.LangKey("option.wdmla.general.align.icon.right.top") @Config.DefaultBoolean(true) @Config.Comment("Always put harvest / interaction icons at the right top corner of default block info") @@ -92,9 +100,45 @@ public static class TextColor { @Config.DefaultInt(ColorPalette.FAILURE) public int failure; - @Config.LangKey("option.wdmla.general.textcolor.modname") + } + + @Config.Comment("Mod name display settings.") + @Config.LangKey("option.wdmla.modname.category") + public static class ModName { + + @Config.LangKey("option.wdmla.modname.hud.show") + @Config.DefaultBoolean(true) + @Config.Comment("Show mod names in the HUD") + public boolean hudShow; + + @Config.LangKey("option.wdmla.modname.hud.italic") + @Config.DefaultBoolean(true) + @Config.Comment("Render mod names in italics in the HUD") + public boolean hudItalic; + + @Config.LangKey("option.wdmla.modname.hud.color") @Config.DefaultInt(ColorPalette.MOD_NAME) - public int modName; + public int hudColor; + + @Config.LangKey("option.wdmla.modname.hud.color.override") + @Config.DefaultString("") + @Config.Comment("Override HUD mod name color. Supports hex (#RRGGBB, 0xRRGGBB) or names like RED, BLUE, GRAY") + public String hudColorOverride; + + @Config.LangKey("option.wdmla.modname.inventory.show") + @Config.DefaultBoolean(true) + @Config.Comment("Show mod names in inventory tooltips") + public boolean inventoryShow; + + @Config.LangKey("option.wdmla.modname.inventory.italic") + @Config.DefaultBoolean(true) + @Config.Comment("Render mod names in italics in inventory tooltips") + public boolean inventoryItalic; + + @Config.LangKey("option.wdmla.modname.inventory.color.override") + @Config.DefaultString("") + @Config.Comment("Override inventory mod name color. Supports hex (#RRGGBB, 0xRRGGBB) or names like RED, BLUE, GRAY") + public String inventoryColorOverride; } @Config.Comment("The colors used in progress bar. \n" diff --git a/src/main/java/com/gtnewhorizons/wdmla/config/PluginsConfig.java b/src/main/java/com/gtnewhorizons/wdmla/config/PluginsConfig.java index 604dc3c9..3c450177 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/config/PluginsConfig.java +++ b/src/main/java/com/gtnewhorizons/wdmla/config/PluginsConfig.java @@ -88,6 +88,10 @@ public static class DefaultEntity { @Config.DefaultFloat(1.2f) @Config.RangeFloat(min = 0.1f, max = 100f) public float iconDefaultScale; + + @Config.LangKey("option.wdmla.core.show.hptext") + @Config.DefaultBoolean(true) + public boolean showHPText; } public static enum fancyRendererMode { diff --git a/src/main/java/com/gtnewhorizons/wdmla/config/WDMlaConfig.java b/src/main/java/com/gtnewhorizons/wdmla/config/WDMlaConfig.java index 68341046..2e0586ae 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/config/WDMlaConfig.java +++ b/src/main/java/com/gtnewhorizons/wdmla/config/WDMlaConfig.java @@ -104,7 +104,7 @@ private void reloadTheme() { General.textColor.warning, General.textColor.danger, General.textColor.failure, - General.textColor.modName); + General.modName.hudColor); } public > T loadEnum(String category, String name, T defaultValue, String comment) { diff --git a/src/main/java/com/gtnewhorizons/wdmla/example/ExampleHeaderProvider.java b/src/main/java/com/gtnewhorizons/wdmla/example/ExampleHeaderProvider.java index 0980a5cf..d389222e 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/example/ExampleHeaderProvider.java +++ b/src/main/java/com/gtnewhorizons/wdmla/example/ExampleHeaderProvider.java @@ -1,7 +1,5 @@ package com.gtnewhorizons.wdmla.example; -import static mcp.mobius.waila.api.SpecialChars.*; - import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; @@ -11,8 +9,8 @@ import com.gtnewhorizons.wdmla.api.accessor.BlockAccessor; import com.gtnewhorizons.wdmla.api.provider.IBlockComponentProvider; import com.gtnewhorizons.wdmla.api.ui.ITooltip; +import com.gtnewhorizons.wdmla.config.General; import com.gtnewhorizons.wdmla.impl.ui.ThemeHelper; -import com.gtnewhorizons.wdmla.impl.ui.component.TextComponent; public enum ExampleHeaderProvider implements IBlockComponentProvider { @@ -32,8 +30,10 @@ public int getDefaultPriority() { public void appendTooltip(ITooltip tooltip, BlockAccessor accessor) { ThemeHelper.INSTANCE.overrideTooltipIcon(tooltip, new ItemStack(Blocks.lit_furnace), true); ThemeHelper.INSTANCE.overrideTooltipTitle(tooltip, "Furnace"); - tooltip.replaceChildWithTag( - Identifiers.MOD_NAME, - new TextComponent(BLUE + ITALIC + "WDMla").tag(Identifiers.MOD_NAME)); + if (General.modName.hudShow) { + tooltip.replaceChildWithTag( + Identifiers.MOD_NAME, + ThemeHelper.INSTANCE.modName("WDMla").tag(Identifiers.MOD_NAME)); + } } } diff --git a/src/main/java/com/gtnewhorizons/wdmla/impl/ui/ThemeHelper.java b/src/main/java/com/gtnewhorizons/wdmla/impl/ui/ThemeHelper.java index f8f49d49..3944bd11 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/impl/ui/ThemeHelper.java +++ b/src/main/java/com/gtnewhorizons/wdmla/impl/ui/ThemeHelper.java @@ -32,6 +32,7 @@ import com.gtnewhorizons.wdmla.impl.ui.style.TextStyle; import com.gtnewhorizons.wdmla.overlay.WDMlaUIIcons; import com.gtnewhorizons.wdmla.plugin.vanilla.VanillaIdentifiers; +import com.gtnewhorizons.wdmla.util.Color; import com.gtnewhorizons.wdmla.util.FormatUtil; import mcp.mobius.waila.overlay.DisplayUtil; @@ -110,9 +111,7 @@ public void overrideTooltipModName(ITooltip root, ItemStack newItemStack) { } public void overrideTooltipModName(ITooltip root, String newName) { - Theme theme = General.currentTheme.get(); - IComponent replacedModName = new TextComponent(ITALIC + newName) - .style(new TextStyle().color(theme.textColor(MessageType.MOD_NAME))).tag(Identifiers.MOD_NAME); + IComponent replacedModName = modName(newName).tag(Identifiers.MOD_NAME); root.replaceChildWithTag(Identifiers.MOD_NAME, replacedModName); } @@ -122,6 +121,19 @@ public void overrideTooltipHeader(ITooltip root, ItemStack newItemStack) { overrideTooltipModName(root, newItemStack); } + public IComponent modName(String modName) { + String content = modName == null ? "" : modName; + if (General.modName.hudItalic && !content.isEmpty()) { + content = ITALIC + content; + } + return new TextComponent(content).style(new TextStyle().color(resolveModNameColor())); + } + + private int resolveModNameColor() { + int fallback = General.currentTheme.get().textColor(MessageType.MOD_NAME); + return Color.parseColor(General.modName.hudColorOverride, fallback); + } + public IComponent info(String content) { return color(content, MessageType.INFO); } diff --git a/src/main/java/com/gtnewhorizons/wdmla/impl/ui/drawable/BlockDrawable.java b/src/main/java/com/gtnewhorizons/wdmla/impl/ui/drawable/BlockDrawable.java index a29fa24e..50adf552 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/impl/ui/drawable/BlockDrawable.java +++ b/src/main/java/com/gtnewhorizons/wdmla/impl/ui/drawable/BlockDrawable.java @@ -8,6 +8,12 @@ import com.gtnewhorizons.wdmla.overlay.GuiBlockDraw; import mcp.mobius.waila.overlay.OverlayConfig; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import com.gtnewhorizons.wdmla.overlay.GuiDraw; public class BlockDrawable implements IDrawable { @@ -32,7 +38,7 @@ public void draw(IArea area) { rotationPitch += (Minecraft.getMinecraft().theWorld.getTotalWorldTime() - lastTime) * PluginsConfig.core.defaultBlock.rendererRotationSpeed; // custom viewport is unaffected by GLScalef - GuiBlockDraw.drawWorldBlock( + boolean rendered = GuiBlockDraw.drawWorldBlock( (int) ((area.getX() - area.getW() * (SIZE_MULTIPLIER - 1) / 2) * OverlayConfig.scale), (int) ((area.getY() - area.getH() * (SIZE_MULTIPLIER - 1) / 2) * OverlayConfig.scale), (int) (area.getW() * OverlayConfig.scale * SIZE_MULTIPLIER), @@ -42,6 +48,26 @@ public void draw(IArea area) { blockZ, 30f, rotationPitch); + + if (!rendered) { + // If the fancy world-block preview produced no geometry/TESR, fall back to the item icon. + Minecraft mc = Minecraft.getMinecraft(); + Block b = mc.theWorld.getBlock(blockX, blockY, blockZ); + if (b == null) b = Blocks.air; + int meta = mc.theWorld.getBlockMetadata(blockX, blockY, blockZ); + Item it = Item.getItemFromBlock(b); + ItemStack stack = null; + if (it != null) { + try { + int dmg = b.damageDropped(meta); + stack = new ItemStack(it, 1, dmg); + } catch (Throwable t) { + stack = new ItemStack(it, 1, meta); + } + } + if (stack == null) stack = new ItemStack(Blocks.air); + GuiDraw.renderStack(area, stack, false, null); + } lastTime = Minecraft.getMinecraft().theWorld.getTotalWorldTime(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizons/wdmla/overlay/GuiBlockDraw.java b/src/main/java/com/gtnewhorizons/wdmla/overlay/GuiBlockDraw.java index ce73cd26..72ed58ec 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/overlay/GuiBlockDraw.java +++ b/src/main/java/com/gtnewhorizons/wdmla/overlay/GuiBlockDraw.java @@ -2,22 +2,26 @@ import static org.lwjgl.opengl.GL11.*; +import mcp.mobius.waila.overlay.OverlayConfig; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.init.Blocks; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.IIcon; import net.minecraftforge.client.ForgeHooksClient; import org.joml.Vector3f; import org.joml.Vector4i; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; +import org.lwjgl.opengl.GL13; import com.gtnewhorizon.gtnhlib.blockpos.BlockPos; import com.gtnewhorizons.wdmla.util.HotSwapUtil; @@ -34,14 +38,76 @@ public class GuiBlockDraw { private Vector4i rect = new Vector4i(); private final RenderBlocks bufferBuilder = new RenderBlocks(); + private float prevLightmapX; + private float prevLightmapY; + private int prevActiveTexture; + private boolean prevLightmapTex2DEnabled; + private static final GuiBlockDraw instance = new GuiBlockDraw(); public static final float ZOOM = 2.3f; - public static void drawWorldBlock(int x, int y, int width, int height, int blockX, int blockY, int blockZ, + public static boolean drawWorldBlock(int x, int y, int width, int height, int blockX, int blockY, int blockZ, float rotationYaw, float rotationPitch) { - Vector3f center = new Vector3f(blockX + 0.5f, blockY + 0.5f, blockZ + 0.5f); - Minecraft mc = Minecraft.getMinecraft(); + +// Center the camera on the full multi-block shape for doors/beds/double plants. +Block baseBlock = mc.theWorld.getBlock(blockX, blockY, blockZ); +int baseMeta = mc.theWorld.getBlockMetadata(blockX, blockY, blockZ); + +// Some multi-block / 2-tall blocks (e.g. Waystones) have their TileEntity or TESR only on the lower half. +// If we are looking at the upper half, shift rendering to the lower half so the fancy preview works. +TileEntity teHere = mc.theWorld.getTileEntity(blockX, blockY, blockZ); +if (teHere == null) { + TileEntity teBelow = mc.theWorld.getTileEntity(blockX, blockY - 1, blockZ); + if (teBelow != null && TileEntityRendererDispatcher.instance.hasSpecialRenderer(teBelow)) { + blockY -= 1; + baseBlock = mc.theWorld.getBlock(blockX, blockY, blockZ); + baseMeta = mc.theWorld.getBlockMetadata(blockX, blockY, blockZ); + } +} + +int minX = blockX, minY = blockY, minZ = blockZ; +int maxX = blockX, maxY = blockY, maxZ = blockZ; + +if (baseBlock instanceof net.minecraft.block.BlockDoublePlant || baseBlock instanceof net.minecraft.block.BlockDoor) { + if ((baseMeta & 8) == 0) { // lower + maxY = blockY + 1; + } else { // upper + minY = blockY - 1; + } +} else if (baseBlock instanceof net.minecraft.block.BlockBed) { + int facing = baseMeta & 3; + int dx = 0, dz = 0; + if (facing == 0) dz = 1; + else if (facing == 1) dx = -1; + else if (facing == 2) dz = -1; + else if (facing == 3) dx = 1; + + boolean isHead = (baseMeta & 8) != 0; + int ox = isHead ? (blockX - dx) : (blockX + dx); + int oz = isHead ? (blockZ - dz) : (blockZ + dz); + + minX = Math.min(minX, ox); + maxX = Math.max(maxX, ox); + minZ = Math.min(minZ, oz); + maxZ = Math.max(maxZ, oz); +} + + +// Waystones are two-block tall like doors; include both halves for centering (no lighting/AO changes). +if (isWaystoneBlock(baseBlock)) { + Block above = mc.theWorld.getBlock(blockX, blockY + 1, blockZ); + Block below = mc.theWorld.getBlock(blockX, blockY - 1, blockZ); + if (above == baseBlock) { + maxY = blockY + 1; + } else if (below == baseBlock) { + minY = blockY - 1; + } +} + +// Average of the bounding box of all blocks we will render. +Vector3f center = new Vector3f((minX + maxX) * 0.5f + 0.5f, (minY + maxY) * 0.5f + 0.5f, (minZ + maxZ) * 0.5f + 0.5f); + ScaledResolution resolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); // compute window size from scaled width & height int windowWidth = getScaledX(mc, resolution, width); @@ -52,18 +118,20 @@ public static void drawWorldBlock(int x, int y, int width, int height, int block instance.renderedBlock = new BlockPos(blockX, blockY, blockZ); instance.setCameraLookAt(center, ZOOM, Math.toRadians(rotationPitch), Math.toRadians(rotationYaw)); - instance.render(windowX, windowY, windowWidth, windowHeight); + return instance.render(windowX, windowY, windowWidth, windowHeight); } - private void render(int x, int y, int width, int height) { + private boolean render(int x, int y, int width, int height) { rect.set(x, y, width, height); - // setupCamera + // Always restore render state even if a mod block renderer throws. setupCamera(); - - // render World - drawWorld(); - - resetCamera(); + boolean rendered = false; + try { + rendered = drawWorld(); + } finally { + resetCamera(); + } + return rendered; } public void setCameraLookAt(Vector3f lookAt, double radius, double rotationPitch, double rotationYaw) { @@ -88,6 +156,15 @@ public void setupCamera() { int height = rect.w; Minecraft mc = Minecraft.getMinecraft(); + + prevLightmapX = OpenGlHelper.lastBrightnessX; + prevLightmapY = OpenGlHelper.lastBrightnessY; + + prevActiveTexture = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE); + GL13.glActiveTexture(OpenGlHelper.lightmapTexUnit); + prevLightmapTex2DEnabled = GL11.glIsEnabled(GL11.GL_TEXTURE_2D); + GL13.glActiveTexture(prevActiveTexture); + glPushAttrib(GL_ALL_ATTRIB_BITS); glPushClientAttrib(GL_ALL_CLIENT_ATTRIB_BITS); mc.entityRenderer.disableLightmap(0); @@ -122,7 +199,7 @@ protected void scissorView(int x, int y, int width, int height) { GL11.glDisable(GL11.GL_SCISSOR_TEST); } - public static void resetCamera() { + private void resetCamera() { // reset viewport Minecraft minecraft = Minecraft.getMinecraft(); glViewport(0, 0, minecraft.displayWidth, minecraft.displayHeight); @@ -140,11 +217,35 @@ public static void resetCamera() { // reset attributes glPopClientAttrib(); glPopAttrib(); + + // Restore the exact lightmap + active-texture state that existed before we rendered the HUD preview. + // This prevents the "whole screen gets darker" leak that happens when the active texture unit is left + // on the lightmap (or when GL_TEXTURE_2D enable state differs per unit) near night time. + Minecraft mc = Minecraft.getMinecraft(); + + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, prevLightmapX, prevLightmapY); + + GL13.glActiveTexture(OpenGlHelper.lightmapTexUnit); + if (prevLightmapTex2DEnabled) GL11.glEnable(GL11.GL_TEXTURE_2D); + else GL11.glDisable(GL11.GL_TEXTURE_2D); + + GL13.glActiveTexture(prevActiveTexture); + + mc.entityRenderer.disableLightmap(0); + } - protected void drawWorld() { + protected boolean drawWorld() { Minecraft mc = Minecraft.getMinecraft(); + float fancyScale = OverlayConfig.fancyBlockScale; + if (fancyScale <= 0.0f) fancyScale = 1.0f; + boolean renderedAny = false; + glPushMatrix(); + glTranslatef(lookAt.x, lookAt.y, lookAt.z); + glScalef(fancyScale, fancyScale, fancyScale); + glTranslatef(-lookAt.x, -lookAt.y, -lookAt.z); + glEnable(GL_CULL_FACE); glEnable(GL12.GL_RESCALE_NORMAL); RenderHelper.disableStandardItemLighting(); @@ -155,7 +256,7 @@ protected void drawWorld() { glEnable(GL_ALPHA_TEST); Tessellator tessellator = Tessellator.instance; - renderBlocks(tessellator, renderedBlock); + renderedAny |= renderBlocks(tessellator, renderedBlock); RenderHelper.enableStandardItemLighting(); glEnable(GL_LIGHTING); @@ -173,53 +274,331 @@ protected void drawWorld() { TileEntity tile = Minecraft.getMinecraft().theWorld.getTileEntity(x, y, z); if (tile != null && tesr.hasSpecialRenderer(tile)) { if (tile.shouldRenderInPass(finalPass)) { - tesr.renderTileEntityAt(tile, x, y, z, 0); + // Waystones: HUD-only render should ignore lighting/AO influence from the world/hand. + if (isWaystoneBlock(mc.theWorld.getBlock(x, y, z))) { + // Save current lightmap + lighting state, force fullbright for just this TESR draw. + final float prevX = OpenGlHelper.lastBrightnessX; + final float prevY = OpenGlHelper.lastBrightnessY; + final boolean wasLighting = GL11.glIsEnabled(GL11.GL_LIGHTING); + + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glColor4f(1f, 1f, 1f, 1f); + + renderedAny = true; + tesr.renderTileEntityAt(tile, x, y, z, 0); + + // Restore previous state. + if (wasLighting) GL11.glEnable(GL11.GL_LIGHTING); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, prevX, prevY); + } else { + renderedAny = true; + tesr.renderTileEntityAt(tile, x, y, z, 0); + } } } } ForgeHooksClient.setRenderPass(-1); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); + glPopMatrix(); glDepthMask(true); + + return renderedAny; } - public void renderBlocks(Tessellator tessellator, BlockPos blocksToRender) { - if (blocksToRender == null) return; + public boolean renderBlocks(Tessellator tessellator, BlockPos blocksToRender) { + if (blocksToRender == null) return false; + + boolean renderedAny = false; + Minecraft mc = Minecraft.getMinecraft(); - final int savedAo = mc.gameSettings.ambientOcclusion; - mc.gameSettings.ambientOcclusion = 0; + tessellator.startDrawingQuads(); try { - tessellator.setBrightness(15 << 20 | 15 << 4); - for (int i = 0; i < 2; i++) { - int x = blocksToRender.x; - int y = blocksToRender.y; - int z = blocksToRender.z; - Block block = Minecraft.getMinecraft().theWorld.getBlock(x, y, z); - if (block.equals(Blocks.air) || !block.canRenderInPass(i)) continue; - - bufferBuilder.blockAccess = Minecraft.getMinecraft().theWorld; - bufferBuilder.setRenderBounds(0, 0, 0, 1, 1, 1); - bufferBuilder.renderAllFaces = true; - bufferBuilder.renderBlockByRenderType(block, x, y, z); - } + // Full-bright lightmap for HUD rendering. + tessellator.setBrightness(0x00F000F0); + + // Use a full-bright IBlockAccess wrapper, but do NOT mutate global GameSettings (prevents dynamic light flicker). + bufferBuilder.blockAccess = new HudIsolatedFullBrightBlockAccess(mc.theWorld, blocksToRender.x, blocksToRender.y, blocksToRender.z); + bufferBuilder.enableAO = false; + bufferBuilder.renderAllFaces = true; + + // Determine which world positions to render (some blocks span multiple blocks). +final java.util.ArrayList toRender = new java.util.ArrayList(2); +toRender.add(blocksToRender); + +Block baseBlock = mc.theWorld.getBlock(blocksToRender.x, blocksToRender.y, blocksToRender.z); +int baseMeta = mc.theWorld.getBlockMetadata(blocksToRender.x, blocksToRender.y, blocksToRender.z); + +if (baseBlock instanceof net.minecraft.block.BlockDoublePlant || baseBlock instanceof net.minecraft.block.BlockDoor) { + if ((baseMeta & 8) == 0) toRender.add(new BlockPos(blocksToRender.x, blocksToRender.y + 1, blocksToRender.z)); + else toRender.add(new BlockPos(blocksToRender.x, blocksToRender.y - 1, blocksToRender.z)); +} else if (baseBlock instanceof net.minecraft.block.BlockBed) { + int facing = baseMeta & 3; + int dx = 0, dz = 0; + if (facing == 0) dz = 1; + else if (facing == 1) dx = -1; + else if (facing == 2) dz = -1; + else if (facing == 3) dx = 1; + + boolean isHead = (baseMeta & 8) != 0; + int ox = isHead ? (blocksToRender.x - dx) : (blocksToRender.x + dx); + int oz = isHead ? (blocksToRender.z - dz) : (blocksToRender.z + dz); + toRender.add(new BlockPos(ox, blocksToRender.y, oz)); +} + +if (isWaystoneBlock(baseBlock)) { + Block above = mc.theWorld.getBlock(blocksToRender.x, blocksToRender.y + 1, blocksToRender.z); + Block below = mc.theWorld.getBlock(blocksToRender.x, blocksToRender.y - 1, blocksToRender.z); + if (above == baseBlock) toRender.add(new BlockPos(blocksToRender.x, blocksToRender.y + 1, blocksToRender.z)); + else if (below == baseBlock) toRender.add(new BlockPos(blocksToRender.x, blocksToRender.y - 1, blocksToRender.z)); +} + + + +for (int pass = 0; pass < 2; pass++) { + for (int i = 0; i < toRender.size(); i++) { + BlockPos p = toRender.get(i); + int x = p.x; + int y = p.y; + int z = p.z; + + Block block = mc.theWorld.getBlock(x, y, z); + if (block == null || block == Blocks.air || !block.canRenderInPass(pass)) continue; + + + // Ensure the block's bounds match its in-world state (fixes slabs/stairs/etc rendering as full cubes). + block.setBlockBoundsBasedOnState(bufferBuilder.blockAccess, x, y, z); + bufferBuilder.setRenderBoundsFromBlock(block); + +// For grass blocks (vanilla + most modded), never show snowy sides in the HUD preview. +// BlockGrass picks snowy-side textures by checking the block above in getIcon(IBlockAccess,...), +// so we bypass the world-dependent path entirely and render using: +// - base dirt sides/bottom (getIcon(side, meta)) +// - tinted top (biome tint) +// - tinted side overlay (getIconSideOverlay) + +if (block instanceof net.minecraft.block.BlockGrass) { + int meta = mc.theWorld.getBlockMetadata(x, y, z); + + IIcon iconBottom = block.getIcon(0, meta); + IIcon iconTop = block.getIcon(1, meta); + IIcon iconSideBase = block.getIcon(2, meta); + IIcon iconSideOverlay = ((net.minecraft.block.BlockGrass) block).getIconSideOverlay(); + + int c = block.colorMultiplier(bufferBuilder.blockAccess, x, y, z); + float r = ((c >> 16) & 255) / 255.0f; + float g = ((c >> 8) & 255) / 255.0f; + float b = (c & 255) / 255.0f; + + // Manual per-face shading (AO-look) ONLY for this isolated grass preview. + final float shadeBottom = 0.5f; + final float shadeTop = 1.0f; + final float shadeZ = 0.8f; + final float shadeX = 0.6f; + + // Bottom + tessellator.setColorOpaque_F(shadeBottom, shadeBottom, shadeBottom); + bufferBuilder.renderFaceYNeg(block, (double) x, (double) y, (double) z, iconBottom); + + // Top (tinted) + tessellator.setColorOpaque_F(r * shadeTop, g * shadeTop, b * shadeTop); + bufferBuilder.renderFaceYPos(block, (double) x, (double) y, (double) z, iconTop); + + // Sides base + tessellator.setColorOpaque_F(shadeZ, shadeZ, shadeZ); + bufferBuilder.renderFaceZNeg(block, (double) x, (double) y, (double) z, iconSideBase); + bufferBuilder.renderFaceZPos(block, (double) x, (double) y, (double) z, iconSideBase); + + tessellator.setColorOpaque_F(shadeX, shadeX, shadeX); + bufferBuilder.renderFaceXNeg(block, (double) x, (double) y, (double) z, iconSideBase); + bufferBuilder.renderFaceXPos(block, (double) x, (double) y, (double) z, iconSideBase); + + // Sides overlay (tinted) + tessellator.setColorOpaque_F(r * shadeZ, g * shadeZ, b * shadeZ); + bufferBuilder.renderFaceZNeg(block, (double) x, (double) y, (double) z, iconSideOverlay); + bufferBuilder.renderFaceZPos(block, (double) x, (double) y, (double) z, iconSideOverlay); + + tessellator.setColorOpaque_F(r * shadeX, g * shadeX, b * shadeX); + bufferBuilder.renderFaceXNeg(block, (double) x, (double) y, (double) z, iconSideOverlay); + bufferBuilder.renderFaceXPos(block, (double) x, (double) y, (double) z, iconSideOverlay); + + // Reset color + tessellator.setColorOpaque_F(1.0f, 1.0f, 1.0f); + renderedAny = true; + continue; +} + + // For standard cube rendering, call the non-AO path directly (avoids RenderBlocks consulting global AO settings). + int rt = block.getRenderType(); +if (rt == 0) { + // Many vanilla-style blocks (glass, leaves, etc.) return renderAsNormalBlock()==false but still + // render correctly via the standard block renderer. The main case we want to avoid here is + // TESR-based blocks that also report renderType==0 (e.g. DeepResonance crystals). + int meta = mc.theWorld.getBlockMetadata(x, y, z); + TileEntity te = mc.theWorld.getTileEntity(x, y, z); + boolean hasTesr = te != null && TileEntityRendererDispatcher.instance.hasSpecialRenderer(te); + + if (!block.renderAsNormalBlock() && hasTesr) { + // Skip cube rendering; the TESR will render the proper model. + continue; + } + + if (block.renderAsNormalBlock() || !block.hasTileEntity(meta)) { + renderedAny |= bufferBuilder.renderStandardBlock(block, x, y, z); + } else { + // Skip cube rendering; the TESR (if any) will render the proper model. + continue; + } +} else { + + renderedAny |= bufferBuilder.renderBlockByRenderType(block, x, y, z); + } + } +} } finally { - mc.gameSettings.ambientOcclusion = savedAo; tessellator.draw(); tessellator.setTranslation(0, 0, 0); } + return renderedAny; } - public static void setDefaultPassRenderState(int pass) { - glColor4f(1, 1, 1, 1); - if (pass == 0) { // SOLID - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - glDepthMask(true); - } else { // TRANSLUCENT - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(false); - } +private static boolean isWaystoneBlock(net.minecraft.block.Block b) { + if (b == null) return false; + Object regNameObj = net.minecraft.block.Block.blockRegistry.getNameForObject(b); + String reg = regNameObj != null ? regNameObj.toString() : ""; + String unloc = b.getUnlocalizedName(); + String cls = b.getClass().getName(); + String s = (reg + " " + unloc + " " + cls).toLowerCase(java.util.Locale.ROOT); + return s.contains("waystone"); +} + + + +private static final class HudIsolatedFullBrightBlockAccess implements net.minecraft.world.IBlockAccess { + private final net.minecraft.world.IBlockAccess delegate; + private final java.util.HashSet include; + + private HudIsolatedFullBrightBlockAccess(net.minecraft.world.IBlockAccess delegate, int cx, int cy, int cz) { + this.delegate = delegate; + this.include = new java.util.HashSet(6); + + + includePos(cx, cy, cz); + // IMPORTANT: + // Do NOT include generic neighbors (above/below/sides). Many blocks change shading/textures based on neighbor lookups + // (AO, snow checks, CTM/texturepack logic, etc.). + net.minecraft.block.Block b = delegate.getBlock(cx, cy, cz); + int meta = delegate.getBlockMetadata(cx, cy, cz); + + // Minimal "multi-block" support: include only the linked half for known two-block structures. + if (b instanceof net.minecraft.block.BlockDoublePlant) { + if ((meta & 8) == 0) includePos(cx, cy + 1, cz); + else includePos(cx, cy - 1, cz); + } else if (b instanceof net.minecraft.block.BlockDoor) { + if ((meta & 8) == 0) includePos(cx, cy + 1, cz); + else includePos(cx, cy - 1, cz); + } else if (b instanceof net.minecraft.block.BlockBed) { + int facing = meta & 3; + int dx = 0, dz = 0; + if (facing == 0) dz = 1; + else if (facing == 1) dx = -1; + else if (facing == 2) dz = -1; + else if (facing == 3) dx = 1; + + if ((meta & 8) == 0) includePos(cx + dx, cy, cz + dz); + else includePos(cx - dx, cy, cz - dz); + } else if (isWaystoneBlock(b)) { + // Door-style: include only the other half if it is the same block. + net.minecraft.block.Block above = delegate.getBlock(cx, cy + 1, cz); + net.minecraft.block.Block below = delegate.getBlock(cx, cy - 1, cz); + if (above == b) includePos(cx, cy + 1, cz); + else if (below == b) includePos(cx, cy - 1, cz); + } else if (b == net.minecraft.init.Blocks.snow_layer) { + // Snow layer needs the block below to exist for proper rendering (but still don't include anything else). + includePos(cx, cy - 1, cz); + } +} + + private void includePos(int x, int y, int z) { + include.add((((long)x & 0x3FFFFFFL) << 38) | (((long)z & 0x3FFFFFFL) << 12) | ((long)y & 0xFFFL)); } + + private boolean isIncluded(int x, int y, int z) { + long key = (((long)x & 0x3FFFFFFL) << 38) | (((long)z & 0x3FFFFFFL) << 12) | ((long)y & 0xFFFL); + return include.contains(key); + } + @Override + public net.minecraft.block.Block getBlock(int x, int y, int z) { + if (!isIncluded(x, y, z)) return net.minecraft.init.Blocks.air; + return delegate.getBlock(x, y, z); + } + + @Override + public net.minecraft.tileentity.TileEntity getTileEntity(int x, int y, int z) { + if (!isIncluded(x, y, z)) return null; + return delegate.getTileEntity(x, y, z); + } + + @Override + public int getLightBrightnessForSkyBlocks(int x, int y, int z, int p_72802_4_) { + // Full-bright lightmap coords (same value vanilla uses for maximum brightness) + return 0x00F000F0; + } + + @Override + public int getBlockMetadata(int x, int y, int z) { + if (!isIncluded(x, y, z)) return 0; + return delegate.getBlockMetadata(x, y, z); + } + + @Override + public boolean isAirBlock(int x, int y, int z) { + return getBlock(x, y, z) == net.minecraft.init.Blocks.air; + } + + @Override + public net.minecraft.world.biome.BiomeGenBase getBiomeGenForCoords(int x, int z) { + return delegate.getBiomeGenForCoords(x, z); + } + + @Override + public int getHeight() { + return delegate.getHeight(); + } + + @Override + public boolean extendedLevelsInChunkCache() { + return delegate.extendedLevelsInChunkCache(); + } + + @Override + public int isBlockProvidingPowerTo(int x, int y, int z, int side) { + if (!isIncluded(x, y, z)) return 0; + return delegate.isBlockProvidingPowerTo(x, y, z, side); + } + @Override + public boolean isSideSolid(int x, int y, int z, + net.minecraftforge.common.util.ForgeDirection side, + boolean _default) { + if (!isIncluded(x, y, z)) return false; + return delegate.isSideSolid(x, y, z, side, _default); + } + } + + + public static void setDefaultPassRenderState(int pass) { + glColor4f(1, 1, 1, 1); + if (pass == 0) { // SOLID + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDepthMask(true); + } else { // TRANSLUCENT + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/gtnewhorizons/wdmla/overlay/RayTracing.java b/src/main/java/com/gtnewhorizons/wdmla/overlay/RayTracing.java index 7ff10b56..f739691f 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/overlay/RayTracing.java +++ b/src/main/java/com/gtnewhorizons/wdmla/overlay/RayTracing.java @@ -1,6 +1,12 @@ package com.gtnewhorizons.wdmla.overlay; import java.util.ArrayList; +import java.util.List; + +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MathHelper; + +import com.gtnewhorizons.wdmla.config.General; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; @@ -35,52 +41,300 @@ public static RayTracing instance() { return _instance; } - private MovingObjectPosition target = null; - private final Minecraft mc = Minecraft.getMinecraft(); + private MovingObjectPosition target; + private Minecraft mc = Minecraft.getMinecraft(); + + public MovingObjectPosition getTarget() { + return this.target; + } + + public Entity getTargetEntity() { + return this.target != null ? this.target.entityHit : null; + } + + public ItemStack getTargetStack() { + return getIdentifierStack(); + } public void fire() { - if (mc.objectMouseOver != null && mc.objectMouseOver.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY - && !shouldHidePlayer(mc.objectMouseOver.entityHit)) { - this.target = mc.objectMouseOver; + if (mc.theWorld == null || mc.thePlayer == null) { + target = null; return; } + // Block reach distance remains vanilla for blocks EntityLivingBase viewpoint = mc.renderViewEntity; - if (viewpoint == null) return; + double blockReach = mc.playerController.getBlockReachDistance(); + + // Configurable range for entities (mobs). If misconfigured, fall back to block reach. + int cfgRange = General.mobHudRange; + double entityReach = cfgRange > 0 ? cfgRange : blockReach; + + if (entityReach < blockReach) { + entityReach = blockReach; + } + + // Perform both block and entity ray traces, then decide what to show + MovingObjectPosition blockTarget = this.rayTrace(viewpoint, blockReach, 0.0F); + MovingObjectPosition entityTarget = rayTraceEntities(viewpoint, entityReach, 0.0F); + + // Filter out invisible players if configured so + if (entityTarget != null && entityTarget.entityHit != null && shouldHidePlayer(entityTarget.entityHit)) { + entityTarget = null; + } + + World world = viewpoint.worldObj; + + // Decide priority using distance along the ray: + // - If there is an entity hit and no solid block hit, show the entity. + // - If there is a block hit and no entity, show the block. + // - If both exist, compare distances along the view ray, with special handling for HUD-transparent blocks + // (glass, liquids, leaves, etc.) + if (entityTarget != null && entityTarget.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY) { + if (blockTarget == null || blockTarget.typeOfHit != MovingObjectPosition.MovingObjectType.BLOCK) { + this.target = entityTarget; + return; + } + + // We have both an entity and a block along the ray + Block block = world.getBlock(blockTarget.blockX, blockTarget.blockY, blockTarget.blockZ); + + // If the block is HUD-transparent (glass, liquids, leaves...), always prefer the entity + if (isHudTransparentBlock(block, world, blockTarget.blockX, blockTarget.blockY, blockTarget.blockZ)) { + Vec3 eyePos = viewpoint.getPosition(0.0F); + Vec3 entityHit = entityTarget.hitVec; + + if (entityHit == null && entityTarget.entityHit != null) { + Entity e = entityTarget.entityHit; + entityHit = Vec3.createVectorHelper( + e.posX, + e.posY + (double) (e.height * 0.5F), + e.posZ + ); + } + + if (entityHit != null && isPathBlockedBySolid(world, eyePos, entityHit)) { + this.target = blockTarget; + } else { + this.target = entityTarget; + } + return; + } + + // For solid blocks, choose whichever is closer along the view ray. + // Entity distance + double entityDistSq; + if (entityTarget.hitVec != null) { + entityDistSq = viewpoint.getDistanceSq( + entityTarget.hitVec.xCoord, + entityTarget.hitVec.yCoord, + entityTarget.hitVec.zCoord + ); + } else if (entityTarget.entityHit != null) { + entityDistSq = viewpoint.getDistanceSqToEntity(entityTarget.entityHit); + } else { + entityDistSq = Double.MAX_VALUE; + } + + // Block distance + double blockDistSq; + if (blockTarget.hitVec != null) { + blockDistSq = viewpoint.getDistanceSq( + blockTarget.hitVec.xCoord, + blockTarget.hitVec.yCoord, + blockTarget.hitVec.zCoord + ); + } else { + // Fallback: center of the block + double cx = blockTarget.blockX + 0.5D; + double cy = blockTarget.blockY + 0.5D; + double cz = blockTarget.blockZ + 0.5D; + blockDistSq = viewpoint.getDistanceSq(cx, cy, cz); + } - this.target = this.rayTrace(viewpoint, mc.playerController.getBlockReachDistance(), 0); + // Small epsilon to avoid weird float equality issues + if (entityDistSq <= blockDistSq + 1.0E-4D) { + this.target = entityTarget; + } else { + this.target = blockTarget; + } + return; + } + + // No entity hit; fall back to whatever block (if any) we have + this.target = blockTarget; } private static boolean shouldHidePlayer(Entity targetEnt) { // Check if entity is player with invisibility effect - if (targetEnt instanceof EntityPlayer thePlayer) { + if (targetEnt instanceof EntityPlayer) { + EntityPlayer thePlayer = (EntityPlayer) targetEnt; boolean shouldHidePlayerSetting = !ConfigHandler.instance().getConfig("vanilla.show_invisible_players"); return shouldHidePlayerSetting && thePlayer.isInvisible(); } return false; } - public MovingObjectPosition getTarget() { - return this.target; + public MovingObjectPosition rayTrace(EntityLivingBase entity, double par1, float par3) { + Vec3 vec3 = entity.getPosition(par3); + Vec3 vec31 = entity.getLook(par3); + Vec3 vec32 = vec3.addVector(vec31.xCoord * par1, vec31.yCoord * par1, vec31.zCoord * par1); + + if (ConfigHandler.instance().getConfig(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_LIQUID, true)) { + return entity.worldObj.rayTraceBlocks(vec3, vec32, true); + } else { + return entity.worldObj.rayTraceBlocks(vec3, vec32); + } } - public ItemStack getTargetStack() { - return this.target.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK ? this.getIdentifierStack() : null; + MovingObjectPosition rayTraceEntities(EntityLivingBase viewer, double maxDistance, float partialTicks) { + if (viewer == null || viewer.worldObj == null) { + return null; + } + + World world = viewer.worldObj; + + Vec3 eyePos = viewer.getPosition(partialTicks); + Vec3 lookVec = viewer.getLook(partialTicks); + Vec3 reachVec = eyePos.addVector( + lookVec.xCoord * maxDistance, + lookVec.yCoord * maxDistance, + lookVec.zCoord * maxDistance + ); + + Entity closestEntity = null; + Vec3 hitVec = null; + double closestDistance = maxDistance; + + List entities = world.getEntitiesWithinAABBExcludingEntity( + viewer, + viewer.boundingBox.addCoord( + lookVec.xCoord * maxDistance, + lookVec.yCoord * maxDistance, + lookVec.zCoord * maxDistance + ).expand(1.0D, 1.0D, 1.0D) + ); + + for (Entity candidate : entities) { + if (!candidate.canBeCollidedWith()) { + continue; + } + if (shouldHidePlayer(candidate)) { + continue; + } + + float border = candidate.getCollisionBorderSize(); + AxisAlignedBB aabb = candidate.boundingBox.expand(border, border, border); + MovingObjectPosition intercept = aabb.calculateIntercept(eyePos, reachVec); + + // If there's any *solid* block between us and this entity along the look vector, + // consider the entity occluded and skip it. Transparent blocks (glass, liquids, etc.) + // are ignored so mobs can still be targeted through them. + if (intercept != null && isPathBlockedBySolid(world, eyePos, intercept.hitVec)) { + continue; + } + + if (aabb.isVecInside(eyePos)) { + if (0.0D < closestDistance) { + closestEntity = candidate; + hitVec = (intercept == null) ? eyePos : intercept.hitVec; + closestDistance = 0.0D; + } + } else if (intercept != null) { + double distanceToHit = eyePos.distanceTo(intercept.hitVec); + + if (distanceToHit < closestDistance || closestDistance == 0.0D) { + closestEntity = candidate; + hitVec = intercept.hitVec; + closestDistance = distanceToHit; + } + } + } + + if (closestEntity != null) { + MovingObjectPosition result = new MovingObjectPosition(closestEntity); + result.hitVec = hitVec; + return result; + } + + return null; } - public Entity getTargetEntity() { - return this.target.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY ? this.getIdentifierEntity() - : null; + public MovingObjectPosition getMouseOver() { + return target; } - public MovingObjectPosition rayTrace(EntityLivingBase entity, double par1, float par3) { - Vec3 vec3 = entity.getPosition(par3); - Vec3 vec31 = entity.getLook(par3); - Vec3 vec32 = vec3.addVector(vec31.xCoord * par1, vec31.yCoord * par1, vec31.zCoord * par1); + public ArrayList getIdentifierItems() { + ArrayList items = new ArrayList(); + MovingObjectPosition position = this.target; - if (ConfigHandler.instance().getConfig(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_LIQUID, true)) - return entity.worldObj.rayTraceBlocks(vec3, vec32, true); - else return entity.worldObj.rayTraceBlocks(vec3, vec32, false); + if (position == null) return items; + + if (position.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + int x = position.blockX; + int y = position.blockY; + int z = position.blockZ; + World world = mc.theWorld; + Block mouseoverBlock = world.getBlock(x, y, z); + + if (mouseoverBlock == null || mouseoverBlock.isAir(world, x, y, z)) return items; + try { + ItemStack pick = mouseoverBlock.getPickBlock(position, world, x, y, z); + if (pick != null && pick.getItem() != null) { + items.add(pick); + return items; + } + } catch (Throwable ignored) {} + + try { + net.minecraft.item.Item item = net.minecraft.item.Item.getItemFromBlock(mouseoverBlock); + if (item != null) { + items.add(new ItemStack(item, 1, world.getBlockMetadata(x, y, z))); + return items; + } + } catch (Throwable ignored) {} + + if (mouseoverBlock instanceof IShearable) { + try { + for (ItemStack s : ((IShearable) mouseoverBlock) + .onSheared(new ItemStack(Items.shears), world, x, y, z, 0)) { + if (s != null && s.getItem() != null) items.add(s); + } + } catch (Throwable ignored) {} + } + + return items; + } + + if (position.typeOfHit == MovingObjectPosition.MovingObjectType.ENTITY + && position.entityHit instanceof EntityLivingBase) { + EntityLivingBase entity = (EntityLivingBase) position.entityHit; + ArrayList tmp = new ArrayList(); + + tmp.add(entity.getHeldItem()); + for (int i = 0; i < 4; i++) { + tmp.add(entity.getEquipmentInSlot(i)); + } + for (ItemStack stack : tmp) { + if (stack != null) items.add(stack); + } + + TileEntity te = entity.worldObj.getTileEntity( + MathHelper.floor_double(entity.posX), + MathHelper.floor_double(entity.posY), + MathHelper.floor_double(entity.posZ)); + if (te != null) { + try { + ItemStack pick = te.getBlockType().getPickBlock( + RayTracing.instance().getTarget(), te.getWorldObj(), te.xCoord, te.yCoord, te.zCoord); + if (pick != null) { + items.add(pick); + } + } catch (Throwable ignored) {} + } + } + + return items; } public ItemStack getIdentifierStack() { @@ -94,51 +348,114 @@ public ItemStack getIdentifierStack() { } public Entity getIdentifierEntity() { - return this.target.entityHit; + return this.target != null ? this.target.entityHit : null; } - public ArrayList getIdentifierItems() { - ArrayList items = new ArrayList<>(); + /** + * Returns true if there is ANY non-transparent (solid) blocker between start and end. + * Handles ANY number of transparent blocks correctly, including the case where a solid + * block is directly touching a transparent one (glass -> stone). + */ + private boolean isPathBlockedBySolid(World world, Vec3 start, Vec3 end) { + if (world == null || start == null || end == null) { + return false; + } + + Vec3 currentStart = start; - if (this.target == null) return items; + boolean stopOnLiquid = ConfigHandler.instance().getConfig( + Configuration.CATEGORY_GENERAL, + Constants.CFG_WAILA_LIQUID, + true + ); - World world = mc.theWorld; + for (int i = 0; i < 64; i++) { + MovingObjectPosition hit = world.rayTraceBlocks(currentStart, end, stopOnLiquid); - int x = this.target.blockX; - int y = this.target.blockY; - int z = this.target.blockZ; + if (hit == null || hit.hitVec == null) { + return false; + } - Block mouseoverBlock = world.getBlock(x, y, z); - TileEntity tileEntity = world.getTileEntity(x, y, z); - if (mouseoverBlock == null) return items; + Block hitBlock = world.getBlock(hit.blockX, hit.blockY, hit.blockZ); - if (tileEntity == null) { - try { - ItemStack block = new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z)); + // First non-transparent block = blocked + if (!isHudTransparentBlock(hitBlock, world, hit.blockX, hit.blockY, hit.blockZ)) { + return true; + } + + // Step a tiny amount forward along the ray + double dirX = end.xCoord - hit.hitVec.xCoord; + double dirY = end.yCoord - hit.hitVec.yCoord; + double dirZ = end.zCoord - hit.hitVec.zCoord; + double length = MathHelper.sqrt_double(dirX * dirX + dirY * dirY + dirZ * dirZ); + if (length < 1.0E-6D) { + return false; + } - if (block.getItem() != null) items.add(block); + dirX /= length; + dirY /= length; + dirZ /= length; - } catch (Exception ignored) {} + currentStart = hit.hitVec.addVector(dirX * 1.0E-4D, dirY * 1.0E-4D, dirZ * 1.0E-4D); + + int cx = MathHelper.floor_double(currentStart.xCoord); + int cy = MathHelper.floor_double(currentStart.yCoord); + int cz = MathHelper.floor_double(currentStart.zCoord); + + Block inside = world.getBlock(cx, cy, cz); + if (inside != null && !inside.isAir(world, cx, cy, cz) + && !isHudTransparentBlock(inside, world, cx, cy, cz)) { + return true; + } + + if (currentStart.squareDistanceTo(end) < 1.0E-8D) { + return false; + } } - if (!items.isEmpty()) return items; + return true; + } + + /** + * Determines whether a block should be treated as transparent for HUD raytracing purposes. + * This allows entities to be targeted through glass, liquids, leaves, etc., while still + * blocking entities that are actually behind solid walls. + */ + private boolean isHudTransparentBlock(Block block, World world, int x, int y, int z) { + if (block == null) return false; try { - ItemStack pick = mouseoverBlock.getPickBlock(this.target, world, x, y, z); - if (pick != null) items.add(pick); - } catch (Exception ignored) {} + if (block.getMaterial() != null && block.getMaterial().isLiquid()) { + return true; + } - if (!items.isEmpty()) return items; + if (block instanceof net.minecraft.block.BlockTorch + || block instanceof net.minecraft.block.BlockRedstoneTorch) { + return false; + } - if (mouseoverBlock instanceof IShearable shearable) { - if (shearable.isShearable(new ItemStack(Items.shears), world, x, y, z)) { - items.addAll(shearable.onSheared(new ItemStack(Items.shears), world, x, y, z, 0)); + if (block instanceof net.minecraft.block.BlockLeaves) { + return true; } - } - if (items.isEmpty()) items.add(0, new ItemStack(mouseoverBlock, 1, world.getBlockMetadata(x, y, z))); + int pass = 0; + try { + pass = block.getRenderBlockPass(); + } catch (Throwable ignored) {} + if (pass == 1) { + return true; + } - return items; - } + AxisAlignedBB col = null; + try { + col = block.getCollisionBoundingBoxFromPool(world, x, y, z); + } catch (Throwable ignored) {} + if (col == null) { + return true; + } + } catch (Throwable ignored) {} + + return false; + } } diff --git a/src/main/java/com/gtnewhorizons/wdmla/overlay/WDMlaTickHandler.java b/src/main/java/com/gtnewhorizons/wdmla/overlay/WDMlaTickHandler.java index 288267db..3620a62d 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/overlay/WDMlaTickHandler.java +++ b/src/main/java/com/gtnewhorizons/wdmla/overlay/WDMlaTickHandler.java @@ -1,7 +1,5 @@ package com.gtnewhorizons.wdmla.overlay; -import static mcp.mobius.waila.api.SpecialChars.ITALIC; - import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; @@ -21,15 +19,14 @@ import com.gtnewhorizons.wdmla.WDMla; import com.gtnewhorizons.wdmla.api.accessor.Accessor; -import com.gtnewhorizons.wdmla.api.ui.MessageType; import com.gtnewhorizons.wdmla.config.General; import com.gtnewhorizons.wdmla.impl.ObjectDataCenter; import com.gtnewhorizons.wdmla.impl.WDMlaClientRegistration; +import com.gtnewhorizons.wdmla.impl.ui.ThemeHelper; import com.gtnewhorizons.wdmla.impl.ui.component.RootComponent; import com.gtnewhorizons.wdmla.impl.ui.component.TextComponent; import com.gtnewhorizons.wdmla.impl.ui.sizer.Area; import com.gtnewhorizons.wdmla.impl.ui.sizer.Size; -import com.gtnewhorizons.wdmla.impl.ui.style.TextStyle; import com.gtnewhorizons.wdmla.impl.ui.value.HUDRenderArea; import cpw.mods.fml.client.config.GuiConfig; @@ -70,11 +67,11 @@ public void screenRender(GuiScreenEvent.DrawScreenEvent.Post event) { if (mainHUD == null) { mainHUD = new RootComponent(); mainHUD.child(new TextComponent(StatCollector.translateToLocal("hud.msg.wdmla.cfg.dummy.1"))) - .child(new TextComponent(StatCollector.translateToLocal("hud.msg.wdmla.cfg.dummy.2"))).child( - new TextComponent(ITALIC + StatCollector.translateToLocal("hud.msg.wdmla.cfg.dummy.3")) - .style( - new TextStyle().color( - General.currentTheme.get().textColor(MessageType.MOD_NAME)))); + .child(new TextComponent(StatCollector.translateToLocal("hud.msg.wdmla.cfg.dummy.2"))); + if (General.modName.hudShow) { + mainHUD.child( + ThemeHelper.INSTANCE.modName(StatCollector.translateToLocal("hud.msg.wdmla.cfg.dummy.3"))); + } } Area bgArea = new HUDRenderArea(new Size(mainHUD.getWidth(), mainHUD.getHeight())).computeBackground(); diff --git a/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultBlockInfoProvider.java b/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultBlockInfoProvider.java index 904c9281..cab34218 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultBlockInfoProvider.java +++ b/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultBlockInfoProvider.java @@ -1,37 +1,70 @@ package com.gtnewhorizons.wdmla.plugin.core; -import static mcp.mobius.waila.api.SpecialChars.*; - import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; import com.gtnewhorizons.wdmla.api.Identifiers; -import com.gtnewhorizons.wdmla.api.Theme; import com.gtnewhorizons.wdmla.api.TooltipPosition; import com.gtnewhorizons.wdmla.api.accessor.BlockAccessor; import com.gtnewhorizons.wdmla.api.provider.IBlockComponentProvider; import com.gtnewhorizons.wdmla.api.ui.IComponent; import com.gtnewhorizons.wdmla.api.ui.ITooltip; -import com.gtnewhorizons.wdmla.api.ui.MessageType; import com.gtnewhorizons.wdmla.config.General; import com.gtnewhorizons.wdmla.config.PluginsConfig; import com.gtnewhorizons.wdmla.impl.ui.ThemeHelper; import com.gtnewhorizons.wdmla.impl.ui.component.BlockComponent; import com.gtnewhorizons.wdmla.impl.ui.component.HPanelComponent; import com.gtnewhorizons.wdmla.impl.ui.component.ItemComponent; -import com.gtnewhorizons.wdmla.impl.ui.component.TextComponent; -import com.gtnewhorizons.wdmla.impl.ui.style.TextStyle; import com.gtnewhorizons.wdmla.util.FormatUtil; import com.gtnewhorizons.wdmla.wailacompat.RayTracingCompat; import mcp.mobius.waila.overlay.DisplayUtil; import mcp.mobius.waila.utils.ModIdentification; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; public enum DefaultBlockInfoProvider implements IBlockComponentProvider { INSTANCE; +private static boolean isNitorBlock(BlockAccessor accessor) { + if (accessor == null) return false; + net.minecraft.block.Block b = accessor.getBlock(); + if (b == null) return false; + + int meta = accessor.getMetadata(); + + // Thaumcraft 4 Nitor is placed as a block variant (commonly BlockAiry / "blockAiry") with meta 1. + // Detect by registry/unlocalized/class name + metadata to avoid false positives. + Object regNameObj = net.minecraft.block.Block.blockRegistry.getNameForObject(b); + String regName = regNameObj != null ? regNameObj.toString() : ""; + String unloc = b.getUnlocalizedName(); + String cls = b.getClass().getName(); + String s = (regName + " " + unloc + " " + cls).toLowerCase(java.util.Locale.ROOT); + + if (meta == 1 && (s.contains("blockairy") || s.contains("airy") || s.contains("nitor"))) { + // Prefer exact airy variant match when available, but keep it permissive across TC addons. + return true; + } + + // Fallback: some packs expose a registry name containing 'nitor' directly. + return s.contains("nitor"); +} + +private static net.minecraft.item.ItemStack getThaumcraftNitorStack() { + try { + Object obj = net.minecraft.item.Item.itemRegistry.getObject("Thaumcraft:ItemResource"); + if (!(obj instanceof net.minecraft.item.Item)) obj = net.minecraft.item.Item.itemRegistry.getObject("thaumcraft:ItemResource"); + if (!(obj instanceof net.minecraft.item.Item)) obj = net.minecraft.item.Item.itemRegistry.getObject("thaumcraft:itemResource"); + if (obj instanceof net.minecraft.item.Item) { + return new net.minecraft.item.ItemStack((net.minecraft.item.Item) obj, 1, 1); + } + } catch (Throwable t) { + // ignore + } + return null; +} @Override public ResourceLocation getUid() { return Identifiers.DEFAULT_BLOCK; @@ -50,17 +83,46 @@ public void appendTooltip(ITooltip tooltip, BlockAccessor accessor) { // step 2: construct an actual icon ITooltip row = tooltip.horizontal(); ItemStack itemStack = overrideStack != null ? overrideStack : accessor.getItemForm(); + if (itemStack == null) { + // Some blocks/items return null in strange modded cases. Fall back to "block -> item" mapping. + try { + net.minecraft.item.Item it = net.minecraft.item.Item.getItemFromBlock(accessor.getBlock()); + if (it != null) itemStack = new ItemStack(it, 1, accessor.getMetadata()); + } catch (Throwable t) { + // ignore + } + } + if (isNitorBlock(accessor)) { + ItemStack nitor = getThaumcraftNitorStack(); + if (nitor != null) itemStack = nitor; + } if (PluginsConfig.core.defaultBlock.showIcon) { - if (PluginsConfig.core.defaultBlock.fancyRenderer == PluginsConfig.Core.fancyRendererMode.ALL - || (PluginsConfig.core.defaultBlock.fancyRenderer == PluginsConfig.Core.fancyRendererMode.FALLBACK - && itemStack.getItem() == null)) { - row.child( - new BlockComponent( - accessor.getHitResult().blockX, - accessor.getHitResult().blockY, - accessor.getHitResult().blockZ).tag(Identifiers.ITEM_ICON)); + final ItemStack safeStack = itemStack != null ? itemStack : new ItemStack(Blocks.air); + + if (forceItemIcon(accessor) || itemStack == null) { + row.child(new ItemComponent(safeStack).doDrawOverlay(false).tag(Identifiers.ITEM_ICON)); } else { - row.child(new ItemComponent(itemStack).doDrawOverlay(false).tag(Identifiers.ITEM_ICON)); + PluginsConfig.Core.fancyRendererMode mode = PluginsConfig.core.defaultBlock.fancyRenderer; + + // ALL: always try fancy. + // FALLBACK: only use fancy when the item icon isn't usable. + boolean wantsFancy = mode == PluginsConfig.Core.fancyRendererMode.ALL + || (mode == PluginsConfig.Core.fancyRendererMode.FALLBACK + && (itemStack.getItem() == null)); + + // If the in-world preview can't reasonably render (invisible blocks, pure ISBRH-only blocks, etc.), + // always fall back to the item icon. + boolean canFancy = wantsFancy && canUseFancyBlockPreview(accessor); + + if (canFancy) { + row.child( + new BlockComponent( + accessor.getHitResult().blockX, + accessor.getHitResult().blockY, + accessor.getHitResult().blockZ).tag(Identifiers.ITEM_ICON)); + } else { + row.child(new ItemComponent(safeStack).doDrawOverlay(false).tag(Identifiers.ITEM_ICON)); + } } } @@ -71,7 +133,7 @@ public void appendTooltip(ITooltip tooltip, BlockAccessor accessor) { String rawName = accessor.getServerData().getString("CustomName"); itemName = EnumChatFormatting.ITALIC + FormatUtil.formatNameByPixelCount(rawName); } else { - itemName = DisplayUtil.itemDisplayNameShortFormatted(itemStack); + itemName = DisplayUtil.itemDisplayNameShortFormatted(itemStack != null ? itemStack : new ItemStack(Blocks.air)); } ITooltip title = row_vertical.horizontal(); IComponent nameComponent = ThemeHelper.INSTANCE.title(itemName).tag(Identifiers.ITEM_NAME); @@ -94,20 +156,9 @@ public void tick(float x, float y) { } }.tag(Identifiers.TARGET_NAME_ROW)); } - String modName = ModIdentification.nameFromStack(itemStack); - if (PluginsConfig.core.defaultBlock.showModName) { - Theme theme = General.currentTheme.get(); - if (modName != null) { - row_vertical.child( - new TextComponent(ITALIC + modName) - .style(new TextStyle().color(theme.textColor(MessageType.MOD_NAME))) - .tag(Identifiers.MOD_NAME)); - } else { - // reserve for replacement - row_vertical.child( - new TextComponent("").style(new TextStyle().color(theme.textColor(MessageType.MOD_NAME))) - .tag(Identifiers.MOD_NAME)); - } + String modName = ModIdentification.nameFromStack(itemStack != null ? itemStack : new ItemStack(Blocks.air)); + if (PluginsConfig.core.defaultBlock.showModName && General.modName.hudShow) { + row_vertical.child(ThemeHelper.INSTANCE.modName(modName).tag(Identifiers.MOD_NAME)); } } @@ -115,4 +166,54 @@ public void tick(float x, float y) { public boolean isPriorityFixed() { return true; } + + /** + * Some mod blocks are not safe/reliable to render via the in-world block preview (custom render types, + * invisible blocks, etc.). In those cases we fall back to the item icon even when fancy rendering is enabled. + */ + private static boolean canUseFancyBlockPreview(BlockAccessor accessor) { + if (accessor == null) return false; + Block b = accessor.getBlock(); + if (b == null || b == Blocks.air) return false; + + // Absolutely invisible / special portal surfaces render poorly (or not at all) in the HUD preview. + if (b == Blocks.portal || b == Blocks.end_portal) return false; + + int rt; + try { + rt = b.getRenderType(); + } catch (Throwable t) { + return false; + } + + // Invisible / no-render blocks. + if (rt < 0) return false; + + // TESR blocks are fine; the preview renderer handles TESR separately. + try { + if (accessor.getTileEntity() != null + && net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher.instance + .hasSpecialRenderer(accessor.getTileEntity())) { + return true; + } + } catch (Throwable t) { + } + return true; + } +private static boolean forceItemIcon(BlockAccessor accessor) { + Block b = accessor.getBlock(); + if (b == null) return false; + + // Thaumcraft Nitor: always show as item icon (Thaumcraft:ItemResource:1). + if (isNitorBlock(accessor)) return true; + + // Always show ladders as the item icon (vanilla + modded ladders) + if (b == Blocks.ladder || b instanceof net.minecraft.block.BlockLadder) return true; + String unloc = b.getUnlocalizedName(); + if (unloc != null && unloc.toLowerCase(java.util.Locale.ROOT).contains("ladder")) return true; + + return false; +} + + } diff --git a/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultEntityInfoProvider.java b/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultEntityInfoProvider.java index e7d230a7..893dfbd1 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultEntityInfoProvider.java +++ b/src/main/java/com/gtnewhorizons/wdmla/plugin/core/DefaultEntityInfoProvider.java @@ -1,7 +1,5 @@ package com.gtnewhorizons.wdmla.plugin.core; -import static mcp.mobius.waila.api.SpecialChars.*; - import net.minecraft.entity.EntityLiving; import net.minecraft.util.ResourceLocation; @@ -10,16 +8,13 @@ import com.gtnewhorizons.wdmla.api.accessor.EntityAccessor; import com.gtnewhorizons.wdmla.api.provider.IEntityComponentProvider; import com.gtnewhorizons.wdmla.api.ui.ITooltip; -import com.gtnewhorizons.wdmla.api.ui.MessageType; import com.gtnewhorizons.wdmla.config.General; import com.gtnewhorizons.wdmla.config.PluginsConfig; import com.gtnewhorizons.wdmla.impl.ui.ThemeHelper; import com.gtnewhorizons.wdmla.impl.ui.component.EntityComponent; import com.gtnewhorizons.wdmla.impl.ui.component.HPanelComponent; -import com.gtnewhorizons.wdmla.impl.ui.component.TextComponent; import com.gtnewhorizons.wdmla.impl.ui.sizer.Padding; import com.gtnewhorizons.wdmla.impl.ui.sizer.Size; -import com.gtnewhorizons.wdmla.impl.ui.style.TextStyle; import com.gtnewhorizons.wdmla.util.FormatUtil; import mcp.mobius.waila.utils.ModIdentification; @@ -67,10 +62,9 @@ public void appendTooltip(ITooltip tooltip, EntityAccessor accessor) { } row_vertical.child(ThemeHelper.INSTANCE.title(name).tag(Identifiers.ENTITY_NAME)); } - if (PluginsConfig.core.defaultEntity.showModName) { + if (PluginsConfig.core.defaultEntity.showModName && General.modName.hudShow) { row_vertical.child( - new TextComponent(ITALIC + ModIdentification.nameFromEntity(accessor.getEntity())) - .style(new TextStyle().color(General.currentTheme.get().textColor(MessageType.MOD_NAME))) + ThemeHelper.INSTANCE.modName(ModIdentification.nameFromEntity(accessor.getEntity())) .tag(Identifiers.MOD_NAME)); } } diff --git a/src/main/java/com/gtnewhorizons/wdmla/plugin/core/EntityHealthProvider.java b/src/main/java/com/gtnewhorizons/wdmla/plugin/core/EntityHealthProvider.java index 71b8765f..bf3c341a 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/plugin/core/EntityHealthProvider.java +++ b/src/main/java/com/gtnewhorizons/wdmla/plugin/core/EntityHealthProvider.java @@ -9,6 +9,7 @@ import com.gtnewhorizons.wdmla.api.accessor.EntityAccessor; import com.gtnewhorizons.wdmla.api.provider.IEntityComponentProvider; import com.gtnewhorizons.wdmla.api.ui.ITooltip; +import com.gtnewhorizons.wdmla.config.PluginsConfig; import com.gtnewhorizons.wdmla.impl.ui.ThemeHelper; import com.gtnewhorizons.wdmla.impl.ui.component.HPanelComponent; import com.gtnewhorizons.wdmla.impl.ui.component.HealthComponent; @@ -43,8 +44,9 @@ public void appendTooltip(ITooltip tooltip, EntityAccessor accessor) { int maxHPForText = ConfigHandler.instance() .getConfig(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_MAXHP, 40); if (livingEntity.getMaxHealth() > maxHPForText) { + String prefix = PluginsConfig.core.defaultEntity.showHPText ? "HP: " : " "; tooltip.child( - new HPanelComponent().child(new HealthComponent(1, 1)).text("HP: ") + new HPanelComponent().child(new HealthComponent(1, 1)).text(prefix) .child(ThemeHelper.INSTANCE.info(FormatUtil.STANDARD.format(livingEntity.getHealth()))) .text(" / ") .child(ThemeHelper.INSTANCE.info(FormatUtil.STANDARD.format(livingEntity.getMaxHealth()))) diff --git a/src/main/java/com/gtnewhorizons/wdmla/util/Color.java b/src/main/java/com/gtnewhorizons/wdmla/util/Color.java index ebf7a5f3..3eaa3935 100644 --- a/src/main/java/com/gtnewhorizons/wdmla/util/Color.java +++ b/src/main/java/com/gtnewhorizons/wdmla/util/Color.java @@ -1,9 +1,37 @@ package com.gtnewhorizons.wdmla.util; +import java.util.Locale; + import net.minecraft.util.MathHelper; public class Color { + private static final int[] MC_COLORS = new int[] { + 0x000000, // black + 0x0000AA, // dark blue + 0x00AA00, // dark green + 0x00AAAA, // dark aqua + 0xAA0000, // dark red + 0xAA00AA, // dark purple + 0xFFAA00, // gold + 0xAAAAAA, // gray + 0x555555, // dark gray + 0x5555FF, // blue + 0x55FF55, // green + 0x55FFFF, // aqua + 0xFF5555, // red + 0xFF55FF, // light purple + 0xFFFF55, // yellow + 0xFFFFFF // white + }; + + private static final String[] MC_CODES = new String[] { + "\u00a70", "\u00a71", "\u00a72", "\u00a73", + "\u00a74", "\u00a75", "\u00a76", "\u00a77", + "\u00a78", "\u00a79", "\u00a7a", "\u00a7b", + "\u00a7c", "\u00a7d", "\u00a7e", "\u00a7f" + }; + public static int setLightness(int color, float multiplier) { int a = (color >> 24) & 0xFF; int r = (color >> 16) & 0xFF; @@ -35,4 +63,72 @@ public static int setInterporation(int colorBegin, int colorEnd, float interpora return (a << 24) | (r << 16) | (g << 8) | b; } + + public static int parseColor(String color, int fallback) { + if (color == null) { + return fallback; + } + + String s = color.trim(); + if (s.isEmpty()) { + return fallback; + } + + String hex = s; + if (hex.charAt(0) == '#') { + hex = hex.substring(1); + } else if (hex.length() > 2 && (hex.startsWith("0x") || hex.startsWith("0X"))) { + hex = hex.substring(2); + } + if (hex.matches("(?i)[0-9a-f]{6}")) { + try { + return 0xFF000000 | Integer.parseInt(hex, 16); + } catch (NumberFormatException ignored) { + return fallback; + } + } + + String key = s.toUpperCase(Locale.ROOT); + return switch (key) { + case "BLACK" -> 0xFF000000; + case "DARK_BLUE" -> 0xFF0000AA; + case "DARK_GREEN" -> 0xFF00AA00; + case "DARK_AQUA" -> 0xFF00AAAA; + case "DARK_RED" -> 0xFFAA0000; + case "DARK_PURPLE" -> 0xFFAA00AA; + case "GOLD", "ORANGE" -> 0xFFFFAA00; + case "GRAY", "GREY", "LIGHT_GRAY", "LIGHTGRAY", "LIGHT_GREY", "LIGHTGREY" -> 0xFFAAAAAA; + case "DARK_GRAY", "DARKGRAY", "DARK_GREY", "DARKGREY" -> 0xFF555555; + case "BLUE" -> 0xFF5555FF; + case "GREEN", "LIME" -> 0xFF55FF55; + case "CYAN", "AQUA" -> 0xFF55FFFF; + case "RED" -> 0xFFFF5555; + case "PURPLE", "MAGENTA", "LIGHT_PURPLE", "LIGHTPURPLE" -> 0xFFFF55FF; + case "YELLOW" -> 0xFFFFFF55; + case "WHITE" -> 0xFFFFFFFF; + default -> fallback; + }; + } + + public static String toNearestChatColorCode(int argb) { + int r = (argb >> 16) & 0xFF; + int g = (argb >> 8) & 0xFF; + int b = argb & 0xFF; + + int bestIndex = 9; + int bestDist = Integer.MAX_VALUE; + for (int i = 0; i < MC_COLORS.length; i++) { + int c = MC_COLORS[i]; + int dr = r - ((c >> 16) & 0xFF); + int dg = g - ((c >> 8) & 0xFF); + int db = b - (c & 0xFF); + int dist = (dr * dr) + (dg * dg) + (db * db); + if (dist < bestDist) { + bestDist = dist; + bestIndex = i; + } + } + + return MC_CODES[bestIndex]; + } } 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 f0280663..5a02d0d6 100644 --- a/src/main/java/mcp/mobius/waila/api/impl/ConfigHandler.java +++ b/src/main/java/mcp/mobius/waila/api/impl/ConfigHandler.java @@ -176,6 +176,9 @@ private void loadDefaultConfigInternal() { OverlayConfig.scale = config.get(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_SCALE, 100).getInt() / 100.0f; + OverlayConfig.fancyBlockScale = config.get(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_FANCYBLOCK_SCALE, 100, + "Scale for the fancy block preview render (percent).").getInt() / 100.0f; + config.get(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_NHEARTS, 20).getInt(); config.get(Configuration.CATEGORY_GENERAL, Constants.CFG_WAILA_MAXHP, 40).getInt(); diff --git a/src/main/java/mcp/mobius/waila/handlers/VanillaTooltipHandler.java b/src/main/java/mcp/mobius/waila/handlers/VanillaTooltipHandler.java index 7eeef4ef..fe772bf4 100644 --- a/src/main/java/mcp/mobius/waila/handlers/VanillaTooltipHandler.java +++ b/src/main/java/mcp/mobius/waila/handlers/VanillaTooltipHandler.java @@ -2,6 +2,10 @@ import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import com.gtnewhorizons.wdmla.api.ui.MessageType; +import com.gtnewhorizons.wdmla.config.General; +import com.gtnewhorizons.wdmla.util.Color; + import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @@ -15,7 +19,18 @@ public class VanillaTooltipHandler { @SubscribeEvent @SideOnly(Side.CLIENT) public void tooltipEvent(ItemTooltipEvent event) { + if (!General.modName.inventoryShow) { + return; + } String canonicalName = ModIdentification.nameFromStack(event.itemStack); - if (canonicalName != null && !canonicalName.isEmpty()) event.toolTip.add("\u00a79\u00a7o" + canonicalName); + if (canonicalName != null && !canonicalName.isEmpty()) { + int fallback = General.currentTheme.get().textColor(MessageType.MOD_NAME); + int color = Color.parseColor(General.modName.inventoryColorOverride, fallback); + StringBuilder prefix = new StringBuilder(Color.toNearestChatColorCode(color)); + if (General.modName.inventoryItalic) { + prefix.append("\u00a7o"); + } + event.toolTip.add(prefix + canonicalName); + } } } diff --git a/src/main/java/mcp/mobius/waila/handlers/nei/TooltipHandlerWaila.java b/src/main/java/mcp/mobius/waila/handlers/nei/TooltipHandlerWaila.java index 1e3e7d34..dbb8475c 100644 --- a/src/main/java/mcp/mobius/waila/handlers/nei/TooltipHandlerWaila.java +++ b/src/main/java/mcp/mobius/waila/handlers/nei/TooltipHandlerWaila.java @@ -6,6 +6,9 @@ import net.minecraft.item.ItemStack; import codechicken.nei.guihook.IContainerTooltipHandler; +import com.gtnewhorizons.wdmla.api.ui.MessageType; +import com.gtnewhorizons.wdmla.config.General; +import com.gtnewhorizons.wdmla.util.Color; import mcp.mobius.waila.utils.ModIdentification; /** @@ -21,8 +24,18 @@ public List handleItemDisplayName(GuiContainer arg0, ItemStack itemstack @Override public List handleItemTooltip(GuiContainer arg0, ItemStack itemstack, int arg2, int arg3, List currenttip) { + if (!General.modName.inventoryShow) { + return currenttip; + } String canonicalName = ModIdentification.nameFromStack(itemstack); - if (canonicalName != null && !canonicalName.isEmpty()) currenttip.add("\u00a79\u00a7o" + canonicalName); + if (canonicalName != null && !canonicalName.isEmpty()) { + StringBuilder prefix = new StringBuilder(); + prefix.append(resolveModNameColorCode()); + if (General.modName.inventoryItalic) { + prefix.append("\u00a7o"); + } + currenttip.add(prefix + canonicalName); + } return currenttip; } @@ -31,4 +44,9 @@ public List handleTooltip(GuiContainer arg0, int arg1, int arg2, List