diff --git a/build.gradle b/build.gradle index a284f20..cc8b680 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.7-SNAPSHOT' + id 'fabric-loom' version '1.8-SNAPSHOT' id 'maven-publish' } @@ -88,3 +88,7 @@ publishing { // retrieving dependencies. } } + +loom { + accessWidenerPath = file("src/main/resources/hudbars.accesswidener") +} diff --git a/gradle.properties b/gradle.properties index 20ca42d..004cfdd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,13 +2,13 @@ org.gradle.jvmargs=-Xmx1G # Fabric Properties # check these on https://modmuss50.me/fabric.html -minecraft_version=1.21.1 -yarn_mappings=1.21.1+build.3 -loader_version=0.16.2 +minecraft_version=1.21.4 +yarn_mappings=1.21.4+build.1 +loader_version=0.16.9 # Mod Properties -mod_version=1.0 +mod_version=1.1.3 maven_group=systems.brn archives_base_name=hudbars # Dependencies # check this on https://modmuss50.me/fabric.html -fabric_version=0.102.1+1.21.1 +fabric_version=0.110.5+1.21.4 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a441313..9355b41 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/systems/brn/hudbars/mixin/InGameHudMixin.java b/src/main/java/systems/brn/hudbars/mixin/InGameHudMixin.java index 6b9c401..96dfcd7 100644 --- a/src/main/java/systems/brn/hudbars/mixin/InGameHudMixin.java +++ b/src/main/java/systems/brn/hudbars/mixin/InGameHudMixin.java @@ -6,12 +6,16 @@ import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; import net.minecraft.client.render.RenderTickCounter; +import net.minecraft.entity.JumpingMount; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.player.HungerManager; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.registry.tag.FluidTags; +import net.minecraft.util.Util; import net.minecraft.util.math.MathHelper; +import net.minecraft.world.GameMode; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -23,13 +27,111 @@ import static net.minecraft.entity.effect.StatusEffects.WITHER; @Mixin(InGameHud.class) public class InGameHudMixin { - @Inject(method = "renderHealthBar", at = @At("HEAD"), cancellable = true) - private void renderHealthBar(DrawContext context, PlayerEntity player, int x, int y, int lines, int regeneratingHeartIndex, float maxHealthFloat, int lastHealth, int health, int absorption, boolean blinking, CallbackInfo ci) { + + @Inject(method = "render", at = @At("HEAD"), cancellable = true) + private void renderMainHud(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) { + InGameHud hud = (InGameHud) (Object) this; + MinecraftClient client = hud.client; + PlayerEntity player = client.player; + + if (client.interactionManager.getCurrentGameMode() == GameMode.SPECTATOR) { + hud.getSpectatorHud().renderSpectatorMenu(context); + } else { + hud.renderHotbar(context, tickCounter); + } + + if (player == null) return; + + // Calculate HUD bar positions + int centerX = context.getScaledWindowWidth() / 2 - 91; + + // Render Jump Bar for Mount + JumpingMount jumpingMount = client.player.getJumpingMount(); + if (jumpingMount != null) { + renderMountJumpBar(jumpingMount, context, centerX); + } + + // Render Status Bars (Health, Armor, Food, Air) + renderStatusBars(context); + + // Render Mount Health (if applicable) + renderMountHealth(context); + + // Render Experience Bar + renderExperienceBar(context, centerX); + + // Render Held Item Tooltip + if (client.interactionManager.getCurrentGameMode() != GameMode.SPECTATOR) { + hud.renderHeldItemTooltip(context); + } else if (player.isSpectator()) { + hud.getSpectatorHud().render(context); + } + hud.renderCrosshair(context, tickCounter); + ci.cancel(); + } + + private void renderStatusBars(DrawContext context) { + InGameHud hud = (InGameHud) (Object) this; + MinecraftClient client = hud.client; + PlayerEntity player = hud.getCameraPlayer(); + if (player != null) { + int lastHealth = MathHelper.ceil(player.getHealth()); + boolean bl = hud.heartJumpEndTick > (long) hud.getTicks() && (hud.heartJumpEndTick - (long) hud.getTicks()) / 3L % 2L == 1L; + int screenWidth = context.getScaledWindowWidth(); + int screenHeight = context.getScaledWindowHeight(); + int baseY = screenHeight - 39; + long l = Util.getMeasuringTimeMs(); + if (lastHealth < hud.lastHealthValue && player.timeUntilRegen > 0) { + hud.lastHealthCheckTime = l; + hud.heartJumpEndTick = (long) (hud.getTicks() + 20); + } else if (lastHealth > hud.lastHealthValue && player.timeUntilRegen > 0) { + hud.lastHealthCheckTime = l; + hud.heartJumpEndTick = (long) (hud.getTicks() + 10); + } + + if (l - hud.lastHealthCheckTime > 1000L) { + hud.lastHealthValue = lastHealth; + hud.renderHealthValue = lastHealth; + hud.lastHealthCheckTime = l; + } + + hud.lastHealthValue = lastHealth; + int health = hud.renderHealthValue; + int x = context.getScaledWindowWidth() / 2 - 91; + int m = context.getScaledWindowWidth() / 2 + 91; + int y = context.getScaledWindowHeight() - 39; + float maxHealthFloat = Math.max((float) player.getAttributeValue(EntityAttributes.MAX_HEALTH), (float) Math.max(health, lastHealth)); + int absorption = MathHelper.ceil(player.getAbsorptionAmount()); + int p = MathHelper.ceil((maxHealthFloat + (float) absorption) / 2.0F / 10.0F); + int q = Math.max(10 - (p - 2), 3); + + renderArmor(context, player, y, p, q, x); + renderHealthBar(context, player, x, y, maxHealthFloat, lastHealth, health, absorption, bl); + renderFood(context, player, y, m); + renderAir(context, player, baseY, screenWidth / 2 + 91); + + } + } + + private void renderMountHealth(DrawContext context) { + InGameHud hud = (InGameHud) (Object) this; + LivingEntity mount = hud.getRiddenEntity(); + if (mount != null) { + int health = (int) Math.ceil(mount.getHealth()); + int screenHeight = context.getScaledWindowHeight(); + int screenWidth = context.getScaledWindowWidth(); + int baseX = screenWidth / 2 + 12; + int baseY = screenHeight - 52; + renderHealthBar(context, mount, baseX, baseY, mount.getMaxHealth(), health, health, 0, false); + } + } + + private void renderHealthBar(DrawContext context, LivingEntity player, int x, int y, float maxHealthFloat, int lastHealth, int health, int absorption, boolean blinking) { int barWidth = 80; int barHeight = 10; int maxHealth = (int) maxHealthFloat; - y -= 12; + y -= 8; int color; int borderColor; @@ -60,35 +162,116 @@ public class InGameHudMixin { String healthText = "%d/%d".formatted(lastHealth + absorption, maxHealth); drawBar(context, x, y, barWidth, barHeight, healthPercentage, color, borderColor, healthText, 0, 0); - - ci.cancel(); } @Unique - private static int generateSubBarColor(int color) { - // Mask the alpha channel (first byte) - int alpha = color & 0xFF000000; + private static int getArmorToughness(LivingEntity livingEntity) { + return MathHelper.floor(livingEntity.getAttributeValue(EntityAttributes.ARMOR_TOUGHNESS)); + } - // Extract RGB channels - int red = (color & 0x00FF0000) >> 16; - int green = (color & 0x0000FF00) >> 8; - int blue = color & 0x000000FF; + @Unique + private static int computeArmorColor(float armorPercentage) { + // Gradually interpolate between grey (low armor) and green (high armor) + int red = (int) (0xAA * (1.0f - armorPercentage)); // Red decreases as armor increases + int green = (int) (0xFF * armorPercentage); // Green increases as armor increases + int blue = (int) (0x55 + (0xAA * armorPercentage)); // Blue shifts from grey to green - // Halve each RGB channel - red = Math.max(red / 2, 0); - green = Math.max(green / 2, 0); - blue = Math.max(blue / 2, 0); - - // Combine the halved RGB channels back into the color - int RGB = (red << 16) | (green << 8) | blue; - - // Combine the alpha channel with the modified RGB channels - return alpha | RGB; + // Combine the RGB values into a single color integer + return (0xFF << 24) | (red << 16) | (green << 8) | blue; } - @Inject(method = "renderFood", at = @At("HEAD"), cancellable = true) - private void renderFood(DrawContext context, PlayerEntity player, int top, int right, CallbackInfo ci) { + @Unique + private static void renderArmor(DrawContext context, PlayerEntity player, int i, int j, int k, int x) { + int armorLevel = player.getArmor(); + int armorToughness = getArmorToughness(player); + + // Skip rendering if armor level is 0 + if (armorLevel > 0) { + + int barWidth = 80; + int barHeight = 10; + int posY = i - (j - 1) * k - 21; + + // Compute the color based on the armor level percentage + float armorPercentage = (float) armorLevel / 20.0f; + float toughnessPercentage = (float) armorToughness / 20.0f; + + int color = computeArmorColor(armorPercentage); + + // Armor text to display + String armorText = "%d/%d".formatted(armorLevel, 20); + + // Use the drawBar method to render the armor bar + drawBar(context, x, posY, barWidth, barHeight, armorPercentage, color, 0xFF000000, armorText, toughnessPercentage, generateSubBarColor(color)); + + RenderSystem.disableBlend(); + } + } + + + private void renderMountJumpBar(JumpingMount mount, DrawContext context, int x) { + InGameHud hud = (InGameHud) (Object) this; + MinecraftClient client = hud.client; + + int screenHeight = context.getScaledWindowHeight(); + int y = screenHeight - 68; + float jumpStrength = client.player.getMountJumpStrength(); + + // Background color for the bar + int backgroundColor = 0xFF555555; // Grey background + + // Main bar color + int barColor = 0xFF00FF00; // Green color for the jump bar (can be adjusted) + + // Border color + int borderColor = 0xFFFFFFFF; // White border + + // Text to display + String text = ""; // Assuming no text to be displayed on the jump bar + + // Inner bar color, if applicable + int innerBarColor = 0xFFFF0000; // Red color for the inner bar (can be adjusted) + + // Calculate fill percentage based on jump strength + float fillPercentage = jumpStrength; + + // Check for cooldown + float innerPercentage = (mount.getJumpCooldown() > 0) ? 1.0f : 0.0f; + + // Draw the bar using the drawBar function + drawBar(context, x, y, 182, 5, fillPercentage, barColor, borderColor, text, innerPercentage, innerBarColor); + } + + private void renderExperienceBar(DrawContext context, int x) { + InGameHud hud = (InGameHud) (Object) this; + MinecraftClient client = hud.client; + PlayerEntity player = client.player; + + if (player == null) return; + + int screenHeight = context.getScaledWindowHeight(); + int experienceNeededForNextLevel = player.getNextLevelExperience(); + if (experienceNeededForNextLevel > 0) { + int experienceGotForThisLevel = (int) (player.experienceProgress * experienceNeededForNextLevel); + int experienceLevel = player.experienceLevel; + int barWidth = 183; + int barHeight = 10; + int filledWidth = (int) (player.experienceProgress * barWidth); + int y = screenHeight - 34; + + RenderSystem.enableBlend(); + + // Display experienceGotForThisLevel/experienceNeededForNextLevel + String experienceText = "%d/%d (%d)".formatted(experienceGotForThisLevel, experienceNeededForNextLevel, experienceLevel); + + drawBar(context, x, y, barWidth, barHeight, (float) filledWidth / barWidth, 0xFFAAAA00, 0xFF000000, experienceText, 0, 0); + + RenderSystem.disableBlend(); + } + } + + private void renderFood(DrawContext context, PlayerEntity player, int top, int right) { HungerManager hungerManager = player.getHungerManager(); int foodLevel = hungerManager.getFoodLevel(); float saturationLevel = hungerManager.getSaturationLevel(); @@ -97,7 +280,7 @@ public class InGameHudMixin { int barHeight = 10; int x = right - barWidth + 1; - int y = top - 12; + int y = top - 8; int color; @@ -121,99 +304,30 @@ public class InGameHudMixin { String foodText = "%d(%d)/%d".formatted(foodLevel, (int) saturationLevel, 20); // Calculate the saturation percentage relative to the current food level - float saturationPercentage = saturationLevel > 0 ? (saturationLevel / (float) foodLevel) : 0; + float saturationPercentage = saturationLevel / 20.0f; // Use the drawBar method with the innerPercentage and innerColor for saturation drawBar(context, x, y, barWidth, barHeight, foodPercentage, color, 0xFF000000, foodText, saturationPercentage, generateSubBarColor(color)); - - ci.cancel(); } - @Unique - private static int getArmorToughness(LivingEntity livingEntity) { - return MathHelper.floor(livingEntity.getAttributeValue(EntityAttributes.GENERIC_ARMOR_TOUGHNESS)); - } + private void renderAir(DrawContext context, PlayerEntity player, int top, int right) { + int air = player.getAir(); + int maxAir = player.getMaxAir(); + int barWidth = 80; + int barHeight = 10; + int x = right - barWidth + 1; + int y = top - 22; - @Unique - private static int computeArmorColor(float armorPercentage) { - // Gradually interpolate between grey (low armor) and green (high armor) - int red = (int) (0xAA * (1.0f - armorPercentage)); // Red decreases as armor increases - int green = (int) (0xFF * armorPercentage); // Green increases as armor increases - int blue = (int) (0x55 + (0xAA * armorPercentage)); // Blue shifts from grey to green - - // Combine the RGB values into a single color integer - return (0xFF << 24) | (red << 16) | (green << 8) | blue; - } - - @Inject(method = "renderArmor", at = @At("HEAD"), cancellable = true) - private static void renderArmor(DrawContext context, PlayerEntity player, int i, int j, int k, int x, CallbackInfo ci) { - int armorLevel = player.getArmor(); - int armorToughness = getArmorToughness(player); - - // Skip rendering if armor level is 0 - if (armorLevel > 0) { - - int barWidth = 80; - int barHeight = 10; - int posY = i - (j - 1) * k - 26; - - // Compute the color based on the armor level percentage - float armorPercentage = (float) armorLevel / 20.0f; - float toughnessPercentage = (float) armorToughness / 20.0f; - - int color = computeArmorColor(armorPercentage); - - // Armor text to display - String armorText = "%d/%d".formatted(armorLevel, 20); - - // Use the drawBar method to render the armor bar - drawBar(context, x, posY, barWidth, barHeight, armorPercentage, color, 0xFF000000, armorText, toughnessPercentage, generateSubBarColor(color)); - - RenderSystem.disableBlend(); + if (air < maxAir || player.isSubmergedIn(FluidTags.WATER)) { + float airPercentage = (float) air / (float) maxAir; + if (airPercentage < 0) { + airPercentage = 0; + } + String airText = "%d/%d".formatted(air, maxAir); + drawBar(context, x, y, barWidth, barHeight, airPercentage, 0xFF00FFFF, 0xFF000000, airText, 0, 0); } - ci.cancel(); } - @Inject(method = "renderExperienceBar", at = @At("HEAD"), cancellable = true) - private void renderExperienceBar(DrawContext context, int x, CallbackInfo ci) { - MinecraftClient client = MinecraftClient.getInstance(); - PlayerEntity player = client.player; - if (player == null) { - ci.cancel(); - return; - } - - client.getProfiler().push("expBar"); - - int experienceNeededForNextLevel = player.getNextLevelExperience(); - if (experienceNeededForNextLevel > 0) { - int experienceGotForThisLevel = (int) (player.experienceProgress * experienceNeededForNextLevel); - int experienceLevel = player.experienceLevel; - int barWidth = 183; - int barHeight = 10; - int filledWidth = (int) (player.experienceProgress * barWidth); - int y = context.getScaledWindowHeight() - 36; - - RenderSystem.enableBlend(); - - // Display experienceGotForThisLevel/experienceNeededForNextLevel - String experienceText = "%d/%d (%d)".formatted(experienceGotForThisLevel, experienceNeededForNextLevel, experienceLevel); - - drawBar(context, x, y, barWidth, barHeight, (float) filledWidth / barWidth, 0xFFAAAA00, 0xFF000000, experienceText, 0, 0); - - RenderSystem.disableBlend(); - } - - client.getProfiler().pop(); - ci.cancel(); - } - - @Inject(method = "renderExperienceLevel", at = @At("HEAD"), cancellable = true) - private void renderExperienceLevel(DrawContext context, RenderTickCounter tickCounter, CallbackInfo ci) { - ci.cancel(); - } - - // Common helper method for rendering bars @Unique private static void drawBar(DrawContext context, int x, int y, int barWidth, int barHeight, float percentage, int color, int borderColor, String text, float innerPercentage, int innerColor) { @@ -239,4 +353,26 @@ public class InGameHudMixin { TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; context.drawText(textRenderer, text, x + 2, y + 1, 0xFFFFFFFF, false); } -} \ No newline at end of file + + @Unique + private static int generateSubBarColor(int color) { + // Mask the alpha channel (first byte) + int alpha = color & 0xFF000000; + + // Extract RGB channels + int red = (color & 0x00FF0000) >> 16; + int green = (color & 0x0000FF00) >> 8; + int blue = color & 0x000000FF; + + // Halve each RGB channel + red = Math.max(red / 2, 0); + green = Math.max(green / 2, 0); + blue = Math.max(blue / 2, 0); + + // Combine the halved RGB channels back into the color + int RGB = (red << 16) | (green << 8) | blue; + + // Combine the alpha channel with the modified RGB channels + return alpha | RGB; + } +} diff --git a/src/main/resources/hudbars.accesswidener b/src/main/resources/hudbars.accesswidener new file mode 100644 index 0000000..0d4c1a1 --- /dev/null +++ b/src/main/resources/hudbars.accesswidener @@ -0,0 +1,13 @@ +accessWidener v2 named +accessible method net/minecraft/client/gui/hud/InGameHud getCameraPlayer ()Lnet/minecraft/entity/player/PlayerEntity; +accessible field net/minecraft/client/gui/hud/InGameHud client Lnet/minecraft/client/MinecraftClient; +accessible field net/minecraft/client/gui/hud/InGameHud heartJumpEndTick J +accessible field net/minecraft/client/gui/hud/InGameHud lastHealthValue I +accessible field net/minecraft/client/gui/hud/InGameHud lastHealthCheckTime J +accessible field net/minecraft/client/gui/hud/InGameHud renderHealthValue I +accessible field net/minecraft/client/gui/hud/InGameHud random Lnet/minecraft/util/math/random/Random; +accessible method net/minecraft/client/gui/hud/InGameHud renderHotbar (Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V +accessible method net/minecraft/client/gui/hud/InGameHud renderHeldItemTooltip (Lnet/minecraft/client/gui/DrawContext;)V +accessible method net/minecraft/client/gui/hud/InGameHud getRiddenEntity ()Lnet/minecraft/entity/LivingEntity; +accessible method net/minecraft/client/gui/hud/InGameHud getHeartCount (Lnet/minecraft/entity/LivingEntity;)I +accessible method net/minecraft/client/gui/hud/InGameHud renderCrosshair (Lnet/minecraft/client/gui/DrawContext;Lnet/minecraft/client/render/RenderTickCounter;)V \ No newline at end of file