diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index 347e969b2..3eca8725f 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -252,32 +252,32 @@ public class AllItems { .model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier"))) .register(); - public static final ItemEntry + public static final ItemEntry COPPER_BACKTANK = REGISTRATE.item("copper_backtank", p -> new BacktankItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving"), COPPER_BACKTANK_PLACEABLE)) .model(AssetLookup.customGenericItemModel("_", "item")) .tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag) .register(), - NETHERITE_BACKTANK = REGISTRATE.item("netherite_backtank", p -> new BacktankItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"), NETHERITE_BACKTANK_PLACEABLE)) + NETHERITE_BACKTANK = REGISTRATE.item("netherite_backtank", p -> new BacktankItem.MultiLayered(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"), NETHERITE_BACKTANK_PLACEABLE)) .model(AssetLookup.customGenericItemModel("_", "item")) .tag(AllItemTags.PRESSURIZED_AIR_SOURCES.tag) .register(); - public static final ItemEntry + public static final ItemEntry COPPER_DIVING_HELMET = REGISTRATE.item("copper_diving_helmet", p -> new DivingHelmetItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving"))) .register(), - NETHERITE_DIVING_HELMET = REGISTRATE.item("netherite_diving_helmet", p -> new DivingHelmetItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"))) + NETHERITE_DIVING_HELMET = REGISTRATE.item("netherite_diving_helmet", p -> new DivingHelmetItem.MultiLayered(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"))) .register(); - public static final ItemEntry + public static final ItemEntry COPPER_DIVING_BOOTS = REGISTRATE.item("copper_diving_boots", p -> new DivingBootsItem(AllArmorMaterials.COPPER, p, Create.asResource("copper_diving"))) .register(), - NETHERITE_DIVING_BOOTS = REGISTRATE.item("netherite_diving_boots", p -> new DivingBootsItem(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"))) + NETHERITE_DIVING_BOOTS = REGISTRATE.item("netherite_diving_boots", p -> new DivingBootsItem.MultiLayered(ArmorMaterials.NETHERITE, p, Create.asResource("netherite_diving"))) .register(); public static final ItemEntry SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new) diff --git a/src/main/java/com/simibubi/create/content/curiosities/armor/BacktankItem.java b/src/main/java/com/simibubi/create/content/curiosities/armor/BacktankItem.java index ce63ccc04..d5b0c0828 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/armor/BacktankItem.java +++ b/src/main/java/com/simibubi/create/content/curiosities/armor/BacktankItem.java @@ -1,10 +1,12 @@ package com.simibubi.create.content.curiosities.armor; +import java.util.Locale; import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; import com.simibubi.create.content.curiosities.armor.CapacityEnchantment.ICapacityEnchantable; +import com.simibubi.create.foundation.item.MultiLayeredArmorItem; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; @@ -27,9 +29,9 @@ public class BacktankItem extends BaseArmorItem implements ICapacityEnchantable private final Supplier blockItem; - public BacktankItem(ArmorMaterial material, Properties properties, ResourceLocation textureLoc, Supplier copperBacktankPlaceable) { + public BacktankItem(ArmorMaterial material, Properties properties, ResourceLocation textureLoc, Supplier placeable) { super(material, SLOT, properties, textureLoc); - this.blockItem = copperBacktankPlaceable; + this.blockItem = placeable; } @Nullable @@ -108,4 +110,15 @@ public class BacktankItem extends BaseArmorItem implements ICapacityEnchantable return this.getOrCreateDescriptionId(); } } + + public static class MultiLayered extends BacktankItem implements MultiLayeredArmorItem { + public MultiLayered(ArmorMaterial material, Properties properties, ResourceLocation textureLoc, Supplier placeable) { + super(material, properties, textureLoc, placeable); + } + + @Override + public String getArmorTexture(ItemStack stack, Entity entity, EquipmentSlot slot, String layer) { + return String.format(Locale.ROOT, "%s:textures/models/armor/%s_layer_%s.png", textureLoc.getNamespace(), textureLoc.getPath(), layer); + } + } } diff --git a/src/main/java/com/simibubi/create/content/curiosities/armor/DivingBootsItem.java b/src/main/java/com/simibubi/create/content/curiosities/armor/DivingBootsItem.java index 9b0abbed3..dfffc1bd5 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/armor/DivingBootsItem.java +++ b/src/main/java/com/simibubi/create/content/curiosities/armor/DivingBootsItem.java @@ -1,5 +1,8 @@ package com.simibubi.create.content.curiosities.armor; +import java.util.Locale; + +import com.simibubi.create.foundation.item.MultiLayeredArmorItem; import com.simibubi.create.foundation.utility.NBTHelper; import net.minecraft.resources.ResourceLocation; @@ -9,6 +12,7 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ArmorMaterial; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -72,4 +76,15 @@ public class DivingBootsItem extends BaseArmorItem { } return true; } + + public static class MultiLayered extends DivingBootsItem implements MultiLayeredArmorItem { + public MultiLayered(ArmorMaterial material, Properties properties, ResourceLocation textureLoc) { + super(material, properties, textureLoc); + } + + @Override + public String getArmorTexture(ItemStack stack, Entity entity, EquipmentSlot slot, String layer) { + return String.format(Locale.ROOT, "%s:textures/models/armor/%s_layer_%s.png", textureLoc.getNamespace(), textureLoc.getPath(), layer); + } + } } diff --git a/src/main/java/com/simibubi/create/content/curiosities/armor/DivingHelmetItem.java b/src/main/java/com/simibubi/create/content/curiosities/armor/DivingHelmetItem.java index 5bfd951d0..a525dc198 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/armor/DivingHelmetItem.java +++ b/src/main/java/com/simibubi/create/content/curiosities/armor/DivingHelmetItem.java @@ -1,6 +1,9 @@ package com.simibubi.create.content.curiosities.armor; +import java.util.Locale; + import com.simibubi.create.foundation.advancement.AllAdvancements; +import com.simibubi.create.foundation.item.MultiLayeredArmorItem; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; @@ -82,4 +85,15 @@ public class DivingHelmetItem extends BaseArmorItem { entity.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 30, 0, true, false, true)); BacktankUtil.consumeAir(entity, backtank, 1); } + + public static class MultiLayered extends DivingHelmetItem implements MultiLayeredArmorItem { + public MultiLayered(ArmorMaterial material, Properties properties, ResourceLocation textureLoc) { + super(material, properties, textureLoc); + } + + @Override + public String getArmorTexture(ItemStack stack, Entity entity, EquipmentSlot slot, String layer) { + return String.format(Locale.ROOT, "%s:textures/models/armor/%s_layer_%s.png", textureLoc.getNamespace(), textureLoc.getPath(), layer); + } + } } diff --git a/src/main/java/com/simibubi/create/foundation/item/MultiLayeredArmorItem.java b/src/main/java/com/simibubi/create/foundation/item/MultiLayeredArmorItem.java new file mode 100644 index 000000000..025faf134 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/item/MultiLayeredArmorItem.java @@ -0,0 +1,20 @@ +package com.simibubi.create.foundation.item; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.DyeableLeatherItem; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.common.extensions.IForgeItem; + +/** + * This interface is meant to be implemented on {@link ArmorItem}s, which will allow them to be rendered on both the inner model and outer model. + * + *

Classes implementing this interface must not also implement {@link DyeableLeatherItem}. + * + *

Classes that implement this interface and override {@link IForgeItem#getArmorTexture(ItemStack, Entity, EquipmentSlot, String) getArmorTexture} + * must note that the {@code String} argument will be used for layer context instead of the type. + * This string will always be {@code "1"} when querying the location for the outer model or {@code "2"} when querying the location for the inner model. + */ +public interface MultiLayeredArmorItem { +} diff --git a/src/main/java/com/simibubi/create/foundation/mixin/HumanoidArmorLayerMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/HumanoidArmorLayerMixin.java new file mode 100644 index 000000000..4aa5abd0f --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/mixin/HumanoidArmorLayerMixin.java @@ -0,0 +1,87 @@ +package com.simibubi.create.foundation.mixin; + +import javax.annotation.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.ModifyVariable; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.simibubi.create.foundation.item.MultiLayeredArmorItem; + +import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ArmorItem; +import net.minecraft.world.item.ItemStack; + +@Mixin(HumanoidArmorLayer.class) +public class HumanoidArmorLayerMixin { + @Shadow + @Final + private HumanoidModel innerModel; + @Shadow + @Final + private HumanoidModel outerModel; + + @Unique + private boolean intercepted; + @Unique + private Boolean useInnerTexture; + + @Shadow + private void renderArmorPiece(PoseStack poseStack, MultiBufferSource buffer, LivingEntity livingEntity, EquipmentSlot slot, int packedLight, HumanoidModel model) { + } + + @Shadow + private boolean usesInnerModel(EquipmentSlot slot) { + return false; + } + + @Inject(method = "renderArmorPiece", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/layers/HumanoidArmorLayer;getParentModel()Lnet/minecraft/client/model/EntityModel;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) + private void onRenderArmorPiece(PoseStack poseStack, MultiBufferSource buffer, LivingEntity livingEntity, EquipmentSlot slot, int packedLight, HumanoidModel model, CallbackInfo ci, ItemStack stack, ArmorItem armorItem) { + if (intercepted) { + return; + } + + if (armorItem instanceof MultiLayeredArmorItem) { + intercepted = true; + + useInnerTexture = true; + renderArmorPiece(poseStack, buffer, livingEntity, slot, packedLight, innerModel); + useInnerTexture = false; + renderArmorPiece(poseStack, buffer, livingEntity, slot, packedLight, outerModel); + + useInnerTexture = null; + intercepted = false; + ci.cancel(); + } + } + + @Inject(method = "usesInnerModel", at = @At("HEAD"), cancellable = true) + private void onUsesInnerModel(EquipmentSlot slot, CallbackInfoReturnable cir) { + if (useInnerTexture != null) { + cir.setReturnValue(useInnerTexture); + } + } + + @ModifyVariable(method = "getArmorResource", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/client/ForgeHooksClient;getArmorTexture(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/item/ItemStack;Ljava/lang/String;Lnet/minecraft/world/entity/EquipmentSlot;Ljava/lang/String;)Ljava/lang/String;", shift = Shift.BEFORE), ordinal = 0) + private String modifyType(@Nullable String type, Entity entity, ItemStack stack, EquipmentSlot slot, @Nullable String typeArg) { + if (stack.getItem() instanceof MultiLayeredArmorItem) { + return usesInnerModel(slot) ? "2" : "1"; + } + + return type; + } +} diff --git a/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1.png b/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1.png index f4ecc6756..850bcece6 100644 Binary files a/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1.png and b/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1.png differ diff --git a/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1_overlay.png b/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1_overlay.png deleted file mode 100644 index 3cce355f6..000000000 Binary files a/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1_overlay.png and /dev/null differ diff --git a/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1_original.png b/src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_2.png similarity index 100% rename from src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_1_original.png rename to src/main/resources/assets/create/textures/models/armor/netherite_diving_layer_2.png diff --git a/src/main/resources/create.mixins.json b/src/main/resources/create.mixins.json index a8cd94f97..b58d1fc36 100644 --- a/src/main/resources/create.mixins.json +++ b/src/main/resources/create.mixins.json @@ -7,6 +7,7 @@ "mixins": [ "CustomItemUseEffectsMixin", "EntityMixin", + "HumanoidArmorLayerMixin", "MapItemSavedDataMixin", "ContraptionDriverInteractMixin", "accessor.AbstractProjectileDispenseBehaviorAccessor",