package net.minecraft.world.entity.projectile; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.SynchedEntityData.Builder; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.level.ClipContext.Block; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.HitResult.Type; import org.jetbrains.annotations.Nullable; public abstract class AbstractHurtingProjectile extends Projectile { public static final double INITAL_ACCELERATION_POWER = 0.1; public static final double DEFLECTION_SCALE = 0.5; public double accelerationPower = 0.1; protected AbstractHurtingProjectile(EntityType entityType, Level level) { super(entityType, level); } protected AbstractHurtingProjectile(EntityType entityType, double x, double y, double z, Level level) { this(entityType, level); this.setPos(x, y, z); } public AbstractHurtingProjectile(EntityType entityType, double x, double y, double z, Vec3 movement, Level level) { this(entityType, level); this.snapTo(x, y, z, this.getYRot(), this.getXRot()); this.reapplyPosition(); this.assignDirectionalMovement(movement, this.accelerationPower); } public AbstractHurtingProjectile(EntityType entityType, LivingEntity owner, Vec3 movement, Level level) { this(entityType, owner.getX(), owner.getY(), owner.getZ(), movement, level); this.setOwner(owner); this.setRot(owner.getYRot(), owner.getXRot()); } @Override protected void defineSynchedData(Builder builder) { } @Override public boolean shouldRenderAtSqrDistance(double distance) { double d = this.getBoundingBox().getSize() * 4.0; if (Double.isNaN(d)) { d = 4.0; } d *= 64.0; return distance < d * d; } protected Block getClipType() { return Block.COLLIDER; } @Override public void tick() { Entity entity = this.getOwner(); this.applyInertia(); if (this.level().isClientSide || (entity == null || !entity.isRemoved()) && this.level().hasChunkAt(this.blockPosition())) { HitResult hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity, this.getClipType()); Vec3 vec3; if (hitResult.getType() != Type.MISS) { vec3 = hitResult.getLocation(); } else { vec3 = this.position().add(this.getDeltaMovement()); } ProjectileUtil.rotateTowardsMovement(this, 0.2F); this.setPos(vec3); this.applyEffectsFromBlocks(); super.tick(); if (this.shouldBurn()) { this.igniteForSeconds(1.0F); } if (hitResult.getType() != Type.MISS && this.isAlive()) { this.hitTargetOrDeflectSelf(hitResult); } this.createParticleTrail(); } else { this.discard(); } } private void applyInertia() { Vec3 vec3 = this.getDeltaMovement(); Vec3 vec32 = this.position(); float g; if (this.isInWater()) { for (int i = 0; i < 4; i++) { float f = 0.25F; this.level().addParticle(ParticleTypes.BUBBLE, vec32.x - vec3.x * 0.25, vec32.y - vec3.y * 0.25, vec32.z - vec3.z * 0.25, vec3.x, vec3.y, vec3.z); } g = this.getLiquidInertia(); } else { g = this.getInertia(); } this.setDeltaMovement(vec3.add(vec3.normalize().scale(this.accelerationPower)).scale(g)); } private void createParticleTrail() { ParticleOptions particleOptions = this.getTrailParticle(); Vec3 vec3 = this.position(); if (particleOptions != null) { this.level().addParticle(particleOptions, vec3.x, vec3.y + 0.5, vec3.z, 0.0, 0.0, 0.0); } } @Override public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { return false; } @Override protected boolean canHitEntity(Entity target) { return super.canHitEntity(target) && !target.noPhysics; } protected boolean shouldBurn() { return true; } @Nullable protected ParticleOptions getTrailParticle() { return ParticleTypes.SMOKE; } /** * Return the motion factor for this projectile. The factor is multiplied by the original motion. */ protected float getInertia() { return 0.95F; } protected float getLiquidInertia() { return 0.8F; } @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); tag.putDouble("acceleration_power", this.accelerationPower); } @Override public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.accelerationPower = tag.getDoubleOr("acceleration_power", 0.1); } @Override public float getLightLevelDependentMagicValue() { return 1.0F; } private void assignDirectionalMovement(Vec3 movement, double accelerationPower) { this.setDeltaMovement(movement.normalize().scale(accelerationPower)); this.hasImpulse = true; } @Override protected void onDeflection(@Nullable Entity entity, boolean deflectedByPlayer) { super.onDeflection(entity, deflectedByPlayer); if (deflectedByPlayer) { this.accelerationPower = 0.1; } else { this.accelerationPower *= 0.5; } } }