mirror of
https://github.com/Sollace/Unicopia.git
synced 2025-01-31 19:16:44 +01:00
Add some animations and controls for steering the hot air balloon
This commit is contained in:
parent
2fd8d0c42f
commit
f7777358e2
11 changed files with 423 additions and 111 deletions
|
@ -112,7 +112,7 @@ public class WorldRenderDelegate {
|
|||
return true;
|
||||
}
|
||||
|
||||
pony.updateRelativePosition();
|
||||
pony.updateSupportingEntity();
|
||||
|
||||
matrices.push();
|
||||
|
||||
|
|
|
@ -12,31 +12,15 @@ public class AirBalloonEntityModel extends EntityModel<AirBalloonEntity> {
|
|||
|
||||
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<AirBalloonEntity> {
|
|||
.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();
|
||||
}
|
||||
}
|
|
@ -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<AirBalloonEntity, AirBalloonEntityModel> {
|
||||
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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<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> 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<? extends AirBalloonEntity> 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<Box> 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<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 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,12 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, 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<T extends LivingEntity> implements Equine<T>, 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<T extends LivingEntity> implements Equine<T>, 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
|
||||
|
|
|
@ -49,7 +49,7 @@ public interface UEntities {
|
|||
.dimensions(EntityDimensions.fixed(0.9F, 0.5F)));
|
||||
EntityType<AirBalloonEntity> 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 <T extends Entity> EntityType<T> register(String name, FabricEntityTypeBuilder<T> builder) {
|
||||
EntityType<T> type = builder.build();
|
||||
|
|
|
@ -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 |
Loading…
Reference in a new issue