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 b55264fe..43bd770b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java @@ -112,7 +112,7 @@ public class WorldRenderDelegate { return true; } - pony.updateRelativePosition(); + pony.updateSupportingEntity(); matrices.push(); 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 c3868fcc..ef3bc55f 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 @@ -12,31 +12,15 @@ public class AirBalloonEntityModel extends EntityModel { private final ModelPart root; - private final ModelPart burner; - private final ModelPart balloon; + private float inflation; public AirBalloonEntityModel(ModelPart root) { this.root = root; - burner = root.getChild("burner"); - balloon = root.getChild("balloon"); } - public static TexturedModelData getTexturedModelData() { + public static TexturedModelData getBasketModelData() { ModelData modelData = new ModelData(); 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)); - - 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)); - balloon.addChild("rope_c_r2", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-14, -11, 11, -0.4363F, 0, -0.4363F)); - balloon.addChild("rope_b_r2", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(17, -11, 11, -0.4363F, 0, 0.4363F)); - balloon.addChild("rope_a_r2", ModelPartBuilder.create().uv(0, 107).cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(17, -11, -16, 0.4363F, 0, 0.4363F)); - ModelPartData basket = root.addChild("basket", ModelPartBuilder.create().uv(0, 0).cuboid(-16, -1, -16, 32, 2, 30, Dilation.NONE), ModelTransform.pivot(0, 24, 0)); basket.addChild("walls", ModelPartBuilder.create().uv(0, 66).cuboid(-17, -12, -16, 2, 11, 30, Dilation.NONE) .uv(64, 68).cuboid(15, -12, -16, 2, 11, 30, Dilation.NONE) @@ -48,21 +32,65 @@ public class AirBalloonEntityModel extends EntityModel { .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.NONE); - return TexturedModelData.of(modelData, 512, 512); + return TexturedModelData.of(modelData, 256, 128); } + + public static TexturedModelData getBurnerModelData() { + ModelData modelData = new ModelData(); + ModelPartData root = modelData.getRoot(); + + ModelPartData burner = root.addChild("burner", ModelPartBuilder.create().uv(8, 0).cuboid(-6, -47, -6, 11, 15, 11, Dilation.NONE), ModelTransform.pivot(0, 24, 0)); + burner.addChild("rope_d_r1", ModelPartBuilder.create().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().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().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().cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of( 5, -45, -6, 0.7854F, 0, 0.7854F)); + return TexturedModelData.of(modelData, 64, 128); + } + + public static TexturedModelData getCanopyModelData() { + ModelData modelData = new ModelData(); + ModelPartData root = modelData.getRoot(); + ModelPartData balloon = root.addChild("canopy", ModelPartBuilder.create().cuboid(-54, -178, -59, 112, 120, 112, Dilation.NONE), ModelTransform.pivot(0, 24, 0)); + balloon.addChild("rope_d_r2", ModelPartBuilder.create().cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-14, -11, -16, 0.4363F, 0, -0.4363F)); + balloon.addChild("rope_c_r2", ModelPartBuilder.create().cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(-14, -11, 11, -0.4363F, 0, -0.4363F)); + balloon.addChild("rope_b_r2", ModelPartBuilder.create().cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(17, -11, 11, -0.4363F, 0, 0.4363F)); + balloon.addChild("rope_a_r2", ModelPartBuilder.create().cuboid(-2, -68, 0, 2, 68, 2, Dilation.NONE), ModelTransform.of(17, -11, -16, 0.4363F, 0, 0.4363F)); + return TexturedModelData.of(modelData, 512, 256); + } + @Override public void setAngles(AirBalloonEntity entity, float tickDelta, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { - root.yaw = entity.getBodyYaw(); - burner.visible = entity.hasBurner(); - balloon.visible = entity.hasBalloon(); + inflation = entity.getInflation(tickDelta); + root.yaw = 0;//netHeadYaw; float xSpeed = (float)(entity.getX() - entity.prevX); + root.roll = MathHelper.sin(xSpeed); + float zSpeed = (float)(entity.getZ() - entity.prevZ); + root.pitch = MathHelper.sin(zSpeed); - root.pitch = MathHelper.sin(-xSpeed); + if (root.hasChild("burner")) { + boolean lifted = inflation > 0.8F; + root.pivotY = 32 * (1 - inflation); + root.pivotX = inflation * MathHelper.sin(limbSwingAmount + entity.age / 5F) / 4F; + root.getChild("burner").getChild("rope_a_r1").visible = lifted; + root.getChild("burner").getChild("rope_b_r1").visible = lifted; + root.getChild("burner").getChild("rope_c_r1").visible = lifted; + root.getChild("burner").getChild("rope_d_r1").visible = lifted; + } + if (root.hasChild("canopy")) { + root.pivotY = 0; + root.pivotX = inflation * MathHelper.cos(limbSwingAmount + entity.age / 5F) / 4F; + } } @Override public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float r, float g, float b, float a) { + matrices.push(); + if (root.hasChild("canopy")) { + matrices.translate(0, 1 * (1 - inflation), 0); + matrices.scale(1, MathHelper.lerp(inflation, -0.05F, 1), 1); + } root.render(matrices, vertexConsumer, light, overlay, r, g, b, a); + matrices.pop(); } } \ No newline at end of file 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 cbf2a7b0..4cfeeb37 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 @@ -1,25 +1,73 @@ package com.minelittlepony.unicopia.client.render.entity; +import java.util.function.Predicate; + import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.AirBalloonEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.entity.*; +import net.minecraft.client.render.entity.feature.FeatureRenderer; +import net.minecraft.client.render.entity.feature.FeatureRendererContext; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; +import net.minecraft.util.math.Box; 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); + super(context, new AirBalloonEntityModel(AirBalloonEntityModel.getBasketModelData().createModel()), 0); + addFeature(new BalloonFeature("burner", new AirBalloonEntityModel(AirBalloonEntityModel.getBurnerModelData().createModel()), this, AirBalloonEntity::hasBurner)); + addFeature(new BalloonFeature("canopy", new AirBalloonEntityModel(AirBalloonEntityModel.getCanopyModelData().createModel()), this, AirBalloonEntity::hasBalloon)); + } + + @Override + public void render(AirBalloonEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + super.render(entity, yaw, tickDelta, matrices, vertices, light); + + if (MinecraftClient.getInstance().getEntityRenderDispatcher().shouldRenderHitboxes() && !entity.isInvisible() && !MinecraftClient.getInstance().hasReducedDebugInfo()) { + for (Box box : entity.getBoundingBoxes()) { + WorldRenderer.drawBox(matrices, vertices.getBuffer(RenderLayer.getLines()), box.offset(entity.getPos().multiply(-1)), 1.0f, 1.0f, 1.0f, 1.0f); + } + } } @Override public Identifier getTexture(AirBalloonEntity entity) { - return TEXTURE; + return getComponentTexture(entity, "basket"); } @Override protected float getLyingAngle(AirBalloonEntity entity) { return 0; } + + private Identifier getComponentTexture(AirBalloonEntity entity, String componentName) { + return Unicopia.id("textures/entity/air_balloon/" + componentName + ".png"); + } + + final class BalloonFeature extends FeatureRenderer { + private final AirBalloonEntityModel model; + private final Predicate visibilityTest; + private final String componentName; + + public BalloonFeature(String componentName, AirBalloonEntityModel model, + FeatureRendererContext context, + Predicate visibilityTest) { + super(context); + this.componentName = componentName; + this.model = model; + this.visibilityTest = visibilityTest; + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, AirBalloonEntity entity, + float limbAngle, float limbDistance, float tickDelta, float animationProgress, float yaw, float pitch) { + if (visibilityTest.test(entity)) { + render(getModel(), model, getComponentTexture(entity, componentName), matrices, vertices, light, entity, limbAngle, limbDistance, 0, yaw, pitch, tickDelta, 1, 1, 1); + } + } + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java index 659ab5aa..5bb79eef 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java @@ -10,6 +10,7 @@ import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleTypes; import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.math.*; @@ -22,8 +23,8 @@ import java.util.List; import java.util.function.Consumer; import com.minelittlepony.unicopia.entity.collision.EntityCollisions; +import com.minelittlepony.unicopia.entity.collision.MultiBox; import com.minelittlepony.unicopia.entity.duck.EntityDuck; -import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.server.world.WeatherConditions; @@ -33,11 +34,16 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C private static final byte BURNER_ACTIVE = 4; private static final TrackedData FLAGS = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); private static final TrackedData BOOSTING = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData INFLATION = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); private boolean prevBoosting; + private int prevInflation; + private Vec3d oldPosition = Vec3d.ZERO; + private Vec3d manualVelocity = Vec3d.ZERO; public AirBalloonEntity(EntityType type, World world) { super(type, world); + setPersistent(); } @Override @@ -45,6 +51,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C super.initDataTracker(); dataTracker.startTracking(FLAGS, 0); dataTracker.startTracking(BOOSTING, 0); + dataTracker.startTracking(INFLATION, 0); } public boolean hasBalloon() { @@ -56,13 +63,29 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C } public boolean hasBurner() { - return getFlag((byte)(HAS_BURNER | HAS_BALLOON)); + return getFlag(HAS_BURNER); } public void setHasBurner(boolean hasBurner) { setFlag(HAS_BURNER, hasBurner); } + public float getInflation(float tickDelta) { + return MathHelper.lerp(tickDelta, prevInflation, getInflation()) / (float)getMaxInflation(); + } + + private void setInflation(int inflation) { + dataTracker.set(INFLATION, MathHelper.clamp(inflation, 0, getMaxInflation())); + } + + private int getInflation() { + return dataTracker.get(INFLATION); + } + + protected int getMaxInflation() { + return 100; + } + public boolean isBurnerActive() { return getFlag((byte)(HAS_BURNER | BURNER_ACTIVE | HAS_BALLOON)); } @@ -94,34 +117,52 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C @Override public List getBoundingBoxes() { - if (hasBalloon()) { - Box balloonBox = getBalloonBoundingBox(); - return List.of(getBoundingBox(), balloonBox.withMinY(balloonBox.minY - 0.5)); + if (hasBalloon() && getInflation(1) > 0.999F) { + return List.of(getInteriorBoundingBox(), getBalloonBoundingBox()); } - return List.of(getBoundingBox()); + return List.of(getInteriorBoundingBox()); } - private Vec3d oldPosition = Vec3d.ZERO; - private Vec3d oldServerPosition = Vec3d.ZERO; - @Override public void tick() { + this.shouldSave(); setAir(getMaxAir()); int boostTicks = getBoostTicks(); + int inflation = getInflation(); + prevInflation = inflation; + if (boostTicks > 0) { boostTicks--; + if (inflation < getMaxInflation()) { + boostTicks--; + } setBoostTicks(boostTicks); } - addVelocity(0, isBurnerActive() ? 0.005 : -0.03, 0); + boolean boosting = boostTicks > 0; - if (!isAirworthy() && isSubmergedInWater()) { - double yy = getVelocity().y; - setVelocity(getVelocity().multiply(0.9, 0.4, 0.9).add(0, Math.abs(yy) / 2F, 0)); + if (hasBurner() && isBurnerActive()) { + if (inflation < getMaxInflation()) { + inflation++; + if (boosting) { + inflation++; + } + setInflation(inflation); + } + } else { + if (inflation < getMaxInflation() && inflation > 0) { + setInflation(--inflation); + } } - boolean boosting = boostTicks > 0; + addVelocity(0, isBurnerActive() && inflation >= getMaxInflation() ? 0.005 : -0.013, 0); + addVelocity(manualVelocity.multiply(0.1)); + manualVelocity = manualVelocity.multiply(0.9); + + if (!isAirworthy() && isSubmergedInWater()) { + setVelocity(getVelocity().multiply(0.9, 0.4, 0.9).add(0, 0.02, 0)); + } Random rng = getWorld().random; @@ -139,7 +180,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C ); } } - } else { + } else if (inflation >= getMaxInflation()) { if (hasBurner() && isBurnerActive()) { addVelocity(WeatherConditions.getAirflow(getBlockPos(), getWorld()).multiply(0.2)); setVelocity(getVelocity().multiply(0.3, 1, 0.3)); @@ -170,25 +211,36 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C prevBoosting = boosting; oldPosition = getPos(); - oldServerPosition = LivingEntityDuck.serverPos(this); for (Box box : getBoundingBoxes()) { - for (Entity e : getWorld().getOtherEntities(this, box.expand(0, 0.5, 0))) { + for (Entity e : getWorld().getOtherEntities(this, box.expand(0, 0.5, 0).stretch(getVelocity().multiply(-1)))) { updatePassenger(e, box, e.getY() > getY() + 3); } } + + if (getFireTicks() > 0) { + setFireTicks(1); + } + super.tick(); + setBoundingBox(MultiBox.of(getBoundingBox(), getBoundingBoxes())); } private void updatePassenger(Entity e, Box box, boolean inBalloon) { - if (getVelocity().y > 0 && e.getBoundingBox().minY < box.maxY) { - e.setPos(e.getX(), box.maxY, e.getZ()); - } - if (getVelocity().y < 0 && e.getBoundingBox().minY > box.maxY) { - e.setPos(e.getX(), box.maxY, e.getZ()); + double height = box.getYLength(); + + if (height < 3 || e.getBoundingBox().minY > box.minY + height / 2D) { + if (getVelocity().y > 0 && e.getBoundingBox().minY < box.maxY + 0.02) { + e.setPos(e.getX(), box.maxY, e.getZ()); + } + if (getVelocity().y < 0 && e.getBoundingBox().minY > box.maxY) { + e.setPos(e.getX(), box.maxY, e.getZ()); + } } + e.setVelocity(e.getVelocity().multiply(0.1, 0.5, 0.1)); + if (inBalloon && !e.isSneaky() && Math.abs(e.getVelocity().y) > 0.079) { e.setVelocity(e.getVelocity().multiply(1, e.getVelocity().y < 0 ? -0.9 : 1.2, 1).add(0, 0.8, 0)); if (Math.abs(e.getVelocity().y) > 2) { @@ -198,11 +250,8 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C Living.getOrEmpty(e).ifPresent(living -> { living.setSupportingEntity(this); - living.setPositionOffset( - e.getPos().subtract(oldPosition), - LivingEntityDuck.serverPos(living.asEntity()).subtract(oldServerPosition) - ); - living.updateRelativePosition(); + living.setPositionOffset(e.getPos().subtract(oldPosition)); + living.updateRelativePosition(box); }); if (getWorld().isClient) { @@ -214,25 +263,43 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C } } + @Override + public ActionResult interactAt(PlayerEntity player, Vec3d hitPos, Hand hand) { + ItemStack stack = player.getStackInHand(hand); + + if (hitPos.y > (3 * getInflation(1))) { + if (hasBalloon() && hasBurner()) { + if (stack.isOf(Items.FLINT_AND_STEEL)) { + setBurnerActive(!isBurnerActive()); + if (isBurnerActive()) { + playSound(SoundEvents.ENTITY_GHAST_SHOOT, 1, 1); + } + stack.damage(1, player, p -> p.sendEquipmentBreakStatus(hand == Hand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND)); + playSound(SoundEvents.ITEM_FLINTANDSTEEL_USE, 1, 1); + return ActionResult.SUCCESS; + } + + if (stack.isEmpty() && Math.abs(hitPos.x) > 1 && Math.abs(hitPos.z) > 1) { + double xPush = Math.signum(hitPos.x); + double zPush = Math.signum(hitPos.z); + if (!getWorld().isClient) { + manualVelocity = manualVelocity.add(0.3 * xPush, 0, 0.3 * zPush); + } + } else if (stack.isEmpty() && isBurnerActive()) { + setBoostTicks(50); + } + } + } + + player.sendMessage(Text.literal(hitPos + "")); + return ActionResult.PASS; + } + @Override protected ActionResult interactMob(PlayerEntity player, Hand hand) { ItemStack stack = player.getStackInHand(hand); - if (hasBalloon() && hasBurner()) { - if (stack.isOf(Items.FLINT_AND_STEEL)) { - setBurnerActive(!isBurnerActive()); - if (isBurnerActive()) { - playSound(SoundEvents.ENTITY_GHAST_SHOOT, 1, 1); - } - stack.damage(1, player, p -> p.sendEquipmentBreakStatus(hand == Hand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND)); - playSound(SoundEvents.ITEM_FLINTANDSTEEL_USE, 1, 1); - return ActionResult.SUCCESS; - } - if (stack.isEmpty() && isBurnerActive()) { - setBoostTicks(50); - } - } if (stack.isOf(UItems.LARGE_BALLOON) && !hasBalloon()) { if (!player.getAbilities().creativeMode) { @@ -277,23 +344,27 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C @Override public Box getVisibilityBoundingBox() { if (hasBalloon()) { - return getBoundingBox().union(getBalloonBoundingBox()); + return MultiBox.unbox(getBoundingBox()).union(getBalloonBoundingBox()); } - return getBoundingBox(); + return MultiBox.unbox(getBoundingBox()); } protected Box getInteriorBoundingBox() { - return getBoundingBox().contract(0.7, 0, 0.7); + Box box = MultiBox.unbox(getBoundingBox()); + return box.withMinY(box.minY - 0.2).contract(0.2, 0, 0.2); } protected Box getBalloonBoundingBox() { - return getBoundingBox().offset(0.125, 11, 0).expand(2.25, 0, 2); + float inflation = getInflation(1); + return MultiBox.unbox(getBoundingBox()) + .offset(0.125, 7.3 * inflation, 0.125) + .expand(2.25, 3.7 * inflation, 2.25); } @Override public void getCollissionShapes(ShapeContext context, Consumer output) { - Box box = getBoundingBox().expand(0.3, 0, 0.3); + Box box = MultiBox.unbox(getBoundingBox()).expand(0.3, 0, 0.3); double wallheight = box.maxY + 0.7; double wallThickness = 0.7; @@ -313,9 +384,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C // top of balloon if (hasBalloon()) { - output.accept(VoxelShapes.cuboid( - getBoundingBox().offset(0.12, 7.5, 0.12).expand(2.4, 3.5, 2.4) - )); + output.accept(VoxelShapes.cuboid(getBalloonBoundingBox())); } } @@ -326,6 +395,8 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C setHasBurner(compound.getBoolean("hasBurner")); setBurnerActive(compound.getBoolean("burnerActive")); setBoostTicks(compound.getInt("boostTicks")); + prevInflation = compound.getInt("inflationAmount"); + setInflation(prevInflation); } @Override @@ -335,10 +406,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C compound.putBoolean("hasBurner", hasBurner()); compound.putBoolean("burnerActive", isBurnerActive()); compound.putInt("boostTicks", getBoostTicks()); - } - - static Vec3d getWind(World world, BlockPos pos) { - return Vec3d.ofCenter(pos).normalize().multiply(1, 0, 1).multiply(0.002);//.multiply((world.getRandom().nextFloat() - 0.5) * 0.2); + compound.putInt("inflationAmount", getInflation()); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java index a99aaed4..8dce0045 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java @@ -72,6 +72,12 @@ public abstract class Living implements Equine, Caste private boolean invisible = false; + @Nullable + private Entity supportingEntity; + + @Nullable + private Vec3d supportPositionOffset; + @Nullable private Caster attacker; @@ -173,33 +179,36 @@ public abstract class Living implements Equine, Caste return vehicle != null && getCarrierId().filter(vehicle.getUuid()::equals).isPresent(); } - @Nullable - private Entity supportingEntity; - @Nullable - private Vec3d supportPositionOffset; - @Nullable - private Vec3d serverPositionOffset; - public void setSupportingEntity(Entity supportingEntity) { this.supportingEntity = supportingEntity; } - public void setPositionOffset(@Nullable Vec3d positionOffset, @Nullable Vec3d serverPositionOffset) { + public void setPositionOffset(@Nullable Vec3d positionOffset) { this.supportPositionOffset = positionOffset; - this.serverPositionOffset = serverPositionOffset; } public void updatePositionOffset() { - setPositionOffset( - supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos()), - supportingEntity instanceof LivingEntity l ? LivingEntityDuck.serverPos(entity).subtract(LivingEntityDuck.serverPos(l)) : null - ); + setPositionOffset(supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos())); } - public void updateRelativePosition() { + public static void checkGroundCollission(Entity entity, Box box) { + double height = box.getYLength(); + + if (height < 3 || entity.getBoundingBox().minY > box.minY + height / 2D) { + if (entity.getBoundingBox().minY < box.maxY) { + entity.setPos(entity.getX(), box.maxY - 0.002, entity.getZ()); + } + if (entity.getBoundingBox().minY > box.maxY) { + entity.setPos(entity.getX(), box.maxY - 0.002, entity.getZ()); + } + } + } + + public void updateRelativePosition(Box box) { if (supportingEntity == null || supportPositionOffset == null) { return; } + Vec3d newPos = supportingEntity.getPos().add(supportPositionOffset); Vec3d posChange = entity.getPos().subtract(newPos); entity.setPosition(newPos); @@ -231,33 +240,36 @@ public abstract class Living implements Equine, Caste entity.setOnGround(true); entity.verticalCollision = true; entity.groundCollision = true; - //entity.distanceTraveled = 0; entity.fallDistance = 0; } @Override public boolean beforeUpdate() { + updateSupportingEntity(); + return false; + } + + public void updateSupportingEntity() { if (supportingEntity != null) { Box ownBox = entity.getBoundingBox().expand(0.1); - if (MultiBoundingBoxEntity.getBoundingBoxes(supportingEntity).stream().noneMatch(box -> { + + MultiBoundingBoxEntity.getBoundingBoxes(supportingEntity).stream().filter(box -> { return box.expand(0, 0.5, 0).intersects(ownBox); - })) { + }).findFirst().ifPresentOrElse(box -> { + if (supportPositionOffset == null) { + updatePositionOffset(); + } else { + updateRelativePosition(box); + } + entity.setOnGround(true); + entity.verticalCollision = true; + entity.groundCollision = true; + }, () -> { supportingEntity = null; supportPositionOffset = null; - } - } - if (supportingEntity != null) { - if (supportPositionOffset == null) { - updatePositionOffset(); - } else { - updateRelativePosition(); - } - entity.setOnGround(true); - entity.verticalCollision = true; - entity.groundCollision = true; + }); } - return false; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java index 0e142aa8..2f242f95 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(2.5F, 0.1F))); + .dimensions(EntityDimensions.changing(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/collision/MultiBox.java b/src/main/java/com/minelittlepony/unicopia/entity/collision/MultiBox.java new file mode 100644 index 00000000..921cafbe --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/collision/MultiBox.java @@ -0,0 +1,156 @@ +package com.minelittlepony.unicopia.entity.collision; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import com.google.common.base.Suppliers; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; + +public final class MultiBox extends Box { + private final Box first; + private final BoxChildren children; + + public static MultiBox of(Box first, List children) { + return new MultiBox(first, new BoxChildren(children)); + } + + public static Box unbox(Box box) { + return box instanceof MultiBox m ? m.first : box; + } + + private MultiBox(Box first, BoxChildren children) { + super(first.minX, first.minY, first.minZ, first.maxX, first.maxY, first.maxZ); + this.first = unbox(first); + this.children = children; + } + + @Override + public Optional raycast(Vec3d min, Vec3d max) { + return first.raycast(min, max).or(() -> children.raycast(min, max)); + } + + @Override + public boolean contains(double x, double y, double z) { + return super.contains(x, y, z) || children.contains(x, y, z); + } + + @Override + public Box withMinX(double minX) { + return new MultiBox(super.withMinX(minX), children); + } + + @Override + public Box withMinY(double minY) { + return new MultiBox(super.withMinY(minY), children); + } + + @Override + public Box withMinZ(double minZ) { + return new MultiBox(super.withMinZ(minZ), children); + } + + @Override + public Box withMaxX(double maxX) { + return new MultiBox(super.withMaxX(maxX), children); + } + + @Override + public Box withMaxY(double maxY) { + return new MultiBox(super.withMaxY(maxY), children); + } + + @Override + public Box withMaxZ(double maxZ) { + return new MultiBox(super.withMaxZ(maxZ), children); + } + + @Override + public Box shrink(double x, double y, double z) { + return new MultiBox(super.shrink(x, y, z), children.altered(b -> b.shrink(x, y, z))); + } + + @Override + public Box stretch(double x, double y, double z) { + return new MultiBox(super.stretch(x, y, z), children.altered(b -> b.stretch(x, y, z))); + } + + @Override + public Box expand(double x, double y, double z) { + return new MultiBox(super.expand(x, y, z), children.altered(b -> b.expand(x, y, z))); + } + + @Override + public Box intersection(Box box) { + return new MultiBox(super.intersection(box), children); + } + + @Override + public Box union(Box box) { + return new MultiBox(super.union(box), children); + } + + @Override + public Box offset(double x, double y, double z) { + return new MultiBox(super.offset(x, y, z), children.altered(b -> b.offset(x, y, z))); + } + + @Override + public Box offset(BlockPos blockPos) { + return new MultiBox(super.offset(blockPos), children.altered(b -> b.offset(blockPos))); + } + + @Override + public String toString() { + return "MULTI_AABB[" + this.minX + ", " + this.minY + ", " + this.minZ + "] -> [" + this.maxX + ", " + this.maxY + ", " + this.maxZ + "]{" + children + "}"; + } + + static final class BoxChildren { + private final Box[] children; + private final Supplier toString; + + private BoxChildren(Box[] children) { + this.children = children; + toString = Suppliers.memoize(() -> Arrays.stream(this.children).map(Box::toString).collect(Collectors.joining(","))); + } + + public BoxChildren(List children) { + this(children.stream().map(MultiBox::unbox).toArray(Box[]::new)); + } + + public BoxChildren altered(Function alteration) { + BoxChildren copy = new BoxChildren(new Box[children.length]); + for (int i = 0; i < children.length; i++) { + copy.children[i] = alteration.apply(children[i]); + } + return copy; + } + + public Optional raycast(Vec3d min, Vec3d max) { + Optional trace = Optional.empty(); + for (int i = 0; trace.isEmpty() && i < children.length; i++) { + trace = children[i].raycast(min, max); + } + return trace; + } + + public boolean contains(double x, double y, double z) { + for (int i = 0; i < children.length; i++) { + if (children[i].contains(x, y, z)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return toString.get(); + } + } +} diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon.png deleted file mode 100644 index 141d1a73..00000000 Binary files a/src/main/resources/assets/unicopia/textures/entity/air_balloon.png and /dev/null differ diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon/basket.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon/basket.png new file mode 100644 index 00000000..0d1de1c9 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/air_balloon/basket.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon/burner.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon/burner.png new file mode 100644 index 00000000..3fd93fd2 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/air_balloon/burner.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy.png new file mode 100644 index 00000000..526d6d66 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy.png differ