From 0f994ecad2b28acfecd7a70d82797805aad47af9 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Fri, 8 Apr 2022 01:41:20 +0200 Subject: [PATCH] Super cool entities and fun, Part II - Cats and Wolves will now sit down properly on seats - Adjusted some vertical offsets for seated passengers - Driver entities will now wear a hat when their train is given a schedule --- .../com/simibubi/create/AllBlockPartials.java | 2 + .../components/actors/SeatBlock.java | 3 + .../components/actors/SeatEntity.java | 33 ++++ .../AbstractContraptionEntity.java | 8 +- .../entity/CarriageContraptionEntity.java | 10 +- .../schedule/TrainHatArmorLayer.java | 175 ++++++++++++++++++ .../management/schedule/TrainHatOffsets.java | 101 ++++++++++ .../simibubi/create/events/ClientEvents.java | 2 + .../accessor/AgeableListModelAccessor.java | 15 ++ .../resources/META-INF/accesstransformer.cfg | 9 + .../create/models/entity/train_hat.json | 59 ++++++ .../create/textures/entity/train_hat.png | Bin 0 -> 6853 bytes src/main/resources/create.mixins.json | 1 + 13 files changed, 416 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatArmorLayer.java create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatOffsets.java create mode 100644 src/main/java/com/simibubi/create/foundation/mixin/accessor/AgeableListModelAccessor.java create mode 100644 src/main/resources/assets/create/models/entity/train_hat.json create mode 100644 src/main/resources/assets/create/textures/entity/train_hat.png diff --git a/src/main/java/com/simibubi/create/AllBlockPartials.java b/src/main/java/com/simibubi/create/AllBlockPartials.java index dbb8e1a36..e002678a6 100644 --- a/src/main/java/com/simibubi/create/AllBlockPartials.java +++ b/src/main/java/com/simibubi/create/AllBlockPartials.java @@ -143,6 +143,8 @@ public class AllBlockPartials { CRAFTING_BLUEPRINT_1x1 = entity("crafting_blueprint_small"), CRAFTING_BLUEPRINT_2x2 = entity("crafting_blueprint_medium"), CRAFTING_BLUEPRINT_3x3 = entity("crafting_blueprint_large"), + + TRAIN_HAT = entity("train_hat"), COUPLING_ATTACHMENT = entity("minecart_coupling/attachment"), COUPLING_RING = entity("minecart_coupling/ring"), COUPLING_CONNECTOR = entity("minecart_coupling/connector") diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatBlock.java index 880f3a1a0..edb929582 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatBlock.java @@ -18,6 +18,7 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.DyeColor; @@ -161,6 +162,8 @@ public class SeatBlock extends Block { seat.setPos(pos.getX() + .5f, pos.getY(), pos.getZ() + .5f); world.addFreshEntity(seat); entity.startRiding(seat, true); + if (entity instanceof TamableAnimal ta) + ta.setInSittingPose(true); } public DyeColor getColor() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatEntity.java index 15a795f08..761a6c30b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/SeatEntity.java @@ -13,6 +13,13 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.animal.Cat; +import net.minecraft.world.entity.animal.Parrot; +import net.minecraft.world.entity.animal.Wolf; +import net.minecraft.world.entity.monster.Creeper; +import net.minecraft.world.entity.monster.Skeleton; +import net.minecraft.world.entity.monster.Slime; import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -45,6 +52,30 @@ public class SeatEntity extends Entity implements IEntityAdditionalSpawnData { setBoundingBox(bb.move(diff)); } + @Override + protected void positionRider(Entity pEntity, Entity.MoveFunction pCallback) { + if (!this.hasPassenger(pEntity)) + return; + double d0 = this.getY() + this.getPassengersRidingOffset() + pEntity.getMyRidingOffset(); + pCallback.accept(pEntity, this.getX(), d0 + getCustomEntitySeatOffset(pEntity), this.getZ()); + } + + public static double getCustomEntitySeatOffset(Entity entity) { + if (entity instanceof Slime) + return 0.25f; + if (entity instanceof Parrot) + return 1 / 16f; + if (entity instanceof Skeleton) + return 1 / 8f; + if (entity instanceof Creeper) + return 1 / 8f; + if (entity instanceof Cat) + return 1 / 8f; + if (entity instanceof Wolf) + return 1 / 16f; + return 0; + } + @Override public void setDeltaMovement(Vec3 p_213317_1_) {} @@ -69,6 +100,8 @@ public class SeatEntity extends Entity implements IEntityAdditionalSpawnData { @Override protected void removePassenger(Entity entity) { super.removePassenger(entity); + if (entity instanceof TamableAnimal ta) + ta.setInSittingPose(false); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java index fe9401d56..a1a0acabf 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java @@ -51,6 +51,7 @@ import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.decoration.HangingEntity; import net.minecraft.world.entity.player.Player; @@ -120,6 +121,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit } } passenger.startRiding(this, true); + if (passenger instanceof TamableAnimal ta) + ta.setInSittingPose(true); if (level.isClientSide) return; contraption.getSeatMapping() @@ -132,6 +135,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit protected void removePassenger(Entity passenger) { Vec3 transformedVector = getPassengerPosition(passenger, 1); super.removePassenger(passenger); + if (passenger instanceof TamableAnimal ta) + ta.setInSittingPose(false); if (level.isClientSide) return; if (transformedVector != null) @@ -150,7 +155,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit Vec3 transformedVector = getPassengerPosition(passenger, 1); if (transformedVector == null) return; - callback.accept(passenger, transformedVector.x, transformedVector.y, transformedVector.z); + callback.accept(passenger, transformedVector.x, + transformedVector.y + SeatEntity.getCustomEntitySeatOffset(passenger) - 1 / 8f, transformedVector.z); } protected Vec3 getPassengerPosition(Entity passenger, float partialTicks) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java index 9dbb767a8..501571e5c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java @@ -52,6 +52,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { SynchedEntityData.defineId(CarriageContraptionEntity.class, AllEntityDataSerializers.CARRIAGE_DATA); private static final EntityDataAccessor> TRACK_GRAPH = SynchedEntityData.defineId(CarriageContraptionEntity.class, EntityDataSerializers.OPTIONAL_UUID); + private static final EntityDataAccessor SCHEDULED = + SynchedEntityData.defineId(CarriageContraptionEntity.class, EntityDataSerializers.BOOLEAN); public UUID trainId; public int carriageIndex; @@ -79,6 +81,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { super.defineSynchedData(); entityData.define(CARRIAGE_DATA, new CarriageSyncData()); entityData.define(TRACK_GRAPH, Optional.empty()); + entityData.define(SCHEDULED, false); } public void syncCarriage() { @@ -113,6 +116,10 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { private CarriageSyncData getCarriageData() { return entityData.get(CARRIAGE_DATA); } + + public boolean hasSchedule() { + return entityData.get(SCHEDULED); + } public static CarriageContraptionEntity create(Level world, CarriageContraption contraption) { CarriageContraptionEntity entity = @@ -167,6 +174,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { CarriageSyncData carriageData = getCarriageData(); if (!level.isClientSide) { + entityData.set(SCHEDULED, carriage.train.runtime.getSchedule() != null); if (tickCount % getType().updateInterval() == 0 && carriageData.isDirty()) { entityData.set(CARRIAGE_DATA, null); entityData.set(CARRIAGE_DATA, carriageData); @@ -257,9 +265,9 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { public Couple checkConductors() { Couple sides = Couple.create(false, false); - if (!(contraption instanceof CarriageContraption cc)) return sides; + sides.setFirst(cc.blazeBurnerConductors.getFirst()); sides.setSecond(cc.blazeBurnerConductors.getSecond()); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatArmorLayer.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatArmorLayer.java new file mode 100644 index 000000000..8e459a22b --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatArmorLayer.java @@ -0,0 +1,175 @@ +package com.simibubi.create.content.logistics.trains.management.schedule; + +import com.jozufozu.flywheel.util.transform.TransformStack; +import com.mojang.blaze3d.vertex.PoseStack; +import com.simibubi.create.AllBlockPartials; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import com.simibubi.create.content.logistics.trains.entity.CarriageContraption; +import com.simibubi.create.content.logistics.trains.entity.CarriageContraptionEntity; +import com.simibubi.create.foundation.mixin.accessor.AgeableListModelAccessor; +import com.simibubi.create.foundation.render.CachedBufferer; +import com.simibubi.create.foundation.utility.Couple; + +import net.minecraft.client.model.AgeableListModel; +import net.minecraft.client.model.AxolotlModel; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.HierarchicalModel; +import net.minecraft.client.model.LavaSlimeModel; +import net.minecraft.client.model.SlimeModel; +import net.minecraft.client.model.WolfModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.ModelPart.Cube; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.Sheets; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.client.renderer.entity.LivingEntityRenderer; +import net.minecraft.client.renderer.entity.RenderLayerParent; +import net.minecraft.client.renderer.entity.layers.RenderLayer; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; + +public class TrainHatArmorLayer> extends RenderLayer { + + private Vec3 offset; + + public TrainHatArmorLayer(RenderLayerParent renderer, Vec3 offset) { + super(renderer); + this.offset = offset; + } + + @Override + public void render(PoseStack ms, MultiBufferSource buffer, int light, LivingEntity entity, float yaw, float pitch, + float pt, float p_225628_8_, float p_225628_9_, float p_225628_10_) { + if (!shouldRenderOn(entity)) + return; + + M entityModel = getParentModel(); + RenderType renderType = Sheets.cutoutBlockSheet(); + ms.pushPose(); + + boolean valid = false; + TransformStack msr = TransformStack.cast(ms); + + if (entityModel instanceof AgeableListModel model) { + if (model.young) { + if (model.scaleHead) { + float f = 1.5F / model.babyHeadScale; + ms.scale(f, f, f); + } + ms.translate(0.0D, model.babyYHeadOffset / 16.0F, model.babyZHeadOffset / 16.0F); + } + + ModelPart head = getHeadPart(model); + if (head != null) { + head.translateAndRotate(ms); + + if (model instanceof WolfModel) + head = head.getChild("real_head"); + if (model instanceof AxolotlModel) + head = head.getChild("head"); + + ms.translate(offset.x / 16f, offset.y / 16f, offset.z / 16f); + + if (!head.isEmpty()) { + Cube cube = head.cubes.get(0); + ms.translate(offset.x / 16f, (cube.minY - cube.maxY + offset.y) / 16f, offset.z / 16f); + float max = Math.max(cube.maxX - cube.minX, cube.maxZ - cube.minZ) / 8f; + ms.scale(max, max, max); + } + + valid = true; + } + } + + else if (entityModel instanceof HierarchicalModel model) { + boolean slime = model instanceof SlimeModel || model instanceof LavaSlimeModel; + ModelPart head = model.root().children.get(slime ? "cube" : "head"); + + if (head != null) { + head.translateAndRotate(ms); + + if (!head.isEmpty()) { + Cube cube = head.cubes.get(0); + ms.translate(offset.x, (cube.minY - cube.maxY + offset.y) / 16f, offset.z / 16f); + float max = Math.max(cube.maxX - cube.minX, cube.maxZ - cube.minZ) / (slime ? 6.5f : 8f); + ms.scale(max, max, max); + } + + valid = true; + } + } + + if (valid) { + ms.scale(1, -1, -1); + ms.translate(0, -2.25f / 16f, 0); + msr.rotateX(-8.5f); + BlockState air = Blocks.AIR.defaultBlockState(); + CachedBufferer.partial(AllBlockPartials.TRAIN_HAT, air) + .forEntityRender() + .light(light) + .renderInto(ms, buffer.getBuffer(renderType)); + } + + ms.popPose(); + } + + private boolean shouldRenderOn(LivingEntity entity) { + if (entity == null) + return false; + if (!entity.isPassenger()) + return false; + Entity vehicle = entity.getVehicle(); + if (!(vehicle instanceof CarriageContraptionEntity cce)) + return false; + if (!cce.hasSchedule()) + return false; + Contraption contraption = cce.getContraption(); + if (!(contraption instanceof CarriageContraption cc)) + return false; + BlockPos seatOf = cc.getSeatOf(entity.getUUID()); + if (seatOf == null) + return false; + Couple validSides = cc.conductorSeats.get(seatOf); + return validSides != null; + } + + public static void registerOnAll(EntityRenderDispatcher renderManager) { + for (EntityRenderer renderer : renderManager.getSkinMap() + .values()) + registerOn(renderer); + for (EntityRenderer renderer : renderManager.renderers.values()) + registerOn(renderer); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void registerOn(EntityRenderer entityRenderer) { + if (!(entityRenderer instanceof LivingEntityRenderer)) + return; + + LivingEntityRenderer livingRenderer = (LivingEntityRenderer) entityRenderer; + EntityModel model = livingRenderer.getModel(); + + if (!(model instanceof HierarchicalModel) && !(model instanceof AgeableListModel)) + return; + + Vec3 offset = TrainHatOffsets.getOffset(model); + TrainHatArmorLayer layer = new TrainHatArmorLayer<>(livingRenderer, offset); + livingRenderer.addLayer((TrainHatArmorLayer) layer); + } + + private static ModelPart getHeadPart(AgeableListModel model) { + for (ModelPart part : ((AgeableListModelAccessor) model).create$callHeadParts()) + return part; + for (ModelPart part : ((AgeableListModelAccessor) model).create$callBodyParts()) + return part; + return null; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatOffsets.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatOffsets.java new file mode 100644 index 000000000..f1f7ddc98 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/TrainHatOffsets.java @@ -0,0 +1,101 @@ +package com.simibubi.create.content.logistics.trains.management.schedule; + +import net.minecraft.client.model.AgeableListModel; +import net.minecraft.client.model.AxolotlModel; +import net.minecraft.client.model.BeeModel; +import net.minecraft.client.model.BlazeModel; +import net.minecraft.client.model.ChickenModel; +import net.minecraft.client.model.CowModel; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.FoxModel; +import net.minecraft.client.model.GuardianModel; +import net.minecraft.client.model.HierarchicalModel; +import net.minecraft.client.model.HoglinModel; +import net.minecraft.client.model.IronGolemModel; +import net.minecraft.client.model.LavaSlimeModel; +import net.minecraft.client.model.OcelotModel; +import net.minecraft.client.model.PandaModel; +import net.minecraft.client.model.ParrotModel; +import net.minecraft.client.model.PigModel; +import net.minecraft.client.model.QuadrupedModel; +import net.minecraft.client.model.SheepModel; +import net.minecraft.client.model.SlimeModel; +import net.minecraft.client.model.SnowGolemModel; +import net.minecraft.client.model.SpiderModel; +import net.minecraft.client.model.WolfModel; +import net.minecraft.world.phys.Vec3; + +public class TrainHatOffsets { + + // sorry + public static Vec3 getOffset(EntityModel model) { + + float x = 0; + float y = 0; + float z = 0; + + if (model instanceof AgeableListModel) { + if (model instanceof WolfModel) { + x += .5f; + y += 1.5f; + z += .25f; + } else if (model instanceof OcelotModel) { + y += 1f; + z -= .25f; + } else if (model instanceof ChickenModel) { + z -= .25f; + } else if (model instanceof FoxModel) { + x += .5f; + y += 2f; + z -= 1f; + } else if (model instanceof QuadrupedModel) { + y += 2f; + + if (model instanceof CowModel) + z -= 1.25f; + else if (model instanceof PandaModel) + z += .5f; + else if (model instanceof PigModel) + z -= 2f; + else if (model instanceof SheepModel) { + z -= .75f; + y -= 1.5f; + + } + } else if (model instanceof HoglinModel) + z -= 4.5f; + else if (model instanceof BeeModel) { + z -= .75f; + y -= 4f; + } else if (model instanceof AxolotlModel) { + z -= 5f; + y += .5f; + } + } + + if (model instanceof HierarchicalModel) { + if (model instanceof BlazeModel) + y += 4; + else if (model instanceof GuardianModel) + y += 20; + else if (model instanceof IronGolemModel) { + z -= 1.5f; + y -= 2f; + } else if (model instanceof SnowGolemModel) { + z -= .75f; + y -= 3f; + } else if (model instanceof SlimeModel || model instanceof LavaSlimeModel) { + y += 22; + } else if (model instanceof SpiderModel) { + z -= 3.5f; + y += 2f; + } else if (model instanceof ParrotModel) { + z -= 1.5f; + } + } + + return new Vec3(x, y, z); + + } + +} diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index 08f3df72d..6bd26fcc7 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -37,6 +37,7 @@ import com.simibubi.create.content.logistics.item.LinkedControllerClientHandler; import com.simibubi.create.content.logistics.trains.entity.CarriageCouplingRenderer; import com.simibubi.create.content.logistics.trains.entity.TrainRelocator; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBlockItem; +import com.simibubi.create.content.logistics.trains.management.schedule.TrainHatArmorLayer; import com.simibubi.create.content.logistics.trains.track.TrackPlacement; import com.simibubi.create.content.logistics.trains.track.TrackRemoval; import com.simibubi.create.foundation.config.AllConfigs; @@ -352,6 +353,7 @@ public class ClientEvents { EntityRenderDispatcher dispatcher = Minecraft.getInstance() .getEntityRenderDispatcher(); CopperBacktankArmorLayer.registerOnAll(dispatcher); + TrainHatArmorLayer.registerOnAll(dispatcher); } @SubscribeEvent diff --git a/src/main/java/com/simibubi/create/foundation/mixin/accessor/AgeableListModelAccessor.java b/src/main/java/com/simibubi/create/foundation/mixin/accessor/AgeableListModelAccessor.java new file mode 100644 index 000000000..9f2680736 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/mixin/accessor/AgeableListModelAccessor.java @@ -0,0 +1,15 @@ +package com.simibubi.create.foundation.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.client.model.AgeableListModel; +import net.minecraft.client.model.geom.ModelPart; + +@Mixin(AgeableListModel.class) +public interface AgeableListModelAccessor { + @Invoker("headParts") + Iterable create$callHeadParts(); + @Invoker("bodyParts") + Iterable create$callBodyParts(); +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index dbf7cf3d1..098382e16 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -29,3 +29,12 @@ public net.minecraft.world.level.biome.BiomeManager f_47863_ # biomeZoomSeed public net.minecraft.world.level.block.entity.BeaconBlockEntity f_58648_ # beamSections public net.minecraft.world.level.chunk.HashMapPalette f_62658_ # values public net.minecraft.world.level.chunk.PaletteResize + +public net.minecraft.client.model.geom.ModelPart f_104212_ # cubes +public net.minecraft.client.model.geom.ModelPart f_104213_ # children +public net.minecraft.client.model.AgeableListModel f_102007_ # scaleHead +public net.minecraft.client.model.AgeableListModel f_170338_ # babyYHeadOffset +public net.minecraft.client.model.AgeableListModel f_170339_ # babyZHeadOffset +public net.minecraft.client.model.AgeableListModel f_102010_ # babyHeadScale +public net.minecraft.client.model.AgeableListModel f_102011_ # babyBodyScale +public net.minecraft.client.model.AgeableListModel f_102012_ # bodyYOffset \ No newline at end of file diff --git a/src/main/resources/assets/create/models/entity/train_hat.json b/src/main/resources/assets/create/models/entity/train_hat.json new file mode 100644 index 000000000..28cc936ef --- /dev/null +++ b/src/main/resources/assets/create/models/entity/train_hat.json @@ -0,0 +1,59 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "create:entity/train_hat", + "particle": "create:entity/train_hat" + }, + "elements": [ + { + "from": [-4.5, 0, -4.5], + "to": [4.5, 3, 4.5], + "rotation": {"angle": 0, "axis": "x", "origin": [0, 0, -1.5]}, + "faces": { + "north": {"uv": [12, 4, 15, 13], "rotation": 90, "texture": "#0"}, + "east": {"uv": [9, 4, 12, 13], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 2, 9, 5], "rotation": 180, "texture": "#0"}, + "west": {"uv": [9, 4, 12, 13], "rotation": 90, "texture": "#0"}, + "up": {"uv": [0, 5, 9, 14], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [4.5, 0, -4.5], + "to": [-4.5, 3, 4.5], + "rotation": {"angle": 0, "axis": "x", "origin": [0, 0, -1.5]}, + "faces": { + "north": {"uv": [12, 4, 15, 13], "rotation": 90, "texture": "#0"}, + "east": {"uv": [9, 4, 12, 13], "rotation": 270, "texture": "#0"}, + "south": {"uv": [0, 2, 9, 5], "rotation": 180, "texture": "#0"}, + "west": {"uv": [9, 4, 12, 13], "rotation": 90, "texture": "#0"}, + "up": {"uv": [0, 5, 9, 14], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [-4.7, 0, -0.3], + "to": [4.7, 2, 4.7], + "rotation": {"angle": 0, "axis": "x", "origin": [0, 0, -1.5]}, + "faces": { + "east": {"uv": [2, 16, 7, 14], "rotation": 180, "texture": "#0"}, + "south": {"uv": [7, 14, 16, 16], "texture": "#0"}, + "west": {"uv": [2, 14, 7, 16], "texture": "#0"} + } + }, + { + "from": [-4.5, 1.3961, 4.04328], + "to": [4.5, 2.3961, 6.04328], + "rotation": {"angle": 22.5, "axis": "x", "origin": [0, 0, -1.5]}, + "faces": { + "up": {"uv": [0, 0, 9, 2], "rotation": 180, "texture": "#0"} + } + }, + { + "from": [-4.5, 2.31684, 3.9585], + "to": [4.5, 3.31684, 5.9585], + "rotation": {"angle": 22.5, "axis": "x", "origin": [0, 0, -1.5]}, + "faces": { + "down": {"uv": [0, 0, 9, 2], "texture": "#0"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/entity/train_hat.png b/src/main/resources/assets/create/textures/entity/train_hat.png new file mode 100644 index 0000000000000000000000000000000000000000..b7e599e0a0261387fc872fcb5fc86a7721083e27 GIT binary patch literal 6853 zcmeHLc|4Ts9v{1=6sZ(CjX^k>)tD_rjEtJd5=rsS%sY%_X3PvjC?%DXROC9cRVpei zN6Se{2T>_|s6;|IDQoLN?mI)Jd(S=h_W9iNU&buY^L&5b-~PPM=ZSY-yGjkN4Tr&C zYIFxX7wB7E`c_qjK9`J405F*Hp)GEnVi!Py5D57kZV-qNM+iU!7|!LuVBwF?dUA!Y zjc1M5m~^W;nC|Gju)a#I`~3*4u%Ta!r¬w8UI6czub&85?;=*!FCHtY>HJ@t;|j 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