Layered with mixins

- Render netherite diving gear with both layers properly
- Mixin to HumanoidArmorLayer to render both layers for all slots
This commit is contained in:
PepperCode1 2022-10-07 18:48:49 -07:00
parent 3d74c41c2d
commit 53cc386b86
10 changed files with 158 additions and 8 deletions

View file

@ -252,32 +252,32 @@ public class AllItems {
.model((c, p) -> p.withExistingParent(c.getName(), p.mcLoc("item/barrier")))
.register();
public static final ItemEntry<BacktankItem>
public static final ItemEntry<? extends BacktankItem>
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<DivingHelmetItem>
public static final ItemEntry<? extends DivingHelmetItem>
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<DivingBootsItem>
public static final ItemEntry<? extends DivingBootsItem>
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<SandPaperItem> SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new)

View file

@ -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<BacktankBlockItem> blockItem;
public BacktankItem(ArmorMaterial material, Properties properties, ResourceLocation textureLoc, Supplier<BacktankBlockItem> copperBacktankPlaceable) {
public BacktankItem(ArmorMaterial material, Properties properties, ResourceLocation textureLoc, Supplier<BacktankBlockItem> 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<BacktankBlockItem> 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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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.
*
* <p>Classes implementing this interface <b>must not</b> also implement {@link DyeableLeatherItem}.
*
* <p>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 {
}

View file

@ -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<Boolean> 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;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

View file

@ -7,6 +7,7 @@
"mixins": [
"CustomItemUseEffectsMixin",
"EntityMixin",
"HumanoidArmorLayerMixin",
"MapItemSavedDataMixin",
"ContraptionDriverInteractMixin",
"accessor.AbstractProjectileDispenseBehaviorAccessor",