From 67ae714c6453d1e36916042ce0a22fb3ca81f750 Mon Sep 17 00:00:00 2001 From: SushiCannibale Date: Wed, 13 Aug 2025 19:19:50 +0200 Subject: [PATCH] feat: dynamic tinting --- .../charmsnfabrics/client/CnFClient.java | 14 ++++- .../client/model/FlowerCrownItemModel.java | 25 ++++----- .../client/renderer/FlowerCrownRenderer.java | 3 -- .../client/util/FlowersTint.java | 54 +++++++++++++++++++ .../charmsnfabrics/common/CnFRegistries.java | 16 +++--- .../common/data/FlowerCrownContent.java | 26 +++++++++ .../common/data/SavedColors.java | 24 --------- .../common/item/FlowerCrown.java | 51 ++++++++++-------- 8 files changed, 144 insertions(+), 69 deletions(-) create mode 100644 src/main/java/fr/sushi/charmsnfabrics/client/util/FlowersTint.java create mode 100644 src/main/java/fr/sushi/charmsnfabrics/common/data/FlowerCrownContent.java delete mode 100644 src/main/java/fr/sushi/charmsnfabrics/common/data/SavedColors.java diff --git a/src/main/java/fr/sushi/charmsnfabrics/client/CnFClient.java b/src/main/java/fr/sushi/charmsnfabrics/client/CnFClient.java index eec3644..1dbef1a 100644 --- a/src/main/java/fr/sushi/charmsnfabrics/client/CnFClient.java +++ b/src/main/java/fr/sushi/charmsnfabrics/client/CnFClient.java @@ -8,6 +8,7 @@ import fr.sushi.charmsnfabrics.client.model.FlowerCrownItemModel; import fr.sushi.charmsnfabrics.client.model.FlowerCrownModel; import fr.sushi.charmsnfabrics.client.renderer.FloralWorkbenchRenderer; import fr.sushi.charmsnfabrics.client.renderer.FlowerCrownRenderer; +import fr.sushi.charmsnfabrics.client.util.FlowersTint; import fr.sushi.charmsnfabrics.common.CnFRegistries; import net.minecraft.data.loot.LootTableProvider; import net.minecraft.resources.ResourceLocation; @@ -19,6 +20,7 @@ import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; +import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; import net.neoforged.neoforge.client.event.RegisterItemModelsEvent; import net.neoforged.neoforge.client.gui.ConfigurationScreen; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @@ -76,7 +78,8 @@ public class CnFClient } @SubscribeEvent - private static void onRegisterItemModels(final RegisterItemModelsEvent event) + private static void onRegisterItemModels( + final RegisterItemModelsEvent event) { event.register( ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID, @@ -84,6 +87,15 @@ public class CnFClient FlowerCrownItemModel.Unbaked.MAP_CODEC); } + @SubscribeEvent + private static void onRegisterTinters( + RegisterColorHandlersEvent.ItemTintSources event) + { + event.register( + ResourceLocation.fromNamespaceAndPath(CharmsAndFabrics.MODID, + "flower_tint"), FlowersTint.MAP_CODEC); + } + // @SubscribeEvent // private static void onRegisterStandaloneModels( // ModelEvent.RegisterStandalone event) diff --git a/src/main/java/fr/sushi/charmsnfabrics/client/model/FlowerCrownItemModel.java b/src/main/java/fr/sushi/charmsnfabrics/client/model/FlowerCrownItemModel.java index d0adcc0..677495e 100644 --- a/src/main/java/fr/sushi/charmsnfabrics/client/model/FlowerCrownItemModel.java +++ b/src/main/java/fr/sushi/charmsnfabrics/client/model/FlowerCrownItemModel.java @@ -4,8 +4,9 @@ import com.mojang.math.Transformation; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import fr.sushi.charmsnfabrics.CharmsAndFabrics; +import fr.sushi.charmsnfabrics.client.util.FlowersTint; import fr.sushi.charmsnfabrics.common.CnFRegistries; -import fr.sushi.charmsnfabrics.common.data.SavedColors; +import fr.sushi.charmsnfabrics.common.data.FlowerCrownContent; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -16,7 +17,6 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.client.ClientHooks; @@ -35,8 +35,6 @@ import java.util.Map; public class FlowerCrownItemModel implements ItemModel { - /* TODO: Center flower0&1 textures and play with transform to get them at the right place for every layer */ - private static final Map TRANSFORMS = new HashMap<>(); @@ -117,29 +115,32 @@ public class FlowerCrownItemModel implements ItemModel return new BlockModelWrapper(List.of(), quads, this.renderProperties); } - private ItemModel bakeLayer(int layer, DyeColor color) + private ItemModel bakeLayer(int layer, ItemStack flower) { SpriteGetter sprites = this.context.blockModelBaker().sprites(); Material material = ClientHooks.getBlockMaterial(this.getFlowerLocation(layer)); TextureAtlasSprite sprite = sprites.get(material, DEBUG_NAME); - /* Create all the bits that makes the model */ List bits = UnbakedElementsHelper.createUnbakedItemElements(0, sprite); List quads = UnbakedElementsHelper.bakeElements(bits, $ -> sprite, new ComposedModelState(BlockModelRotation.X0_Y0, TRANSFORMS.get(layer))); - return new BlockModelWrapper(List.of(), quads, this.renderProperties); + return new BlockModelWrapper(List.of(new FlowersTint(layer)), quads, + this.renderProperties); } - private ItemModel composeModel(SavedColors colors) + private ItemModel composeModel(FlowerCrownContent colors) { List models = new ArrayList<>(); models.add(this.bakeBase()); for (int i = 0; i < colors.flowers().size(); i++) { - models.add(this.bakeLayer(i, colors.flowers().get(i))); + if (!colors.flowers().get(i).isEmpty()) + { + models.add(this.bakeLayer(i, colors.flowers().get(i))); + } } return new CompositeModel(models); } @@ -150,11 +151,11 @@ public class FlowerCrownItemModel implements ItemModel ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed) { - SavedColors comp = - stack.get(CnFRegistries.DataComponents.SAVED_FLOWERS.get()); + FlowerCrownContent comp = + stack.get(CnFRegistries.DataComponents.FLOWER_CROWN_CONTENT.get()); if (comp == null) { - comp = new SavedColors(List.of()); + comp = new FlowerCrownContent(List.of()); } this.composeModel(comp) .update(renderState, stack, itemModelResolver, displayContext, diff --git a/src/main/java/fr/sushi/charmsnfabrics/client/renderer/FlowerCrownRenderer.java b/src/main/java/fr/sushi/charmsnfabrics/client/renderer/FlowerCrownRenderer.java index 4ddb010..7bbdfd0 100644 --- a/src/main/java/fr/sushi/charmsnfabrics/client/renderer/FlowerCrownRenderer.java +++ b/src/main/java/fr/sushi/charmsnfabrics/client/renderer/FlowerCrownRenderer.java @@ -4,7 +4,6 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import fr.sushi.charmsnfabrics.client.CnFLayers; import fr.sushi.charmsnfabrics.client.model.FlowerCrownModel; -import fr.sushi.charmsnfabrics.common.data.SavedColors; import fr.sushi.charmsnfabrics.common.item.FlowerCrown; import net.minecraft.client.Minecraft; import net.minecraft.client.model.EntityModel; @@ -15,8 +14,6 @@ import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.ItemRenderer; import net.minecraft.client.renderer.entity.RenderLayerParent; import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; -import net.minecraft.client.renderer.item.BlockModelWrapper; -import net.minecraft.client.renderer.item.ItemModel; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; diff --git a/src/main/java/fr/sushi/charmsnfabrics/client/util/FlowersTint.java b/src/main/java/fr/sushi/charmsnfabrics/client/util/FlowersTint.java new file mode 100644 index 0000000..133576a --- /dev/null +++ b/src/main/java/fr/sushi/charmsnfabrics/client/util/FlowersTint.java @@ -0,0 +1,54 @@ +package fr.sushi.charmsnfabrics.client.util; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import fr.sushi.charmsnfabrics.common.CnFRegistries; +import fr.sushi.charmsnfabrics.common.data.FlowerCrownContent; +import net.minecraft.client.color.item.ItemTintSource; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.jetbrains.annotations.Nullable; + +public record FlowersTint(int layer) implements ItemTintSource +{ + public static final MapCodec MAP_CODEC = + RecordCodecBuilder.mapCodec(instance -> instance + .group(Codec.INT.fieldOf("layer") + .forGetter(FlowersTint::layer)) + .apply(instance, FlowersTint::new)); + + @Override + public int calculate(ItemStack stack, @Nullable ClientLevel level, + @Nullable LivingEntity entity) + { + FlowerCrownContent data = + stack.get(CnFRegistries.DataComponents.FLOWER_CROWN_CONTENT.get()); + if (data != null) + { + ItemStack flower = data.flowers().get(this.layer); + if (flower.is(ItemTags.FLOWERS)) { + + } + if (flower.is(Items.POPPY)) + { + return DyeColor.RED.getTextureDiffuseColor(); + } + else if (flower.is(Items.DANDELION)) + { + return DyeColor.YELLOW.getTextureDiffuseColor(); + } + } + return 0xFFFFFFFF; + } + + @Override + public MapCodec type() + { + return MAP_CODEC; + } +} diff --git a/src/main/java/fr/sushi/charmsnfabrics/common/CnFRegistries.java b/src/main/java/fr/sushi/charmsnfabrics/common/CnFRegistries.java index 0fd89b3..80f7d98 100644 --- a/src/main/java/fr/sushi/charmsnfabrics/common/CnFRegistries.java +++ b/src/main/java/fr/sushi/charmsnfabrics/common/CnFRegistries.java @@ -2,7 +2,7 @@ package fr.sushi.charmsnfabrics.common; import fr.sushi.charmsnfabrics.CharmsAndFabrics; import fr.sushi.charmsnfabrics.common.block.FloralWorkbench; -import fr.sushi.charmsnfabrics.common.data.SavedColors; +import fr.sushi.charmsnfabrics.common.data.FlowerCrownContent; import fr.sushi.charmsnfabrics.common.entities.block.FloralWorkbenchBlockEntity; import fr.sushi.charmsnfabrics.common.item.FlowerCrown; import net.minecraft.core.component.DataComponentType; @@ -38,8 +38,8 @@ public class CnFRegistries ITEMS.registerItem("flower_crown", (properties) -> new FlowerCrown(properties.stacksTo(1) .component( - DataComponents.SAVED_FLOWERS, - new SavedColors( + DataComponents.FLOWER_CROWN_CONTENT, + new FlowerCrownContent( List.of())))); /* BlockItems */ public static final DeferredItem FLORAL_WORKBENCH = @@ -112,12 +112,12 @@ public class CnFRegistries DeferredRegister.createDataComponents( Registries.DATA_COMPONENT_TYPE, CharmsAndFabrics.MODID); - public static final Supplier> - SAVED_FLOWERS = - COMPONENT_TYPES.registerComponentType("saved_flowers", - builder -> builder.persistent(SavedColors.CODEC) + public static final Supplier> + FLOWER_CROWN_CONTENT = + COMPONENT_TYPES.registerComponentType("flower_crown_content", + builder -> builder.persistent(FlowerCrownContent.CODEC) .networkSynchronized( - SavedColors.STREAM_CODEC)); + FlowerCrownContent.STREAM_CODEC)); } public static void register(IEventBus bus) diff --git a/src/main/java/fr/sushi/charmsnfabrics/common/data/FlowerCrownContent.java b/src/main/java/fr/sushi/charmsnfabrics/common/data/FlowerCrownContent.java new file mode 100644 index 0000000..a951a21 --- /dev/null +++ b/src/main/java/fr/sushi/charmsnfabrics/common/data/FlowerCrownContent.java @@ -0,0 +1,26 @@ +package fr.sushi.charmsnfabrics.common.data; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; + +import java.util.List; + +public record FlowerCrownContent(List flowers) +{ + public static final FlowerCrownContent EMPTY = + new FlowerCrownContent(List.of()); + public static final Codec CODEC = + ItemStack.CODEC.listOf().flatXmap((items) -> DataResult.success( + new FlowerCrownContent(items)), + content -> DataResult.success(content.flowers)); + public static final StreamCodec + STREAM_CODEC = ItemStack.STREAM_CODEC.apply(ByteBufCodecs.list()) + .map(FlowerCrownContent::new, + content -> content.flowers); + + public static int MAX_SIZE = 7; +} diff --git a/src/main/java/fr/sushi/charmsnfabrics/common/data/SavedColors.java b/src/main/java/fr/sushi/charmsnfabrics/common/data/SavedColors.java deleted file mode 100644 index 5eb10b5..0000000 --- a/src/main/java/fr/sushi/charmsnfabrics/common/data/SavedColors.java +++ /dev/null @@ -1,24 +0,0 @@ -package fr.sushi.charmsnfabrics.common.data; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import io.netty.buffer.ByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.world.item.DyeColor; - -import java.util.List; - -public record SavedColors(List flowers) -{ - public static final Codec CODEC = RecordCodecBuilder.create( - instance -> instance - .group(DyeColor.CODEC.listOf().fieldOf("flowers") - .forGetter(SavedColors::flowers)) - .apply(instance, SavedColors::new)); - - public static final StreamCodec STREAM_CODEC = - StreamCodec.composite( - DyeColor.STREAM_CODEC.apply(ByteBufCodecs.list(256)), - SavedColors::flowers, SavedColors::new); -} diff --git a/src/main/java/fr/sushi/charmsnfabrics/common/item/FlowerCrown.java b/src/main/java/fr/sushi/charmsnfabrics/common/item/FlowerCrown.java index 6952ff9..5e2c636 100644 --- a/src/main/java/fr/sushi/charmsnfabrics/common/item/FlowerCrown.java +++ b/src/main/java/fr/sushi/charmsnfabrics/common/item/FlowerCrown.java @@ -1,12 +1,19 @@ package fr.sushi.charmsnfabrics.common.item; import fr.sushi.charmsnfabrics.CharmsAndFabrics; +import fr.sushi.charmsnfabrics.common.CnFRegistries; +import fr.sushi.charmsnfabrics.common.data.FlowerCrownContent; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionResult; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; import top.theillusivec4.curios.api.SlotContext; import top.theillusivec4.curios.api.type.capability.ICurioItem; +import java.util.ArrayList; +import java.util.List; + public class FlowerCrown extends Item implements ICurioItem { public FlowerCrown(Properties properties) @@ -32,25 +39,27 @@ public class FlowerCrown extends Item implements ICurioItem "textures/models/accessory/flower_crown.png"); } -// @Override -// public InteractionResult useOn(UseOnContext context) -// { -// var player = context.getPlayer(); -// var level = context.getLevel(); -// var hand = context.getHand(); -// -// ItemStack stack = player.getItemInHand(hand); -// SavedColors comp = -// stack.get(CnFRegistries.DataComponents.SAVED_FLOWERS.get()); -// -// stack.set(CnFRegistries.DataComponents.SAVED_FLOWERS.get(), -// new SavedColors(List.of(DyeColor.PURPLE, DyeColor.LIME))); -// if (comp != null && !comp.flowers().isEmpty()) -// { -// player.displayClientMessage(Component.literal( -// comp.flowers().stream().map(DyeColor::toString) -// .collect(Collectors.joining(" "))), false); -// } -// return InteractionResult.SUCCESS; -// } + @Override + public InteractionResult useOn(UseOnContext context) + { + var player = context.getPlayer(); + var hand = context.getHand(); + + ItemStack stack = player.getItemInHand(hand); + ItemStack offStack = player.getOffhandItem(); + + FlowerCrownContent content = stack.get( + CnFRegistries.DataComponents.FLOWER_CROWN_CONTENT.get()); + if (content == null || + content.flowers().size() >= FlowerCrownContent.MAX_SIZE) + { + return InteractionResult.FAIL; + } + List list = new ArrayList<>(content.flowers()); + list.add(offStack); + stack.set(CnFRegistries.DataComponents.FLOWER_CROWN_CONTENT.get(), + new FlowerCrownContent(list)); + + return InteractionResult.SUCCESS; + } }