package com.atsuishio.superbwarfare.entity; import com.atsuishio.superbwarfare.ModUtils; import com.atsuishio.superbwarfare.config.server.CannonConfig; import com.atsuishio.superbwarfare.config.server.ExplosionDestroyConfig; import com.atsuishio.superbwarfare.entity.projectile.ProjectileEntity; import com.atsuishio.superbwarfare.init.*; import com.atsuishio.superbwarfare.item.ContainerBlockItem; import com.atsuishio.superbwarfare.menu.SpeedboatMenu; import com.atsuishio.superbwarfare.network.ModVariables; import com.atsuishio.superbwarfare.network.message.ShakeClientMessage; import com.atsuishio.superbwarfare.tools.CustomExplosion; import com.atsuishio.superbwarfare.tools.EntityFindUtil; import com.atsuishio.superbwarfare.tools.ParticleTool; import com.atsuishio.superbwarfare.tools.SoundTool; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.ContainerHelper; import net.minecraft.world.Containers; 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.animal.WaterAnimal; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ThrownPotion; import net.minecraft.world.entity.vehicle.ContainerEntity; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidType; import net.minecraftforge.items.wrapper.InvWrapper; import net.minecraftforge.network.NetworkHooks; import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.PlayMessages; import net.minecraftforge.registries.ForgeRegistries; import org.jetbrains.annotations.Nullable; import org.joml.Math; import software.bernie.geckolib.animatable.GeoEntity; import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.core.animation.AnimatableManager; import software.bernie.geckolib.core.animation.AnimationController; import software.bernie.geckolib.core.animation.AnimationState; import software.bernie.geckolib.core.animation.RawAnimation; import software.bernie.geckolib.core.object.PlayState; import software.bernie.geckolib.util.GeckoLibUtil; import java.util.Comparator; import java.util.List; public class SpeedboatEntity extends Entity implements GeoEntity, IChargeEntity, IVehicleEntity, HasCustomInventoryScreen, ContainerEntity { public static final EntityDataAccessor HEALTH = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor ENERGY = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor ROT_Y = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor DELTA_ROT = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor POWER = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor ROTOR = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.FLOAT); public static final EntityDataAccessor COOL_DOWN = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.INT); protected static final EntityDataAccessor LAST_ATTACKER_UUID = SynchedEntityData.defineId(SpeedboatEntity.class, EntityDataSerializers.STRING); public static final float MAX_HEALTH = CannonConfig.SPEEDBOAT_HP.get(); public static final float MAX_ENERGY = CannonConfig.SPEEDBOAT_MAX_ENERGY.get().floatValue(); public static final int CONTAINER_SIZE = 102; private NonNullList items = NonNullList.withSize(CONTAINER_SIZE, ItemStack.EMPTY); private LazyOptional itemHandler = LazyOptional.of(() -> new InvWrapper(this)); private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); private int lerpSteps; private double lerpX; private double lerpY; private double lerpZ; private double lerpYRot; private double lerpXRot; public float turretYRot; public float turretXRot; public float turretYRotO; public float turretXRotO; public float heat; public boolean cannotFire; public SpeedboatEntity(PlayMessages.SpawnEntity packet, Level world) { this(ModEntities.SPEEDBOAT.get(), world); } public SpeedboatEntity(EntityType type, Level world) { super(type, world); } @Override protected void defineSynchedData() { this.entityData.define(HEALTH, MAX_HEALTH); this.entityData.define(ENERGY, 0f); this.entityData.define(ROT_Y, 0f); this.entityData.define(DELTA_ROT, 0f); this.entityData.define(POWER, 0f); this.entityData.define(ROTOR, 0f); this.entityData.define(COOL_DOWN, 0); this.entityData.define(LAST_ATTACKER_UUID, "undefined"); } @Override public void addAdditionalSaveData(CompoundTag compound) { compound.putFloat("Health", this.entityData.get(HEALTH)); compound.putFloat("Energy", this.entityData.get(ENERGY)); compound.putString("LastAttacker", this.entityData.get(LAST_ATTACKER_UUID)); ContainerHelper.saveAllItems(compound, this.getItemStacks()); } @Override public void readAdditionalSaveData(CompoundTag compound) { this.entityData.set(ENERGY, compound.getFloat("Energy")); if (compound.contains("Health")) { this.entityData.set(HEALTH, compound.getFloat("Health")); } else { this.entityData.set(HEALTH, MAX_HEALTH); } if (compound.contains("LastAttacker")) { this.entityData.set(LAST_ATTACKER_UUID, compound.getString("LastAttacker")); } ContainerHelper.loadAllItems(compound, this.getItemStacks()); } @Override public boolean canCollideWith(Entity pEntity) { if (this.getDeltaMovement().length() > 0.2) { return false; } else { return canVehicleCollide(this, pEntity); } } public static boolean canVehicleCollide(Entity pVehicle, Entity pEntity) { return (pEntity.canBeCollidedWith() || pEntity.isPushable()) && !pVehicle.isPassengerOfSameVehicle(pEntity); } @Override public boolean canBeCollidedWith() { return super.canBeCollidedWith(); } @Override public boolean isPushable() { return true; } @Override public Packet getAddEntityPacket() { return NetworkHooks.getEntitySpawningPacket(this); } @Override public double getPassengersRidingOffset() { return super.getPassengersRidingOffset() - 1.3; } @Override public void remove(Entity.RemovalReason pReason) { if (!this.level().isClientSide && pReason != RemovalReason.DISCARDED) { Containers.dropContents(this.level(), this, this); } super.remove(pReason); } @Override public boolean hurt(DamageSource source, float amount) { if (this.level() instanceof ServerLevel serverLevel) { ParticleTool.sendParticle(serverLevel, ModParticleTypes.FIRE_STAR.get(), this.getX(), this.getY() + 2.5, this.getZ(), 4, 0.2, 0.2, 0.2, 0.2, false); } 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(ModDamageTypes.PROJECTILE_BOOM)) { amount *= 2f; } if (source.is(ModDamageTypes.CANNON_FIRE)) { amount *= 3f; } if (source.is(ModDamageTypes.GUN_FIRE_ABSOLUTE)) { amount *= 1.2f; } if (source.getEntity() != null) { this.entityData.set(LAST_ATTACKER_UUID, source.getEntity().getStringUUID()); } this.level().playSound(null, this.getOnPos(), ModSounds.HIT.get(), SoundSource.PLAYERS, 1, 1); this.entityData.set(HEALTH, this.entityData.get(HEALTH) - 0.5f * java.lang.Math.max(amount - 3, 0)); return true; } @Override public boolean isPickable() { return !this.isRemoved(); } @Override public InteractionResult interact(Player player, InteractionHand hand) { if (player.isShiftKeyDown()) { if (player.getMainHandItem().is(ModItems.CROWBAR.get()) && this.getFirstPassenger() == null) { ItemStack stack = ContainerBlockItem.createInstance(this); if (!player.addItem(stack)) { player.drop(stack, false); } this.remove(RemovalReason.DISCARDED); this.discard(); return InteractionResult.sidedSuccess(this.level().isClientSide()); } else { player.openMenu(this); return !player.level().isClientSide ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } } else { player.startRiding(this); return InteractionResult.sidedSuccess(this.level().isClientSide()); } } public double getSubmergedHeight(Entity entity) { for (FluidType fluidType : ForgeRegistries.FLUID_TYPES.get().getValues()) { if (entity.level().getFluidState(entity.blockPosition()).getFluidType() == fluidType) return entity.getFluidTypeHeight(fluidType); } return 0; } @Override public void baseTick() { super.baseTick(); if (this.entityData.get(COOL_DOWN) > 0) { this.entityData.set(COOL_DOWN, this.entityData.get(COOL_DOWN) - 1); } if (heat > 0) { heat--; } if (heat < 40) { cannotFire = false; } Entity driver = this.getFirstPassenger(); if (driver instanceof Player player) { if (heat > 100) { cannotFire = true; if (!player.level().isClientSide() && player instanceof ServerPlayer serverPlayer) { SoundTool.playLocalSound(serverPlayer, ModSounds.MINIGUN_OVERHEAT.get(), 1f, 1f); } } } double fluidFloat; fluidFloat = -0.05 + 0.1 * getSubmergedHeight(this); this.setDeltaMovement(this.getDeltaMovement().add(0.0, fluidFloat, 0.0)); this.move(MoverType.SELF, this.getDeltaMovement()); if (this.onGround()) { this.setDeltaMovement(this.getDeltaMovement().multiply(0.2, 0.85, 0.2)); } else { float f = 0.73f + 0.09f * Mth.abs(90 - (float) calculateAngle(this.getDeltaMovement(), this.getViewVector(1))) / 90; this.setDeltaMovement(this.getDeltaMovement().add(this.getViewVector(1).normalize().scale(0.04 * this.getDeltaMovement().length()))); this.setDeltaMovement(this.getDeltaMovement().multiply(f, 0.85, f)); } if (this.level() instanceof ServerLevel) { this.entityData.set(ROT_Y, this.getYRot()); } handleClientSync(); this.tickLerp(); this.controlBoat(); if (this.entityData.get(HEALTH) <= 0) { this.ejectPassengers(); destroy(); } if (this.isVehicle() && this.getDeltaMovement().length() > 0.05) { crushEntities(this.getDeltaMovement()); } collideBlock(); gunnerAngle(); gunnerFire(); attractEntity(); this.refreshDimensions(); } public boolean zooming() { Entity driver = this.getFirstPassenger(); if (driver == null) return false; if (driver instanceof Player player) { return player.getCapability(ModVariables.PLAYER_VARIABLES_CAPABILITY, null).orElse(new ModVariables.PlayerVariables()).zoom; } return false; } /** * 机枪塔开火 */ private void gunnerFire() { if (this.entityData.get(COOL_DOWN) != 0 || cannotFire) return; Entity driver = this.getFirstPassenger(); if (driver == null) return; if (driver instanceof Player player && !(player.getMainHandItem().is(ModTags.Items.GUN))) { Level level = player.level(); if (level instanceof ServerLevel && player.getCapability(ModVariables.PLAYER_VARIABLES_CAPABILITY, null).orElse(new ModVariables.PlayerVariables()).holdFire) { ProjectileEntity projectile = new ProjectileEntity(driver.level()) .shooter(player) .damage(CannonConfig.SPEEDBOAT_GUN_DAMAGE.get()) .headShot(2f) .zoom(false); projectile.bypassArmorRate(0.9f); projectile.setPos(this.xo - this.getViewVector(1).scale(0.54).x - this.getDeltaMovement().x, this.yo + 3.0, this.zo - this.getViewVector(1).scale(0.54).z - this.getDeltaMovement().z); projectile.shoot(player, player.getLookAngle().x, player.getLookAngle().y + (zooming() ? 0.002f : -0.009f), player.getLookAngle().z, 25, (float) 0.6); this.level().addFreshEntity(projectile); float pitch = heat <= 60 ? 1 : (float) (1 - 0.015 * java.lang.Math.abs(60 - heat)); if (player instanceof ServerPlayer serverPlayer) { SoundTool.playLocalSound(serverPlayer, ModSounds.M_2_FIRE_1P.get(), 2, 1); serverPlayer.level().playSound(null, serverPlayer.getOnPos(), ModSounds.M_2_FIRE_3P.get(), SoundSource.PLAYERS, 4, pitch); serverPlayer.level().playSound(null, serverPlayer.getOnPos(), ModSounds.M_2_FAR.get(), SoundSource.PLAYERS, 12, pitch); serverPlayer.level().playSound(null, serverPlayer.getOnPos(), ModSounds.M_2_VERYFAR.get(), SoundSource.PLAYERS, 24, pitch); } final Vec3 center = new Vec3(this.getX(), this.getEyeY(), this.getZ()); for (Entity target : level.getEntitiesOfClass(Entity.class, new AABB(center, center).inflate(4), e -> true).stream().sorted(Comparator.comparingDouble(e -> e.distanceToSqr(center))).toList()) { if (target instanceof ServerPlayer serverPlayer) { ModUtils.PACKET_HANDLER.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new ShakeClientMessage(6, 5, 7, this.getX(), this.getEyeY(), this.getZ())); } } this.entityData.set(COOL_DOWN, 3); heat += 4; } } } /** * 撞击实体并造成伤害 * @param velocity 动量 */ public void crushEntities(Vec3 velocity) { var frontBox = getBoundingBox().move(velocity.scale(0.5)); var velAdd = velocity.add(0, 0, 0).scale(1.5); for (var entity : level().getEntities(EntityTypeTest.forClass(Entity.class), frontBox, entity -> entity != this && entity != getFirstPassenger() && entity.getVehicle() == null)) { double entitySize = entity.getBbWidth() * entity.getBbHeight(); double thisSize = this.getBbWidth() * this.getBbHeight(); double f = Math.min(entitySize / thisSize, 2); double f1 = Math.min(thisSize / entitySize, 4); if (!(entity instanceof TargetEntity)) { this.push(-f * velAdd.x, -f * velAdd.y, -f * velAdd.z); } if (velocity.length() > 0.2 && entity.isAlive() && !(entity instanceof ItemEntity || entity instanceof Projectile || entity instanceof ProjectileEntity)) { if (!this.level().isClientSide) { this.level().playSound(null, this, ModSounds.VEHICLE_STRIKE.get(), this.getSoundSource(), 1, 1); } entity.push(f1 * velAdd.x, f1 * velAdd.y, f1 * velAdd.z); entity.hurt(ModDamageTypes.causeVehicleStrikeDamage(this.level().registryAccess(), this, this.getFirstPassenger() == null ? this : this.getFirstPassenger()), (float) (25 * velocity.length())); } } } /** * 撞掉莲叶 */ public void collideBlock() { AABB aabb = AABB.ofSize(new Vec3(this.getX(), this.getY() + this.getBbHeight() * 0.5, this.getZ()), 3.6, 2.6, 3.6); BlockPos.betweenClosedStream(aabb).forEach((pos) -> { BlockState blockstate = this.level().getBlockState(pos); if (blockstate.is(Blocks.LILY_PAD)) { this.level().destroyBlock(pos, true); } }); } private void controlBoat() { Entity passenger0 = this.getPassengers().isEmpty() ? null : this.getPassengers().get(0); if (this.entityData.get(ENERGY) <= 0) return; if (passenger0 == null) { this.getPersistentData().putBoolean("left", false); this.getPersistentData().putBoolean("right", false); this.getPersistentData().putBoolean("forward", false); this.getPersistentData().putBoolean("backward", false); } float diffY = 0; if (this.getPersistentData().getBoolean("forward")) { this.entityData.set(POWER, this.entityData.get(POWER) + 0.02f); } if (this.getPersistentData().getBoolean("backward")) { this.entityData.set(POWER, this.entityData.get(POWER) - 0.02f); if (this.getPersistentData().getBoolean("right")) { diffY = Mth.clamp(diffY + 1f, 0, 10); handleSetDiffY(diffY); } else if (this.getPersistentData().getBoolean("left")) { diffY = Mth.clamp(diffY - 1f, -10, 0); handleSetDiffY(diffY); } } else { if (this.getPersistentData().getBoolean("right")) { diffY = Mth.clamp(diffY - 1f, -10, 0); handleSetDiffY(diffY); } else if (this.getPersistentData().getBoolean("left")) { diffY = Mth.clamp(diffY + 1f, 0, 10); handleSetDiffY(diffY); } } if (this.getPersistentData().getBoolean("forward") || this.getPersistentData().getBoolean("backward")) { this.entityData.set(ENERGY, Math.max(this.entityData.get(ENERGY) - CannonConfig.SPEEDBOAT_ENERGY_COST.get().floatValue(), 0)); } if (level().isClientSide) { level().playLocalSound(this.getX(), this.getY() + this.getBbHeight() * 0.5, this.getZ(), this.getEngineSound(), this.getSoundSource(), Math.min((this.getPersistentData().getBoolean("forward") || this.getPersistentData().getBoolean("backward") ? 7.5f : 5f) * 2 * Mth.abs(this.entityData.get(POWER)), 0.25f), (random.nextFloat() * 0.1f + 1f), false); } this.entityData.set(POWER, this.entityData.get(POWER) * 0.9f); this.entityData.set(ROTOR, this.entityData.get(ROTOR) + this.entityData.get(POWER)); this.entityData.set(DELTA_ROT, this.entityData.get(DELTA_ROT) * 0.8f); if (this.isInWater() || this.isUnderWater()) { this.setYRot(this.entityData.get(ROT_Y) - this.entityData.get(DELTA_ROT)); this.setDeltaMovement(this.getDeltaMovement().add(new Vec3(this.getLookAngle().x, 0, this.getLookAngle().z).scale(this.entityData.get(POWER)))); } } private void gunnerAngle() { Entity driver = this.getFirstPassenger(); if (driver == null) return; float gunAngle = -Math.clamp(-140f, 140f, Mth.wrapDegrees(driver.getYHeadRot() - this.getYRot())); turretYRotO = this.getTurretYRot(); turretXRotO = this.getTurretXRot(); this.setTurretYRot(gunAngle); this.setTurretXRot(driver.getXRot() - this.getXRot()); } public float getTurretYRot() { return this.turretYRot; } public void setTurretYRot(float pTurretYRot) { this.turretYRot = pTurretYRot; } public float getTurretXRot() { return this.turretXRot; } public void setTurretXRot(float pTurretXRot) { this.turretXRot = pTurretXRot; } private void handleSetDiffY(float diffY) { this.entityData.set(DELTA_ROT, (float) Mth.clamp(diffY * 1.3 * Math.max(8 * this.getDeltaMovement().length(), 0.5), -2, 2)); } private void handleClientSync() { if (isControlledByLocalInstance()) { lerpSteps = 0; syncPacketPositionCodec(getX(), getY(), getZ()); } if (lerpSteps <= 0) { return; } double interpolatedX = getX() + (lerpX - getX()) / (double) lerpSteps; double interpolatedY = getY() + (lerpY - getY()) / (double) lerpSteps; double interpolatedZ = getZ() + (lerpZ - getZ()) / (double) lerpSteps; double interpolatedYaw = Mth.wrapDegrees(lerpYRot - (double) getYRot()); setYRot(getYRot() + (float) interpolatedYaw / (float) lerpSteps); setXRot(getXRot() + (float) (lerpXRot - (double) getXRot()) / (float) lerpSteps); setPos(interpolatedX, interpolatedY, interpolatedZ); setRot(getYRot(), getXRot()); --lerpSteps; } protected SoundEvent getEngineSound() { return ModSounds.BOAT_ENGINE.get(); } @Override protected void positionRider(Entity pPassenger, MoveFunction pCallback) { if (this.hasPassenger(pPassenger)) { double posY = this.getY() + this.getPassengersRidingOffset() + pPassenger.getMyRidingOffset(); if (!zooming()) { pPassenger.setYRot(pPassenger.getYRot() - 1.27f * this.entityData.get(DELTA_ROT)); pPassenger.setYHeadRot(pPassenger.getYHeadRot() - 1.27f * this.entityData.get(DELTA_ROT)); } if (this.getPassengers().size() > 1) { int i = this.getPassengers().indexOf(pPassenger); if (i == 0) { pCallback.accept(pPassenger, this.getX(), posY, this.getZ()); return; } double zOffset = -0.8; if (i % 2 == 0) { zOffset = 0.8; } double xOffset = (int) -((i - 1) / 2.0 + 1) * 0.95; Vec3 vec3 = (new Vec3(xOffset, 0.0D, zOffset)).yRot(-this.getYRot() * ((float) java.lang.Math.PI / 180F) - ((float) java.lang.Math.PI / 2F)); pCallback.accept(pPassenger, this.getX() + vec3.x, posY, this.getZ() + vec3.z); } else { pCallback.accept(pPassenger, this.getX(), posY, this.getZ()); } } } public boolean hasEnoughSpaceFor(Entity pEntity) { return pEntity.getBbWidth() < this.getBbWidth(); } public void attractEntity() { List list = this.level().getEntities(this, this.getBoundingBox().inflate(0.2F, -0.01F, 0.2F), EntitySelector.pushableBy(this)); if (!list.isEmpty()) { boolean flag = !this.level().isClientSide && !(this.getControllingPassenger() instanceof Player); for (Entity entity : list) { if (!entity.hasPassenger(this)) { if (flag && this.getPassengers().size() < this.getMaxPassengers() && !entity.isPassenger() && this.hasEnoughSpaceFor(entity) && entity instanceof LivingEntity && !(entity instanceof WaterAnimal) && !(entity instanceof Player)) { entity.startRiding(this); } else { this.push(entity); } } } } } 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; } } private void tickLerp() { if (this.isControlledByLocalInstance()) { this.lerpSteps = 0; this.syncPacketPositionCodec(this.getX(), this.getY(), this.getZ()); } if (this.lerpSteps > 0) { double d0 = this.getX() + (this.lerpX - this.getX()) / (double) this.lerpSteps; double d1 = this.getY() + (this.lerpY - this.getY()) / (double) this.lerpSteps; double d2 = this.getZ() + (this.lerpZ - this.getZ()) / (double) this.lerpSteps; double d3 = Mth.wrapDegrees(this.lerpYRot - (double) this.getYRot()); this.setYRot(this.getYRot() + (float) d3 / (float) this.lerpSteps); this.setXRot(this.getXRot() + (float) (this.lerpXRot - (double) this.getXRot()) / (float) this.lerpSteps); --this.lerpSteps; this.setPos(d0, d1, d2); this.setRot(this.getYRot(), this.getXRot()); } } @Override public void lerpTo(double pX, double pY, double pZ, float pYaw, float pPitch, int pPosRotationIncrements, boolean pTeleport) { this.lerpX = pX; this.lerpY = pY; this.lerpZ = pZ; this.lerpYRot = pYaw; this.lerpXRot = pPitch; this.lerpSteps = 10; } private void destroy() { Entity attacker = EntityFindUtil.findEntity(this.level(), this.entityData.get(LAST_ATTACKER_UUID)); CustomExplosion explosion = new CustomExplosion(this.level(), attacker, ModDamageTypes.causeProjectileBoomDamage(this.level().registryAccess(), attacker, attacker), 45f, this.getX(), this.getY(), this.getZ(), 3f, ExplosionDestroyConfig.EXPLOSION_DESTROY.get() ? Explosion.BlockInteraction.DESTROY : Explosion.BlockInteraction.KEEP).setDamageMultiplier(1); explosion.explode(); net.minecraftforge.event.ForgeEventFactory.onExplosionStart(this.level(), explosion); explosion.finalizeExplosion(false); ParticleTool.spawnMediumExplosionParticles(this.level(), this.position()); this.discard(); } protected void clampRotation(Entity entity) { float f = Mth.wrapDegrees(entity.getXRot()); float f1 = Mth.clamp(f, -40.0F, 20F); entity.xRotO += f1 - f; entity.setXRot(entity.getXRot() + f1 - f); entity.setYBodyRot(this.getYRot()); float f2 = Mth.wrapDegrees(entity.getYRot() - this.getYRot()); float f3 = Mth.clamp(f2, -140.0F, 140.0F); entity.yRotO += f3 - f2; entity.setYRot(entity.getYRot() + f3 - f2); entity.setYHeadRot(entity.getYRot()); } @Override public void onPassengerTurned(Entity entity) { this.clampRotation(entity); } private PlayState firePredicate(AnimationState event) { if (this.entityData.get(COOL_DOWN) > 1 && !cannotFire) { return event.setAndContinue(RawAnimation.begin().thenPlay("animation.speedboat.fire")); } return event.setAndContinue(RawAnimation.begin().thenLoop("animation.speedboat.idle")); } @Override public void registerControllers(AnimatableManager.ControllerRegistrar data) { data.add(new AnimationController<>(this, "movement", 0, this::firePredicate)); } @Override public AnimatableInstanceCache getAnimatableInstanceCache() { return this.cache; } @Override public void charge(int amount) { this.entityData.set(ENERGY, Math.min(this.entityData.get(ENERGY) + amount, MAX_ENERGY)); } @Override protected boolean canAddPassenger(Entity pPassenger) { return this.getPassengers().size() < this.getMaxPassengers(); } public int getMaxPassengers() { return 5; } @Override public void openCustomInventoryScreen(Player pPlayer) { pPlayer.openMenu(this); if (!pPlayer.level().isClientSide) { this.gameEvent(GameEvent.CONTAINER_OPEN, pPlayer); } } @Nullable @Override public ResourceLocation getLootTable() { return null; } @Override public void setLootTable(@Nullable ResourceLocation pLootTable) { } @Override public long getLootTableSeed() { return 0; } @Override public void setLootTableSeed(long pLootTableSeed) { } @Override public NonNullList getItemStacks() { return this.items; } @Override public void clearItemStacks() { this.items.clear(); } @Override public int getContainerSize() { return CONTAINER_SIZE; } @Override public ItemStack getItem(int pSlot) { return this.items.get(pSlot); } @Override public ItemStack removeItem(int pSlot, int pAmount) { return ContainerHelper.removeItem(this.items, pSlot, pAmount); } @Override public ItemStack removeItemNoUpdate(int pSlot) { ItemStack itemstack = this.getItemStacks().get(pSlot); if (itemstack.isEmpty()) { return ItemStack.EMPTY; } else { this.getItemStacks().set(pSlot, ItemStack.EMPTY); return itemstack; } } @Override public void setItem(int pSlot, ItemStack pStack) { this.getItemStacks().set(pSlot, pStack); if (!pStack.isEmpty() && pStack.getCount() > this.getMaxStackSize()) { pStack.setCount(this.getMaxStackSize()); } } @Override public void setChanged() { } @Override public boolean stillValid(Player pPlayer) { return !this.isRemoved() && this.position().closerThan(pPlayer.position(), 8.0D); } @Override public void clearContent() { this.getItemStacks().clear(); } @Nullable @Override public AbstractContainerMenu createMenu(int pContainerId, Inventory pPlayerInventory, Player pPlayer) { if (pPlayer.isSpectator()) { return null; } else { return new SpeedboatMenu(pContainerId, pPlayerInventory, this); } } @Override public LazyOptional getCapability(Capability capability, @Nullable Direction facing) { if (this.isAlive() && capability == ForgeCapabilities.ITEM_HANDLER) { return itemHandler.cast(); } return super.getCapability(capability, facing); } @Override public void invalidateCaps() { super.invalidateCaps(); itemHandler.invalidate(); } @Override public void reviveCaps() { super.reviveCaps(); itemHandler = LazyOptional.of(() -> new InvWrapper(this)); } @Override public void stopOpen(Player pPlayer) { this.level().gameEvent(GameEvent.CONTAINER_CLOSE, this.position(), GameEvent.Context.of(pPlayer)); } }