package net.minecraftforge.client; import java.awt.Color; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Random; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import cpw.mods.fml.common.FMLCommonHandler; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiIngame; import net.minecraft.client.gui.GuiNewChat; import net.minecraft.client.gui.GuiPlayerInfo; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.multiplayer.NetClientHandler; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.entity.RenderItem; import net.minecraft.crash.CallableMinecraftVersion; import net.minecraft.entity.boss.BossStatus; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; import net.minecraft.potion.Potion; import net.minecraft.scoreboard.Score; import net.minecraft.scoreboard.ScoreObjective; import net.minecraft.scoreboard.ScorePlayerTeam; import net.minecraft.scoreboard.Scoreboard; import net.minecraft.util.Direction; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.FoodStats; import net.minecraft.util.Icon; import net.minecraft.util.MathHelper; import net.minecraft.util.StatCollector; import net.minecraft.util.StringUtils; import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.chunk.Chunk; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.MinecraftForge; import static net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType.*; public class GuiIngameForge extends GuiIngame { private static final int WHITE = 0xFFFFFF; //Flags to toggle the rendering of certain aspects of the HUD, valid conditions //must be met for them to render normally. If those conditions are met, but this flag //is false, they will not be rendered. public static boolean renderHelmet = true; public static boolean renderPortal = true; public static boolean renderHotbar = true; public static boolean renderCrosshairs = true; public static boolean renderBossHealth = true; public static boolean renderHealth = true; public static boolean renderArmor = true; public static boolean renderFood = true; public static boolean renderAir = true; public static boolean renderExperiance = true; public static boolean renderObjective = true; private ScaledResolution res = null; private FontRenderer fontrenderer = null; private RenderGameOverlayEvent eventParent; private static final String MC_VERSION = (new CallableMinecraftVersion(null)).minecraftVersion(); public GuiIngameForge(Minecraft mc) { super(mc); } @Override public void renderGameOverlay(float partialTicks, boolean hasScreen, int mouseX, int mouseY) { res = new ScaledResolution(mc.gameSettings, mc.displayWidth, mc.displayHeight); eventParent = new RenderGameOverlayEvent(partialTicks, res, mouseX, mouseY); int width = res.getScaledWidth(); int height = res.getScaledHeight(); if (pre(ALL)) return; fontrenderer = mc.fontRenderer; mc.entityRenderer.setupOverlayRendering(); GL11.glEnable(GL11.GL_BLEND); if (Minecraft.isFancyGraphicsEnabled()) { renderVignette(mc.thePlayer.getBrightness(partialTicks), width, height); } else { GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } if (renderHelmet) renderHelmet(res, partialTicks, hasScreen, mouseX, mouseY); if (renderPortal && !mc.thePlayer.isPotionActive(Potion.confusion)) { renderPortal(width, height, partialTicks); } if (!mc.playerController.enableEverythingIsScrewedUpMode()) { GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); zLevel = -90.0F; rand.setSeed((long)(updateCounter * 312871)); mc.renderEngine.bindTexture("/gui/icons.png"); if (renderCrosshairs) renderCrosshairs(width, height); if (renderBossHealth) renderBossHealth(); if (this.mc.playerController.shouldDrawHUD()) { if (renderArmor) renderArmor(width, height); if (renderHealth) renderHealth(width, height); if (renderFood) renderFood(width, height); if (renderAir) renderAir(width, height); } if (renderHotbar) renderHotbar(width, height, partialTicks); } if (renderExperiance) renderExperience(width, height); renderSleepFade(width, height); renderToolHightlight(width, height); renderHUDText(width, height); renderRecordOverlay(width, height, partialTicks); ScoreObjective objective = mc.theWorld.getScoreboard().func_96539_a(1); if (renderObjective && objective != null) { this.func_96136_a(objective, height, width, fontrenderer); } GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); GL11.glDisable(GL11.GL_ALPHA_TEST); renderChat(width, height); renderPlayerList(width, height); GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glDisable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_ALPHA_TEST); } public ScaledResolution getResolution() { return res; } protected void renderHotbar(int width, int height, float partialTicks) { if (pre(HOTBAR)) return; mc.mcProfiler.startSection("actionBar"); mc.renderEngine.bindTexture("/gui/gui.png"); InventoryPlayer inv = mc.thePlayer.inventory; drawTexturedModalRect(width / 2 - 91, height - 22, 0, 0, 182, 22); drawTexturedModalRect(width / 2 - 91 - 1 + inv.currentItem * 20, height - 22 - 1, 0, 22, 24, 22); GL11.glDisable(GL11.GL_BLEND); GL11.glEnable(GL12.GL_RESCALE_NORMAL); RenderHelper.enableGUIStandardItemLighting(); for (int i = 0; i < 9; ++i) { int x = width / 2 - 90 + i * 20 + 2; int z = height - 16 - 3; renderInventorySlot(i, x, z, partialTicks); } RenderHelper.disableStandardItemLighting(); GL11.glDisable(GL12.GL_RESCALE_NORMAL); mc.mcProfiler.endSection(); post(HOTBAR); } protected void renderCrosshairs(int width, int height) { if (pre(CROSSHAIRS)) return; GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ONE_MINUS_SRC_COLOR); drawTexturedModalRect(width / 2 - 7, height / 2 - 7, 0, 0, 16, 16); GL11.glDisable(GL11.GL_BLEND); post(CROSSHAIRS); } @Override protected void renderBossHealth() { if (pre(BOSSHEALTH)) return; mc.mcProfiler.startSection("bossHealth"); super.renderBossHealth(); mc.mcProfiler.endSection(); post(BOSSHEALTH); } private void renderHelmet(ScaledResolution res, float partialTicks, boolean hasScreen, int mouseX, int mouseY) { if (pre(HELMET)) return; ItemStack itemstack = this.mc.thePlayer.inventory.armorItemInSlot(3); if (this.mc.gameSettings.thirdPersonView == 0 && itemstack != null && itemstack.getItem() != null) { if (itemstack.itemID == Block.pumpkin.blockID) { renderPumpkinBlur(res.getScaledWidth(), res.getScaledHeight()); } else { itemstack.getItem().renderHelmetOverlay(itemstack, mc.thePlayer, res, partialTicks, hasScreen, mouseX, mouseY); } } post(HELMET); } protected void renderArmor(int width, int height) { if (pre(ARMOR)) return; mc.mcProfiler.startSection("armor"); int left = width / 2 - 91; int top = height - 49; int level = ForgeHooks.getTotalArmorValue(mc.thePlayer); for (int i = 1; level > 0 && i < 20; i += 2) { if (i < level) { drawTexturedModalRect(left, top, 34, 9, 9, 9); } else if (i == level) { drawTexturedModalRect(left, top, 25, 9, 9, 9); } else if (i > level) { drawTexturedModalRect(left, top, 16, 9, 9, 9); } left += 8; } mc.mcProfiler.endSection(); post(ARMOR); } protected void renderPortal(int width, int height, float partialTicks) { if (pre(PORTAL)) return; float f1 = mc.thePlayer.prevTimeInPortal + (mc.thePlayer.timeInPortal - mc.thePlayer.prevTimeInPortal) * partialTicks; if (f1 > 0.0F) { renderPortalOverlay(f1, width, height); } post(PORTAL); } protected void renderAir(int width, int height) { if (pre(AIR)) return; mc.mcProfiler.startSection("air"); int left = width / 2 + 91; int top = height - 49; if (mc.thePlayer.isInsideOfMaterial(Material.water)) { int air = mc.thePlayer.getAir(); int full = MathHelper.ceiling_double_int((double)(air - 2) * 10.0D / 300.0D); int partial = MathHelper.ceiling_double_int((double)air * 10.0D / 300.0D) - full; for (int i = 0; i < full + partial; ++i) { drawTexturedModalRect(left - i * 8 - 9, top, (i < full ? 16 : 25), 18, 9, 9); } } mc.mcProfiler.endSection(); post(AIR); } public void renderHealth(int width, int height) { if (pre(HEALTH)) return; mc.mcProfiler.startSection("health"); boolean highlight = mc.thePlayer.hurtResistantTime / 3 % 2 == 1; if (mc.thePlayer.hurtResistantTime < 10) { highlight = false; } int health = mc.thePlayer.getHealth(); int healthLast = mc.thePlayer.prevHealth; int left = width / 2 - 91; int top = height - 39; int regen = -1; if (mc.thePlayer.isPotionActive(Potion.regeneration)) { regen = this.updateCounter % 25; } for (int i = 0; i < 10; ++i) { int idx = i * 2 + 1; int iconX = 16; if (mc.thePlayer.isPotionActive(Potion.poison)) iconX += 36; else if (mc.thePlayer.isPotionActive(Potion.wither)) iconX += 72; int x = left + i * 8; int y = top; if (health <= 4) y = top + rand.nextInt(2); if (i == regen) y -= 2; byte iconY = 0; if (mc.theWorld.getWorldInfo().isHardcoreModeEnabled()) iconY = 5; drawTexturedModalRect(x, y, 16 + (highlight ? 9 : 0), 9 * iconY, 9, 9); if (highlight) { if (idx < healthLast) drawTexturedModalRect(x, y, iconX + 54, 9 * iconY, 9, 9); else if (idx == healthLast) drawTexturedModalRect(x, y, iconX + 63, 9 * iconY, 9, 9); } if (idx < health) drawTexturedModalRect(x, y, iconX + 36, 9 * iconY, 9, 9); else if (idx == health) drawTexturedModalRect(x, y, iconX + 45, 9 * iconY, 9, 9); } mc.mcProfiler.endSection(); post(HEALTH); } public void renderFood(int width, int height) { if (pre(FOOD)) return; mc.mcProfiler.startSection("food"); int left = width / 2 + 91; int top = height - 39; boolean unused = false;// Unused flag in vanilla, seems to be part of a 'fade out' mechanic FoodStats stats = mc.thePlayer.getFoodStats(); int level = stats.getFoodLevel(); int levelLast = stats.getPrevFoodLevel(); for (int i = 0; i < 10; ++i) { int idx = i * 2 + 1; int x = left - i * 8 - 9; int y = top; int icon = 16; byte backgound = 0; if (mc.thePlayer.isPotionActive(Potion.hunger)) { icon += 36; backgound = 13; } if (unused) backgound = 1; //Probably should be a += 1 but vanilla never uses this if (mc.thePlayer.getFoodStats().getSaturationLevel() <= 0.0F && updateCounter % (level * 3 + 1) == 0) { y = top + (rand.nextInt(3) - 1); } this.drawTexturedModalRect(x, y, 16 + backgound * 9, 27, 9, 9); if (unused) { if (idx < levelLast) { drawTexturedModalRect(x, y, icon + 54, 27, 9, 9); } if (idx == levelLast) { drawTexturedModalRect(x, y, icon + 63, 27, 9, 9); } } if (idx < level) { drawTexturedModalRect(x, y, icon + 36, 27, 9, 9); } if (idx == level) { drawTexturedModalRect(x, y, icon + 45, 27, 9, 9); } } mc.mcProfiler.endSection(); post(FOOD); } protected void renderSleepFade(int width, int height) { if (mc.thePlayer.getSleepTimer() > 0) { mc.mcProfiler.startSection("sleep"); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDisable(GL11.GL_ALPHA_TEST); int sleepTime = mc.thePlayer.getSleepTimer(); float opacity = (float)sleepTime / 100.0F; if (opacity > 1.0F) { opacity = 1.0F - (float)(sleepTime - 100) / 10.0F; } int color = (int)(220.0F * opacity) << 24 | 1052704; drawRect(0, 0, width, height, color); GL11.glEnable(GL11.GL_ALPHA_TEST); GL11.glEnable(GL11.GL_DEPTH_TEST); mc.mcProfiler.endSection(); } } protected void renderExperience(int width, int height) { if (pre(EXPERIENCE)) return; GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); if (mc.playerController.shouldDrawHUD()) { mc.mcProfiler.startSection("expBar"); mc.renderEngine.bindTexture("/gui/icons.png"); int cap = this.mc.thePlayer.xpBarCap(); int left = width / 2 - 91; if (cap > 0) { short short1 = 182; int l2 = (int)(this.mc.thePlayer.experience * (float)(short1 + 1)); int k2 = height - 32 + 3; this.drawTexturedModalRect(left, k2, 0, 64, short1, 5); if (l2 > 0) { this.drawTexturedModalRect(left, k2, 0, 69, l2, 5); } } mc.mcProfiler.endSection(); } if (mc.playerController.func_78763_f() && mc.thePlayer.experienceLevel > 0) { mc.mcProfiler.startSection("expLevel"); boolean flag1 = false; int color = flag1 ? 16777215 : 8453920; String text = "" + mc.thePlayer.experienceLevel; int x = (width - fontrenderer.getStringWidth(text)) / 2; int y = height - 31 - 4; fontrenderer.drawString(text, x + 1, y, 0); fontrenderer.drawString(text, x - 1, y, 0); fontrenderer.drawString(text, x, y + 1, 0); fontrenderer.drawString(text, x, y - 1, 0); fontrenderer.drawString(text, x, y, color); mc.mcProfiler.endSection(); } GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); post(EXPERIENCE); } protected void renderToolHightlight(int width, int height) { if (this.mc.gameSettings.heldItemTooltips) { mc.mcProfiler.startSection("toolHighlight"); if (this.remainingHighlightTicks > 0 && this.highlightingItemStack != null) { String name = this.highlightingItemStack.getDisplayName(); int opacity = (int)((float)this.remainingHighlightTicks * 256.0F / 10.0F); if (opacity > 255) opacity = 255; if (opacity > 0) { int y = height - 59; if (!mc.playerController.shouldDrawHUD()) y += 14; GL11.glPushMatrix(); GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); FontRenderer font = highlightingItemStack.getItem().getFontRenderer(highlightingItemStack); if (font != null) { int x = (width - font.getStringWidth(name)) / 2; font.drawStringWithShadow(name, x, y, WHITE | (opacity << 24)); } else { int x = (width - fontrenderer.getStringWidth(name)) / 2; fontrenderer.drawStringWithShadow(name, x, y, WHITE | (opacity << 24)); } GL11.glDisable(GL11.GL_BLEND); GL11.glPopMatrix(); } } mc.mcProfiler.endSection(); } } protected void renderHUDText(int width, int height) { mc.mcProfiler.startSection("forgeHudText"); ArrayList<String> left = new ArrayList<String>(); ArrayList<String> right = new ArrayList<String>(); if (mc.isDemo()) { long time = mc.theWorld.getTotalWorldTime(); if (time >= 120500L) { right.add(StatCollector.translateToLocal("demo.demoExpired")); } else { right.add(String.format(StatCollector.translateToLocal("demo.remainingTime"), StringUtils.ticksToElapsedTime((int)(120500L - time)))); } } if (this.mc.gameSettings.showDebugInfo) { mc.mcProfiler.startSection("debug"); GL11.glPushMatrix(); left.add("Minecraft " + MC_VERSION + " (" + this.mc.debug + ")"); left.add(mc.debugInfoRenders()); left.add(mc.getEntityDebug()); left.add(mc.debugInfoEntities()); left.add(mc.getWorldProviderName()); left.add(null); //Spacer long max = Runtime.getRuntime().maxMemory(); long total = Runtime.getRuntime().totalMemory(); long free = Runtime.getRuntime().freeMemory(); long used = total - free; right.add("Used memory: " + used * 100L / max + "% (" + used / 1024L / 1024L + "MB) of " + max / 1024L / 1024L + "MB"); right.add("Allocated memory: " + total * 100L / max + "% (" + total / 1024L / 1024L + "MB)"); int x = MathHelper.floor_double(mc.thePlayer.posX); int y = MathHelper.floor_double(mc.thePlayer.posY); int z = MathHelper.floor_double(mc.thePlayer.posZ); float yaw = mc.thePlayer.rotationYaw; int heading = MathHelper.floor_double((double)(mc.thePlayer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3; left.add(String.format("x: %.5f (%d) // c: %d (%d)", mc.thePlayer.posX, x, x >> 4, x & 15)); left.add(String.format("y: %.3f (feet pos, %.3f eyes pos)", mc.thePlayer.boundingBox.minY, mc.thePlayer.posY)); left.add(String.format("z: %.5f (%d) // c: %d (%d)", mc.thePlayer.posZ, z, z >> 4, z & 15)); left.add(String.format("f: %d (%s) / %f", heading, Direction.directions[heading], MathHelper.wrapAngleTo180_float(yaw))); if (mc.theWorld != null && mc.theWorld.blockExists(x, y, z)) { Chunk chunk = this.mc.theWorld.getChunkFromBlockCoords(x, z); left.add(String.format("lc: %d b: %s bl: %d sl: %d rl: %d", chunk.getTopFilledSegment() + 15, chunk.getBiomeGenForWorldCoords(x & 15, z & 15, mc.theWorld.getWorldChunkManager()).biomeName, chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15), chunk.getSavedLightValue(EnumSkyBlock.Sky, x & 15, y, z & 15), chunk.getBlockLightValue(x & 15, y, z & 15, 0))); } else { left.add(null); } left.add(String.format("ws: %.3f, fs: %.3f, g: %b, fl: %d", mc.thePlayer.capabilities.getWalkSpeed(), mc.thePlayer.capabilities.getFlySpeed(), mc.thePlayer.onGround, mc.theWorld.getHeightValue(x, z))); right.add(null); for (String s : FMLCommonHandler.instance().getBrandings().subList(1, FMLCommonHandler.instance().getBrandings().size())) { right.add(s); } GL11.glPopMatrix(); mc.mcProfiler.endSection(); } RenderGameOverlayEvent.Text event = new RenderGameOverlayEvent.Text(eventParent, left, right); if (!MinecraftForge.EVENT_BUS.post(event)) { for (int x = 0; x < left.size(); x++) { String msg = left.get(x); if (msg == null) continue; fontrenderer.drawStringWithShadow(msg, 2, 2 + x * 10, WHITE); } for (int x = 0; x < right.size(); x++) { String msg = right.get(x); if (msg == null) continue; int w = fontrenderer.getStringWidth(msg); fontrenderer.drawStringWithShadow(msg, width - w - 10, 2 + x * 10, WHITE); } } mc.mcProfiler.endSection(); post(TEXT); } protected void renderRecordOverlay(int width, int height, float partialTicks) { if (recordPlayingUpFor > 0) { mc.mcProfiler.startSection("overlayMessage"); float hue = (float)recordPlayingUpFor - partialTicks; int opacity = (int)(hue * 256.0F / 20.0F); if (opacity > 255) opacity = 255; if (opacity > 0) { GL11.glPushMatrix(); GL11.glTranslatef((float)(width / 2), (float)(height - 48), 0.0F); GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); int color = (recordIsPlaying ? Color.HSBtoRGB(hue / 50.0F, 0.7F, 0.6F) & WHITE : WHITE); fontrenderer.drawString(recordPlaying, -fontrenderer.getStringWidth(recordPlaying) / 2, -4, color | (opacity << 24)); GL11.glDisable(GL11.GL_BLEND); GL11.glPopMatrix(); } mc.mcProfiler.endSection(); } } protected void renderChat(int width, int height) { GL11.glPushMatrix(); GL11.glTranslatef(0.0F, (float)(height - 48), 0.0F); mc.mcProfiler.startSection("chat"); persistantChatGUI.drawChat(updateCounter); mc.mcProfiler.endSection(); GL11.glPopMatrix(); } protected void renderPlayerList(int width, int height) { ScoreObjective scoreobjective = this.mc.theWorld.getScoreboard().func_96539_a(0); NetClientHandler handler = mc.thePlayer.sendQueue; if (mc.gameSettings.keyBindPlayerList.pressed && (!mc.isIntegratedServerRunning() || handler.playerInfoList.size() > 1 || scoreobjective != null)) { this.mc.mcProfiler.startSection("playerList"); List players = handler.playerInfoList; int maxPlayers = handler.currentServerMaxPlayers; int rows = maxPlayers; int columns = 1; for (columns = 1; rows > 20; rows = (maxPlayers + columns - 1) / columns) { columns++; } int columnWidth = 300 / columns; if (columnWidth > 150) { columnWidth = 150; } int left = (width - columns * columnWidth) / 2; byte border = 10; drawRect(left - 1, border - 1, left + columnWidth * columns, border + 9 * rows, Integer.MIN_VALUE); for (int i = 0; i < maxPlayers; i++) { int xPos = left + i % columns * columnWidth; int yPos = border + i / columns * 9; drawRect(xPos, yPos, xPos + columnWidth - 1, yPos + 8, 553648127); GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); GL11.glEnable(GL11.GL_ALPHA_TEST); if (i < players.size()) { GuiPlayerInfo player = (GuiPlayerInfo)players.get(i); ScorePlayerTeam team = mc.theWorld.getScoreboard().getPlayersTeam(player.name); String displayName = ScorePlayerTeam.func_96667_a(team, player.name); fontrenderer.drawStringWithShadow(displayName, xPos, yPos, 16777215); if (scoreobjective != null) { int endX = xPos + fontrenderer.getStringWidth(displayName) + 5; int maxX = xPos + columnWidth - 12 - 5; if (maxX - endX > 5) { Score score = scoreobjective.getScoreboard().func_96529_a(player.name, scoreobjective); String scoreDisplay = EnumChatFormatting.YELLOW + "" + score.func_96652_c(); fontrenderer.drawStringWithShadow(scoreDisplay, maxX - fontrenderer.getStringWidth(scoreDisplay), yPos, 16777215); } } GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); mc.renderEngine.bindTexture("/gui/icons.png"); int pingIndex = 4; int ping = player.responseTime; if (ping < 0) pingIndex = 5; else if (ping < 150) pingIndex = 0; else if (ping < 300) pingIndex = 1; else if (ping < 600) pingIndex = 2; else if (ping < 1000) pingIndex = 3; zLevel += 100.0F; drawTexturedModalRect(xPos + columnWidth - 12, yPos, 0, 176 + pingIndex * 8, 10, 8); zLevel -= 100.0F; } } } } //Helper macros private boolean pre(ElementType type) { return MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Pre(eventParent, type)); } private void post(ElementType type) { MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(eventParent, type)); } }