mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Fix janky hot air balloon physics
This commit is contained in:
parent
93d11531d6
commit
fbe444b56c
6 changed files with 313 additions and 233 deletions
|
@ -167,8 +167,6 @@ public class WorldRenderDelegate {
|
|||
return true;
|
||||
}
|
||||
|
||||
pony.updateSupportingEntity();
|
||||
|
||||
matrices.push();
|
||||
|
||||
Entity owner = pony.asEntity();
|
||||
|
|
|
@ -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<T extends LivingEntity> implements Equine<T>, 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<T extends LivingEntity> implements Equine<T>, Caste
|
|||
|
||||
private final Enchantments enchants = addTicker(new Enchantments(this));
|
||||
private final ItemTracker armour = addTicker(new ItemTracker(this));
|
||||
//private final Transportation<T> transportation = new Transportation<>(this);
|
||||
private final Transportation<T> transportation = new Transportation<>(this);
|
||||
|
||||
protected Living(T entity, TrackedData<NbtCompound> effect) {
|
||||
this.entity = entity;
|
||||
|
@ -171,6 +163,10 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
|
|||
return armour;
|
||||
}
|
||||
|
||||
public Transportation<T> getTransportation() {
|
||||
return transportation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T asEntity() {
|
||||
return entity;
|
||||
|
@ -202,73 +198,6 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, 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<T extends LivingEntity> implements Equine<T>, 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<T extends LivingEntity> implements Equine<T>, Caste
|
|||
|
||||
updateDragonBreath();
|
||||
|
||||
if (ticksOutsideVehicle == 0) {
|
||||
updatePositionOffset();
|
||||
|
||||
ticksInVehicle++;
|
||||
} else {
|
||||
ticksInVehicle = 0;
|
||||
}
|
||||
transportation.tick();
|
||||
}
|
||||
|
||||
public void updateAttributeModifier(UUID id, EntityAttribute attribute, float desiredValue, Float2ObjectFunction<EntityAttributeModifier> modifierSupplier, boolean permanent) {
|
||||
|
|
|
@ -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<T extends LivingEntity> implements Tickable {
|
||||
|
||||
private final Living<T> living;
|
||||
|
||||
@Nullable
|
||||
private MultiBoundingBoxEntity vehicle;
|
||||
@Nullable
|
||||
private Entity vehicleEntity;
|
||||
@Nullable
|
||||
private Box vehicleBox;
|
||||
|
||||
private int ticksInVehicle;
|
||||
|
||||
private Vec3d lastVehiclePosition = Vec3d.ZERO;
|
||||
|
||||
Transportation(Living<T> living) {
|
||||
this.living = living;
|
||||
}
|
||||
|
||||
public <E extends Entity & MultiBoundingBoxEntity> 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<VoxelShape> 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;
|
||||
}
|
||||
}
|
|
@ -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<Box> getBoundingBoxes();
|
||||
|
||||
default List<Box> getGravityZoneBoxes() {
|
||||
return getBoundingBoxes();
|
||||
}
|
||||
|
||||
Map<Box, List<Entity>> getCollidingEntities(Stream<Box> boundingBoxes);
|
||||
|
||||
SoundEvent getWalkedOnSound(double y);
|
||||
|
||||
@Override
|
||||
default void getCollissionShapes(ShapeContext context, Consumer<VoxelShape> output) {
|
||||
for (Box box : getBoundingBoxes()) {
|
||||
output.accept(VoxelShapes.cuboid(box));
|
||||
}
|
||||
}
|
||||
|
||||
static List<Box> getBoundingBoxes(Entity entity) {
|
||||
return entity instanceof MultiBoundingBoxEntity multi ? multi.getBoundingBoxes() : List.of(entity.getBoundingBox());
|
||||
}
|
||||
|
|
|
@ -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<String> BASKET_TYPE = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.STRING);
|
||||
private static final TrackedData<Integer> BALLOON_DESIGN = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
|
||||
|
||||
private static final Predicate<Entity> 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<? extends AirBalloonEntity> 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<Box> 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<Entity> 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<Box, List<Entity>> collidingEntities = getCollidingEntities(getBoundingBoxes().stream());
|
||||
|
||||
for (Map.Entry<Box, List<Entity>> 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<VoxelShape> output) {
|
||||
public List<Box> 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<Box> getBoundingBoxes() {
|
||||
List<Box> 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<Box> boundingBoxes = getGravityZoneBoxes();
|
||||
super.move(movementType, movement);
|
||||
if (movementType == MovementType.SELF) {
|
||||
Vec3d actualMovement = getPos().subtract(oldPos);
|
||||
Map<Box, List<Entity>> collidingEntities = getCollidingEntities(
|
||||
boundingBoxes.stream().map(box -> box.stretch(actualMovement))
|
||||
);
|
||||
|
||||
for (Map.Entry<Box, List<Entity>> 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<VoxelShape> 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<Box, List<Entity>> getCollidingEntities(Stream<Box> 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<Identifier, BasketType> 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue