Add some animations and controls for steering the hot air balloon

This commit is contained in:
Sollace 2023-08-12 15:59:13 +01:00
parent 2fd8d0c42f
commit f7777358e2
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
11 changed files with 423 additions and 111 deletions

View file

@ -112,7 +112,7 @@ public class WorldRenderDelegate {
return true; return true;
} }
pony.updateRelativePosition(); pony.updateSupportingEntity();
matrices.push(); matrices.push();

View file

@ -12,31 +12,15 @@ public class AirBalloonEntityModel extends EntityModel<AirBalloonEntity> {
private final ModelPart root; private final ModelPart root;
private final ModelPart burner; private float inflation;
private final ModelPart balloon;
public AirBalloonEntityModel(ModelPart root) { public AirBalloonEntityModel(ModelPart root) {
this.root = root; this.root = root;
burner = root.getChild("burner");
balloon = root.getChild("balloon");
} }
public static TexturedModelData getTexturedModelData() { public static TexturedModelData getBasketModelData() {
ModelData modelData = new ModelData(); ModelData modelData = new ModelData();
ModelPartData root = modelData.getRoot(); 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)); 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) 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) .uv(64, 68).cuboid(15, -12, -16, 2, 11, 30, Dilation.NONE)
@ -48,21 +32,65 @@ public class AirBalloonEntityModel extends EntityModel<AirBalloonEntity> {
.uv(80, 32).cuboid(-17, -13, -18, 34, 2, 4, Dilation.NONE) .uv(80, 32).cuboid(-17, -13, -18, 34, 2, 4, Dilation.NONE)
.uv(0, 19).cuboid(7, -13, 12, 10, 2, 4, Dilation.NONE) .uv(0, 19).cuboid(7, -13, 12, 10, 2, 4, Dilation.NONE)
.uv(0, 0).cuboid(-17, -13, 12, 10, 2, 4, Dilation.NONE), ModelTransform.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 @Override
public void setAngles(AirBalloonEntity entity, float tickDelta, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { public void setAngles(AirBalloonEntity entity, float tickDelta, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
root.yaw = entity.getBodyYaw(); inflation = entity.getInflation(tickDelta);
burner.visible = entity.hasBurner();
balloon.visible = entity.hasBalloon();
root.yaw = 0;//netHeadYaw;
float xSpeed = (float)(entity.getX() - entity.prevX); 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 @Override
public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float r, float g, float b, float a) { 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); root.render(matrices, vertexConsumer, light, overlay, r, g, b, a);
matrices.pop();
} }
} }

View file

@ -1,25 +1,73 @@
package com.minelittlepony.unicopia.client.render.entity; package com.minelittlepony.unicopia.client.render.entity;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.entity.AirBalloonEntity; 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.*;
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.Identifier;
import net.minecraft.util.math.Box;
public class AirBalloonEntityRenderer extends MobEntityRenderer<AirBalloonEntity, AirBalloonEntityModel> { public class AirBalloonEntityRenderer extends MobEntityRenderer<AirBalloonEntity, AirBalloonEntityModel> {
private static final Identifier TEXTURE = Unicopia.id("textures/entity/air_balloon.png");
public AirBalloonEntityRenderer(EntityRendererFactory.Context context) { 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 @Override
public Identifier getTexture(AirBalloonEntity entity) { public Identifier getTexture(AirBalloonEntity entity) {
return TEXTURE; return getComponentTexture(entity, "basket");
} }
@Override @Override
protected float getLyingAngle(AirBalloonEntity entity) { protected float getLyingAngle(AirBalloonEntity entity) {
return 0; return 0;
} }
private Identifier getComponentTexture(AirBalloonEntity entity, String componentName) {
return Unicopia.id("textures/entity/air_balloon/" + componentName + ".png");
}
final class BalloonFeature extends FeatureRenderer<AirBalloonEntity, AirBalloonEntityModel> {
private final AirBalloonEntityModel model;
private final Predicate<AirBalloonEntity> visibilityTest;
private final String componentName;
public BalloonFeature(String componentName, AirBalloonEntityModel model,
FeatureRendererContext<AirBalloonEntity, AirBalloonEntityModel> context,
Predicate<AirBalloonEntity> 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);
}
}
}
} }

