package net.minecraft.client.gui; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.systems.RenderSystem; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.function.UnaryOperator; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.client.AttackIndicatorStatus; import net.minecraft.client.Camera; import net.minecraft.client.DeltaTracker; import net.minecraft.client.Minecraft; import net.minecraft.client.Options; import net.minecraft.client.gui.components.BossHealthOverlay; import net.minecraft.client.gui.components.ChatComponent; import net.minecraft.client.gui.components.DebugScreenOverlay; import net.minecraft.client.gui.components.PlayerTabOverlay; import net.minecraft.client.gui.components.SubtitleOverlay; import net.minecraft.client.gui.components.spectator.SpectatorGui; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.MobEffectTextureManager; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.numbers.NumberFormat; import net.minecraft.network.chat.numbers.StyledFormat; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.sounds.SoundEvents; import net.minecraft.tags.FluidTags; import net.minecraft.util.ARGB; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.StringUtil; import net.minecraft.util.profiling.Profiler; import net.minecraft.world.MenuProvider; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.PlayerRideableJumping; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.player.Player; import net.minecraft.world.food.FoodData; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.equipment.Equippable; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.scores.DisplaySlot; import net.minecraft.world.scores.Objective; import net.minecraft.world.scores.PlayerScoreEntry; import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.Scoreboard; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4fStack; @Environment(EnvType.CLIENT) public class Gui { private static final ResourceLocation CROSSHAIR_SPRITE = ResourceLocation.withDefaultNamespace("hud/crosshair"); private static final ResourceLocation CROSSHAIR_ATTACK_INDICATOR_FULL_SPRITE = ResourceLocation.withDefaultNamespace("hud/crosshair_attack_indicator_full"); private static final ResourceLocation CROSSHAIR_ATTACK_INDICATOR_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace( "hud/crosshair_attack_indicator_background" ); private static final ResourceLocation CROSSHAIR_ATTACK_INDICATOR_PROGRESS_SPRITE = ResourceLocation.withDefaultNamespace( "hud/crosshair_attack_indicator_progress" ); private static final ResourceLocation EFFECT_BACKGROUND_AMBIENT_SPRITE = ResourceLocation.withDefaultNamespace("hud/effect_background_ambient"); private static final ResourceLocation EFFECT_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace("hud/effect_background"); private static final ResourceLocation HOTBAR_SPRITE = ResourceLocation.withDefaultNamespace("hud/hotbar"); private static final ResourceLocation HOTBAR_SELECTION_SPRITE = ResourceLocation.withDefaultNamespace("hud/hotbar_selection"); private static final ResourceLocation HOTBAR_OFFHAND_LEFT_SPRITE = ResourceLocation.withDefaultNamespace("hud/hotbar_offhand_left"); private static final ResourceLocation HOTBAR_OFFHAND_RIGHT_SPRITE = ResourceLocation.withDefaultNamespace("hud/hotbar_offhand_right"); private static final ResourceLocation HOTBAR_ATTACK_INDICATOR_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace( "hud/hotbar_attack_indicator_background" ); private static final ResourceLocation HOTBAR_ATTACK_INDICATOR_PROGRESS_SPRITE = ResourceLocation.withDefaultNamespace("hud/hotbar_attack_indicator_progress"); private static final ResourceLocation JUMP_BAR_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace("hud/jump_bar_background"); private static final ResourceLocation JUMP_BAR_COOLDOWN_SPRITE = ResourceLocation.withDefaultNamespace("hud/jump_bar_cooldown"); private static final ResourceLocation JUMP_BAR_PROGRESS_SPRITE = ResourceLocation.withDefaultNamespace("hud/jump_bar_progress"); private static final ResourceLocation EXPERIENCE_BAR_BACKGROUND_SPRITE = ResourceLocation.withDefaultNamespace("hud/experience_bar_background"); private static final ResourceLocation EXPERIENCE_BAR_PROGRESS_SPRITE = ResourceLocation.withDefaultNamespace("hud/experience_bar_progress"); private static final ResourceLocation ARMOR_EMPTY_SPRITE = ResourceLocation.withDefaultNamespace("hud/armor_empty"); private static final ResourceLocation ARMOR_HALF_SPRITE = ResourceLocation.withDefaultNamespace("hud/armor_half"); private static final ResourceLocation ARMOR_FULL_SPRITE = ResourceLocation.withDefaultNamespace("hud/armor_full"); private static final ResourceLocation FOOD_EMPTY_HUNGER_SPRITE = ResourceLocation.withDefaultNamespace("hud/food_empty_hunger"); private static final ResourceLocation FOOD_HALF_HUNGER_SPRITE = ResourceLocation.withDefaultNamespace("hud/food_half_hunger"); private static final ResourceLocation FOOD_FULL_HUNGER_SPRITE = ResourceLocation.withDefaultNamespace("hud/food_full_hunger"); private static final ResourceLocation FOOD_EMPTY_SPRITE = ResourceLocation.withDefaultNamespace("hud/food_empty"); private static final ResourceLocation FOOD_HALF_SPRITE = ResourceLocation.withDefaultNamespace("hud/food_half"); private static final ResourceLocation FOOD_FULL_SPRITE = ResourceLocation.withDefaultNamespace("hud/food_full"); private static final ResourceLocation AIR_SPRITE = ResourceLocation.withDefaultNamespace("hud/air"); private static final ResourceLocation AIR_POPPING_SPRITE = ResourceLocation.withDefaultNamespace("hud/air_bursting"); private static final ResourceLocation AIR_EMPTY_SPRITE = ResourceLocation.withDefaultNamespace("hud/air_empty"); private static final ResourceLocation HEART_VEHICLE_CONTAINER_SPRITE = ResourceLocation.withDefaultNamespace("hud/heart/vehicle_container"); private static final ResourceLocation HEART_VEHICLE_FULL_SPRITE = ResourceLocation.withDefaultNamespace("hud/heart/vehicle_full"); private static final ResourceLocation HEART_VEHICLE_HALF_SPRITE = ResourceLocation.withDefaultNamespace("hud/heart/vehicle_half"); private static final ResourceLocation VIGNETTE_LOCATION = ResourceLocation.withDefaultNamespace("textures/misc/vignette.png"); public static final ResourceLocation NAUSEA_LOCATION = ResourceLocation.withDefaultNamespace("textures/misc/nausea.png"); private static final ResourceLocation SPYGLASS_SCOPE_LOCATION = ResourceLocation.withDefaultNamespace("textures/misc/spyglass_scope.png"); private static final ResourceLocation POWDER_SNOW_OUTLINE_LOCATION = ResourceLocation.withDefaultNamespace("textures/misc/powder_snow_outline.png"); private static final Comparator SCORE_DISPLAY_ORDER = Comparator.comparing(PlayerScoreEntry::value) .reversed() .thenComparing(PlayerScoreEntry::owner, String.CASE_INSENSITIVE_ORDER); private static final Component DEMO_EXPIRED_TEXT = Component.translatable("demo.demoExpired"); private static final Component SAVING_TEXT = Component.translatable("menu.savingLevel"); private static final float MIN_CROSSHAIR_ATTACK_SPEED = 5.0F; private static final int NUM_HEARTS_PER_ROW = 10; private static final int LINE_HEIGHT = 10; private static final String SPACER = ": "; private static final float PORTAL_OVERLAY_ALPHA_MIN = 0.2F; private static final int HEART_SIZE = 9; private static final int HEART_SEPARATION = 8; private static final int NUM_AIR_BUBBLES = 10; private static final int AIR_BUBBLE_SIZE = 9; private static final int AIR_BUBBLE_SEPERATION = 8; private static final int AIR_BUBBLE_POPPING_DURATION = 2; private static final int EMPTY_AIR_BUBBLE_DELAY_DURATION = 1; private static final float AIR_BUBBLE_POP_SOUND_VOLUME_BASE = 0.5F; private static final float AIR_BUBBLE_POP_SOUND_VOLUME_INCREMENT = 0.1F; private static final float AIR_BUBBLE_POP_SOUND_PITCH_BASE = 1.0F; private static final float AIR_BUBBLE_POP_SOUND_PITCH_INCREMENT = 0.1F; private static final int NUM_AIR_BUBBLE_POPPED_BEFORE_SOUND_VOLUME_INCREASE = 3; private static final int NUM_AIR_BUBBLE_POPPED_BEFORE_SOUND_PITCH_INCREASE = 5; private static final float AUTOSAVE_FADE_SPEED_FACTOR = 0.2F; private static final int SAVING_INDICATOR_WIDTH_PADDING_RIGHT = 5; private static final int SAVING_INDICATOR_HEIGHT_PADDING_BOTTOM = 5; private final RandomSource random = RandomSource.create(); private final Minecraft minecraft; private final ChatComponent chat; private int tickCount; @Nullable private Component overlayMessageString; private int overlayMessageTime; private boolean animateOverlayMessageColor; private boolean chatDisabledByPlayerShown; public float vignetteBrightness = 1.0F; private int toolHighlightTimer; private ItemStack lastToolHighlight = ItemStack.EMPTY; private final DebugScreenOverlay debugOverlay; private final SubtitleOverlay subtitleOverlay; /** * The spectator GUI for this in-game GUI instance */ private final SpectatorGui spectatorGui; private final PlayerTabOverlay tabList; private final BossHealthOverlay bossOverlay; /** * A timer for the current title and subtitle displayed */ private int titleTime; /** * The current title displayed */ @Nullable private Component title; /** * The current sub-title displayed */ @Nullable private Component subtitle; /** * The time that the title take to fade in */ private int titleFadeInTime; /** * The time that the title is display */ private int titleStayTime; /** * The time that the title take to fade out */ private int titleFadeOutTime; private int lastHealth; private int displayHealth; /** * The last recorded system time */ private long lastHealthTime; /** * Used with updateCounter to make the heart bar flash */ private long healthBlinkTime; private int lastBubblePopSoundPlayed; private float autosaveIndicatorValue; private float lastAutosaveIndicatorValue; private final LayeredDraw layers = new LayeredDraw(); private float scopeScale; public Gui(Minecraft minecraft) { this.minecraft = minecraft; this.debugOverlay = new DebugScreenOverlay(minecraft); this.spectatorGui = new SpectatorGui(minecraft); this.chat = new ChatComponent(minecraft); this.tabList = new PlayerTabOverlay(minecraft, this); this.bossOverlay = new BossHealthOverlay(minecraft); this.subtitleOverlay = new SubtitleOverlay(minecraft); this.resetTitleTimes(); LayeredDraw layeredDraw = new LayeredDraw() .add(this::renderCameraOverlays) .add(this::renderCrosshair) .add(this::renderHotbarAndDecorations) .add(this::renderExperienceLevel) .add(this::renderEffects) .add((guiGraphics, deltaTracker) -> this.bossOverlay.render(guiGraphics)); LayeredDraw layeredDraw2 = new LayeredDraw() .add(this::renderDemoOverlay) .add((guiGraphics, deltaTracker) -> { if (this.debugOverlay.showDebugScreen()) { this.debugOverlay.render(guiGraphics); } }) .add(this::renderScoreboardSidebar) .add(this::renderOverlayMessage) .add(this::renderTitle) .add(this::renderChat) .add(this::renderTabList) .add((guiGraphics, deltaTracker) -> this.subtitleOverlay.render(guiGraphics)); this.layers.add(layeredDraw, () -> !minecraft.options.hideGui).add(this::renderSleepOverlay).add(layeredDraw2, () -> !minecraft.options.hideGui); } /** * Set the different times for the titles to their default values */ public void resetTitleTimes() { this.titleFadeInTime = 10; this.titleStayTime = 70; this.titleFadeOutTime = 20; } public void render(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { this.layers.render(guiGraphics, deltaTracker); } private void renderCameraOverlays(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (Minecraft.useFancyGraphics()) { this.renderVignette(guiGraphics, this.minecraft.getCameraEntity()); } LocalPlayer localPlayer = this.minecraft.player; float f = deltaTracker.getGameTimeDeltaTicks(); this.scopeScale = Mth.lerp(0.5F * f, this.scopeScale, 1.125F); if (this.minecraft.options.getCameraType().isFirstPerson()) { if (localPlayer.isScoping()) { this.renderSpyglassOverlay(guiGraphics, this.scopeScale); } else { this.scopeScale = 0.5F; for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) { ItemStack itemStack = localPlayer.getItemBySlot(equipmentSlot); Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE); if (equippable != null && equippable.slot() == equipmentSlot && equippable.cameraOverlay().isPresent()) { this.renderTextureOverlay( guiGraphics, ((ResourceLocation)equippable.cameraOverlay().get()).withPath((UnaryOperator)(string -> "textures/" + string + ".png")), 1.0F ); } } } } if (localPlayer.getTicksFrozen() > 0) { this.renderTextureOverlay(guiGraphics, POWDER_SNOW_OUTLINE_LOCATION, localPlayer.getPercentFrozen()); } float g = deltaTracker.getGameTimeDeltaPartialTick(false); float h = Mth.lerp(g, localPlayer.oPortalEffectIntensity, localPlayer.portalEffectIntensity); float i = localPlayer.getEffectBlendFactor(MobEffects.NAUSEA, g); if (h > 0.0F) { this.renderPortalOverlay(guiGraphics, h); } else if (i > 0.0F) { float j = this.minecraft.options.screenEffectScale().get().floatValue(); if (j < 1.0F) { float k = i * (1.0F - j); this.renderConfusionOverlay(guiGraphics, k); } } } private void renderSleepOverlay(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (this.minecraft.player.getSleepTimer() > 0) { Profiler.get().push("sleep"); float f = this.minecraft.player.getSleepTimer(); float g = f / 100.0F; if (g > 1.0F) { g = 1.0F - (f - 100.0F) / 10.0F; } int i = (int)(220.0F * g) << 24 | 1052704; guiGraphics.fill(RenderType.guiOverlay(), 0, 0, guiGraphics.guiWidth(), guiGraphics.guiHeight(), i); Profiler.get().pop(); } } private void renderOverlayMessage(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { Font font = this.getFont(); if (this.overlayMessageString != null && this.overlayMessageTime > 0) { Profiler.get().push("overlayMessage"); float f = this.overlayMessageTime - deltaTracker.getGameTimeDeltaPartialTick(false); int i = (int)(f * 255.0F / 20.0F); if (i > 255) { i = 255; } if (i > 8) { guiGraphics.pose().pushPose(); guiGraphics.pose().translate((float)(guiGraphics.guiWidth() / 2), (float)(guiGraphics.guiHeight() - 68), 0.0F); int j; if (this.animateOverlayMessageColor) { j = Mth.hsvToArgb(f / 50.0F, 0.7F, 0.6F, i); } else { j = ARGB.color(i, -1); } int k = font.width(this.overlayMessageString); guiGraphics.drawStringWithBackdrop(font, this.overlayMessageString, -k / 2, -4, k, j); guiGraphics.pose().popPose(); } Profiler.get().pop(); } } private void renderTitle(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (this.title != null && this.titleTime > 0) { Font font = this.getFont(); Profiler.get().push("titleAndSubtitle"); float f = this.titleTime - deltaTracker.getGameTimeDeltaPartialTick(false); int i = 255; if (this.titleTime > this.titleFadeOutTime + this.titleStayTime) { float g = this.titleFadeInTime + this.titleStayTime + this.titleFadeOutTime - f; i = (int)(g * 255.0F / this.titleFadeInTime); } if (this.titleTime <= this.titleFadeOutTime) { i = (int)(f * 255.0F / this.titleFadeOutTime); } i = Mth.clamp(i, 0, 255); if (i > 8) { guiGraphics.pose().pushPose(); guiGraphics.pose().translate((float)(guiGraphics.guiWidth() / 2), (float)(guiGraphics.guiHeight() / 2), 0.0F); guiGraphics.pose().pushPose(); guiGraphics.pose().scale(4.0F, 4.0F, 4.0F); int j = font.width(this.title); int k = ARGB.color(i, -1); guiGraphics.drawStringWithBackdrop(font, this.title, -j / 2, -10, j, k); guiGraphics.pose().popPose(); if (this.subtitle != null) { guiGraphics.pose().pushPose(); guiGraphics.pose().scale(2.0F, 2.0F, 2.0F); int l = font.width(this.subtitle); guiGraphics.drawStringWithBackdrop(font, this.subtitle, -l / 2, 5, l, k); guiGraphics.pose().popPose(); } guiGraphics.pose().popPose(); } Profiler.get().pop(); } } private void renderChat(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (!this.chat.isChatFocused()) { Window window = this.minecraft.getWindow(); int i = Mth.floor(this.minecraft.mouseHandler.getScaledXPos(window)); int j = Mth.floor(this.minecraft.mouseHandler.getScaledYPos(window)); this.chat.render(guiGraphics, this.tickCount, i, j, false); } } private void renderScoreboardSidebar(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { Scoreboard scoreboard = this.minecraft.level.getScoreboard(); Objective objective = null; PlayerTeam playerTeam = scoreboard.getPlayersTeam(this.minecraft.player.getScoreboardName()); if (playerTeam != null) { DisplaySlot displaySlot = DisplaySlot.teamColorToSlot(playerTeam.getColor()); if (displaySlot != null) { objective = scoreboard.getDisplayObjective(displaySlot); } } Objective objective2 = objective != null ? objective : scoreboard.getDisplayObjective(DisplaySlot.SIDEBAR); if (objective2 != null) { this.displayScoreboardSidebar(guiGraphics, objective2); } } private void renderTabList(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { Scoreboard scoreboard = this.minecraft.level.getScoreboard(); Objective objective = scoreboard.getDisplayObjective(DisplaySlot.LIST); if (!this.minecraft.options.keyPlayerList.isDown() || this.minecraft.isLocalServer() && this.minecraft.player.connection.getListedOnlinePlayers().size() <= 1 && objective == null) { this.tabList.setVisible(false); } else { this.tabList.setVisible(true); this.tabList.render(guiGraphics, guiGraphics.guiWidth(), scoreboard, objective); } } private void renderCrosshair(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { Options options = this.minecraft.options; if (options.getCameraType().isFirstPerson()) { if (this.minecraft.gameMode.getPlayerMode() != GameType.SPECTATOR || this.canRenderCrosshairForSpectator(this.minecraft.hitResult)) { if (this.debugOverlay.showDebugScreen() && !this.minecraft.player.isReducedDebugInfo() && !options.reducedDebugInfo().get()) { Camera camera = this.minecraft.gameRenderer.getMainCamera(); Matrix4fStack matrix4fStack = RenderSystem.getModelViewStack(); matrix4fStack.pushMatrix(); matrix4fStack.mul(guiGraphics.pose().last().pose()); matrix4fStack.translate(guiGraphics.guiWidth() / 2, guiGraphics.guiHeight() / 2, 0.0F); matrix4fStack.rotateX(-camera.getXRot() * (float) (Math.PI / 180.0)); matrix4fStack.rotateY(camera.getYRot() * (float) (Math.PI / 180.0)); matrix4fStack.scale(-1.0F, -1.0F, -1.0F); this.debugOverlay.render3dCrosshair(); matrix4fStack.popMatrix(); } else { int i = 15; guiGraphics.blitSprite(RenderType::crosshair, CROSSHAIR_SPRITE, (guiGraphics.guiWidth() - 15) / 2, (guiGraphics.guiHeight() - 15) / 2, 15, 15); if (this.minecraft.options.attackIndicator().get() == AttackIndicatorStatus.CROSSHAIR) { float f = this.minecraft.player.getAttackStrengthScale(0.0F); boolean bl = false; if (this.minecraft.crosshairPickEntity != null && this.minecraft.crosshairPickEntity instanceof LivingEntity && f >= 1.0F) { bl = this.minecraft.player.getCurrentItemAttackStrengthDelay() > 5.0F; bl &= this.minecraft.crosshairPickEntity.isAlive(); } int j = guiGraphics.guiHeight() / 2 - 7 + 16; int k = guiGraphics.guiWidth() / 2 - 8; if (bl) { guiGraphics.blitSprite(RenderType::crosshair, CROSSHAIR_ATTACK_INDICATOR_FULL_SPRITE, k, j, 16, 16); } else if (f < 1.0F) { int l = (int)(f * 17.0F); guiGraphics.blitSprite(RenderType::crosshair, CROSSHAIR_ATTACK_INDICATOR_BACKGROUND_SPRITE, k, j, 16, 4); guiGraphics.blitSprite(RenderType::crosshair, CROSSHAIR_ATTACK_INDICATOR_PROGRESS_SPRITE, 16, 4, 0, 0, k, j, l, 4); } } } } } } /** * Checks if the crosshair can be rendered for a spectator based on the provided {@link HitResult}. *

* @return {@code true} if the crosshair can be rendered for a spectator, {@code false} otherwise. * * @param rayTrace the result of a ray trace operation. */ private boolean canRenderCrosshairForSpectator(@Nullable HitResult rayTrace) { if (rayTrace == null) { return false; } else if (rayTrace.getType() == Type.ENTITY) { return ((EntityHitResult)rayTrace).getEntity() instanceof MenuProvider; } else if (rayTrace.getType() == Type.BLOCK) { BlockPos blockPos = ((BlockHitResult)rayTrace).getBlockPos(); Level level = this.minecraft.level; return level.getBlockState(blockPos).getMenuProvider(level, blockPos) != null; } else { return false; } } private void renderEffects(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { Collection collection = this.minecraft.player.getActiveEffects(); if (!collection.isEmpty() && (this.minecraft.screen == null || !this.minecraft.screen.showsActiveEffects())) { int i = 0; int j = 0; MobEffectTextureManager mobEffectTextureManager = this.minecraft.getMobEffectTextures(); List list = Lists.newArrayListWithExpectedSize(collection.size()); for (MobEffectInstance mobEffectInstance : Ordering.natural().reverse().sortedCopy(collection)) { Holder holder = mobEffectInstance.getEffect(); if (mobEffectInstance.showIcon()) { int k = guiGraphics.guiWidth(); int l = 1; if (this.minecraft.isDemo()) { l += 15; } if (holder.value().isBeneficial()) { i++; k -= 25 * i; } else { j++; k -= 25 * j; l += 26; } float f = 1.0F; if (mobEffectInstance.isAmbient()) { guiGraphics.blitSprite(RenderType::guiTextured, EFFECT_BACKGROUND_AMBIENT_SPRITE, k, l, 24, 24); } else { guiGraphics.blitSprite(RenderType::guiTextured, EFFECT_BACKGROUND_SPRITE, k, l, 24, 24); if (mobEffectInstance.endsWithin(200)) { int m = mobEffectInstance.getDuration(); int n = 10 - m / 20; f = Mth.clamp(m / 10.0F / 5.0F * 0.5F, 0.0F, 0.5F) + Mth.cos(m * (float) Math.PI / 5.0F) * Mth.clamp(n / 10.0F * 0.25F, 0.0F, 0.25F); f = Mth.clamp(f, 0.0F, 1.0F); } } TextureAtlasSprite textureAtlasSprite = mobEffectTextureManager.get(holder); int n = k; int o = l; float g = f; list.add((Runnable)() -> { int kx = ARGB.white(g); guiGraphics.blitSprite(RenderType::guiTextured, textureAtlasSprite, n + 3, o + 3, 18, 18, kx); }); } } list.forEach(Runnable::run); } } private void renderHotbarAndDecorations(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (this.minecraft.gameMode.getPlayerMode() == GameType.SPECTATOR) { this.spectatorGui.renderHotbar(guiGraphics); } else { this.renderItemHotbar(guiGraphics, deltaTracker); } int i = guiGraphics.guiWidth() / 2 - 91; PlayerRideableJumping playerRideableJumping = this.minecraft.player.jumpableVehicle(); if (playerRideableJumping != null) { this.renderJumpMeter(playerRideableJumping, guiGraphics, i); } else if (this.isExperienceBarVisible()) { this.renderExperienceBar(guiGraphics, i); } if (this.minecraft.gameMode.canHurtPlayer()) { this.renderPlayerHealth(guiGraphics); } this.renderVehicleHealth(guiGraphics); if (this.minecraft.gameMode.getPlayerMode() != GameType.SPECTATOR) { this.renderSelectedItemName(guiGraphics); } else if (this.minecraft.player.isSpectator()) { this.spectatorGui.renderTooltip(guiGraphics); } } private void renderItemHotbar(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { Player player = this.getCameraPlayer(); if (player != null) { ItemStack itemStack = player.getOffhandItem(); HumanoidArm humanoidArm = player.getMainArm().getOpposite(); int i = guiGraphics.guiWidth() / 2; int j = 182; int k = 91; guiGraphics.pose().pushPose(); guiGraphics.pose().translate(0.0F, 0.0F, -90.0F); guiGraphics.blitSprite(RenderType::guiTextured, HOTBAR_SPRITE, i - 91, guiGraphics.guiHeight() - 22, 182, 22); guiGraphics.blitSprite( RenderType::guiTextured, HOTBAR_SELECTION_SPRITE, i - 91 - 1 + player.getInventory().getSelectedSlot() * 20, guiGraphics.guiHeight() - 22 - 1, 24, 23 ); if (!itemStack.isEmpty()) { if (humanoidArm == HumanoidArm.LEFT) { guiGraphics.blitSprite(RenderType::guiTextured, HOTBAR_OFFHAND_LEFT_SPRITE, i - 91 - 29, guiGraphics.guiHeight() - 23, 29, 24); } else { guiGraphics.blitSprite(RenderType::guiTextured, HOTBAR_OFFHAND_RIGHT_SPRITE, i + 91, guiGraphics.guiHeight() - 23, 29, 24); } } guiGraphics.pose().popPose(); int l = 1; for (int m = 0; m < 9; m++) { int n = i - 90 + m * 20 + 2; int o = guiGraphics.guiHeight() - 16 - 3; this.renderSlot(guiGraphics, n, o, deltaTracker, player, player.getInventory().getItem(m), l++); } if (!itemStack.isEmpty()) { int m = guiGraphics.guiHeight() - 16 - 3; if (humanoidArm == HumanoidArm.LEFT) { this.renderSlot(guiGraphics, i - 91 - 26, m, deltaTracker, player, itemStack, l++); } else { this.renderSlot(guiGraphics, i + 91 + 10, m, deltaTracker, player, itemStack, l++); } } if (this.minecraft.options.attackIndicator().get() == AttackIndicatorStatus.HOTBAR) { float f = this.minecraft.player.getAttackStrengthScale(0.0F); if (f < 1.0F) { int n = guiGraphics.guiHeight() - 20; int o = i + 91 + 6; if (humanoidArm == HumanoidArm.RIGHT) { o = i - 91 - 22; } int p = (int)(f * 19.0F); guiGraphics.blitSprite(RenderType::guiTextured, HOTBAR_ATTACK_INDICATOR_BACKGROUND_SPRITE, o, n, 18, 18); guiGraphics.blitSprite(RenderType::guiTextured, HOTBAR_ATTACK_INDICATOR_PROGRESS_SPRITE, 18, 18, 0, 18 - p, o, n + 18 - p, 18, p); } } } } /** * Renders the jump meter for a rideable entity on the screen using the provided rideable object, GuiGraphics object, and x-coordinate. * * @param rideable the PlayerRideableJumping object representing the rideable entity. * @param guiGraphics the GuiGraphics object used for rendering. * @param x the x-coordinate for rendering the jump meter. */ private void renderJumpMeter(PlayerRideableJumping rideable, GuiGraphics guiGraphics, int x) { Profiler.get().push("jumpBar"); float f = this.minecraft.player.getJumpRidingScale(); int i = 182; int j = (int)(f * 183.0F); int k = guiGraphics.guiHeight() - 32 + 3; guiGraphics.blitSprite(RenderType::guiTextured, JUMP_BAR_BACKGROUND_SPRITE, x, k, 182, 5); if (rideable.getJumpCooldown() > 0) { guiGraphics.blitSprite(RenderType::guiTextured, JUMP_BAR_COOLDOWN_SPRITE, x, k, 182, 5); } else if (j > 0) { guiGraphics.blitSprite(RenderType::guiTextured, JUMP_BAR_PROGRESS_SPRITE, 182, 5, 0, 0, x, k, j, 5); } Profiler.get().pop(); } /** * Renders the experience bar on the screen using the provided GuiGraphics object and x-coordinate. * * @param guiGraphics the GuiGraphics object used for rendering. * @param x the x-coordinate for rendering the experience bar. */ private void renderExperienceBar(GuiGraphics guiGraphics, int x) { Profiler.get().push("expBar"); int i = this.minecraft.player.getXpNeededForNextLevel(); if (i > 0) { int j = 182; int k = (int)(this.minecraft.player.experienceProgress * 183.0F); int l = guiGraphics.guiHeight() - 32 + 3; guiGraphics.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_BACKGROUND_SPRITE, x, l, 182, 5); if (k > 0) { guiGraphics.blitSprite(RenderType::guiTextured, EXPERIENCE_BAR_PROGRESS_SPRITE, 182, 5, 0, 0, x, l, k, 5); } } Profiler.get().pop(); } private void renderExperienceLevel(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { int i = this.minecraft.player.experienceLevel; if (this.isExperienceBarVisible() && i > 0) { Profiler.get().push("expLevel"); String string = i + ""; int j = (guiGraphics.guiWidth() - this.getFont().width(string)) / 2; int k = guiGraphics.guiHeight() - 31 - 4; guiGraphics.drawString(this.getFont(), string, j + 1, k, 0, false); guiGraphics.drawString(this.getFont(), string, j - 1, k, 0, false); guiGraphics.drawString(this.getFont(), string, j, k + 1, 0, false); guiGraphics.drawString(this.getFont(), string, j, k - 1, 0, false); guiGraphics.drawString(this.getFont(), string, j, k, 8453920, false); Profiler.get().pop(); } } private boolean isExperienceBarVisible() { return this.minecraft.player.jumpableVehicle() == null && this.minecraft.gameMode.hasExperience(); } /** * Renders the name of the selected item on the screen using the provided GuiGraphics object. * * @param guiGraphics the GuiGraphics object used for rendering. */ private void renderSelectedItemName(GuiGraphics guiGraphics) { Profiler.get().push("selectedItemName"); if (this.toolHighlightTimer > 0 && !this.lastToolHighlight.isEmpty()) { MutableComponent mutableComponent = Component.empty().append(this.lastToolHighlight.getHoverName()).withStyle(this.lastToolHighlight.getRarity().color()); if (this.lastToolHighlight.has(DataComponents.CUSTOM_NAME)) { mutableComponent.withStyle(ChatFormatting.ITALIC); } int i = this.getFont().width(mutableComponent); int j = (guiGraphics.guiWidth() - i) / 2; int k = guiGraphics.guiHeight() - 59; if (!this.minecraft.gameMode.canHurtPlayer()) { k += 14; } int l = (int)(this.toolHighlightTimer * 256.0F / 10.0F); if (l > 255) { l = 255; } if (l > 0) { guiGraphics.drawStringWithBackdrop(this.getFont(), mutableComponent, j, k, i, ARGB.color(l, -1)); } } Profiler.get().pop(); } private void renderDemoOverlay(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (this.minecraft.isDemo()) { Profiler.get().push("demo"); Component component; if (this.minecraft.level.getGameTime() >= 120500L) { component = DEMO_EXPIRED_TEXT; } else { component = Component.translatable( "demo.remainingTime", StringUtil.formatTickDuration((int)(120500L - this.minecraft.level.getGameTime()), this.minecraft.level.tickRateManager().tickrate()) ); } int i = this.getFont().width(component); int j = guiGraphics.guiWidth() - i - 10; int k = 5; guiGraphics.drawStringWithBackdrop(this.getFont(), component, j, 5, i, -1); Profiler.get().pop(); } } /** * Displays the scoreboard sidebar on the screen using the provided GuiGraphics object and objective. * * @param guiGraphics the GuiGraphics object used for rendering. * @param objective the objective representing the scoreboard sidebar. */ private void displayScoreboardSidebar(GuiGraphics guiGraphics, Objective objective) { Scoreboard scoreboard = objective.getScoreboard(); NumberFormat numberFormat = objective.numberFormatOrDefault(StyledFormat.SIDEBAR_DEFAULT); @Environment(EnvType.CLIENT) record DisplayEntry(Component name, Component score, int scoreWidth) { } DisplayEntry[] lvs = (DisplayEntry[])scoreboard.listPlayerScores(objective) .stream() .filter(playerScoreEntry -> !playerScoreEntry.isHidden()) .sorted(SCORE_DISPLAY_ORDER) .limit(15L) .map(playerScoreEntry -> { PlayerTeam playerTeam = scoreboard.getPlayersTeam(playerScoreEntry.owner()); Component componentx = playerScoreEntry.ownerName(); Component component2 = PlayerTeam.formatNameForTeam(playerTeam, componentx); Component component3 = playerScoreEntry.formatValue(numberFormat); int ix = this.getFont().width(component3); return new DisplayEntry(component2, component3, ix); }) .toArray(DisplayEntry[]::new); Component component = objective.getDisplayName(); int i = this.getFont().width(component); int j = i; int k = this.getFont().width(": "); for (DisplayEntry lv : lvs) { j = Math.max(j, this.getFont().width(lv.name) + (lv.scoreWidth > 0 ? k + lv.scoreWidth : 0)); } int m = lvs.length; int n = m * 9; int o = guiGraphics.guiHeight() / 2 + n / 3; int p = 3; int q = guiGraphics.guiWidth() - j - 3; int r = guiGraphics.guiWidth() - 3 + 2; int s = this.minecraft.options.getBackgroundColor(0.3F); int t = this.minecraft.options.getBackgroundColor(0.4F); int u = o - m * 9; guiGraphics.fill(q - 2, u - 9 - 1, r, u - 1, t); guiGraphics.fill(q - 2, u - 1, r, o, s); guiGraphics.drawString(this.getFont(), component, q + j / 2 - i / 2, u - 9, -1, false); for (int v = 0; v < m; v++) { DisplayEntry lv2 = lvs[v]; int w = o - (m - v) * 9; guiGraphics.drawString(this.getFont(), lv2.name, q, w, -1, false); guiGraphics.drawString(this.getFont(), lv2.score, r - lv2.scoreWidth, w, -1, false); } } /** * Retrieves the player entity that the camera is currently focused on. *

* @return the player entity that the camera is focused on, or null if the camera is not focused on a player. */ @Nullable private Player getCameraPlayer() { return this.minecraft.getCameraEntity() instanceof Player player ? player : null; } /** * Retrieves the living entity representing the player's vehicle with health, if any. *

* @return the living entity representing the player's vehicle with health, or null if the player is not in a vehicle or the vehicle does not have health. */ @Nullable private LivingEntity getPlayerVehicleWithHealth() { Player player = this.getCameraPlayer(); if (player != null) { Entity entity = player.getVehicle(); if (entity == null) { return null; } if (entity instanceof LivingEntity) { return (LivingEntity)entity; } } return null; } /** * Retrieves the maximum number of hearts representing the vehicle's health for the given mount entity. *

* @return the maximum number of hearts representing the vehicle's health, or 0 if the mount entity is null or does not show vehicle health. * * @param vehicle the living entity representing the vehicle. */ private int getVehicleMaxHearts(@Nullable LivingEntity vehicle) { if (vehicle != null && vehicle.showVehicleHealth()) { float f = vehicle.getMaxHealth(); int i = (int)(f + 0.5F) / 2; if (i > 30) { i = 30; } return i; } else { return 0; } } /** * Retrieves the number of rows of visible hearts needed to represent the given mount health. *

* @return the number of rows of visible hearts needed to represent the mount health. * * @param vehicleHealth the health of the mount entity. */ private int getVisibleVehicleHeartRows(int vehicleHealth) { return (int)Math.ceil(vehicleHealth / 10.0); } /** * Renders the player's health, armor, food, and air bars on the screen. * * @param guiGraphics the graphics object used for rendering. */ private void renderPlayerHealth(GuiGraphics guiGraphics) { Player player = this.getCameraPlayer(); if (player != null) { int i = Mth.ceil(player.getHealth()); boolean bl = this.healthBlinkTime > this.tickCount && (this.healthBlinkTime - this.tickCount) / 3L % 2L == 1L; long l = Util.getMillis(); if (i < this.lastHealth && player.invulnerableTime > 0) { this.lastHealthTime = l; this.healthBlinkTime = this.tickCount + 20; } else if (i > this.lastHealth && player.invulnerableTime > 0) { this.lastHealthTime = l; this.healthBlinkTime = this.tickCount + 10; } if (l - this.lastHealthTime > 1000L) { this.displayHealth = i; this.lastHealthTime = l; } this.lastHealth = i; int j = this.displayHealth; this.random.setSeed(this.tickCount * 312871); int k = guiGraphics.guiWidth() / 2 - 91; int m = guiGraphics.guiWidth() / 2 + 91; int n = guiGraphics.guiHeight() - 39; float f = Math.max((float)player.getAttributeValue(Attributes.MAX_HEALTH), Math.max(j, i)); int o = Mth.ceil(player.getAbsorptionAmount()); int p = Mth.ceil((f + o) / 2.0F / 10.0F); int q = Math.max(10 - (p - 2), 3); int r = n - 10; int s = -1; if (player.hasEffect(MobEffects.REGENERATION)) { s = this.tickCount % Mth.ceil(f + 5.0F); } Profiler.get().push("armor"); renderArmor(guiGraphics, player, n, p, q, k); Profiler.get().popPush("health"); this.renderHearts(guiGraphics, player, k, n, q, s, f, i, j, o, bl); LivingEntity livingEntity = this.getPlayerVehicleWithHealth(); int t = this.getVehicleMaxHearts(livingEntity); if (t == 0) { Profiler.get().popPush("food"); this.renderFood(guiGraphics, player, n, m); r -= 10; } Profiler.get().popPush("air"); this.renderAirBubbles(guiGraphics, player, t, r, m); Profiler.get().pop(); } } private static void renderArmor(GuiGraphics guiGraphics, Player player, int y, int heartRows, int height, int x) { int i = player.getArmorValue(); if (i > 0) { int j = y - (heartRows - 1) * height - 10; for (int k = 0; k < 10; k++) { int l = x + k * 8; if (k * 2 + 1 < i) { guiGraphics.blitSprite(RenderType::guiTextured, ARMOR_FULL_SPRITE, l, j, 9, 9); } if (k * 2 + 1 == i) { guiGraphics.blitSprite(RenderType::guiTextured, ARMOR_HALF_SPRITE, l, j, 9, 9); } if (k * 2 + 1 > i) { guiGraphics.blitSprite(RenderType::guiTextured, ARMOR_EMPTY_SPRITE, l, j, 9, 9); } } } } /** * Renders the player's hearts, including health, absorption, and highlight hearts, on the screen. * * @param guiGraphics the graphics object used for rendering. * @param player the player entity. * @param x the x-coordinate of the hearts' position. * @param y the y-coordinate of the hearts' position. * @param height the height of each heart. * @param offsetHeartIndex the index of the offset heart. * @param maxHealth the maximum health of the player. * @param currentHealth the current health of the player. * @param displayHealth the displayed health of the player. * @param absorptionAmount the absorption amount of the player. * @param renderHighlight determines whether to render the highlight hearts. */ private void renderHearts( GuiGraphics guiGraphics, Player player, int x, int y, int height, int offsetHeartIndex, float maxHealth, int currentHealth, int displayHealth, int absorptionAmount, boolean renderHighlight ) { Gui.HeartType heartType = Gui.HeartType.forPlayer(player); boolean bl = player.level().getLevelData().isHardcore(); int i = Mth.ceil(maxHealth / 2.0); int j = Mth.ceil(absorptionAmount / 2.0); int k = i * 2; for (int l = i + j - 1; l >= 0; l--) { int m = l / 10; int n = l % 10; int o = x + n * 8; int p = y - m * height; if (currentHealth + absorptionAmount <= 4) { p += this.random.nextInt(2); } if (l < i && l == offsetHeartIndex) { p -= 2; } this.renderHeart(guiGraphics, Gui.HeartType.CONTAINER, o, p, bl, renderHighlight, false); int q = l * 2; boolean bl2 = l >= i; if (bl2) { int r = q - k; if (r < absorptionAmount) { boolean bl3 = r + 1 == absorptionAmount; this.renderHeart(guiGraphics, heartType == Gui.HeartType.WITHERED ? heartType : Gui.HeartType.ABSORBING, o, p, bl, false, bl3); } } if (renderHighlight && q < displayHealth) { boolean bl4 = q + 1 == displayHealth; this.renderHeart(guiGraphics, heartType, o, p, bl, true, bl4); } if (q < currentHealth) { boolean bl4 = q + 1 == currentHealth; this.renderHeart(guiGraphics, heartType, o, p, bl, false, bl4); } } } private void renderHeart(GuiGraphics guiGraphics, Gui.HeartType heartType, int x, int y, boolean hardcore, boolean halfHeart, boolean blinking) { guiGraphics.blitSprite(RenderType::guiTextured, heartType.getSprite(hardcore, blinking, halfHeart), x, y, 9, 9); } private void renderAirBubbles(GuiGraphics guiGraphics, Player player, int vehicleMaxHealth, int y, int x) { int i = player.getMaxAirSupply(); int j = Math.clamp(player.getAirSupply(), 0, i); boolean bl = player.isEyeInFluid(FluidTags.WATER); if (bl || j < i) { y = this.getAirBubbleYLine(vehicleMaxHealth, y); int k = getCurrentAirSupplyBubble(j, i, -2); int l = getCurrentAirSupplyBubble(j, i, 0); int m = 10 - getCurrentAirSupplyBubble(j, i, getEmptyBubbleDelayDuration(j, bl)); boolean bl2 = k != l; if (!bl) { this.lastBubblePopSoundPlayed = 0; } for (int n = 1; n <= 10; n++) { int o = x - (n - 1) * 8 - 9; if (n <= k) { guiGraphics.blitSprite(RenderType::guiTextured, AIR_SPRITE, o, y, 9, 9); } else if (bl2 && n == l && bl) { guiGraphics.blitSprite(RenderType::guiTextured, AIR_POPPING_SPRITE, o, y, 9, 9); this.playAirBubblePoppedSound(n, player, m); } else if (n > 10 - m) { int p = m == 10 && this.tickCount % 2 == 0 ? this.random.nextInt(2) : 0; guiGraphics.blitSprite(RenderType::guiTextured, AIR_EMPTY_SPRITE, o, y + p, 9, 9); } } } } private int getAirBubbleYLine(int vehicleMaxHealth, int startX) { int i = this.getVisibleVehicleHeartRows(vehicleMaxHealth) - 1; return startX - i * 10; } private static int getCurrentAirSupplyBubble(int currentAirSupply, int maxAirSupply, int offset) { return Mth.ceil((float)((currentAirSupply + offset) * 10) / maxAirSupply); } private static int getEmptyBubbleDelayDuration(int airSupply, boolean inWater) { return airSupply != 0 && inWater ? 1 : 0; } private void playAirBubblePoppedSound(int bubble, Player player, int pitch) { if (this.lastBubblePopSoundPlayed != bubble) { float f = 0.5F + 0.1F * Math.max(0, pitch - 3 + 1); float g = 1.0F + 0.1F * Math.max(0, pitch - 5 + 1); player.playSound(SoundEvents.BUBBLE_POP, f, g); this.lastBubblePopSoundPlayed = bubble; } } private void renderFood(GuiGraphics guiGraphics, Player player, int y, int x) { FoodData foodData = player.getFoodData(); int i = foodData.getFoodLevel(); for (int j = 0; j < 10; j++) { int k = y; ResourceLocation resourceLocation; ResourceLocation resourceLocation2; ResourceLocation resourceLocation3; if (player.hasEffect(MobEffects.HUNGER)) { resourceLocation = FOOD_EMPTY_HUNGER_SPRITE; resourceLocation2 = FOOD_HALF_HUNGER_SPRITE; resourceLocation3 = FOOD_FULL_HUNGER_SPRITE; } else { resourceLocation = FOOD_EMPTY_SPRITE; resourceLocation2 = FOOD_HALF_SPRITE; resourceLocation3 = FOOD_FULL_SPRITE; } if (player.getFoodData().getSaturationLevel() <= 0.0F && this.tickCount % (i * 3 + 1) == 0) { k = y + (this.random.nextInt(3) - 1); } int l = x - j * 8 - 9; guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation, l, k, 9, 9); if (j * 2 + 1 < i) { guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation3, l, k, 9, 9); } if (j * 2 + 1 == i) { guiGraphics.blitSprite(RenderType::guiTextured, resourceLocation2, l, k, 9, 9); } } } /** * Renders the health of the player's vehicle on the screen. * * @param guiGraphics the graphics object used for rendering. */ private void renderVehicleHealth(GuiGraphics guiGraphics) { LivingEntity livingEntity = this.getPlayerVehicleWithHealth(); if (livingEntity != null) { int i = this.getVehicleMaxHearts(livingEntity); if (i != 0) { int j = (int)Math.ceil(livingEntity.getHealth()); Profiler.get().popPush("mountHealth"); int k = guiGraphics.guiHeight() - 39; int l = guiGraphics.guiWidth() / 2 + 91; int m = k; for (int n = 0; i > 0; n += 20) { int o = Math.min(i, 10); i -= o; for (int p = 0; p < o; p++) { int q = l - p * 8 - 9; guiGraphics.blitSprite(RenderType::guiTextured, HEART_VEHICLE_CONTAINER_SPRITE, q, m, 9, 9); if (p * 2 + 1 + n < j) { guiGraphics.blitSprite(RenderType::guiTextured, HEART_VEHICLE_FULL_SPRITE, q, m, 9, 9); } if (p * 2 + 1 + n == j) { guiGraphics.blitSprite(RenderType::guiTextured, HEART_VEHICLE_HALF_SPRITE, q, m, 9, 9); } } m -= 10; } } } } /** * Renders a texture overlay on the screen with the specified shader location and alpha value. * * @param guiGraphics the graphics object used for rendering. * @param shaderLocation the location of the shader texture. * @param alpha the alpha value to apply to the overlay. */ private void renderTextureOverlay(GuiGraphics guiGraphics, ResourceLocation shaderLocation, float alpha) { int i = ARGB.white(alpha); guiGraphics.blit( RenderType::guiTexturedOverlay, shaderLocation, 0, 0, 0.0F, 0.0F, guiGraphics.guiWidth(), guiGraphics.guiHeight(), guiGraphics.guiWidth(), guiGraphics.guiHeight(), i ); } /** * Renders the overlay for the spyglass effect. * * @param guiGraphics the graphics object used for rendering. * @param scopeScale the scale factor for the spyglass scope. */ private void renderSpyglassOverlay(GuiGraphics guiGraphics, float scopeScale) { float f = Math.min(guiGraphics.guiWidth(), guiGraphics.guiHeight()); float h = Math.min(guiGraphics.guiWidth() / f, guiGraphics.guiHeight() / f) * scopeScale; int i = Mth.floor(f * h); int j = Mth.floor(f * h); int k = (guiGraphics.guiWidth() - i) / 2; int l = (guiGraphics.guiHeight() - j) / 2; int m = k + i; int n = l + j; guiGraphics.blit(RenderType::guiTextured, SPYGLASS_SCOPE_LOCATION, k, l, 0.0F, 0.0F, i, j, i, j); guiGraphics.fill(RenderType.guiOverlay(), 0, n, guiGraphics.guiWidth(), guiGraphics.guiHeight(), -90, -16777216); guiGraphics.fill(RenderType.guiOverlay(), 0, 0, guiGraphics.guiWidth(), l, -90, -16777216); guiGraphics.fill(RenderType.guiOverlay(), 0, l, k, n, -90, -16777216); guiGraphics.fill(RenderType.guiOverlay(), m, l, guiGraphics.guiWidth(), n, -90, -16777216); } /** * Updates the brightness of the vignette effect based on the brightness of the given entity's position. * * @param entity the entity used to determine the brightness. */ private void updateVignetteBrightness(Entity entity) { BlockPos blockPos = BlockPos.containing(entity.getX(), entity.getEyeY(), entity.getZ()); float f = LightTexture.getBrightness(entity.level().dimensionType(), entity.level().getMaxLocalRawBrightness(blockPos)); float g = Mth.clamp(1.0F - f, 0.0F, 1.0F); this.vignetteBrightness = this.vignetteBrightness + (g - this.vignetteBrightness) * 0.01F; } /** * Renders the vignette effect on the screen based on the distance to the world border and the entity's position. * * @param guiGraphics the graphics object used for rendering. * @param entity the entity used to determine the distance to the world border. */ private void renderVignette(GuiGraphics guiGraphics, @Nullable Entity entity) { WorldBorder worldBorder = this.minecraft.level.getWorldBorder(); float f = 0.0F; if (entity != null) { float g = (float)worldBorder.getDistanceToBorder(entity); double d = Math.min(worldBorder.getLerpSpeed() * worldBorder.getWarningTime() * 1000.0, Math.abs(worldBorder.getLerpTarget() - worldBorder.getSize())); double e = Math.max(worldBorder.getWarningBlocks(), d); if (g < e) { f = 1.0F - (float)(g / e); } } int i; if (f > 0.0F) { f = Mth.clamp(f, 0.0F, 1.0F); i = ARGB.colorFromFloat(1.0F, 0.0F, f, f); } else { float h = this.vignetteBrightness; h = Mth.clamp(h, 0.0F, 1.0F); i = ARGB.colorFromFloat(1.0F, h, h, h); } guiGraphics.blit( RenderType::vignette, VIGNETTE_LOCATION, 0, 0, 0.0F, 0.0F, guiGraphics.guiWidth(), guiGraphics.guiHeight(), guiGraphics.guiWidth(), guiGraphics.guiHeight(), i ); } /** * Renders the portal overlay effect on the screen with the specified alpha value. * * @param guiGraphics the graphics object used for rendering. * @param alpha the alpha value of the overlay. */ private void renderPortalOverlay(GuiGraphics guiGraphics, float alpha) { if (alpha < 1.0F) { alpha *= alpha; alpha *= alpha; alpha = alpha * 0.8F + 0.2F; } int i = ARGB.white(alpha); TextureAtlasSprite textureAtlasSprite = this.minecraft.getBlockRenderer().getBlockModelShaper().getParticleIcon(Blocks.NETHER_PORTAL.defaultBlockState()); guiGraphics.blitSprite(RenderType::guiTexturedOverlay, textureAtlasSprite, 0, 0, guiGraphics.guiWidth(), guiGraphics.guiHeight(), i); } private void renderConfusionOverlay(GuiGraphics guiGraphics, float intensity) { int i = guiGraphics.guiWidth(); int j = guiGraphics.guiHeight(); guiGraphics.pose().pushPose(); float f = Mth.lerp(intensity, 2.0F, 1.0F); guiGraphics.pose().translate(i / 2.0F, j / 2.0F, 0.0F); guiGraphics.pose().scale(f, f, f); guiGraphics.pose().translate(-i / 2.0F, -j / 2.0F, 0.0F); float g = 0.2F * intensity; float h = 0.4F * intensity; float k = 0.2F * intensity; guiGraphics.blit(resourceLocation -> RenderType.guiNauseaOverlay(), NAUSEA_LOCATION, 0, 0, 0.0F, 0.0F, i, j, i, j, ARGB.colorFromFloat(1.0F, g, h, k)); guiGraphics.pose().popPose(); } private void renderSlot(GuiGraphics guiGraphics, int x, int y, DeltaTracker deltaTracker, Player player, ItemStack stack, int seed) { if (!stack.isEmpty()) { float f = stack.getPopTime() - deltaTracker.getGameTimeDeltaPartialTick(false); if (f > 0.0F) { float g = 1.0F + f / 5.0F; guiGraphics.pose().pushPose(); guiGraphics.pose().translate((float)(x + 8), (float)(y + 12), 0.0F); guiGraphics.pose().scale(1.0F / g, (g + 1.0F) / 2.0F, 1.0F); guiGraphics.pose().translate((float)(-(x + 8)), (float)(-(y + 12)), 0.0F); } guiGraphics.renderItem(player, stack, x, y, seed); if (f > 0.0F) { guiGraphics.pose().popPose(); } guiGraphics.renderItemDecorations(this.minecraft.font, stack, x, y); } } /** * Advances the tick for the autosave indicator and optionally ticks the object if not paused. */ public void tick(boolean pause) { this.tickAutosaveIndicator(); if (!pause) { this.tick(); } } /** * Advances the tick for various elements and updates their state. */ private void tick() { if (this.overlayMessageTime > 0) { this.overlayMessageTime--; } if (this.titleTime > 0) { this.titleTime--; if (this.titleTime <= 0) { this.title = null; this.subtitle = null; } } this.tickCount++; Entity entity = this.minecraft.getCameraEntity(); if (entity != null) { this.updateVignetteBrightness(entity); } if (this.minecraft.player != null) { ItemStack itemStack = this.minecraft.player.getInventory().getSelectedItem(); if (itemStack.isEmpty()) { this.toolHighlightTimer = 0; } else if (this.lastToolHighlight.isEmpty() || !itemStack.is(this.lastToolHighlight.getItem()) || !itemStack.getHoverName().equals(this.lastToolHighlight.getHoverName())) { this.toolHighlightTimer = (int)(40.0 * this.minecraft.options.notificationDisplayTime().get()); } else if (this.toolHighlightTimer > 0) { this.toolHighlightTimer--; } this.lastToolHighlight = itemStack; } this.chat.tick(); } /** * Updates the autosave indicator state. */ private void tickAutosaveIndicator() { MinecraftServer minecraftServer = this.minecraft.getSingleplayerServer(); boolean bl = minecraftServer != null && minecraftServer.isCurrentlySaving(); this.lastAutosaveIndicatorValue = this.autosaveIndicatorValue; this.autosaveIndicatorValue = Mth.lerp(0.2F, this.autosaveIndicatorValue, bl ? 1.0F : 0.0F); } /** * Sets the currently playing record display name and updates the overlay message. * * @param displayName the display name of the currently playing record. */ public void setNowPlaying(Component displayName) { Component component = Component.translatable("record.nowPlaying", displayName); this.setOverlayMessage(component, true); this.minecraft.getNarrator().sayNow(component); } /** * Sets the overlay message to be displayed on the screen. * * @param component the {@link Component} representing the overlay message. * @param animateColor a boolean indicating whether to animate the color of the overlay message. */ public void setOverlayMessage(Component component, boolean animateColor) { this.setChatDisabledByPlayerShown(false); this.overlayMessageString = component; this.overlayMessageTime = 60; this.animateOverlayMessageColor = animateColor; } /** * {@return {@code true} if the chat is disabled, {@code false} if chat is enabled} */ public void setChatDisabledByPlayerShown(boolean chatDisabledByPlayerShown) { this.chatDisabledByPlayerShown = chatDisabledByPlayerShown; } /** * {@return {@code true} if the chat disabled message is being shown, {@code false} otherwise} */ public boolean isShowingChatDisabledByPlayer() { return this.chatDisabledByPlayerShown && this.overlayMessageTime > 0; } /** * Sets the fade-in, stay, and fade-out times for the title display. * * @param titleFadeInTime the fade-in time for the title message in ticks. * @param titleStayTime the stay time for the title message in ticks. * @param titleFadeOutTime the fade-out time for the title message in ticks. */ public void setTimes(int titleFadeInTime, int titleStayTime, int titleFadeOutTime) { if (titleFadeInTime >= 0) { this.titleFadeInTime = titleFadeInTime; } if (titleStayTime >= 0) { this.titleStayTime = titleStayTime; } if (titleFadeOutTime >= 0) { this.titleFadeOutTime = titleFadeOutTime; } if (this.titleTime > 0) { this.titleTime = this.titleFadeInTime + this.titleStayTime + this.titleFadeOutTime; } } /** * Sets the subtitle to be displayed in the title screen. * * @param subtitle the subtitle {@link Component} to be displayed. */ public void setSubtitle(Component subtitle) { this.subtitle = subtitle; } /** * Sets the title to be displayed in the title screen. * * @param title the title {@link Component} to be displayed. */ public void setTitle(Component title) { this.title = title; this.titleTime = this.titleFadeInTime + this.titleStayTime + this.titleFadeOutTime; } public void clearTitles() { this.title = null; this.subtitle = null; this.titleTime = 0; } /** * {@return a pointer to the persistent Chat GUI, containing all previous chat messages and such} */ public ChatComponent getChat() { return this.chat; } /** * {@return the number of GUI ticks elapsed} */ public int getGuiTicks() { return this.tickCount; } /** * {@return the {@link Font} used for rendering text in the GUI} */ public Font getFont() { return this.minecraft.font; } /** * {@return the {@link SpectatorGui} instance} */ public SpectatorGui getSpectatorGui() { return this.spectatorGui; } /** * {@return the {@link PlayerTabOverlay} overlay} */ public PlayerTabOverlay getTabList() { return this.tabList; } /** * Called when the player is disconnected from the server. * Resets various UI elements and clears messages. */ public void onDisconnected() { this.tabList.reset(); this.bossOverlay.reset(); this.minecraft.getToastManager().clear(); this.debugOverlay.reset(); this.chat.clearMessages(true); this.clearTitles(); this.resetTitleTimes(); } /** * {@return the {@link BossHealthOverlay} instance associated with the client} */ public BossHealthOverlay getBossOverlay() { return this.bossOverlay; } public DebugScreenOverlay getDebugOverlay() { return this.debugOverlay; } /** * Clears the chunk cache in the debug screen. */ public void clearCache() { this.debugOverlay.clearChunkCache(); } public void renderSavingIndicator(GuiGraphics guiGraphics, DeltaTracker deltaTracker) { if (this.minecraft.options.showAutosaveIndicator().get() && (this.autosaveIndicatorValue > 0.0F || this.lastAutosaveIndicatorValue > 0.0F)) { int i = Mth.floor( 255.0F * Mth.clamp(Mth.lerp(deltaTracker.getRealtimeDeltaTicks(), this.lastAutosaveIndicatorValue, this.autosaveIndicatorValue), 0.0F, 1.0F) ); if (i > 8) { Font font = this.getFont(); int j = font.width(SAVING_TEXT); int k = ARGB.color(i, -1); int l = guiGraphics.guiWidth() - j - 5; int m = guiGraphics.guiHeight() - 9 - 5; guiGraphics.drawStringWithBackdrop(font, SAVING_TEXT, l, m, j, k); } } } @Environment(EnvType.CLIENT) static enum HeartType { CONTAINER( ResourceLocation.withDefaultNamespace("hud/heart/container"), ResourceLocation.withDefaultNamespace("hud/heart/container_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/container"), ResourceLocation.withDefaultNamespace("hud/heart/container_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/container_hardcore"), ResourceLocation.withDefaultNamespace("hud/heart/container_hardcore_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/container_hardcore"), ResourceLocation.withDefaultNamespace("hud/heart/container_hardcore_blinking") ), NORMAL( ResourceLocation.withDefaultNamespace("hud/heart/full"), ResourceLocation.withDefaultNamespace("hud/heart/full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/half"), ResourceLocation.withDefaultNamespace("hud/heart/half_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/hardcore_full"), ResourceLocation.withDefaultNamespace("hud/heart/hardcore_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/hardcore_half"), ResourceLocation.withDefaultNamespace("hud/heart/hardcore_half_blinking") ), POISIONED( ResourceLocation.withDefaultNamespace("hud/heart/poisoned_full"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_half"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_half_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_hardcore_full"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_hardcore_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_hardcore_half"), ResourceLocation.withDefaultNamespace("hud/heart/poisoned_hardcore_half_blinking") ), WITHERED( ResourceLocation.withDefaultNamespace("hud/heart/withered_full"), ResourceLocation.withDefaultNamespace("hud/heart/withered_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/withered_half"), ResourceLocation.withDefaultNamespace("hud/heart/withered_half_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/withered_hardcore_full"), ResourceLocation.withDefaultNamespace("hud/heart/withered_hardcore_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/withered_hardcore_half"), ResourceLocation.withDefaultNamespace("hud/heart/withered_hardcore_half_blinking") ), ABSORBING( ResourceLocation.withDefaultNamespace("hud/heart/absorbing_full"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_half"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_half_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_hardcore_full"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_hardcore_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_hardcore_half"), ResourceLocation.withDefaultNamespace("hud/heart/absorbing_hardcore_half_blinking") ), FROZEN( ResourceLocation.withDefaultNamespace("hud/heart/frozen_full"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_half"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_half_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_hardcore_full"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_hardcore_full_blinking"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_hardcore_half"), ResourceLocation.withDefaultNamespace("hud/heart/frozen_hardcore_half_blinking") ); private final ResourceLocation full; private final ResourceLocation fullBlinking; private final ResourceLocation half; private final ResourceLocation halfBlinking; private final ResourceLocation hardcoreFull; private final ResourceLocation hardcoreFullBlinking; private final ResourceLocation hardcoreHalf; private final ResourceLocation hardcoreHalfBlinking; private HeartType( final ResourceLocation full, final ResourceLocation fullBlinking, final ResourceLocation half, final ResourceLocation halfBlinking, final ResourceLocation hardcoreFull, final ResourceLocation hardcoreBlinking, final ResourceLocation hardcoreHalf, final ResourceLocation hardcoreHalfBlinking ) { this.full = full; this.fullBlinking = fullBlinking; this.half = half; this.halfBlinking = halfBlinking; this.hardcoreFull = hardcoreFull; this.hardcoreFullBlinking = hardcoreBlinking; this.hardcoreHalf = hardcoreHalf; this.hardcoreHalfBlinking = hardcoreHalfBlinking; } public ResourceLocation getSprite(boolean hardcore, boolean halfHeart, boolean blinking) { if (!hardcore) { if (halfHeart) { return blinking ? this.halfBlinking : this.half; } else { return blinking ? this.fullBlinking : this.full; } } else if (halfHeart) { return blinking ? this.hardcoreHalfBlinking : this.hardcoreHalf; } else { return blinking ? this.hardcoreFullBlinking : this.hardcoreFull; } } /** * Returns the {@link HeartType} based on the player's status effects. *

* @return the {@link HeartType} based on the player's status effects. * * @param player the player for which to determine the HeartType. */ static Gui.HeartType forPlayer(Player player) { Gui.HeartType heartType; if (player.hasEffect(MobEffects.POISON)) { heartType = POISIONED; } else if (player.hasEffect(MobEffects.WITHER)) { heartType = WITHERED; } else if (player.isFullyFrozen()) { heartType = FROZEN; } else { heartType = NORMAL; } return heartType; } } }