From 5448db4bdd40018cdf0fcb99a8d4433bc6c6d4c0 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 15 Sep 2022 19:53:45 +0200 Subject: [PATCH] More work on hot air balloons --- .../render/entity/AirBalloonEntityModel.java | 22 +- .../entity/AirBalloonEntityRenderer.java | 8 +- .../unicopia/entity/AirBalloonEntity.java | 197 ++++++++++++++++-- .../unicopia/entity/UEntities.java | 2 +- .../entity/behaviour/EntityAppearance.java | 50 +---- .../entity/collision/EntityCollisions.java | 72 +++++++ .../unicopia/mixin/MixinWorld.java | 4 +- .../unicopia/textures/entity/air_balloon.png | Bin 0 -> 3122 bytes 8 files changed, 274 insertions(+), 81 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/collision/EntityCollisions.java create mode 100644 src/main/resources/assets/unicopia/textures/entity/air_balloon.png diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityModel.java index d6c5701e..69c98c4d 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityModel.java @@ -6,6 +6,7 @@ import net.minecraft.client.model.*; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; public class AirBalloonEntityModel extends EntityModel { @@ -13,13 +14,11 @@ public class AirBalloonEntityModel extends EntityModel { private final ModelPart burner; private final ModelPart balloon; - private final ModelPart basket; public AirBalloonEntityModel(ModelPart root) { this.root = root; burner = root.getChild("burner"); balloon = root.getChild("balloon"); - basket = root.getChild("basket"); } public static TexturedModelData getTexturedModelData() { @@ -27,10 +26,10 @@ public class AirBalloonEntityModel extends EntityModel { ModelPartData root = modelData.getRoot(); ModelPartData burner = root.addChild("burner", ModelPartBuilder.create().uv(8, 107).cuboid(-6, -47, -6, 11, 15, 11, Dilation.NONE), ModelTransform.pivot(0, 24, 0)); - burner.addChild("rope_d_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-5, -46, -6, 0.7854F, 0, -0.7854F)); - burner.addChild("rope_c_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-4, -44, 3, -0.7854F, 0, -0.7854F)); - burner.addChild("rope_b_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(5, -46, 1, -0.7854F, 0, 0.7854F)); - burner.addChild("rope_a_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(5, -45, -6, 0.7854F, 0, 0.7854F)); + burner.addChild("rope_d_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-5, -46, -6, 0.7854F, 0, -0.7854F)); + burner.addChild("rope_c_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-4, -44, 3, -0.7854F, 0, -0.7854F)); + burner.addChild("rope_b_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of( 5, -46, 1, -0.7854F, 0, 0.7854F)); + burner.addChild("rope_a_r1", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of( 5, -45, -6, 0.7854F, 0, 0.7854F)); ModelPartData balloon = root.addChild("balloon", ModelPartBuilder.create().uv(64, 1).cuboid(-54, -178, -59, 112, 120, 112, Dilation.NONE), ModelTransform.pivot(0, 24, 0)); balloon.addChild("rope_d_r2", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-14, -11, -16, 0.4363F, 0, -0.4363F)); @@ -43,16 +42,23 @@ public class AirBalloonEntityModel extends EntityModel { .uv(64, 68).cuboid(15, -12, -16, 2, 11, 30, Dilation.NONE) .uv(80, 38).cuboid(-16, -12, -17, 32, 11, 2, Dilation.NONE) .uv(0, 32).cuboid(8, -12, 13, 8, 11, 2, Dilation.NONE) - .uv(0, 6).cuboid(-16, -12, 13, 8, 11, 2, Dilation.NONE), ModelTransform.pivot(0, 0, 0)); + .uv(0, 6).cuboid(-16, -12, 13, 8, 11, 2, Dilation.NONE), ModelTransform.NONE); basket.addChild("rim", ModelPartBuilder.create().uv(40, 34).cuboid(-18, -13, -17, 4, 2, 32, Dilation.NONE) .uv(0, 32).cuboid(14, -13, -17, 4, 2, 32, Dilation.NONE) .uv(80, 32).cuboid(-17, -13, -18, 34, 2, 4, Dilation.NONE) .uv(0, 19).cuboid(7, -13, 12, 10, 2, 4, Dilation.NONE) - .uv(0, 0).cuboid(-17, -13, 12, 10, 2, 4, Dilation.NONE), ModelTransform.pivot(0, 0, 0)); + .uv(0, 0).cuboid(-17, -13, 12, 10, 2, 4, Dilation.NONE), ModelTransform.NONE); return TexturedModelData.of(modelData, 512, 512); } @Override public void setAngles(AirBalloonEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + root.yaw = entity.bodyYaw; + burner.visible = entity.hasBurner(); + balloon.visible = entity.hasBalloon(); + + float xSpeed = (float)(entity.getX() - entity.prevX); + + root.pitch = MathHelper.sin(-xSpeed); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java index 807761ed..cbf2a7b0 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java @@ -2,12 +2,12 @@ package com.minelittlepony.unicopia.client.render.entity; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.AirBalloonEntity; -import net.minecraft.client.render.entity.EntityRendererFactory; -import net.minecraft.client.render.entity.LivingEntityRenderer; + +import net.minecraft.client.render.entity.*; import net.minecraft.util.Identifier; -public class AirBalloonEntityRenderer extends LivingEntityRenderer { - private static final Identifier TEXTURE = Unicopia.id("textures/entity/spellbook/normal.png"); +public class AirBalloonEntityRenderer extends MobEntityRenderer { + private static final Identifier TEXTURE = Unicopia.id("textures/entity/air_balloon.png"); public AirBalloonEntityRenderer(EntityRendererFactory.Context context) { super(context, new AirBalloonEntityModel(AirBalloonEntityModel.getTexturedModelData().createModel()), 0); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java index c1ad3dff..c5fddc3e 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java @@ -1,13 +1,25 @@ package com.minelittlepony.unicopia.entity; +import net.minecraft.block.ShapeContext; import net.minecraft.entity.*; +import net.minecraft.entity.data.*; import net.minecraft.entity.mob.FlyingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.math.Box; +import net.minecraft.util.math.*; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.World; -public class AirBalloonEntity extends FlyingEntity { +import java.util.function.Consumer; + +import com.minelittlepony.unicopia.entity.collision.EntityCollisions; + +public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.ComplexCollidable { + private static final byte HAS_BALLOON = 1; + private static final byte HAS_BURNER = 2; + private static final byte BURNER_ACTIVE = 4; + private static final TrackedData FLAGS = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); public AirBalloonEntity(EntityType type, World world) { super(type, world); @@ -16,50 +28,152 @@ public class AirBalloonEntity extends FlyingEntity { @Override protected void initDataTracker() { super.initDataTracker(); + dataTracker.startTracking(FLAGS, 0); + } + + public boolean hasBalloon() { + return getFlag(HAS_BALLOON); + } + + public void setHasBalloon(boolean hasBalloon) { + setFlag(HAS_BALLOON, hasBalloon); + } + + public boolean hasBurner() { + return getFlag((byte)(HAS_BURNER | HAS_BALLOON)); + } + + public void setHasBurner(boolean hasBurner) { + setFlag(HAS_BURNER, hasBurner); + } + + public boolean isBurnerActive() { + return getFlag((byte)(HAS_BURNER | BURNER_ACTIVE)); + } + + public void setBurnerActive(boolean burnerActive) { + setFlag(BURNER_ACTIVE, burnerActive); + } + + private boolean getFlag(byte flag) { + return (dataTracker.get(FLAGS).intValue() & flag) == flag; + } + + private void setFlag(byte flag, boolean val) { + int v = dataTracker.get(FLAGS); + dataTracker.set(FLAGS, val ? (v | flag) : (v & ~flag)); + } + + private boolean isAirworthy() { + return hasBalloon() && (!onGround || isBurnerActive()); } @Override public void tick() { + this.setHasBalloon(true); + this.setHasBurner(true); + this.setBurnerActive(false); - if (!world.isClient) { + setAir(getMaxAir()); - float xSpeed = 0;//-0.015F * (this.age % 1000 < 500 ? -1 : 1); - - addVelocity(xSpeed, 0, 0); + if (isAirworthy()) { + setVelocity(getVelocity() + .add(getWind(world, getBlockPos())) + .normalize() + .multiply(0.2) + .add(0, isBurnerActive() ? 0.09F : isTouchingWater() ? 0.02F : -0.06F, 0)); + } else { + addVelocity(0, isTouchingWater() ? 0.02F : -0.02F, 0); } + if (isLeashed()) { + Vec3d leashPost = getHoldingEntity().getPos(); + Vec3d pos = getPos(); - for (var e : this.world.getOtherEntities(this, getBoundingBox().expand(0.2, 1.0E-7, 0.2))) { - if (!(e instanceof PlayerEntity)) { - e.setVelocity(e.getVelocity().multiply(0.3).add(getVelocity().multiply(0.84))); + if (leashPost.distanceTo(pos) >= 5) { + Vec3d newVel = leashPost.subtract(pos).multiply(0.01); + setVelocity(newVel.lengthSquared() < 0.03 ? Vec3d.ZERO : newVel); + } + } - double diff = (getBoundingBox().maxY + getVelocity().y) - e.getBoundingBox().minY; + if (age % 20 < 10) { + if (getVelocity().y < 0.1) { + addVelocity(0, 0.01, 0); + } + } else { + if (getVelocity().y > -0.1) { + addVelocity(0, -0.01, 0); + } + } - if (diff > 0) { - e.addVelocity(0, diff, 0); + //setVelocity(Vec3d.ZERO); + float weight = 0; + + if (getVelocity().length() > 0) { + Box box = getBoundingBox(); + for (var e : this.world.getOtherEntities(this, box.expand(1, 1.0E-7, 1))) { + Vec3d vel = e.getVelocity(); + + if (getVelocity().y > 0 && box.maxY > e.getBoundingBox().minY) { + e.setPosition(e.getX(), box.maxY + 0.1, e.getZ()); } + + if (!(e instanceof PlayerEntity)) { + e.setVelocity(vel.multiply(0.3).add(getVelocity().multiply(0.786))); + } + + e.setOnGround(true); + + if (horizontalSpeed != 0) { + e.distanceTraveled = 0; + e.horizontalSpeed = 0; + if (e instanceof LivingEntity l) { + l.limbAngle = 0; + l.limbDistance = 0; + } + } + + weight++; } - e.distanceTraveled = 0; - e.horizontalSpeed = 0; - if (e instanceof LivingEntity l) { - l.limbAngle = 0; - l.limbDistance = 0; + Box balloonTopBox = getBoundingBox().offset(0.125, 11, 0).expand(2.25, 0, 2); + + for (var e : this.world.getOtherEntities(this, balloonTopBox.expand(1.0E-7))) { + Vec3d vel = e.getVelocity(); + + double yVel = vel.y + Math.max(balloonTopBox.maxY - e.getBoundingBox().minY, 0); + yVel /= 8; + yVel += 0.3; + + e.setVelocity(vel.getX(), yVel, vel.getZ()); + e.setVelocity(e.getVelocity().multiply(0.3).add(getVelocity().multiply(0.786))); + e.setOnGround(true); } } + if (getVelocity().y > -0.6 && !isTouchingWater()) { + if (isBurnerActive()) { + weight -= 3; + } + addVelocity(0, MathHelper.clamp(-weight / 10F, -1, isLeashed() ? 0.2F : 1), 0); + } + super.tick(); - } @Override public void onPlayerCollision(PlayerEntity player) { - player.setVelocity(player.getVelocity().multiply(0.9).add(getVelocity().multiply(0.56))); + if (getVelocity().lengthSquared() > 0) { + // player.setVelocity(getVelocity().multiply(1.3)); + player.setVelocity(player.getVelocity().multiply(0.3).add(getVelocity().multiply( + getVelocity().y < 0 ? 0.828 : 0.728 + ))); - double diff = (getBoundingBox().maxY + getVelocity().y) - player.getBoundingBox().minY; + double diff = (getBoundingBox().maxY + getVelocity().y) - player.getBoundingBox().minY; - if (diff > 0) { - player.addVelocity(0, diff, 0); + if (diff > 0) { + player.addVelocity(0, diff, 0); + } } } @@ -87,14 +201,53 @@ public class AirBalloonEntity extends FlyingEntity { return getBoundingBox().expand(30, 100, 30); } + @Override + public void getCollissionShapes(ShapeContext context, Consumer output) { + + Box box = getBoundingBox().expand(0.3, 0, 0.3); + + double wallheight = box.maxY + 1; + double wallThickness = 0.7; + + output.accept(VoxelShapes.cuboid(new Box(box.minX, box.minY, box.minZ, box.minX + wallThickness + 0.2, wallheight, box.minZ + wallThickness))); + output.accept(VoxelShapes.cuboid(new Box(box.maxX - wallThickness - 0.2, box.minY, box.minZ, box.maxX, wallheight, box.minZ + wallThickness))); + output.accept(VoxelShapes.cuboid(new Box(box.minX, box.minY, box.maxZ - wallThickness, box.maxX, wallheight, box.maxZ))); + + output.accept(VoxelShapes.cuboid(new Box(box.minX, box.minY, box.minZ, box.minX + wallThickness, wallheight, box.maxZ))); + output.accept(VoxelShapes.cuboid(new Box(box.maxX - wallThickness, box.minY, box.minZ, box.maxX, wallheight, box.maxZ))); + + // top of balloon + if (hasBalloon()) { + output.accept(VoxelShapes.cuboid(getBoundingBox().offset(0.125, 11, 0).expand(2.25, 0, 2))); + } + } + @Override public void readCustomDataFromNbt(NbtCompound compound) { super.readCustomDataFromNbt(compound); + setHasBalloon(compound.getBoolean("hasBalloon")); + setHasBurner(compound.getBoolean("hasBurner")); + setBurnerActive(compound.getBoolean("burnerActive")); } @Override public void writeCustomDataToNbt(NbtCompound compound) { super.writeCustomDataToNbt(compound); + compound.putBoolean("hasBalloon", hasBalloon()); + compound.putBoolean("hasBurner", hasBurner()); + compound.putBoolean("burnerActive", isBurnerActive()); } + static Vec3d getWind(World world, BlockPos pos) { + return Vec3d.ofCenter(pos).normalize().multiply(1, 0, 1).multiply(0.2);//.multiply((world.getRandom().nextFloat() - 0.5) * 0.2); + } } + + + + + + + + + diff --git a/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java index 5c3ec21e..2f1eb0f7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java @@ -49,7 +49,7 @@ public interface UEntities { .dimensions(EntityDimensions.fixed(0.9F, 0.5F))); EntityType AIR_BALLOON = register("air_balloon", FabricEntityTypeBuilder.create(SpawnGroup.MISC, AirBalloonEntity::new) .trackRangeBlocks(1000) - .dimensions(EntityDimensions.fixed(3, 0.1F))); + .dimensions(EntityDimensions.fixed(2.5F, 0.1F))); static EntityType register(String name, FabricEntityTypeBuilder builder) { EntityType type = builder.build(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java index 7e916615..3020a5c5 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/behaviour/EntityAppearance.java @@ -14,8 +14,8 @@ import com.minelittlepony.unicopia.FlightType; import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.entity.UEntityAttributes; +import com.minelittlepony.unicopia.entity.collision.EntityCollisions; import com.minelittlepony.unicopia.entity.player.PlayerDimensions; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.projectile.ProjectileUtil; @@ -42,14 +42,9 @@ import net.minecraft.entity.mob.VexEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.ShulkerBulletEntity; import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.function.BooleanBiFunction; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Box; import net.minecraft.util.shape.VoxelShape; -import net.minecraft.util.shape.VoxelShapes; -import net.minecraft.world.WorldAccess; -public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provider, FlightType.Provider { +public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provider, FlightType.Provider, EntityCollisions.ComplexCollidable { private static final Optional BLOCK_HEIGHT = Optional.of(0.5F); @NotNull @@ -358,43 +353,10 @@ public class EntityAppearance implements NbtSerialisable, PlayerDimensions.Provi return entityNbt; } - void getCollissionShapes(ShapeContext context, Consumer output) { - getCollissionShapes(getAppearance(), context, output); - getAttachments().forEach(e -> getCollissionShapes(e, context, output)); + @Override + public void getCollissionShapes(ShapeContext context, Consumer output) { + EntityCollisions.getCollissionShapes(getAppearance(), context, output); + getAttachments().forEach(e -> EntityCollisions.getCollissionShapes(e, context, output)); } - private static void getCollissionShapes(@Nullable Entity entity, ShapeContext context, Consumer output) { - if (entity == null) { - return; - } - - if (entity.isCollidable()) { - output.accept(VoxelShapes.cuboid(entity.getBoundingBox())); - } else if (entity instanceof FallingBlockEntity) { - BlockPos pos = entity.getBlockPos(); - output.accept(((FallingBlockEntity) entity).getBlockState() - .getCollisionShape(entity.world, entity.getBlockPos(), context) - .offset(pos.getX(), pos.getY(), pos.getZ()) - ); - } - } - - public static List getColissonShapes(@Nullable Entity entity, WorldAccess world, Box box) { - List shapes = new ArrayList<>(); - ShapeContext ctx = entity == null ? ShapeContext.absent() : ShapeContext.of(entity); - VoxelShape entityShape = VoxelShapes.cuboid(box.expand(1.0E-6D)); - - world.getOtherEntities(entity, box.expand(0.5), e -> { - Caster.of(e).flatMap(c -> c.getSpellSlot().get(SpellPredicate.IS_DISGUISE, false)).ifPresent(p -> { - p.getDisguise().getCollissionShapes(ctx, shape -> { - if (!shape.isEmpty() && VoxelShapes.matchesAnywhere(shape, entityShape, BooleanBiFunction.AND)) { - shapes.add(shape); - } - }); - }); - return false; - }); - - return shapes; - } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/collision/EntityCollisions.java b/src/main/java/com/minelittlepony/unicopia/entity/collision/EntityCollisions.java new file mode 100644 index 00000000..cd5c4f0a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/collision/EntityCollisions.java @@ -0,0 +1,72 @@ +package com.minelittlepony.unicopia.entity.collision; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.SpellPredicate; + +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.FallingBlockEntity; +import net.minecraft.util.function.BooleanBiFunction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.WorldAccess; + +public class EntityCollisions { + + public static void getCollissionShapes(@Nullable Entity entity, ShapeContext context, Consumer output) { + if (entity == null) { + return; + } + + if (entity.isCollidable()) { + output.accept(VoxelShapes.cuboid(entity.getBoundingBox())); + if (entity instanceof ComplexCollidable collidable) { + collidable.getCollissionShapes(context, output); + } + } else if (entity instanceof FallingBlockEntity) { + BlockPos pos = entity.getBlockPos(); + output.accept(((FallingBlockEntity) entity).getBlockState() + .getCollisionShape(entity.world, entity.getBlockPos(), context) + .offset(pos.getX(), pos.getY(), pos.getZ()) + ); + } + } + + public static List getColissonShapes(@Nullable Entity entity, WorldAccess world, Box box) { + ShapeContext ctx = entity == null ? ShapeContext.absent() : ShapeContext.of(entity); + return collectCollisionBoxes(box, collector -> { + world.getOtherEntities(entity, box.expand(50), e -> { + Caster.of(e).flatMap(c -> c.getSpellSlot().get(SpellPredicate.IS_DISGUISE, false)).ifPresent(p -> { + p.getDisguise().getCollissionShapes(ctx, collector); + }); + if (e instanceof ComplexCollidable collidable) { + collidable.getCollissionShapes(ctx, collector); + } + return false; + }); + }); + } + + static List collectCollisionBoxes(Box box, Consumer> generator) { + List shapes = new ArrayList<>(); + VoxelShape entityShape = VoxelShapes.cuboid(box.expand(1.0E-6D)); + generator.accept(shape -> { + if (!shape.isEmpty() && VoxelShapes.matchesAnywhere(shape, entityShape, BooleanBiFunction.AND)) { + shapes.add(shape); + } + }); + return shapes; + } + + public interface ComplexCollidable { + void getCollissionShapes(ShapeContext context, Consumer output); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java index da81e14c..02749daa 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinWorld.java @@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import com.minelittlepony.unicopia.BlockDestructionManager; -import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; +import com.minelittlepony.unicopia.entity.collision.EntityCollisions; import com.minelittlepony.unicopia.entity.duck.RotatedView; import net.minecraft.block.BlockState; @@ -51,7 +51,7 @@ abstract class MixinWorld implements WorldAccess, BlockDestructionManager.Source @Override public List getEntityCollisions(@Nullable Entity entity, Box box) { if (box.getAverageSideLength() >= 1.0E-7D) { - List shapes = EntityAppearance.getColissonShapes(entity, this, box); + List shapes = EntityCollisions.getColissonShapes(entity, this, box); if (!shapes.isEmpty()) { return Stream.concat(shapes.stream(), WorldAccess.super.getEntityCollisions(entity, box).stream()).toList(); } diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon.png new file mode 100644 index 0000000000000000000000000000000000000000..687b373a1fac700e57c72766792ff21c27820246 GIT binary patch literal 3122 zcmeHKX;4#H8odc(Kt@mpl@>7~t|$>l8$^Y~X%RspAOviMP82m-*+wA&jR~(?r%@1b zL+Fm!sLdis0wO`f5=4b|kev`AArTNFkOU%|A(G6)Dyw>#nd%=?)qnc^d9U6*?|$by z-#w@5T?pLcGkeyeSpb09zPonp1po;@B7wzB_%<5bIthT4-gn3Lkl34=PVD)JFC9?B zTL;a&R|OUNk`H!`vu@B27Ovg?plY7oA*Vx*=ZfoXvsjk_U8JX zdyjobx8F{#rY*ndeWt;kp(u@0$k`2?A%nj1>f?o zo#_frY=L$pS*z?2r4LH2^RBfNwW%-^gXp#lE1HPaH2jmU@ZdHNio-I>v$5j-iC5OV z6zZAcC~D7}QEJl#;VDjpPlSjAR`(_~FLW)D2k}|o*Vae{4j3b4OU&4ZUMW7G1MFHd zXW?4T7e}L5Cr`IcE)rdAjmhMIw@<11TXDRTOz9pPbI+4^HzB;HZm4jg=|o$(PyaX~ zrB*U)IrcWsUr-y}YOOC51TU%_&84_IzvdE}bJ~J7DseT8LV*@r_#|twErby-;ypyq|qy4EdqbHD~qig+}RqjWNK}v z7r0)K16rBUwM5DmL0-kyXZ)eC>AntXTp;1bsd(A{2&QAv#ixUd)sE(s9&|Zxh&hoh#jz`v&MrRBvvX9RrzJMD#*Nq_2JJla( zeM|4N@w^}|+$G8XnkC2+U4O${F#g#CDmKIbTz~YP2?=}OQ9Je}^Qn&k){Ml|sSbx@xhGeH)8L$)oEIL$;xW%es18g|&{%)?BX=_`q7u6{uE zt>RVoVFPGP^_3%Ya%-Jbj$sR_MO;%OOx5?fS*3{GvnrdM9@-#ly|_ldy9!FNfi9i?iWNO{f>AVD5+wbMF|y7msxPL+6Ysz)x8EO zo{<7efgS4Va=v13^Wtg9|Po)gF zSJ1>P%DS!TP@-`O-BDmX#m5BF9z0(RWzFSY(;L*X^9-f4EeIs+9t5o^_jzsgjFDn5 zq8SLU>o@>ts|i28ZVX{xL7q>;A-4711Tu0Ux#5I2s@KmkzEb{Cw$Ix7n2o)aI3;gKPZr zI}kW`XAssZ4Ypk|R(Pko!pj_4AP51c_nzD#A77KSL0|?Q|0|eTuzwkPGIl_Us2sHb zV9wjB#gL>aT;>v;ghY@^Uix<+p>1>oz%50gOOhm~vwd=6!6UAwU~~n*-Ry=pw^OR~1L972 zVc&NNOxyhKiuFw;rnq4pkhuf9O0oS?oKnFxJsoM5g3NmWY_PUwT(3CyL_IQ5?HU50)m!{x@#eo@ z!;dwJ;3&6kpFb!m>anWH{@3%bR-Lc9+01@?;t!H) zsa)S5P+xj$>tU6WY(4|9yocsPwELw@*TdO1pnv6DkUP+=f%Gm&K(d4LlrSdhVQgHV z5iqnEyTNh}$%-4N75e6zxw2F?;NHq$MBq3I()lj~X~QVcvuHWE?=>KmJ_b1KX&VsX zr~j7`1n!IkY1q3&_w9^gry+wJBYn+6f=!e$TU?9!L$4s9D;#7feq3TU15_6hK+XYi zT!IP-7Q&;|gvL+=DW}+|aRi_z5CJ>s(?VdL28!?KmbN|lShb)W1Jvk}|LUIK zp03{*><@-)GQ6pqu=XR)0Irz%zc&9L%