superb-warfare/src/main/java/com/atsuishio/superbwarfare/entity/vehicle/VehicleEntity.java
2025-01-07 19:32:27 +08:00

408 lines
16 KiB
Java

package com.atsuishio.superbwarfare.entity.vehicle;
import com.atsuishio.superbwarfare.init.ModDamageTypes;
import com.atsuishio.superbwarfare.init.ModItems;
import com.atsuishio.superbwarfare.init.ModParticleTypes;
import com.atsuishio.superbwarfare.init.ModSounds;
import com.atsuishio.superbwarfare.item.ContainerBlockItem;
import com.atsuishio.superbwarfare.tools.ParticleTool;
import com.google.common.collect.Lists;
import com.mojang.math.Axis;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageTypes;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ThrownPotion;
import net.minecraft.world.entity.vehicle.DismountHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.joml.Math;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import java.util.ArrayList;
public class VehicleEntity extends Entity {
public static final EntityDataAccessor<Float> HEALTH = SynchedEntityData.defineId(VehicleEntity.class, EntityDataSerializers.FLOAT);
protected static final EntityDataAccessor<String> LAST_ATTACKER_UUID = SynchedEntityData.defineId(VehicleEntity.class, EntityDataSerializers.STRING);
protected int interpolationSteps;
protected double x;
protected double y;
protected double z;
protected double serverYRot;
protected double serverXRot;
public float roll;
public float prevRoll;
public int lastHurtTick;
public boolean crash;
public float getRoll() {
return roll;
}
public float getRoll(float tickDelta) {
return Mth.lerp(0.6f * tickDelta, prevRoll, getRoll());
}
public float getYaw(float tickDelta) {
return Mth.lerp(0.6f * tickDelta, yRotO, getYRot());
}
public float getPitch(float tickDelta) {
return Mth.lerp(0.6f * tickDelta, xRotO, getXRot());
}
public void setZRot(float rot) {
roll = rot;
}
public VehicleEntity(EntityType<?> pEntityType, Level pLevel) {
super(pEntityType, pLevel);
this.setHealth(this.getMaxHealth());
}
@Override
protected void defineSynchedData() {
this.entityData.define(HEALTH, this.getMaxHealth());
this.entityData.define(LAST_ATTACKER_UUID, "undefined");
}
@Override
protected void readAdditionalSaveData(CompoundTag compound) {
this.entityData.set(LAST_ATTACKER_UUID, compound.getString("LastAttacker"));
this.entityData.set(HEALTH, compound.getFloat("Health"));
}
@Override
public void addAdditionalSaveData(CompoundTag compound) {
compound.putFloat("Health", this.entityData.get(HEALTH));
compound.putString("LastAttacker", this.entityData.get(LAST_ATTACKER_UUID));
}
@Override
public InteractionResult interact(Player player, InteractionHand hand) {
if (player.getVehicle() == this) return InteractionResult.PASS;
ItemStack stack = player.getMainHandItem();
if (player.isShiftKeyDown() && stack.is(ModItems.CROWBAR.get())) {
ItemStack container = ContainerBlockItem.createInstance(this);
if (!player.addItem(container)) {
player.drop(container, false);
}
this.remove(RemovalReason.DISCARDED);
this.discard();
return InteractionResult.SUCCESS;
} else if (this.getHealth() < this.getMaxHealth() && stack.is(Items.IRON_INGOT)) {
this.heal(Math.min(50, this.getMaxHealth()));
stack.shrink(1);
if (!this.level().isClientSide) {
this.level().playSound(null, this, SoundEvents.IRON_GOLEM_REPAIR, this.getSoundSource(), 0.5f, 1);
}
return InteractionResult.SUCCESS;
} else if (!player.isShiftKeyDown()) {
if (this.getFirstPassenger() == null) {
player.setXRot(this.getXRot());
player.setYRot(this.getYRot());
return player.startRiding(this) ? InteractionResult.CONSUME : InteractionResult.PASS;
} else if (!(this.getFirstPassenger() instanceof Player)) {
this.getFirstPassenger().stopRiding();
player.setXRot(this.getXRot());
player.setYRot(this.getYRot());
return player.startRiding(this) ? InteractionResult.CONSUME : InteractionResult.PASS;
}
if (this.getPassengers().size() < this.getMaxPassengers()) {
return player.startRiding(this) ? InteractionResult.CONSUME : InteractionResult.PASS;
}
}
return InteractionResult.PASS;
}
@Override
public boolean hurt(DamageSource source, float amount) {
if (source.getDirectEntity() instanceof ThrownPotion || source.getDirectEntity() instanceof AreaEffectCloud)
return false;
if (source.is(DamageTypes.FALL))
return false;
if (source.is(DamageTypes.CACTUS))
return false;
if (source.is(DamageTypes.DROWN))
return false;
if (source.is(DamageTypes.LIGHTNING_BOLT))
return false;
if (source.is(DamageTypes.FALLING_ANVIL))
return false;
if (source.is(DamageTypes.DRAGON_BREATH))
return false;
if (source.is(DamageTypes.WITHER))
return false;
if (source.is(DamageTypes.WITHER_SKULL))
return false;
if (source.is(DamageTypes.ON_FIRE))
return false;
if (source.is(DamageTypes.IN_FIRE))
return false;
if (source.is(ModDamageTypes.VEHICLE_STRIKE)) {
amount -= 20;
crash = true;
} else {
crash = false;
}
if (source.getEntity() != null) {
this.entityData.set(LAST_ATTACKER_UUID, source.getEntity().getStringUUID());
}
lastHurtTick = 0;
return super.hurt(source, amount);
}
public void heal(float pHealAmount) {
if (this.level() instanceof ServerLevel) {
this.setHealth(this.getHealth() + pHealAmount);
}
}
public void hurt(float pHealAmount) {
if (this.level() instanceof ServerLevel) {
this.setHealth(this.getHealth() - pHealAmount);
}
}
public float getHealth() {
return this.entityData.get(HEALTH);
}
public void setHealth(float pHealth) {
this.entityData.set(HEALTH, Mth.clamp(pHealth, 0.0F, this.getMaxHealth()));
}
public float getMaxHealth() {
return 50;
}
@Override
public boolean canBeCollidedWith() {
return true;
}
@Override
public boolean isPushable() {
return super.isPushable();
}
@Override
public boolean isPickable() {
return !this.isRemoved();
}
@Override
public boolean skipAttackInteraction(@NotNull Entity attacker) {
return hasPassenger(attacker) || super.skipAttackInteraction(attacker);
}
@Override
protected boolean canAddPassenger(Entity pPassenger) {
return this.getPassengers().size() < this.getMaxPassengers();
}
public int getMaxPassengers() {
return 1;
}
@Override
public void baseTick() {
super.baseTick();
lastHurtTick ++;
prevRoll = this.getRoll();
setZRot(roll * 0.9f);
float delta = Math.abs(getYRot() - yRotO);
while (getYRot() > 180F) {
setYRot(getYRot() - 360F);
yRotO = getYRot() - delta;
}
while (getYRot() <= -180F) {
setYRot(getYRot() + 360F);
yRotO = delta + getYRot();
}
handleClientSync();
if (this.level() instanceof ServerLevel && this.getHealth() <= 0) {
destroy();
}
travel();
if (this.getHealth() <= 0.1 * this.getMaxHealth()) {
this.hurt(0.1f);
} else {
this.heal(0.05f);
}
this.refreshDimensions();
}
public void lowHealthWarning() {
if (this.getHealth() <= 0.4 * this.getMaxHealth()) {
if (this.level() instanceof ServerLevel serverLevel) {
ParticleTool.sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 2, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
}
}
if (this.getHealth() <= 0.25 * this.getMaxHealth()) {
if (this.level() instanceof ServerLevel serverLevel) {
ParticleTool.sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 1, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
ParticleTool.sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 1, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
}
}
if (this.getHealth() <= 0.15 * this.getMaxHealth()) {
if (this.level() instanceof ServerLevel serverLevel) {
ParticleTool.sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 1, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
ParticleTool.sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 1, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
}
}
if (this.getHealth() <= 0.1 * this.getMaxHealth()) {
if (this.level() instanceof ServerLevel serverLevel) {
ParticleTool.sendParticle(serverLevel, ParticleTypes.LARGE_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 2, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
ParticleTool.sendParticle(serverLevel, ParticleTypes.CAMPFIRE_COSY_SMOKE, this.getX(), this.getY() + 0.7f * getBbHeight(), this.getZ(), 2, 0.35 * this.getBbWidth(), 0.15 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.01, false);
ParticleTool.sendParticle(serverLevel, ParticleTypes.FLAME, this.getX(), this.getY() + 0.85f * getBbHeight(), this.getZ(), 4, 0.35 * this.getBbWidth(), 0.12 * this.getBbHeight(), 0.35 * this.getBbWidth(), 0.05, false);
ParticleTool.sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), this.getX(), this.getY() + 0.85f * getBbHeight(), this.getZ(), 4, 0.1 * this.getBbWidth(), 0.05 * this.getBbHeight(), 0.1 * this.getBbWidth(), 0.4, false);
}
if (this.tickCount % 15 == 0) {
this.level().playSound(null, this.getOnPos(), SoundEvents.FIRE_AMBIENT, SoundSource.PLAYERS, 1, 1);
}
}
if (this.getHealth() < 0.1f * this.getMaxHealth() && tickCount %13 == 0) {
this.level().playSound(null, this.getOnPos(), ModSounds.NO_HEALTH.get(), SoundSource.PLAYERS, 1, 1);
} else if (this.getHealth() >= 0.1f && this.getHealth() < 0.4f * this.getMaxHealth() && tickCount %10 == 0) {
this.level().playSound(null, this.getOnPos(), ModSounds.LOW_HEALTH.get(), SoundSource.PLAYERS, 1, 1);
}
}
public void destroy() {
}
public void travel() {
}
// From Immersive_Aircraft
public Matrix4f getVehicleTransform() {
Matrix4f transform = new Matrix4f();
transform.translate((float) getX(), (float) getY(), (float) getZ());
transform.rotate(Axis.YP.rotationDegrees(-getYRot()));
transform.rotate(Axis.XP.rotationDegrees(getXRot()));
transform.rotate(Axis.ZP.rotationDegrees(getRoll()));
return transform;
}
public Vector4f transformPosition(Matrix4f transform, float x, float y, float z) {
return transform.transform(new Vector4f(x, y, z, 1));
}
protected void handleClientSync() {
if (isControlledByLocalInstance()) {
interpolationSteps = 0;
syncPacketPositionCodec(getX(), getY(), getZ());
}
if (interpolationSteps <= 0) {
return;
}
double interpolatedX = getX() + (x - getX()) / (double) interpolationSteps;
double interpolatedY = getY() + (y - getY()) / (double) interpolationSteps;
double interpolatedZ = getZ() + (z - getZ()) / (double) interpolationSteps;
double interpolatedYaw = Mth.wrapDegrees(serverYRot - (double) getYRot());
setYRot(getYRot() + (float) interpolatedYaw / (float) interpolationSteps);
setXRot(getXRot() + (float) (serverXRot - (double) getXRot()) / (float) interpolationSteps);
setPos(interpolatedX, interpolatedY, interpolatedZ);
setRot(getYRot(), getXRot());
--interpolationSteps;
}
@Override
public void lerpTo(double x, double y, double z, float yaw, float pitch, int interpolationSteps, boolean interpolate) {
this.x = x;
this.y = y;
this.z = z;
serverYRot = yaw;
serverXRot = pitch;
this.interpolationSteps = 10;
}
public static double calculateAngle(Vec3 move, Vec3 view) {
move = move.multiply(1, 0, 1).normalize();
view = view.multiply(1, 0, 1).normalize();
double startLength = move.length();
double endLength = view.length();
if (startLength > 0.0D && endLength > 0.0D) {
return Math.toDegrees(Math.acos(Mth.clamp(move.dot(view) / (startLength * endLength), -1, 1)));
} else {
return 0.0D;
}
}
protected Vec3 getDismountOffset(double vehicleWidth, double passengerWidth) {
double offset = (vehicleWidth + passengerWidth + (double) 1.0E-5f) / 2.0;
float yaw = getYRot() + 90.0f;
float x = -Mth.sin(yaw * ((float) java.lang.Math.PI / 180));
float z = Mth.cos(yaw * ((float) java.lang.Math.PI / 180));
float n = java.lang.Math.max(java.lang.Math.abs(x), java.lang.Math.abs(z));
return new Vec3((double) x * offset / (double) n, 0.0, (double) z * offset / (double) n);
}
@Override
public Vec3 getDismountLocationForPassenger(LivingEntity passenger) {
Vec3 vec3d = getDismountOffset(getBbWidth() * Mth.SQRT_OF_TWO, passenger.getBbWidth() * Mth.SQRT_OF_TWO);
double ox = getX() - vec3d.x;
double oz = getZ() + vec3d.z;
BlockPos exitPos = new BlockPos((int) ox, (int) getY(), (int) oz);
BlockPos floorPos = exitPos.below();
if (!level().isWaterAt(floorPos)) {
ArrayList<Vec3> list = Lists.newArrayList();
double exitHeight = level().getBlockFloorHeight(exitPos);
if (DismountHelper.isBlockFloorValid(exitHeight)) {
list.add(new Vec3(ox, (double) exitPos.getY() + exitHeight, oz));
}
double floorHeight = level().getBlockFloorHeight(floorPos);
if (DismountHelper.isBlockFloorValid(floorHeight)) {
list.add(new Vec3(ox, (double) floorPos.getY() + floorHeight, oz));
}
for (Pose entityPose : passenger.getDismountPoses()) {
for (Vec3 vec3d2 : list) {
if (!DismountHelper.canDismountTo(level(), vec3d2, passenger, entityPose)) continue;
passenger.setPose(entityPose);
return vec3d2;
}
}
}
return super.getDismountLocationForPassenger(passenger);
}
}