View file

@ -10,6 +10,7 @@ import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes; import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.*; import net.minecraft.util.math.*;
@ -22,8 +23,8 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.minelittlepony.unicopia.entity.collision.EntityCollisions; 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.EntityDuck;
import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.server.world.WeatherConditions; 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 byte BURNER_ACTIVE = 4;
private static final TrackedData<Integer> FLAGS = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); private static final TrackedData<Integer> FLAGS = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
private static final TrackedData<Integer> BOOSTING = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER); private static final TrackedData<Integer> BOOSTING = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
private static final TrackedData<Integer> INFLATION = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
private boolean prevBoosting; private boolean prevBoosting;
private int prevInflation;
private Vec3d oldPosition = Vec3d.ZERO;
private Vec3d manualVelocity = Vec3d.ZERO;
public AirBalloonEntity(EntityType<? extends AirBalloonEntity> type, World world) { public AirBalloonEntity(EntityType<? extends AirBalloonEntity> type, World world) {
super(type, world); super(type, world);
setPersistent();
} }
@Override @Override
@ -45,6 +51,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
super.initDataTracker(); super.initDataTracker();
dataTracker.startTracking(FLAGS, 0); dataTracker.startTracking(FLAGS, 0);
dataTracker.startTracking(BOOSTING, 0); dataTracker.startTracking(BOOSTING, 0);
dataTracker.startTracking(INFLATION, 0);
} }
public boolean hasBalloon() { public boolean hasBalloon() {
@ -56,13 +63,29 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
} }
public boolean hasBurner() { public boolean hasBurner() {
return getFlag((byte)(HAS_BURNER | HAS_BALLOON)); return getFlag(HAS_BURNER);
} }
public void setHasBurner(boolean hasBurner) { public void setHasBurner(boolean hasBurner) {
setFlag(HAS_BURNER, 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() { public boolean isBurnerActive() {
return getFlag((byte)(HAS_BURNER | BURNER_ACTIVE | HAS_BALLOON)); return getFlag((byte)(HAS_BURNER | BURNER_ACTIVE | HAS_BALLOON));
} }
@ -94,34 +117,52 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
@Override @Override
public List<Box> getBoundingBoxes() { public List<Box> getBoundingBoxes() {
if (hasBalloon()) { if (hasBalloon() && getInflation(1) > 0.999F) {
Box balloonBox = getBalloonBoundingBox(); return List.of(getInteriorBoundingBox(), getBalloonBoundingBox());
return List.of(getBoundingBox(), balloonBox.withMinY(balloonBox.minY - 0.5));
} }
return List.of(getBoundingBox()); return List.of(getInteriorBoundingBox());
} }
private Vec3d oldPosition = Vec3d.ZERO;
private Vec3d oldServerPosition = Vec3d.ZERO;
@Override @Override
public void tick() { public void tick() {
this.shouldSave();
setAir(getMaxAir()); setAir(getMaxAir());
int boostTicks = getBoostTicks(); int boostTicks = getBoostTicks();
int inflation = getInflation();
prevInflation = inflation;
if (boostTicks > 0) { if (boostTicks > 0) {
boostTicks--; boostTicks--;
if (inflation < getMaxInflation()) {
boostTicks--;
}
setBoostTicks(boostTicks); setBoostTicks(boostTicks);
} }
addVelocity(0, isBurnerActive() ? 0.005 : -0.03, 0); boolean boosting = boostTicks > 0;
if (!isAirworthy() && isSubmergedInWater()) { if (hasBurner() && isBurnerActive()) {
double yy = getVelocity().y; if (inflation < getMaxInflation()) {
setVelocity(getVelocity().multiply(0.9, 0.4, 0.9).add(0, Math.abs(yy) / 2F, 0)); 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; Random rng = getWorld().random;
@ -139,7 +180,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
); );
} }
} }
} else { } else if (inflation >= getMaxInflation()) {
if (hasBurner() && isBurnerActive()) { if (hasBurner() && isBurnerActive()) {
addVelocity(WeatherConditions.getAirflow(getBlockPos(), getWorld()).multiply(0.2)); addVelocity(WeatherConditions.getAirflow(getBlockPos(), getWorld()).multiply(0.2));
setVelocity(getVelocity().multiply(0.3, 1, 0.3)); setVelocity(getVelocity().multiply(0.3, 1, 0.3));
@ -170,25 +211,36 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
prevBoosting = boosting; prevBoosting = boosting;
oldPosition = getPos(); oldPosition = getPos();
oldServerPosition = LivingEntityDuck.serverPos(this);
for (Box box : getBoundingBoxes()) { 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); updatePassenger(e, box, e.getY() > getY() + 3);
} }
} }
if (getFireTicks() > 0) {
setFireTicks(1);
}
super.tick(); super.tick();
setBoundingBox(MultiBox.of(getBoundingBox(), getBoundingBoxes()));
} }
private void updatePassenger(Entity e, Box box, boolean inBalloon) { private void updatePassenger(Entity e, Box box, boolean inBalloon) {
if (getVelocity().y > 0 && e.getBoundingBox().minY < box.maxY) { double height = box.getYLength();
e.setPos(e.getX(), box.maxY, e.getZ());
} if (height < 3 || e.getBoundingBox().minY > box.minY + height / 2D) {
if (getVelocity().y < 0 && e.getBoundingBox().minY > box.maxY) { if (getVelocity().y > 0 && e.getBoundingBox().minY < box.maxY + 0.02) {
e.setPos(e.getX(), box.maxY, e.getZ()); 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) { 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)); 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) { 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.getOrEmpty(e).ifPresent(living -> {
living.setSupportingEntity(this); living.setSupportingEntity(this);
living.setPositionOffset( living.setPositionOffset(e.getPos().subtract(oldPosition));
e.getPos().subtract(oldPosition), living.updateRelativePosition(box);
LivingEntityDuck.serverPos(living.asEntity()).subtract(oldServerPosition)
);
living.updateRelativePosition();
}); });
if (getWorld().isClient) { 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 @Override
protected ActionResult interactMob(PlayerEntity player, Hand hand) { protected ActionResult interactMob(PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(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 (stack.isOf(UItems.LARGE_BALLOON) && !hasBalloon()) {
if (!player.getAbilities().creativeMode) { if (!player.getAbilities().creativeMode) {
@ -277,23 +344,27 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
@Override @Override
public Box getVisibilityBoundingBox() { public Box getVisibilityBoundingBox() {
if (hasBalloon()) { if (hasBalloon()) {
return getBoundingBox().union(getBalloonBoundingBox()); return MultiBox.unbox(getBoundingBox()).union(getBalloonBoundingBox());
} }
return getBoundingBox(); return MultiBox.unbox(getBoundingBox());
} }
protected Box getInteriorBoundingBox() { 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() { 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 @Override
public void getCollissionShapes(ShapeContext context, Consumer<VoxelShape> output) { public void getCollissionShapes(ShapeContext context, Consumer<VoxelShape> 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 wallheight = box.maxY + 0.7;
double wallThickness = 0.7; double wallThickness = 0.7;
@ -313,9 +384,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
// top of balloon // top of balloon
if (hasBalloon()) { if (hasBalloon()) {
output.accept(VoxelShapes.cuboid( output.accept(VoxelShapes.cuboid(getBalloonBoundingBox()));
getBoundingBox().offset(0.12, 7.5, 0.12).expand(2.4, 3.5, 2.4)
));
} }
} }
@ -326,6 +395,8 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
setHasBurner(compound.getBoolean("hasBurner")); setHasBurner(compound.getBoolean("hasBurner"));
setBurnerActive(compound.getBoolean("burnerActive")); setBurnerActive(compound.getBoolean("burnerActive"));
setBoostTicks(compound.getInt("boostTicks")); setBoostTicks(compound.getInt("boostTicks"));
prevInflation = compound.getInt("inflationAmount");
setInflation(prevInflation);
} }
@Override @Override
@ -335,10 +406,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
compound.putBoolean("hasBurner", hasBurner()); compound.putBoolean("hasBurner", hasBurner());
compound.putBoolean("burnerActive", isBurnerActive()); compound.putBoolean("burnerActive", isBurnerActive());
compound.putInt("boostTicks", getBoostTicks()); compound.putInt("boostTicks", getBoostTicks());
} compound.putInt("inflationAmount", getInflation());
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);
} }
} }

View file

@ -72,6 +72,12 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
private boolean invisible = false; private boolean invisible = false;
@Nullable
private Entity supportingEntity;
@Nullable
private Vec3d supportPositionOffset;
@Nullable @Nullable
private Caster<?> attacker; private Caster<?> attacker;
@ -173,33 +179,36 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
return vehicle != null && getCarrierId().filter(vehicle.getUuid()::equals).isPresent(); 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) { public void setSupportingEntity(Entity supportingEntity) {
this.supportingEntity = supportingEntity; this.supportingEntity = supportingEntity;
} }
public void setPositionOffset(@Nullable Vec3d positionOffset, @Nullable Vec3d serverPositionOffset) { public void setPositionOffset(@Nullable Vec3d positionOffset) {
this.supportPositionOffset = positionOffset; this.supportPositionOffset = positionOffset;
this.serverPositionOffset = serverPositionOffset;
} }
public void updatePositionOffset() { public void updatePositionOffset() {
setPositionOffset( setPositionOffset(supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos()));
supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos()),
supportingEntity instanceof LivingEntity l ? LivingEntityDuck.serverPos(entity).subtract(LivingEntityDuck.serverPos(l)) : null
);
} }
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) { if (supportingEntity == null || supportPositionOffset == null) {
return; return;
} }
Vec3d newPos = supportingEntity.getPos().add(supportPositionOffset); Vec3d newPos = supportingEntity.getPos().add(supportPositionOffset);
Vec3d posChange = entity.getPos().subtract(newPos); Vec3d posChange = entity.getPos().subtract(newPos);
entity.setPosition(newPos); entity.setPosition(newPos);
@ -231,33 +240,36 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
entity.setOnGround(true); entity.setOnGround(true);
entity.verticalCollision = true; entity.verticalCollision = true;
entity.groundCollision = true; entity.groundCollision = true;
//entity.distanceTraveled = 0;
entity.fallDistance = 0; entity.fallDistance = 0;
} }
@Override @Override
public boolean beforeUpdate() { public boolean beforeUpdate() {
updateSupportingEntity();
return false;
}
public void updateSupportingEntity() {
if (supportingEntity != null) { if (supportingEntity != null) {
Box ownBox = entity.getBoundingBox().expand(0.1); 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); 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; supportingEntity = null;
supportPositionOffset = null; supportPositionOffset = null;
} });
}
if (supportingEntity != null) {
if (supportPositionOffset == null) {
updatePositionOffset();
} else {
updateRelativePosition();
}
entity.setOnGround(true);
entity.verticalCollision = true;
entity.groundCollision = true;
} }
return false;
} }
@Override @Override

