From fbe444b56cd52e30383d50a92629f41c7e8c89a0 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 28 Mar 2024 12:08:06 +0000 Subject: [PATCH] Fix janky hot air balloon physics --- .../client/render/WorldRenderDelegate.java | 2 - .../unicopia/entity/Living.java | 127 +--------- .../unicopia/entity/Transportation.java | 141 +++++++++++ .../collision/MultiBoundingBoxEntity.java | 26 +- .../unicopia/entity/mob/AirBalloonEntity.java | 232 +++++++++--------- .../unicopia/mixin/MixinEntity.java | 18 ++ 6 files changed, 313 insertions(+), 233 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/Transportation.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java index 17594ba0..f839fef1 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java @@ -167,8 +167,6 @@ public class WorldRenderDelegate { return true; } - pony.updateSupportingEntity(); - matrices.push(); Entity owner = pony.asEntity(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index 84dce958..86ea4261 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -20,7 +20,6 @@ import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.behaviour.Guest; -import com.minelittlepony.unicopia.entity.collision.MultiBoundingBoxEntity; import com.minelittlepony.unicopia.entity.damage.MagicalDamageSource; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; import com.minelittlepony.unicopia.entity.effect.CorruptInfluenceStatusEffect; @@ -67,7 +66,6 @@ import net.minecraft.sound.SoundCategory; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Box; import net.minecraft.util.math.Direction; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -88,14 +86,6 @@ public abstract class Living implements Equine, Caste private boolean invisible = false; - @Nullable - private Entity supportingEntity; - - @Nullable - private Vec3d supportPositionOffset; - private int ticksOutsideVehicle; - private int ticksInVehicle; - @Nullable private Caster attacker; @Nullable @@ -109,6 +99,8 @@ public abstract class Living implements Equine, Caste private final Enchantments enchants = addTicker(new Enchantments(this)); private final ItemTracker armour = addTicker(new ItemTracker(this)); + //private final Transportation transportation = new Transportation<>(this); + private final Transportation transportation = new Transportation<>(this); protected Living(T entity, TrackedData effect) { this.entity = entity; @@ -171,6 +163,10 @@ public abstract class Living implements Equine, Caste return armour; } + public Transportation getTransportation() { + return transportation; + } + @Override public final T asEntity() { return entity; @@ -202,73 +198,6 @@ public abstract class Living implements Equine, Caste return vehicle != null && getCarrierId().filter(vehicle.getUuid()::equals).isPresent(); } - public boolean setSupportingEntity(@Nullable Entity supportingEntity) { - this.supportingEntity = supportingEntity; - if (supportingEntity != null) { - ticksOutsideVehicle = 0; - } - return true; - } - - @Nullable - public Entity getSupportingEntity() { - return supportingEntity; - } - - public int getTicksInVehicle() { - return ticksInVehicle; - } - - public void setPositionOffset(@Nullable Vec3d positionOffset) { - this.supportPositionOffset = positionOffset; - } - - public void updatePositionOffset() { - setPositionOffset(supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos())); - } - - public void updateRelativePosition(Box box) { - if (supportingEntity == null || supportPositionOffset == null) { - return; - } - if (getPhysics().isFlying()) { - return; - } - - Vec3d newPos = supportingEntity.getPos().add(supportPositionOffset); - Vec3d posChange = entity.getPos().subtract(newPos); - entity.setPosition(newPos); - if (isClient()) { - Vec3d newServerPos = LivingEntityDuck.serverPos(entity); - if (newServerPos.lengthSquared() != 0) { - newServerPos = newServerPos.subtract(posChange); - entity.updateTrackedPositionAndAngles( - newServerPos.x, newServerPos.y, newServerPos.z, - entity.getYaw(), entity.getPitch(), 3, true); - } - } else { - entity.updateTrackedPosition(newPos.x, newPos.y, newPos.z); - } - - if (!(entity instanceof PlayerEntity)) { - entity.lastRenderX = supportingEntity.lastRenderX + supportPositionOffset.x; - entity.lastRenderY = supportingEntity.lastRenderY + supportPositionOffset.y; - entity.lastRenderZ = supportingEntity.lastRenderZ + supportPositionOffset.z; - - if (entity.getVelocity().length() < 0.1) { - LimbAnimationUtil.resetToZero(entity.limbAnimator); - } - } - - entity.horizontalSpeed = 0; - entity.prevHorizontalSpeed = 0; - entity.speed = 0; - entity.setOnGround(true); - entity.verticalCollision = true; - entity.groundCollision = true; - entity.fallDistance = 0; - } - @Override public boolean beforeUpdate() { if (EffectUtils.getAmplifier(entity, UEffects.PARALYSIS) > 1 && entity.getVelocity().horizontalLengthSquared() > 0) { @@ -276,44 +205,10 @@ public abstract class Living implements Equine, Caste updateVelocity(); } - updateSupportingEntity(); + //transportation.updateSupportingEntity(); return false; } - public void updateSupportingEntity() { - if (supportingEntity != null) { - Box ownBox = entity.getBoundingBox() - .stretch(entity.getVelocity()) - .expand(0.1, 0.5, 0.1) - .stretch(supportingEntity.getVelocity().multiply(-2)); - - MultiBoundingBoxEntity.getBoundingBoxes(supportingEntity).stream() - .filter(box -> box.stretch(supportingEntity.getVelocity()).expand(0, 0.5, 0).intersects(ownBox)) - .findFirst() - .ifPresentOrElse(box -> { - ticksOutsideVehicle = 0; - if (supportPositionOffset == null) { - updatePositionOffset(); - } else { - updateRelativePosition(box); - } - entity.setOnGround(true); - entity.verticalCollision = true; - entity.groundCollision = true; - }, () -> { - // Rubberband passengers to try and prevent players falling out when the velocity changes suddenly - if (ticksOutsideVehicle++ > 30) { - supportingEntity = null; - supportPositionOffset = null; - Unicopia.LOGGER.info("Entity left vehicle"); - } else { - supportPositionOffset = supportPositionOffset.multiply(0.25, 1, 0.25); - } - }); - } - - } - @Override public void tick() { tickers.forEach(Tickable::tick); @@ -358,13 +253,7 @@ public abstract class Living implements Equine, Caste updateDragonBreath(); - if (ticksOutsideVehicle == 0) { - updatePositionOffset(); - - ticksInVehicle++; - } else { - ticksInVehicle = 0; - } + transportation.tick(); } public void updateAttributeModifier(UUID id, EntityAttribute attribute, float desiredValue, Float2ObjectFunction modifierSupplier, boolean permanent) { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Transportation.java b/src/main/java/com/minelittlepony/unicopia/entity/Transportation.java new file mode 100644 index 00000000..14ad5346 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/Transportation.java @@ -0,0 +1,141 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.advancement.UCriteria; +import com.minelittlepony.unicopia.entity.collision.MultiBoundingBoxEntity; +import com.minelittlepony.unicopia.entity.duck.EntityDuck; +import com.minelittlepony.unicopia.entity.mob.AirBalloonEntity; +import com.minelittlepony.unicopia.util.Tickable; + +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.MovementType; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.world.event.GameEvent; + +public class Transportation implements Tickable { + + private final Living living; + + @Nullable + private MultiBoundingBoxEntity vehicle; + @Nullable + private Entity vehicleEntity; + @Nullable + private Box vehicleBox; + + private int ticksInVehicle; + + private Vec3d lastVehiclePosition = Vec3d.ZERO; + + Transportation(Living living) { + this.living = living; + } + + public void setVehicle(@Nullable E vehicle) { + this.vehicle = vehicle; + this.vehicleEntity = vehicle; + updatePreviousPosition(); + } + + @Override + public void tick() { + if (vehicle != null) { + ticksInVehicle++; + } else { + ticksInVehicle = 0; + } + + if (ticksInVehicle > 20 && vehicle instanceof AirBalloonEntity) { + UCriteria.RIDE_BALLOON.trigger(living.asEntity()); + } + } + + public void updatePreviousPosition() { + vehicleBox = getVehicleBox(); + lastVehiclePosition = vehicleEntity == null ? Vec3d.ZERO : vehicleEntity.getPos(); + Entity entity = living.asEntity(); + if (vehicleBox != null && living.asEntity().getBoundingBox().intersects(vehicleBox.expand(0.001, 0.5001, 0.001))) { + entity.setOnGround(true); + entity.onLanding(); + entity.verticalCollision = true; + entity.groundCollision = true; + entity.velocityDirty = true; + entity.velocityModified = true; + } + } + + public void onMove(MovementType movementType) { + if (vehicleBox == null || vehicleEntity == null) { + return; + } + + Entity entity = living.asEntity(); + + Box passengerBox = entity.getBoundingBox().expand(0.001); + Vec3d vehicleMovement = vehicleEntity.getPos().subtract(lastVehiclePosition); + + List shapes = new ArrayList<>(); + vehicle.getCollissionShapes(ShapeContext.of(entity), shapes::add); + vehicleMovement = vehicleMovement.add(vehicleEntity.getVelocity()); + vehicleMovement = Entity.adjustMovementForCollisions(entity, vehicleMovement, passengerBox, entity.getWorld(), shapes); + + Vec3d newPos = entity.getPos().add(vehicleMovement); + + if (!vehicleEntity.isOnGround()) { + // surface check to prevent the player from floating + if (newPos.getY() > vehicleBox.minY + 0.1 || newPos.getY() < vehicleBox.minY + 0.1) { + newPos = new Vec3d(newPos.getX(), vehicleBox.minY + 0.01, newPos.getZ()); + } + // containment checks to prevent the player from falling out of the basket when in flight + if (newPos.getY() < vehicleEntity.getPos().getY() + 3) { + double maxDeviation = 0.1; + double z = MathHelper.clamp(newPos.getZ(), vehicleBox.minZ + maxDeviation, vehicleBox.maxZ - maxDeviation); + double x = MathHelper.clamp(newPos.getX(), vehicleBox.minX + maxDeviation, vehicleBox.maxX - maxDeviation); + + newPos = new Vec3d(x, newPos.getY(), z); + } + + entity.setPosition(newPos); + entity.updateTrackedPosition(newPos.x, newPos.y, newPos.z); + entity.setVelocity(Vec3d.ZERO); + } + + entity.setOnGround(true); + entity.onLanding(); + entity.verticalCollision = true; + entity.groundCollision = true; + + if (entity.distanceTraveled > ((EntityDuck)entity).getNextStepSoundDistance()) { + entity.distanceTraveled -= 0.5; + entity.playSound(vehicle.getWalkedOnSound(entity.getY()), 0.5F, 1); + if (!entity.isSneaky()) { + entity.getWorld().emitGameEvent(entity, GameEvent.STEP, entity.getBlockPos()); + } + } + } + + @Nullable + private Box getVehicleBox() { + if (vehicle == null) { + return null; + } + + Box entityBox = living.asEntity().getBoundingBox().stretch(living.asEntity().getVelocity()); + for (Box box : vehicle.getGravityZoneBoxes()) { + if (entityBox.intersects(box.expand(0.001).stretch(vehicleEntity.getVelocity().multiply(1)))) { + return box; + } + } + + setVehicle(null); + return null; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/collision/MultiBoundingBoxEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/collision/MultiBoundingBoxEntity.java index 82ab40ed..ac7b951e 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/collision/MultiBoundingBoxEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/collision/MultiBoundingBoxEntity.java @@ -1,13 +1,37 @@ package com.minelittlepony.unicopia.entity.collision; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; +import com.minelittlepony.unicopia.entity.collision.EntityCollisions.ComplexCollidable; + +import net.minecraft.block.ShapeContext; import net.minecraft.entity.Entity; +import net.minecraft.sound.SoundEvent; import net.minecraft.util.math.Box; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; -public interface MultiBoundingBoxEntity { +public interface MultiBoundingBoxEntity extends ComplexCollidable { List getBoundingBoxes(); + default List getGravityZoneBoxes() { + return getBoundingBoxes(); + } + + Map> getCollidingEntities(Stream boundingBoxes); + + SoundEvent getWalkedOnSound(double y); + + @Override + default void getCollissionShapes(ShapeContext context, Consumer output) { + for (Box box : getBoundingBoxes()) { + output.accept(VoxelShapes.cuboid(box)); + } + } + static List getBoundingBoxes(Entity entity) { return entity instanceof MultiBoundingBoxEntity multi ? multi.getBoundingBoxes() : List.of(entity.getBoundingBox()); } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/AirBalloonEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/AirBalloonEntity.java index 57cbc2ef..72bf2c6c 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/AirBalloonEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/AirBalloonEntity.java @@ -13,7 +13,9 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleTypes; +import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.registry.RegistryKey; +import net.minecraft.sound.SoundEvent; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Identifier; @@ -22,20 +24,22 @@ import net.minecraft.util.function.ValueLists; import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; import net.minecraft.util.shape.VoxelShape; -import net.minecraft.util.shape.VoxelShapes; import net.minecraft.world.World; import net.minecraft.world.event.GameEvent; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; -import java.util.Set; -import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquineContext; @@ -47,7 +51,6 @@ import com.minelittlepony.unicopia.entity.MagicImmune; import com.minelittlepony.unicopia.entity.collision.EntityCollisions; import com.minelittlepony.unicopia.entity.collision.MultiBoundingBoxEntity; import com.minelittlepony.unicopia.entity.collision.MultiBox; -import com.minelittlepony.unicopia.entity.duck.EntityDuck; import com.minelittlepony.unicopia.item.BasketItem; import com.minelittlepony.unicopia.item.HotAirBalloonItem; import com.minelittlepony.unicopia.item.UItems; @@ -61,13 +64,14 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp private static final TrackedData BASKET_TYPE = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.STRING); private static final TrackedData BALLOON_DESIGN = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final Predicate RIDER_PREDICATE = EntityPredicates.EXCEPT_SPECTATOR.and(e -> { + return !(e instanceof PlayerEntity p && p.getAbilities().flying); + }); + private boolean prevBoosting; private int prevInflation; - private Vec3d oldPosition = Vec3d.ZERO; private Vec3d manualVelocity = Vec3d.ZERO; - private int ticksFlying; - public AirBalloonEntity(EntityType type, World world) { super(type, world); intersectionChecked = true; @@ -80,12 +84,12 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp dataTracker.startTracking(ASCENDING, false); dataTracker.startTracking(BOOSTING, 0); dataTracker.startTracking(INFLATION, 0); - dataTracker.startTracking(BASKET_TYPE, ""); + dataTracker.startTracking(BASKET_TYPE, BasketType.DEFAULT.id().toString()); dataTracker.startTracking(BALLOON_DESIGN, 0); } public BasketType getBasketType() { - return BasketType.REGISTRY.get(Identifier.tryParse(dataTracker.get(BASKET_TYPE))); + return BasketType.of(dataTracker.get(BASKET_TYPE)); } public void setBasketType(BasketType type) { @@ -144,14 +148,6 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp return hasBalloon() && hasBurner() && getInflation() >= getMaxInflation(); } - @Override - public List getBoundingBoxes() { - if (hasBalloon() && getInflation(1) > 0.999F) { - return List.of(getInteriorBoundingBox(), getBalloonBoundingBox()); - } - return List.of(getInteriorBoundingBox()); - } - @Override public void tick() { setAir(getMaxAir()); @@ -248,86 +244,12 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp } prevBoosting = boosting; - oldPosition = getPos(); if (getFireTicks() > 0) { setFireTicks(1); } - if (!isOnGround() && (isAirworthy() || isSubmergedInWater() || isLeashed())) { - ticksFlying++; - } else { - ticksFlying = 0; - } - - updatePassengers(false); super.tick(); - setBoundingBox(MultiBox.of(getBoundingBox(), getBoundingBoxes())); - } - - private void updatePassengers(boolean move) { - Set alreadyTicked = new HashSet<>(); - for (Box box : getBoundingBoxes()) { - for (Entity e : getWorld().getOtherEntities(this, box.stretch(getVelocity().multiply(-1)).expand(0, 0.5, 0))) { - - if (e instanceof PlayerEntity p && p.getAbilities().flying) { - continue; - } - - if (!alreadyTicked.add(e)) { - continue; - } - - updatePassenger(e, box, e.getY() > getY() + 3); - } - } - } - - private void updatePassenger(Entity e, Box box, boolean inBalloon) { - - if (e instanceof AirBalloonEntity) { - return; - } - - if (ticksFlying > 0) { - if (Living.getOrEmpty(e).filter(living -> !living.setSupportingEntity(this)).isPresent()) { - return; - } - - Vec3d vel = getVelocity(); - - double height = box.getYLength(); - - if (height < 3 || e.getBoundingBox().minY > box.minY + height / 2D) { - if (vel.y > 0 && e.getBoundingBox().minY < box.maxY + 0.02) { - e.setPos(e.getX(), box.maxY, e.getZ()); - e.setOnGround(true); - } - if (vel.y < 0 && e.getBoundingBox().minY > box.maxY) { - e.setPos(e.getX(), box.maxY, e.getZ()); - e.setOnGround(true); - } - } - - Living.getOrEmpty(e).ifPresent(living -> { - living.setPositionOffset(e.getPos().subtract(oldPosition)); - living.updateRelativePosition(box); - - if (ticksFlying > 20 && living.getTicksInVehicle() > 20) { - UCriteria.RIDE_BALLOON.trigger(e); - } - }); - } - - if (getWorld().isClient) { - if (e.distanceTraveled > ((EntityDuck)e).getNextStepSoundDistance()) { - e.distanceTraveled--; - e.playSound(inBalloon ? USounds.ENTITY_HOT_AIR_BALLOON_STEP : USounds.ENTITY_HOT_AIR_BALLOON_BASKET_STEP, 0.5F, 1); - if (!e.isSneaky()) { - getWorld().emitGameEvent(e, GameEvent.STEP, getBlockPos()); - } - } - } } @Override @@ -459,12 +381,16 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp } @Override - protected void fall(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) { + public Race getSpecies() { + return Race.UNSET; } @Override - public Race getSpecies() { - return Race.UNSET; + public SoundEvent getWalkedOnSound(double y) { + if (y >= getBalloonBoundingBox().minY) { + return USounds.ENTITY_HOT_AIR_BALLOON_STEP; + } + return USounds.ENTITY_HOT_AIR_BALLOON_BASKET_STEP; } @Override @@ -497,6 +423,18 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp move(MovementType.SELF, getVelocity()); setVelocity(getVelocity().multiply(slipperyness)); } + } else { + Map> collidingEntities = getCollidingEntities(getBoundingBoxes().stream()); + + for (Map.Entry> passengers : collidingEntities.entrySet()) { + for (Entity passenger : passengers.getValue()) { + Living living = Living.living(passenger); + if (living != null) { + living.getTransportation().setVehicle(this); + } + + } + } } updateLimbs(false); } @@ -507,17 +445,22 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp return false; } + @Override + protected Box calculateBoundingBox() { + return MultiBox.of(super.calculateBoundingBox(), getBoundingBoxes()); + } + @Override public Box getVisibilityBoundingBox() { if (hasBalloon()) { - return MultiBox.unbox(getBoundingBox()).union(getBalloonBoundingBox()); + return getBalloonBoundingBox().withMinY(getY()); } - return MultiBox.unbox(getBoundingBox()); + return getInteriorBoundingBox(); } protected Box getInteriorBoundingBox() { Box box = MultiBox.unbox(getBoundingBox()); - return box.withMinY(box.minY - 0.2).contract(0.2, 0, 0.2); + return box.withMinY(box.minY - 0.05).contract(0.15, 0, 0.15); } protected Box getBalloonBoundingBox() { @@ -528,34 +471,96 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp } @Override - public void getCollissionShapes(ShapeContext context, Consumer output) { + public List getGravityZoneBoxes() { + Box balloon = getBalloonBoundingBox().expand(0.001); + Box interior = getInteriorBoundingBox().expand(0.001); + return List.of( + // interior - basket to top of balloon + interior.withMaxY(balloon.minY).withMinY(interior.maxY), + // balloon + balloon.withMaxY(balloon.maxY + 0.5).withMinY(balloon.maxY) + ); - Box box = MultiBox.unbox(getBoundingBox()).expand(0.3, 0, 0.3); + } - double wallheight = box.maxY + 0.7; - double wallThickness = 0.7; + @Override + public List getBoundingBoxes() { + List boxes = new ArrayList<>(); + Box box = getInteriorBoundingBox(); + boxes.add(box); + + double wallheight = box.maxY + 0.72; + double wallThickness = 0.2; if (!getBasketType().isOf(BoatEntity.Type.BAMBOO)) { // front left (next to door) - output.accept(VoxelShapes.cuboid(new Box(box.minX, box.minY, box.minZ, box.minX + wallThickness + 0.2, wallheight, box.minZ + wallThickness))); + boxes.add(new Box(box.minX, box.minY, box.minZ, box.minX + wallThickness + 0.4, wallheight, box.minZ + wallThickness)); // front right (next to door) - output.accept(VoxelShapes.cuboid(new Box(box.maxX - wallThickness - 0.2, box.minY, box.minZ, box.maxX, wallheight, box.minZ + wallThickness))); + boxes.add(new Box(box.maxX - wallThickness - 0.4, box.minY, box.minZ, box.maxX, wallheight, box.minZ + wallThickness)); // back - output.accept(VoxelShapes.cuboid(new Box(box.minX, box.minY, box.maxZ - wallThickness, box.maxX, wallheight, box.maxZ))); + boxes.add(new Box(box.minX, box.minY, box.maxZ - wallThickness, box.maxX, wallheight, box.maxZ)); // left - output.accept(VoxelShapes.cuboid(new Box(box.maxX - wallThickness, box.minY, box.minZ, box.maxX, wallheight, box.maxZ))); + boxes.add(new Box(box.maxX - wallThickness, box.minY, box.minZ, box.maxX, wallheight, box.maxZ)); // right - output.accept(VoxelShapes.cuboid(new Box(box.minX, box.minY, box.minZ, box.minX + wallThickness, wallheight, box.maxZ))); + boxes.add(new Box(box.minX, box.minY, box.minZ, box.minX + wallThickness, wallheight, box.maxZ)); } - // top of balloon - if (hasBalloon() && getInflation() > 0) { - output.accept(VoxelShapes.cuboid(getBalloonBoundingBox())); + if (hasBalloon() && getInflation(1) > 0.999F) { + boxes.add(getBalloonBoundingBox()); + } + return boxes; + } + + @Override + public void move(MovementType movementType, Vec3d movement) { + Vec3d oldPos = this.getPos(); + List boundingBoxes = getGravityZoneBoxes(); + super.move(movementType, movement); + if (movementType == MovementType.SELF) { + Vec3d actualMovement = getPos().subtract(oldPos); + Map> collidingEntities = getCollidingEntities( + boundingBoxes.stream().map(box -> box.stretch(actualMovement)) + ); + + for (Map.Entry> passengers : collidingEntities.entrySet()) { + for (Entity passenger : passengers.getValue()) { + movePassenger(passenger, actualMovement); + } + } } } + private void movePassenger(Entity passenger, Vec3d movement) { + Living living = Living.living(passenger); + if (living != null) { + if (living.getPhysics().isGravityNegative()) { + movement = movement.multiply(1, -1, 1); + } + living.getTransportation().setVehicle(this); + } + + List shapes = new ArrayList<>(); + getCollissionShapes(ShapeContext.of(passenger), shapes::add); + movement = Entity.adjustMovementForCollisions(passenger, movement, passenger.getBoundingBox(), getWorld(), shapes); + + passenger.setPosition(passenger.getPos().add(movement)); + passenger.updateTrackedPosition(passenger.getX(), passenger.getY(), passenger.getZ()); + } + + @Override + public Map> getCollidingEntities(Stream boundingBoxes) { + return boundingBoxes.collect(Collectors.toMap(Function.identity(), box -> { + return getWorld().getOtherEntities(this, box.expand(0.001).stretch(getVelocity().multiply(1)), RIDER_PREDICATE).stream().distinct().toList(); + })); + } + + @Override + protected void fall(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) { + } + + @Override public void readCustomDataFromNbt(NbtCompound compound) { super.readCustomDataFromNbt(compound); @@ -577,6 +582,10 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp compound.putInt("inflationAmount", getInflation()); } + static boolean isBetween(double value, double min, double max) { + return value >= min && value <= max; + } + @SuppressWarnings("deprecation") public enum BalloonDesign implements StringIdentifiable { NONE, @@ -607,6 +616,7 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp public record BasketType(Identifier id, @Nullable BoatEntity.Type boatType) { private static final Map REGISTRY = new HashMap<>(); + public static final BasketType DEFAULT = of(BoatEntity.Type.OAK); static { Arrays.stream(BoatEntity.Type.values()).forEach(BasketType::of); } @@ -616,7 +626,7 @@ public class AirBalloonEntity extends MobEntity implements EntityCollisions.Comp } public static BasketType of(String name) { - Identifier id = Identifier.tryParse(name); + Identifier id = name == null || name.isEmpty() ? null : Identifier.tryParse(name); if (id == null) { return of(BoatEntity.Type.OAK); } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java index 6a9c29d3..13644fbb 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntity.java @@ -21,11 +21,13 @@ import com.minelittlepony.unicopia.entity.duck.EntityDuck; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.MovementType; import net.minecraft.entity.Entity.PositionUpdater; import net.minecraft.entity.Entity.RemovalReason; import net.minecraft.fluid.Fluid; import net.minecraft.item.ItemStack; import net.minecraft.registry.tag.TagKey; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @Mixin(Entity.class) @@ -118,4 +120,20 @@ abstract class MixinEntity implements EntityDuck { info.setReturnValue(null); } } + + @Inject(method = "move", at = @At("HEAD")) + private void beforeMove(MovementType movementType, Vec3d movement, CallbackInfo info) { + Living living = Living.living((Entity)(Object)this); + if (living != null) { + living.getTransportation().updatePreviousPosition(); + } + } + + @Inject(method = "move", at = @At("RETURN")) + private void afterMove(MovementType movementType, Vec3d movement, CallbackInfo info) { + Living living = Living.living((Entity)(Object)this); + if (living != null) { + living.getTransportation().onMove(movementType); + } + } }