diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/advancement/AdvancementRenderContext.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/advancement/AdvancementRenderContext.java new file mode 100644 index 0000000000..1b48bbff6d --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/advancement/AdvancementRenderContext.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.client.rendering.v1.advancement; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; + +import net.minecraft.advancements.Advancement; +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.advancements.DisplayInfo; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.navigation.ScreenRectangle; + +@ApiStatus.NonExtendable +public sealed interface AdvancementRenderContext permits AdvancementRenderContext.Icon, AdvancementRenderContext.Frame, AdvancementRenderContext.Background { + /** + * The graphics instance used for rendering. + * @return {@link GuiGraphics} instance + */ + GuiGraphics graphics(); + + /** + * The holder for the advancement. + * @return {@link AdvancementHolder} instance + */ + AdvancementHolder holder(); + + /** + * @return The advancement's progress, or {@code null} if there is no progress. + */ + @Nullable + AdvancementProgress progress(); + + /** + * The advancement being rendered. + * @return {@link Advancement} instance + */ + default Advancement advancement() { + return holder().value(); + } + + /** + * The display info of the advancement. + * @return {@link DisplayInfo} instance + */ + default DisplayInfo display() { + return advancement().display().orElseThrow(); + } + + /** + * @return {@code true} if the advancement has been obtained. + */ + default boolean isObtained() { + AdvancementProgress progress = progress(); + return progress != null && progress.getPercent() >= 1; + } + + @ApiStatus.NonExtendable + non-sealed interface Icon extends AdvancementRenderContext { + /** + * @return The x coordinate of the icon's top-left corner. + */ + int x(); + + /** + * @return The y coordinate of the icon's top-left corner. + */ + int y(); + + /** + * @return {@code true} if the mouse is hovered over the icon. + */ + boolean isHovered(); + + /** + * @return {@code true} if the icon is rendered as a selected tab. + */ + boolean isSelected(); + } + + @ApiStatus.NonExtendable + non-sealed interface Frame extends AdvancementRenderContext { + /** + * @return The x coordinate of the frame's top-left corner. + */ + int x(); + + /** + * @return The y coordinate of the frame's top-left corner. + */ + int y(); + + /** + * @return {@code true} if the mouse is hovered over the frame. + */ + boolean isHovered(); + } + + @ApiStatus.NonExtendable + non-sealed interface Background extends AdvancementRenderContext { + /** + * @return the {@link ScreenRectangle} that the background is contained within. + * @apiNote use {@link ScreenRectangle#left()} and {@link ScreenRectangle#top()} for the starting coordinates of the background. + */ + ScreenRectangle bounds(); + + /** + * @return the background's x scroll offset. + */ + double scrollX(); + + /** + * @return the background's y scroll offset. + */ + double scrollY(); + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/advancement/AdvancementRenderer.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/advancement/AdvancementRenderer.java new file mode 100644 index 0000000000..c2c407285b --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/advancement/AdvancementRenderer.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.client.rendering.v1.advancement; + +import net.minecraft.resources.Identifier; + +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRendererRegistryImpl; + +/** + * Advancement renderers allow for custom advancement icons, frames, and backgrounds + * which render in the {@link net.minecraft.client.gui.screens.advancements.AdvancementsScreen advancements screen} + * and {@link net.minecraft.client.gui.components.toasts.AdvancementToast advancement toasts}. + */ +public final class AdvancementRenderer { + /** + * Registers an {@link IconRenderer} for advancement icons that show on advancement widgets, tabs, and toasts. + * @param iconRenderer the icon renderer + * @param advancementIds identifiers of the advancements + * @throws IllegalArgumentException if an advancement already has a registered icon renderer + * @throws NullPointerException if either an advancement id or the icon renderer is null + */ + public static void registerIcon(IconRenderer iconRenderer, Identifier... advancementIds) { + AdvancementRendererRegistryImpl.registerIcon(iconRenderer, advancementIds); + } + + /** + * Registers a {@link FrameRenderer} for advancement frames that show on advancement widgets. + * @param frameRenderer the frame renderer + * @param advancementIds identifiers of the advancements + * @throws IllegalArgumentException if an advancement already has a registered frame renderer + * @throws NullPointerException if either an advancement id or the frame renderer is null + */ + public static void registerFrame(FrameRenderer frameRenderer, Identifier... advancementIds) { + AdvancementRendererRegistryImpl.registerFrame(frameRenderer, advancementIds); + } + + /** + * Registers a {@link BackgroundRenderer} for the backgrounds of advancement tabs. + * + *

Only root advancements render their backgrounds. + * @param backgroundRenderer the frame renderer + * @param advancementIds identifiers of the advancements + * @throws IllegalArgumentException if an advancement already has a registered background renderer + * @throws NullPointerException if either an advancement id or the background renderer is null + */ + public static void registerBackground(BackgroundRenderer backgroundRenderer, Identifier... advancementIds) { + AdvancementRendererRegistryImpl.registerBackground(backgroundRenderer, advancementIds); + } + + /** + * Called after the icon (display item) of an advancement renders. + * + *

By default, the original icon does not render. + * To have it render, override {@link #shouldRenderOriginalIcon()} and return {@code true}. + */ + @FunctionalInterface + public interface IconRenderer { + /** + * @param context the context of the icon rendering, which has + * {@link net.minecraft.client.gui.GuiGraphics gui graphics} for rendering, + * the {@link net.minecraft.advancements.Advancement advancement} instance, and the icon's coordinates. + */ + void renderAdvancementIcon(AdvancementRenderContext.Icon context); + + /** + * @return {@code true} if the original advancement icon should render alongside this icon renderer. + */ + default boolean shouldRenderOriginalIcon() { + return false; + } + } + + /** + * Called after the frame of an advancement renders. + * + *

By default, the original frame does not render. + * To have it render, override {@link #shouldRenderOriginalFrame()} and return {@code true}. + * + *

The tooltip which shows the advancement's name, description, and progress when hovered will render by default. + * To cancel its rendering, override {@link #shouldRenderTooltip()} and return {@code false}. + */ + @FunctionalInterface + public interface FrameRenderer { + /** + * @param context the context of the frame rendering, which has + * {@link net.minecraft.client.gui.GuiGraphics gui graphics} for rendering, + * the {@link net.minecraft.advancements.Advancement advancement} instance, and the frame's coordinates. + */ + void renderAdvancementFrame(AdvancementRenderContext.Frame context); + + /** + * @return {@code true} if the original advancement frame should render alongside this frame renderer. + */ + default boolean shouldRenderOriginalFrame() { + return false; + } + + /** + * @return {@code true} if the tooltip of a hovered advancement widget should render. + */ + default boolean shouldRenderTooltip() { + return true; + } + } + + /** + * Called after the background of an advancement tab renders. + * + *

By default, the original background does not render. + * To have it render, override {@link #shouldRenderOriginalBackground()} and return {@code true}. + */ + @FunctionalInterface + public interface BackgroundRenderer { + /** + * @param context the context of the frame rendering, which has + * {@link net.minecraft.client.gui.GuiGraphics gui graphics} for rendering, + * the {@link net.minecraft.advancements.Advancement advancement} instance, + * and the background's {@link net.minecraft.client.gui.navigation.ScreenRectangle bounds}. + */ + void renderAdvancementBackground(AdvancementRenderContext.Background context); + + /** + * @return {@code true} if the original advancement background should render alongside this background renderer. + */ + default boolean shouldRenderOriginalBackground() { + return false; + } + } + + private AdvancementRenderer() { + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/advancement/AdvancementRenderContextImpl.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/advancement/AdvancementRenderContextImpl.java new file mode 100644 index 0000000000..42222da2f3 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/advancement/AdvancementRenderContextImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.client.rendering.advancement; + +import org.jspecify.annotations.Nullable; + +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.navigation.ScreenRectangle; + +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderContext; + +public final class AdvancementRenderContextImpl { + public static final class IconImpl implements AdvancementRenderContext.Icon { + private final GuiGraphics graphics; + private final AdvancementHolder holder; + @Nullable + private final AdvancementProgress progress; + private int x; + private int y; + private final boolean hovered; + private final boolean selected; + + public IconImpl(GuiGraphics graphics, AdvancementHolder holder, @Nullable AdvancementProgress progress, int x, int y, boolean hovered, boolean selected) { + this.graphics = graphics; + this.holder = holder; + this.progress = progress; + this.x = x; + this.y = y; + this.hovered = hovered; + this.selected = selected; + } + + public IconImpl(GuiGraphics graphics, AdvancementHolder holder, @Nullable AdvancementProgress progress, boolean hovered, boolean selected) { + this(graphics, holder, progress, 0, 0, hovered, selected); + } + + @Override + public GuiGraphics graphics() { + return graphics; + } + + @Override + public AdvancementHolder holder() { + return holder; + } + + @Override + public @Nullable AdvancementProgress progress() { + return progress; + } + + @Override + public int x() { + return x; + } + + @Override + public int y() { + return y; + } + + @Override + public boolean isHovered() { + return hovered; + } + + @Override + public boolean isSelected() { + return selected; + } + + public void setPos(int x, int y) { + this.x = x; + this.y = y; + } + } + + public record FrameImpl(GuiGraphics graphics, AdvancementHolder holder, @Nullable AdvancementProgress progress, int x, int y, boolean isHovered) implements AdvancementRenderContext.Frame { + } + + public record BackgroundImpl(GuiGraphics graphics, AdvancementHolder holder, @Nullable AdvancementProgress progress, ScreenRectangle bounds, double scrollX, double scrollY) implements AdvancementRenderContext.Background { + } + + private AdvancementRenderContextImpl() { + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/advancement/AdvancementRendererRegistryImpl.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/advancement/AdvancementRendererRegistryImpl.java new file mode 100644 index 0000000000..6822d04129 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/advancement/AdvancementRendererRegistryImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.client.rendering.advancement; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.jspecify.annotations.Nullable; + +import net.minecraft.resources.Identifier; + +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderer; + +public final class AdvancementRendererRegistryImpl { + public static final ScopedValue TAB_ICON_RENDER_CONTEXT = ScopedValue.newInstance(); + private static final Map ICONS = new HashMap<>(); + private static final Map FRAMES = new HashMap<>(); + private static final Map BACKGROUNDS = new HashMap<>(); + + public static void registerIcon(AdvancementRenderer.IconRenderer iconRenderer, Identifier... advancementIds) { + registerRenderer("Icon", ICONS, iconRenderer, advancementIds); + } + + public static void registerFrame(AdvancementRenderer.FrameRenderer frameRenderer, Identifier... advancementIds) { + registerRenderer("Frame", FRAMES, frameRenderer, advancementIds); + } + + public static void registerBackground(AdvancementRenderer.BackgroundRenderer backgroundRenderer, Identifier... advancementIds) { + registerRenderer("Background", BACKGROUNDS, backgroundRenderer, advancementIds); + } + + public static AdvancementRenderer.@Nullable IconRenderer getIconRenderer(Identifier advancementId) { + return ICONS.get(advancementId); + } + + public static AdvancementRenderer.@Nullable FrameRenderer getFrameRenderer(Identifier advancementId) { + return FRAMES.get(advancementId); + } + + public static AdvancementRenderer.@Nullable BackgroundRenderer getBackgroundRenderer(Identifier advancementId) { + return BACKGROUNDS.get(advancementId); + } + + private static void registerRenderer(String type, Map renderers, T renderer, Identifier... advancementIds) { + Objects.requireNonNull(renderer, type + " renderer is null"); + + if (advancementIds.length == 0) { + throw new IllegalArgumentException(type + " advancement renderer registered for no advancements"); + } + + for (Identifier advancementId : advancementIds) { + Objects.requireNonNull(advancementId, " advancement id is null"); + + if (renderers.putIfAbsent(advancementId, renderer) != null) { + throw new IllegalArgumentException(type + " advancement renderer already exists for " + advancementId); + } + } + } + + private AdvancementRendererRegistryImpl() { + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabAccessor.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabAccessor.java new file mode 100644 index 0000000000..6922450353 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabAccessor.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.gui.screens.advancements.AdvancementTab; +import net.minecraft.client.gui.screens.advancements.AdvancementWidget; + +@Mixin(AdvancementTab.class) +public interface AdvancementTabAccessor { + @Accessor("root") + AdvancementWidget fabric_getRoot(); +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabMixin.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabMixin.java new file mode 100644 index 0000000000..867e5452d5 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabMixin.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementNode; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.screens.advancements.AdvancementTab; +import net.minecraft.client.gui.screens.advancements.AdvancementWidget; +import net.minecraft.world.item.ItemStack; + +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderer; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRenderContextImpl; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRendererRegistryImpl; + +@Mixin(AdvancementTab.class) +abstract class AdvancementTabMixin { + @Shadow + @Final + private AdvancementNode rootNode; + + @Shadow + private double scrollX; + + @Shadow + private double scrollY; + + @Shadow + @Final + private AdvancementWidget root; + + @WrapOperation(method = "drawIcon", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementTabType;drawIcon(Lnet/minecraft/client/gui/GuiGraphics;IIILnet/minecraft/world/item/ItemStack;)V")) + private void wrapDrawIcon(@Coerce Object type, GuiGraphics graphics, int xo, int yo, int index, ItemStack icon, Operation original) { + if (AdvancementRendererRegistryImpl.TAB_ICON_RENDER_CONTEXT.isBound()) { + final AdvancementRenderContextImpl.IconImpl context = AdvancementRendererRegistryImpl.TAB_ICON_RENDER_CONTEXT.get(); + ScopedValue.where(AdvancementRendererRegistryImpl.TAB_ICON_RENDER_CONTEXT, context) + .call(() -> original.call(type, graphics, xo, yo, index, icon)); + } else { + original.call(type, graphics, xo, yo, index, icon); + } + } + + @WrapOperation(method = "drawContents", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;enableScissor(IIII)V")) + private void captureWindowSize(GuiGraphics graphics, int x0, int y0, int x1, int y1, Operation original, @Share("bounds") LocalRef bounds) { + bounds.set(new ScreenRectangle(0, 0, x1 - x0, y1 - y0)); + original.call(graphics, x0, y0, x1, y1); + } + + @ModifyExpressionValue(method = "drawContents", at = @At(value = "CONSTANT", args = "intValue=-1", ordinal = 0)) + private int preBackgroundRender(int original, @Share("backgroundRenderer") LocalRef backgroundRenderer) { + AdvancementHolder holder = rootNode.holder(); + backgroundRenderer.set(AdvancementRendererRegistryImpl.getBackgroundRenderer(holder.id())); + return backgroundRenderer.get() == null || backgroundRenderer.get().shouldRenderOriginalBackground() ? original : Integer.MAX_VALUE; + } + + @Inject(method = "drawContents", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementWidget;drawConnectivity(Lnet/minecraft/client/gui/GuiGraphics;IIZ)V", ordinal = 0)) + private void renderAdvancementBackground(GuiGraphics graphics, int windowLeft, int windowTop, CallbackInfo ci, @Share("backgroundRenderer") LocalRef backgroundRenderer, @Share("bounds") LocalRef bounds) { + if (backgroundRenderer.get() != null) { + AdvancementProgress progress = ((AdvancementWidgetAccessor) root).fabric_getProgress(); + backgroundRenderer.get().renderAdvancementBackground( + new AdvancementRenderContextImpl.BackgroundImpl(graphics, rootNode.holder(), progress, bounds.get(), scrollX, scrollY) + ); + } + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabTypeMixin.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabTypeMixin.java new file mode 100644 index 0000000000..d99389e397 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementTabTypeMixin.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.item.ItemStack; + +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderer; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRenderContextImpl; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRendererRegistryImpl; + +@Mixin(targets = "net/minecraft/client/gui/screens/advancements/AdvancementTabType") +abstract class AdvancementTabTypeMixin { + @WrapOperation(method = "drawIcon", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;renderFakeItem(Lnet/minecraft/world/item/ItemStack;II)V")) + private void renderAdvancementIcon(GuiGraphics graphics, ItemStack icon, int x, int y, Operation original) { + if (AdvancementRendererRegistryImpl.TAB_ICON_RENDER_CONTEXT.isBound()) { + final AdvancementRenderContextImpl.IconImpl context = AdvancementRendererRegistryImpl.TAB_ICON_RENDER_CONTEXT.get(); + context.setPos(x, y); + AdvancementRenderer.IconRenderer iconRenderer = AdvancementRendererRegistryImpl.getIconRenderer(context.holder().id()); + + if (iconRenderer.shouldRenderOriginalIcon()) { + original.call(graphics, icon, x, y); + } + + iconRenderer.renderAdvancementIcon(context); + } else { + original.call(graphics, icon, x, y); + } + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementToastMixin.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementToastMixin.java new file mode 100644 index 0000000000..b84df91c39 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementToastMixin.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.toasts.AdvancementToast; +import net.minecraft.client.multiplayer.ClientAdvancements; +import net.minecraft.world.item.ItemStack; + +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderer; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRenderContextImpl; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRendererRegistryImpl; + +@Mixin(AdvancementToast.class) +abstract class AdvancementToastMixin { + @Shadow + @Final + private AdvancementHolder advancement; + + @WrapOperation(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;renderFakeItem(Lnet/minecraft/world/item/ItemStack;II)V")) + private void renderAdvancementIcon(GuiGraphics graphics, ItemStack icon, int x, int y, Operation original) { + AdvancementRenderer.IconRenderer iconRenderer = AdvancementRendererRegistryImpl.getIconRenderer(advancement.id()); + + if (iconRenderer == null || iconRenderer.shouldRenderOriginalIcon()) { + original.call(graphics, icon, x, y); + } + + if (iconRenderer != null) { + ClientAdvancements advancements = Minecraft.getInstance().getConnection().getAdvancements(); + AdvancementProgress progress = ((ClientAdvancementsAccessor) advancements).fabric_getProgress().get(advancement); + iconRenderer.renderAdvancementIcon(new AdvancementRenderContextImpl.IconImpl(graphics, advancement, progress, x, y, false, false)); + } + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementWidgetAccessor.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementWidgetAccessor.java new file mode 100644 index 0000000000..73ff0670d8 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementWidgetAccessor.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import org.jspecify.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.gui.screens.advancements.AdvancementWidget; + +@Mixin(AdvancementWidget.class) +public interface AdvancementWidgetAccessor { + @Accessor("progress") + @Nullable AdvancementProgress fabric_getProgress(); +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementWidgetMixin.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementWidgetMixin.java new file mode 100644 index 0000000000..0f093b8a7a --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementWidgetMixin.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import java.util.List; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import org.jspecify.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.advancements.AdvancementNode; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.advancements.AdvancementWidget; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.util.FormattedCharSequence; +import net.minecraft.world.item.ItemStack; + +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderer; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRenderContextImpl; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRendererRegistryImpl; + +@Mixin(AdvancementWidget.class) +abstract class AdvancementWidgetMixin { + @Shadow + @Final + private AdvancementNode advancementNode; + + @Shadow + private @Nullable AdvancementProgress progress; + + @WrapOperation(method = "draw", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;renderFakeItem(Lnet/minecraft/world/item/ItemStack;II)V")) + private void renderAdvancementIcon(GuiGraphics graphics, ItemStack icon, int x, int y, Operation original) { + renderAdvancementIcon(graphics, x, y, false, () -> original.call(graphics, icon, x, y)); + } + + @WrapOperation(method = "drawHover", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;renderFakeItem(Lnet/minecraft/world/item/ItemStack;II)V")) + private void renderAdvancementIconHover(GuiGraphics graphics, ItemStack icon, int x, int y, Operation original) { + renderAdvancementIcon(graphics, x, y, true, () -> original.call(graphics, icon, x, y)); + } + + @Unique + private void renderAdvancementIcon(GuiGraphics graphics, int x, int y, boolean hovered, Runnable original) { + AdvancementRenderer.IconRenderer iconRenderer = AdvancementRendererRegistryImpl.getIconRenderer(advancementNode.holder().id()); + + if (iconRenderer == null || iconRenderer.shouldRenderOriginalIcon()) { + original.run(); + } + + if (iconRenderer != null) { + iconRenderer.renderAdvancementIcon(new AdvancementRenderContextImpl.IconImpl(graphics, advancementNode.holder(), progress, x, y, hovered, false)); + } + } + + @WrapOperation(method = "draw", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V")) + private void renderAdvancementFrame(GuiGraphics graphics, RenderPipeline renderPipeline, Identifier location, int x, int y, int width, int height, Operation original) { + renderAdvancementFrame(graphics, x, y, false, () -> original.call(graphics, renderPipeline, location, x, y, width, height)); + } + + @WrapOperation(method = "drawHover", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V", ordinal = 3)) + private void renderAdvancementFrameHover(GuiGraphics graphics, RenderPipeline renderPipeline, Identifier location, int x, int y, int width, int height, Operation original) { + renderAdvancementFrame(graphics, x, y, true, () -> original.call(graphics, renderPipeline, location, x, y, width, height)); + } + + @Unique + private void renderAdvancementFrame(GuiGraphics graphics, int x, int y, boolean hovered, Runnable original) { + AdvancementRenderer.FrameRenderer frameRenderer = AdvancementRendererRegistryImpl.getFrameRenderer(advancementNode.holder().id()); + + if (frameRenderer == null || frameRenderer.shouldRenderOriginalFrame()) { + original.run(); + } + + if (frameRenderer != null) { + frameRenderer.renderAdvancementFrame(new AdvancementRenderContextImpl.FrameImpl(graphics, advancementNode.holder(), progress, x, y, hovered)); + } + } + + @Inject(method = "drawHover", at = @At(value = "INVOKE", target = "Ljava/util/List;isEmpty()Z")) + private void captureRenderTooltip(GuiGraphics graphics, int xo, int yo, float fade, int screenxo, int screenyo, CallbackInfo ci, @Share("renderTooltip")LocalBooleanRef renderTooltip) { + AdvancementRenderer.FrameRenderer frameRenderer = AdvancementRendererRegistryImpl.getFrameRenderer(advancementNode.holder().id()); + renderTooltip.set(frameRenderer == null || frameRenderer.shouldRenderTooltip()); + } + + @WrapWithCondition(method = "drawHover", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementWidget;drawMultilineText(Lnet/minecraft/client/gui/GuiGraphics;Ljava/util/List;III)V")) + private boolean cancelTooltipMultilineTextRendering(AdvancementWidget widget, GuiGraphics graphics, List lines, int x, int y, int color, @Share("renderTooltip")LocalBooleanRef renderTooltip) { + return renderTooltip.get(); + } + + @WrapWithCondition(method = "drawHover", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;drawString(Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Component;III)V")) + private boolean cancelTooltipStringRendering(GuiGraphics graphics, Font font, Component str, int x, int y, int color, @Share("renderTooltip") LocalBooleanRef renderTooltip) { + return renderTooltip.get(); + } + + @WrapWithCondition(method = "drawHover", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIIIIIII)V")) + private boolean cancelTooltipTitleBarRendering(GuiGraphics graphics, RenderPipeline renderPipeline, Identifier location, int spriteWidth, int spriteHeight, int textureX, int textureY, int x, int y, int width, int height, @Share("renderTooltip") LocalBooleanRef renderTooltip) { + return renderTooltip.get(); + } + + @WrapWithCondition(method = "drawHover", + at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V"), + slice = @Slice(from = @At(value = "INVOKE", target = "Ljava/util/List;isEmpty()Z"), to = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiGraphics;blitSprite(Lcom/mojang/blaze3d/pipeline/RenderPipeline;Lnet/minecraft/resources/Identifier;IIII)V", ordinal = 2)) + ) + private boolean cancelTooltipTitleBoxRendering(GuiGraphics graphics, RenderPipeline renderPipeline, Identifier location, int x, int y, int width, int height, @Share("renderTooltip") LocalBooleanRef renderTooltip) { + return renderTooltip.get(); + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementsScreenMixin.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementsScreenMixin.java new file mode 100644 index 0000000000..b76ce73bca --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/AdvancementsScreenMixin.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import org.jspecify.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.advancements.AdvancementTab; +import net.minecraft.client.gui.screens.advancements.AdvancementsScreen; + +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRenderContextImpl; +import net.fabricmc.fabric.impl.client.rendering.advancement.AdvancementRendererRegistryImpl; + +@Mixin(AdvancementsScreen.class) +abstract class AdvancementsScreenMixin { + @Shadow + private @Nullable AdvancementTab selectedTab; + + @WrapOperation(method = "renderWindow", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/advancements/AdvancementTab;drawIcon(Lnet/minecraft/client/gui/GuiGraphics;II)V")) + private void wrapDrawIcon(AdvancementTab tab, GuiGraphics graphics, int xo, int yo, Operation original, @Local(name = "mouseX") int mouseX, @Local(name = "mouseY") int mouseY) { + AdvancementHolder holder = tab.getRootNode().holder(); + + if (AdvancementRendererRegistryImpl.getIconRenderer(holder.id()) != null) { + boolean hovered = tab.isMouseOver(xo, yo, mouseX, mouseY); + boolean selected = selectedTab == tab; + AdvancementProgress progress = ((AdvancementWidgetAccessor) ((AdvancementTabAccessor) tab).fabric_getRoot()).fabric_getProgress(); + ScopedValue.where( + AdvancementRendererRegistryImpl.TAB_ICON_RENDER_CONTEXT, + new AdvancementRenderContextImpl.IconImpl(graphics, holder, progress, hovered, selected) + ).call(() -> original.call(tab, graphics, xo, yo)); + } else { + original.call(tab, graphics, xo, yo); + } + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/ClientAdvancementsAccessor.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/ClientAdvancementsAccessor.java new file mode 100644 index 0000000000..218db96d51 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/advancement/ClientAdvancementsAccessor.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering.advancement; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.advancements.AdvancementHolder; +import net.minecraft.advancements.AdvancementProgress; +import net.minecraft.client.multiplayer.ClientAdvancements; + +@Mixin(ClientAdvancements.class) +public interface ClientAdvancementsAccessor { + @Accessor("progress") + Map fabric_getProgress(); +} diff --git a/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json b/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json index a09d8eb3ff..bed1dc4a57 100644 --- a/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json +++ b/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json @@ -35,6 +35,14 @@ "SpecialModelRenderersMixin", "ClientTooltipComponentMixin", "LevelRendererMixin", + "advancement.AdvancementsScreenMixin", + "advancement.AdvancementTabAccessor", + "advancement.AdvancementTabMixin", + "advancement.AdvancementTabTypeMixin", + "advancement.AdvancementToastMixin", + "advancement.AdvancementWidgetAccessor", + "advancement.AdvancementWidgetMixin", + "advancement.ClientAdvancementsAccessor", "renderstate.ItemStackRenderStateLayerRenderStateMixin", "renderstate.ItemStackRenderStateMixin", "renderstate.SkyRenderStateMixin", diff --git a/fabric-rendering-v1/src/testmod/resources/fabric.mod.json b/fabric-rendering-v1/src/testmod/resources/fabric.mod.json index dd2a5b6f01..55f98104ad 100644 --- a/fabric-rendering-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-rendering-v1/src/testmod/resources/fabric.mod.json @@ -13,6 +13,7 @@ "net.fabricmc.fabric.test.rendering.TooltipComponentTestInit" ], "client": [ + "net.fabricmc.fabric.test.rendering.client.AdvancementRenderingTests", "net.fabricmc.fabric.test.rendering.client.ArmorRenderingTests", "net.fabricmc.fabric.test.rendering.client.CustomSpriteSourcesTest", "net.fabricmc.fabric.test.rendering.client.CustomColorResolverTest", diff --git a/fabric-rendering-v1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/AdvancementRenderingTests.java b/fabric-rendering-v1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/AdvancementRenderingTests.java new file mode 100644 index 0000000000..4418c09256 --- /dev/null +++ b/fabric-rendering-v1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/AdvancementRenderingTests.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.rendering.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.resources.Identifier; +import net.minecraft.util.CommonColors; +import net.minecraft.util.Mth; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.advancement.AdvancementRenderer; + +public class AdvancementRenderingTests implements ClientModInitializer { + @Override + public void onInitializeClient() { + AdvancementRenderer.registerIcon(new StoryRootIconRenderer(), Identifier.withDefaultNamespace("story/root")); + AdvancementRenderer.registerBackground(new StoryBackgroundRenderer(), Identifier.withDefaultNamespace("story/root")); + AdvancementRenderer.registerFrame(new MineDiamondFrameRenderer(), Identifier.withDefaultNamespace("story/mine_diamond")); + } + + static class StoryRootIconRenderer implements AdvancementRenderer.IconRenderer { + @Override + public void renderAdvancementIcon(AdvancementRenderContext.Icon context) { + if (context.isHovered()) { + context.graphics().drawString(Minecraft.getInstance().font, "hovered", context.x(), context.y(), -1); + } + + if (context.isSelected()) { + context.graphics().drawString(Minecraft.getInstance().font, "selected", context.x(), context.y() + 9, -1); + } + } + + @Override + public boolean shouldRenderOriginalIcon() { + return true; + } + } + + static class StoryBackgroundRenderer implements AdvancementRenderer.BackgroundRenderer { + private static final Identifier BACKGROUND = Identifier.withDefaultNamespace("textures/painting/unpacked.png"); + + @Override + public void renderAdvancementBackground(AdvancementRenderContext.Background context) { + ScreenRectangle bounds = context.bounds(); + context.graphics().blit(RenderPipelines.GUI_TEXTURED, BACKGROUND, bounds.left(), bounds.top(), 1 - Mth.floor(context.scrollX()), 3 - Mth.floor(context.scrollY()), bounds.width(), bounds.height(), 64, 64); + } + } + + static class MineDiamondFrameRenderer implements AdvancementRenderer.FrameRenderer { + @Override + public void renderAdvancementFrame(AdvancementRenderContext.Frame context) { + int x = context.x(); + int y = context.y(); + context.graphics().fill(x, y, x + 26, y + 26, context.isObtained() ? CommonColors.GREEN : CommonColors.RED); + + if (context.isHovered()) { + context.graphics().drawString(Minecraft.getInstance().font, "hovered", context.x(), context.y(), -1); + } + } + + @Override + public boolean shouldRenderTooltip() { + return false; + } + } +}