View file

@ -49,7 +49,7 @@ public interface UEntities {
.dimensions(EntityDimensions.fixed(0.9F, 0.5F))); .dimensions(EntityDimensions.fixed(0.9F, 0.5F)));
EntityType<AirBalloonEntity> AIR_BALLOON = register("air_balloon", FabricEntityTypeBuilder.create(SpawnGroup.MISC, AirBalloonEntity::new) EntityType<AirBalloonEntity> AIR_BALLOON = register("air_balloon", FabricEntityTypeBuilder.create(SpawnGroup.MISC, AirBalloonEntity::new)
.trackRangeBlocks(1000) .trackRangeBlocks(1000)
.dimensions(EntityDimensions.fixed(2.5F, 0.1F))); .dimensions(EntityDimensions.changing(2.5F, 0.1F)));
static <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) { static <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) {
EntityType<T> type = builder.build(); EntityType<T> type = builder.build();

View file

@ -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<Box> 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<Vec3d> 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<String> toString;
private BoxChildren(Box[] children) {
this.children = children;
toString = Suppliers.memoize(() -> Arrays.stream(this.children).map(Box::toString).collect(Collectors.joining(",")));
}
public BoxChildren(List<Box> children) {
this(children.stream().map(MultiBox::unbox).toArray(Box[]::new));
}
public BoxChildren altered(Function<Box, Box> 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<Vec3d> raycast(Vec3d min, Vec3d max) {
Optional<Vec3d> 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();
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB