diff --git a/build.gradle b/build.gradle index 1ccad4f69..f6078ba07 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,9 @@ buildscript { plugins { id 'net.minecraftforge.gradle' version '[6.0,6.2)' } +plugins { + id 'org.parchmentmc.librarian.forgegradle' version '1.+' +} apply plugin: 'maven-publish' apply plugin: 'org.spongepowered.mixin' @@ -27,8 +30,7 @@ if (lljij.toBoolean()) jarJar.enable() java.toolchain.languageVersion = JavaLanguageVersion.of(17) minecraft { - //mappings channel: 'parchment', version: '1.19.3-2023.03.12-1.19.4' - mappings channel: 'official', version: "${minecraft_version}" + mappings channel: 'parchment', version: '2023.06.26-1.20.1' accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') runs { client { diff --git a/settings.gradle b/settings.gradle index dfb0a696f..1007c3f2b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,9 +1,10 @@ pluginManagement { repositories { + maven { url = 'https://maven.parchmentmc.org' } gradlePluginPortal() maven { name="MinecraftForge" - url = 'https://maven.minecraftforge.net/' } + url = 'https://maven.minecraftforge.net/'} } } plugins { diff --git a/src/main/java/dev/xkmc/modulargolems/content/client/pose/CustomModelAnimation.java b/src/main/java/dev/xkmc/modulargolems/content/client/pose/CustomModelAnimation.java new file mode 100644 index 000000000..f2afccaa1 --- /dev/null +++ b/src/main/java/dev/xkmc/modulargolems/content/client/pose/CustomModelAnimation.java @@ -0,0 +1,58 @@ +package dev.xkmc.modulargolems.content.client.pose;// Save this class in your mod and generate all required imports + +import net.minecraft.client.animation.AnimationChannel; +import net.minecraft.client.animation.AnimationDefinition; +import net.minecraft.client.animation.Keyframe; +import net.minecraft.client.animation.KeyframeAnimations; + +/** + * Made with Blockbench 4.9.4 + * Exported for Minecraft version 1.19 or later with Mojang mappings + * @author Author + */ +public class CustomModelAnimation { + public static final AnimationDefinition warningInAxe = AnimationDefinition.Builder.withLength(1.0F) + .addAnimation("right_arm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.0833F, KeyframeAnimations.degreeVec(-110.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .addAnimation("right_forearm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(-2160.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.5F, KeyframeAnimations.degreeVec(-75.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .build(); + + + public static final AnimationDefinition attackInAxe = AnimationDefinition.Builder.withLength(1.25F) + .addAnimation("right_arm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(-110.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.25F, KeyframeAnimations.degreeVec(-32.5F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.5F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .addAnimation("right_forearm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.25F, KeyframeAnimations.degreeVec(80.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.5F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .build(); + + public static final AnimationDefinition attackInSpear = AnimationDefinition.Builder.withLength(1.75F) + .addAnimation("right_arm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(85.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.25F, KeyframeAnimations.degreeVec(-57.5F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.5F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .addAnimation("right_forearm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.25F, KeyframeAnimations.degreeVec(87.5F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.5F, KeyframeAnimations.degreeVec(-2.5F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .build(); + + public static final AnimationDefinition warningInSpear = AnimationDefinition.Builder.withLength(0.7083F) + .addAnimation("right_arm", new AnimationChannel(AnimationChannel.Targets.ROTATION, + new Keyframe(0.0F, KeyframeAnimations.degreeVec(0.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR), + new Keyframe(0.2083F, KeyframeAnimations.degreeVec(85.0F, 0.0F, 0.0F), AnimationChannel.Interpolations.LINEAR) + )) + .build(); +} \ No newline at end of file diff --git a/src/main/java/dev/xkmc/modulargolems/content/client/pose/MetalGolemPose.java b/src/main/java/dev/xkmc/modulargolems/content/client/pose/MetalGolemPose.java deleted file mode 100644 index 008581c21..000000000 --- a/src/main/java/dev/xkmc/modulargolems/content/client/pose/MetalGolemPose.java +++ /dev/null @@ -1,28 +0,0 @@ -package dev.xkmc.modulargolems.content.client.pose; - -import dev.xkmc.modulargolems.content.entity.metalgolem.MetalGolemEntity; -import dev.xkmc.modulargolems.content.entity.metalgolem.MetalGolemModel; -import net.minecraft.util.Mth; - -public class MetalGolemPose { - - public static final MetalGolemPose DEFAULT = new MetalGolemPose(); - - public void attackModel(MetalGolemEntity entity, MetalGolemModel model, float atkTick) { - model.rightArm.xRot = -2.0F + 1.5F * Mth.triangleWave(atkTick, 10.0F); - model.leftArm.xRot = -2.0F + 1.5F * Mth.triangleWave(atkTick, 10.0F); - model.rightForeArm.xRot = 0; - model.leftForeArm.xRot = 0; - } - - public void aggressive(MetalGolemEntity entity, MetalGolemModel model, float walkTick, float speed, float pTick) { - walking(entity, model, walkTick, speed, pTick); - } - - public void walking(MetalGolemEntity entity, MetalGolemModel model, float walkTick, float speed, float pTick) { - model.rightArm.xRot = (-0.2F + 1.5F * Mth.triangleWave(walkTick, 13.0F)) * speed; - model.leftArm.xRot = (-0.2F - 1.5F * Mth.triangleWave(walkTick, 13.0F)) * speed; - model.rightForeArm.xRot = 0; - model.leftForeArm.xRot = 0; - } -} diff --git a/src/main/java/dev/xkmc/modulargolems/content/client/pose/WeaponPose.java b/src/main/java/dev/xkmc/modulargolems/content/client/pose/WeaponPose.java deleted file mode 100644 index d9013dbf2..000000000 --- a/src/main/java/dev/xkmc/modulargolems/content/client/pose/WeaponPose.java +++ /dev/null @@ -1,31 +0,0 @@ -package dev.xkmc.modulargolems.content.client.pose; - -import dev.xkmc.modulargolems.content.entity.metalgolem.MetalGolemEntity; -import dev.xkmc.modulargolems.content.entity.metalgolem.MetalGolemModel; -import net.minecraft.client.model.AnimationUtils; - -public class WeaponPose extends MetalGolemPose { - - public static final MetalGolemPose WEAPON = new WeaponPose(); - - @Override - public void attackModel(MetalGolemEntity entity, MetalGolemModel model, float atkTick) { - AnimationUtils.swingWeaponDown(model.rightArm, model.leftArm, entity, model.attackTime, atkTick); - model.leftArm.xRot = 0; - model.rightForeArm.xRot = 0; - model.leftForeArm.xRot = 0; - } - - @Override - public void aggressive(MetalGolemEntity entity, MetalGolemModel model, float walkTick, float speed, float pTick) { - model.rightArm.xRot = -1.8f; - model.leftArm.xRot = 0; - model.rightForeArm.xRot = 0; - model.leftForeArm.xRot = 0; - } - - public void walking(MetalGolemEntity entity, MetalGolemModel model, float walkTick, float speed, float pTick) { - super.walking(entity, model, walkTick, speed, pTick); - } - -} diff --git a/src/main/java/dev/xkmc/modulargolems/content/entity/common/AbstractGolemEntity.java b/src/main/java/dev/xkmc/modulargolems/content/entity/common/AbstractGolemEntity.java index 20b77c3d8..4c21af074 100644 --- a/src/main/java/dev/xkmc/modulargolems/content/entity/common/AbstractGolemEntity.java +++ b/src/main/java/dev/xkmc/modulargolems/content/entity/common/AbstractGolemEntity.java @@ -499,7 +499,7 @@ public void handleEntityEvent(byte event) { private static final EntityDataAccessor DATA_MODE = GOLEM_DATA.define(SyncedData.INT, 0, "follow_mode"); private static final EntityDataAccessor GUARD_POS = GOLEM_DATA.define(SyncedData.BLOCK_POS, BlockPos.ZERO, "guard_pos"); - + private static final EntityDataAccessor SQUAD_POS = GOLEM_DATA.define(SyncedData.BLOCK_POS, BlockPos.ZERO, "cap_pos"); public GolemMode getMode() { return GolemModes.get(this.entityData.get(DATA_MODE)); } @@ -512,7 +512,9 @@ public void setMode(int mode, BlockPos pos) { this.entityData.set(DATA_MODE, mode); this.entityData.set(GUARD_POS, pos); } - + public void setCapDistance(BlockPos pos){ + this.entityData.set(SQUAD_POS,pos); + } public boolean initMode(@Nullable Player player) { var config = getConfigEntry(null); int mode = config == null ? 0 : config.defaultMode; diff --git a/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemEntity.java b/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemEntity.java index cc49a5e4f..30c2b25d8 100644 --- a/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemEntity.java +++ b/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemEntity.java @@ -17,6 +17,7 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.AnimationState; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; @@ -35,6 +36,10 @@ @SerialClass public class MetalGolemEntity extends SweepGolemEntity { + public final AnimationState axeAttackAnimationState = new AnimationState(); + public final AnimationState axeWarningAnimationState = new AnimationState(); + public final AnimationState spearAttackAnimationState = new AnimationState(); + public final AnimationState spearWarningAnimationState = new AnimationState(); public MetalGolemEntity(EntityType type, Level level) { super(type, level); this.setMaxUpStep(1); @@ -64,7 +69,6 @@ protected void registerGoals() { this.goalSelector.addGoal(2, new GolemMeleeGoal(this)); super.registerGoals(); } - public void aiStep() { super.aiStep(); if (this.attackAnimationTick > 0) { @@ -105,23 +109,21 @@ public boolean hurt(DamageSource source, float amount) { } return flag; } - + public int getAttackAnimationTick() { + return this.attackAnimationTick; + } public IronGolem.Crackiness getCrackiness() { return IronGolem.Crackiness.byFraction(this.getHealth() / this.getMaxHealth()); } - public void handleEntityEvent(byte event) { - if (event == 4) { - this.attackAnimationTick = 10; - this.playSound(SoundEvents.IRON_GOLEM_ATTACK, 1.0F, 1.0F); - } else { - super.handleEntityEvent(event); + public void handleEntityEvent(byte pId) { + if (pId == 4) { + this.attackAnimationTick=4; + this.axeAttackAnimationState.start(this.tickCount); + this.playSound(SoundEvents.IRON_GOLEM_ATTACK, 1.0F, 1.0F); + }else{ + super.handleEntityEvent(pId); } - - } - - public int getAttackAnimationTick() { - return this.attackAnimationTick; } protected SoundEvent getHurtSound(DamageSource p_28872_) { diff --git a/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemModel.java b/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemModel.java index cff397b49..7f88522f7 100644 --- a/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemModel.java +++ b/src/main/java/dev/xkmc/modulargolems/content/entity/metalgolem/MetalGolemModel.java @@ -1,22 +1,21 @@ package dev.xkmc.modulargolems.content.entity.metalgolem; - import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Axis; import dev.xkmc.modulargolems.content.client.armor.GolemEquipmentModels; -import dev.xkmc.modulargolems.content.client.pose.MetalGolemPose; -import dev.xkmc.modulargolems.content.client.pose.WeaponPose; import dev.xkmc.modulargolems.content.entity.common.IGolemModel; import dev.xkmc.modulargolems.content.entity.common.IHeadedModel; +import dev.xkmc.modulargolems.content.item.equipments.MetalGolemWeaponItem; import net.minecraft.client.model.HierarchicalModel; import net.minecraft.client.model.geom.EntityModelSet; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.item.Item; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; - +import dev.xkmc.modulargolems.content.client.pose.CustomModelAnimation; @OnlyIn(Dist.CLIENT) public class MetalGolemModel extends HierarchicalModel implements IGolemModel, IHeadedModel { @@ -61,31 +60,53 @@ public void copyFrom(MetalGolemModel other) { leftForeArm.copyFrom(other.leftForeArm); rightForeArm.copyFrom(other.rightForeArm); } - - public void setupAnim(MetalGolemEntity entity, float f1, float f2, float f3, float f4, float f5) { - this.head.yRot = f4 * ((float) Math.PI / 180F); - this.head.xRot = f5 * ((float) Math.PI / 180F); - this.rightLeg.xRot = -1.5F * Mth.triangleWave(f1, 13.0F) * f2; - this.leftLeg.xRot = 1.5F * Mth.triangleWave(f1, 13.0F) * f2; + public void setupAnim(MetalGolemEntity pEntity, float pLimbSwing, float pLimbSwingAmount, float pAgeInTicks, float pNetHeadYaw, float pHeadPitch) { + this.root().getAllParts().forEach(ModelPart::resetPose); + int atkTick = pEntity.getAttackAnimationTick(); + Item its =pEntity.getMainHandItem().getItem(); + this.animateWalk(pNetHeadYaw, pHeadPitch, pLimbSwing, pLimbSwingAmount); + if(its instanceof MetalGolemWeaponItem wi){ + switch (wi.getGolemWeaponType(wi)) { + case SWORD, AXE -> { + if (atkTick > 0) { + this.animate(pEntity.axeAttackAnimationState, CustomModelAnimation.attackInAxe, pAgeInTicks); + }else if(pEntity.isAggressive()) { + this.animate(pEntity.axeWarningAnimationState, CustomModelAnimation.warningInAxe, pAgeInTicks); + } + } + case SPEAR -> { + if (atkTick > 0) { + this.animate(pEntity.spearAttackAnimationState, CustomModelAnimation.attackInSpear, pAgeInTicks); + } else if (pEntity.isAggressive()) { + this.animate(pEntity.spearWarningAnimationState, CustomModelAnimation.warningInSpear, pAgeInTicks); + } + } + } + } + } + private void animateWalk(float pNetHeadYaw, float pHeadPitch,float pLimbSwing,float pLimbSwingAmount) { + this.head.yRot = pNetHeadYaw * ((float) Math.PI / 180F); + this.head.xRot = pHeadPitch * ((float) Math.PI / 180F); + this.rightLeg.xRot = -1.5F * Mth.triangleWave(pLimbSwing, 13.0F) * pLimbSwingAmount; + this.leftLeg.xRot = 1.5F * Mth.triangleWave(pLimbSwing, 13.0F) * pLimbSwingAmount; this.rightLeg.yRot = 0.0F; this.leftLeg.yRot = 0.0F; + this.rightArm.xRot = (-0.2F + 1.5F * Mth.triangleWave(pLimbSwing, 13.0F)) * pLimbSwingAmount; + this.leftArm.xRot = (-0.2F - 1.5F * Mth.triangleWave(pLimbSwing, 13.0F)) * pLimbSwingAmount; + this.rightForeArm.xRot = 0; + this.leftForeArm.xRot = 0; + this.resetArmPoses(); } - - public void prepareMobModel(MetalGolemEntity entity, float bob, float speed, float pTick) { - MetalGolemPose pose = MetalGolemPose.DEFAULT; - if (!entity.getMainHandItem().isEmpty()) { - pose = WeaponPose.WEAPON; - } - int atkTick = entity.getAttackAnimationTick(); - if (atkTick > 0) { - pose.attackModel(entity, this, atkTick - pTick); - } else if (entity.isAggressive()) { - pose.aggressive(entity, this, bob, speed, pTick); - } else { - pose.walking(entity, this, bob, speed, pTick); - } + private void resetArmPoses() { + this.leftArm.yRot = 0.0F; + this.leftArm.z = 0.0F; + this.leftArm.x = 0.0F; + this.leftArm.y = -7.0F; + this.rightArm.yRot = 0.0F; + this.rightArm.z = 0.0F; + this.rightArm.x = -0.0F; + this.rightArm.y = -7.0F; } - public void renderToBufferInternal(MetalGolemPartType type, PoseStack stack, VertexConsumer consumer, int i, int j, float f1, float f2, float f3, float f4) { if (type == MetalGolemPartType.BODY) { this.body.render(stack, consumer, i, j, f1, f2, f3, f4); diff --git a/src/main/java/dev/xkmc/modulargolems/content/item/equipments/MetalGolemWeaponItem.java b/src/main/java/dev/xkmc/modulargolems/content/item/equipments/MetalGolemWeaponItem.java index fac7f024c..9e671a733 100644 --- a/src/main/java/dev/xkmc/modulargolems/content/item/equipments/MetalGolemWeaponItem.java +++ b/src/main/java/dev/xkmc/modulargolems/content/item/equipments/MetalGolemWeaponItem.java @@ -1,5 +1,6 @@ package dev.xkmc.modulargolems.content.item.equipments; +import dev.xkmc.modulargolems.init.material.GolemWeaponType; import dev.xkmc.modulargolems.init.registrate.GolemTypes; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.ai.attributes.AttributeModifier; @@ -10,8 +11,8 @@ import net.minecraftforge.common.ForgeMod; public class MetalGolemWeaponItem extends GolemEquipmentItem { - - public MetalGolemWeaponItem(Properties properties, int attackDamage, double percentAttack, float range, float sweep) { + protected final GolemWeaponType gwt; + public MetalGolemWeaponItem(Properties properties, int attackDamage, double percentAttack, float range, float sweep, GolemWeaponType gwt) { super(properties, EquipmentSlot.MAINHAND, GolemTypes.ENTITY_GOLEM::get, builder -> { var uuid = UUID.get(EquipmentSlot.MAINHAND); if (attackDamage > 0) { @@ -27,13 +28,12 @@ public MetalGolemWeaponItem(Properties properties, int attackDamage, double perc builder.put(GolemTypes.GOLEM_SWEEP.get(), new AttributeModifier(uuid, "spear_sweep", sweep, AttributeModifier.Operation.ADDITION)); } }); + this.gwt = gwt; } - @Override public boolean isEnchantable(ItemStack stack) { return true; } - @Override public int getEnchantmentValue() { return 15; @@ -46,5 +46,7 @@ public boolean canApplyAtEnchantingTable(ItemStack stack, Enchantment enchantmen } return super.canApplyAtEnchantingTable(stack, enchantment); } - + public GolemWeaponType getGolemWeaponType(MetalGolemWeaponItem i) { + return i.gwt; + } } diff --git a/src/main/java/dev/xkmc/modulargolems/content/item/wand/SquadWandItem.java b/src/main/java/dev/xkmc/modulargolems/content/item/wand/SquadWandItem.java index d78210bbe..4ae5c5252 100644 --- a/src/main/java/dev/xkmc/modulargolems/content/item/wand/SquadWandItem.java +++ b/src/main/java/dev/xkmc/modulargolems/content/item/wand/SquadWandItem.java @@ -23,7 +23,7 @@ public class SquadWandItem extends BaseWandItem implements GolemInteractItem, IGlowingTarget { - + private static final String KEY_OWNER = "config_captain"; private static final int RANGE = 64; public SquadWandItem(Properties properties, @Nullable ItemEntry base) { @@ -77,9 +77,25 @@ private static boolean choose(Level level, Player user, AbstractGolemEntity new MetalGolemWeaponItem(p, i, 0, 2, 0), "TII", " SI", "S T"), - AXE("item/battle_axe", (p, i) -> new MetalGolemWeaponItem(p, 0, i * 0.05, 0, 2), "III", "IS ", "TST"), - SWORD("item/sword", (p, i) -> new MetalGolemWeaponItem(p, i, 0, 1, 2), "TII", "ISI", "SIT"), - ; + SPEAR("item/long_weapon", (p, i,t) -> new MetalGolemWeaponItem(p, i, 0, 2, 0,t),"TII", " SI", "S T"), + AXE("item/battle_axe", (p, i,t) -> new MetalGolemWeaponItem(p, 0, i * 0.05, 0, 2,t), "III", "IS ", "TST"), + SWORD("item/sword", (p, i,t) -> new MetalGolemWeaponItem(p, i, 0, 1, 2,t), "TII", "ISI", "SIT"),; - private final BiFunction factory; + private final MGFunction function; private final String[] pattern; private final String model; - GolemWeaponType(String model, BiFunction factory, String... pattern) { + GolemWeaponType(String model, MGFunction function, String... pattern) { this.model = model; - this.factory = factory; + this.function = function; this.pattern = pattern; } @@ -32,7 +32,7 @@ public String getName() { } public ItemEntry buildItem(IGolemWeaponMaterial material) { - return REGISTRATE.item(material.getName() + "_" + getName(), p -> factory.apply(material.modify(p.stacksTo(1)), material.getDamage())) + return REGISTRATE.item(material.getName() + "_" + getName(), p -> function.create(material.modify(p.stacksTo(1)), material.getDamage(),this)) .model((ctx, pvd) -> pvd.getBuilder(ctx.getName()).parent(new ModelFile.UncheckedModelFile(pvd.modLoc(model))) .texture("layer0", pvd.modLoc("item/equipments/" + ctx.getName()))) .defaultLang().register(); @@ -56,5 +56,4 @@ public ShapedRecipeBuilder pattern(ShapedRecipeBuilder unlock) { } return unlock; } - }