/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.base.MoreObjects * it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair * org.jspecify.annotations.Nullable */ package net.minecraft.world.entity.projectile; import com.google.common.base.MoreObjects; import it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair; import java.util.function.Consumer; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; import net.minecraft.server.level.ServerEntity; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.EntityTypeTags; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.TraceableEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.projectile.AbstractArrow; import net.minecraft.world.entity.projectile.ProjectileDeflection; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.jspecify.annotations.Nullable; public abstract class Projectile extends Entity implements TraceableEntity { private static final boolean DEFAULT_LEFT_OWNER = false; private static final boolean DEFAULT_HAS_BEEN_SHOT = false; protected @Nullable EntityReference owner; private boolean leftOwner = false; private boolean leftOwnerChecked; private boolean hasBeenShot = false; private @Nullable Entity lastDeflectedBy; Projectile(EntityType type, Level level) { super(type, level); } protected void setOwner(@Nullable EntityReference owner) { this.owner = owner; } public void setOwner(@Nullable Entity owner) { this.setOwner(EntityReference.of(owner)); } @Override public @Nullable Entity getOwner() { return EntityReference.getEntity(this.owner, this.level()); } public Entity getEffectSource() { return (Entity)MoreObjects.firstNonNull((Object)this.getOwner(), (Object)this); } @Override protected void addAdditionalSaveData(ValueOutput output) { EntityReference.store(this.owner, output, "Owner"); if (this.leftOwner) { output.putBoolean("LeftOwner", true); } output.putBoolean("HasBeenShot", this.hasBeenShot); } protected boolean ownedBy(Entity entity) { return this.owner != null && this.owner.matches(entity); } @Override protected void readAdditionalSaveData(ValueInput input) { this.setOwner(EntityReference.read(input, "Owner")); this.leftOwner = input.getBooleanOr("LeftOwner", false); this.hasBeenShot = input.getBooleanOr("HasBeenShot", false); } @Override public void restoreFrom(Entity oldEntity) { super.restoreFrom(oldEntity); if (oldEntity instanceof Projectile) { Projectile projectile = (Projectile)oldEntity; this.owner = projectile.owner; } } @Override public void tick() { if (!this.hasBeenShot) { this.gameEvent(GameEvent.PROJECTILE_SHOOT, this.getOwner()); this.hasBeenShot = true; } this.checkLeftOwner(); super.tick(); this.leftOwnerChecked = false; } protected void checkLeftOwner() { if (!this.leftOwner && !this.leftOwnerChecked) { this.leftOwner = this.isOutsideOwnerCollisionRange(); this.leftOwnerChecked = true; } } private boolean isOutsideOwnerCollisionRange() { Entity owner = this.getOwner(); if (owner != null) { AABB aabb = this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0); return owner.getRootVehicle().getSelfAndPassengers().filter(EntitySelector.CAN_BE_PICKED).noneMatch(entity -> aabb.intersects(entity.getBoundingBox())); } return true; } public Vec3 getMovementToShoot(double xd, double yd, double zd, float pow, float uncertainty) { return new Vec3(xd, yd, zd).normalize().add(this.random.triangle(0.0, 0.0172275 * (double)uncertainty), this.random.triangle(0.0, 0.0172275 * (double)uncertainty), this.random.triangle(0.0, 0.0172275 * (double)uncertainty)).scale(pow); } public void shoot(double xd, double yd, double zd, float pow, float uncertainty) { Vec3 movement = this.getMovementToShoot(xd, yd, zd, pow, uncertainty); this.setDeltaMovement(movement); this.needsSync = true; double sd = movement.horizontalDistance(); this.setYRot((float)(Mth.atan2(movement.x, movement.z) * 57.2957763671875)); this.setXRot((float)(Mth.atan2(movement.y, sd) * 57.2957763671875)); this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); } public void shootFromRotation(Entity source, float xRot, float yRot, float yOffset, float pow, float uncertainty) { float xd = -Mth.sin(yRot * ((float)Math.PI / 180)) * Mth.cos(xRot * ((float)Math.PI / 180)); float yd = -Mth.sin((xRot + yOffset) * ((float)Math.PI / 180)); float zd = Mth.cos(yRot * ((float)Math.PI / 180)) * Mth.cos(xRot * ((float)Math.PI / 180)); this.shoot(xd, yd, zd, pow, uncertainty); Vec3 sourceMovement = source.getKnownMovement(); this.setDeltaMovement(this.getDeltaMovement().add(sourceMovement.x, source.onGround() ? 0.0 : sourceMovement.y, sourceMovement.z)); } @Override public void onAboveBubbleColumn(boolean dragDown, BlockPos pos) { double yd = dragDown ? -0.03 : 0.1; this.setDeltaMovement(this.getDeltaMovement().add(0.0, yd, 0.0)); Projectile.sendBubbleColumnParticles(this.level(), pos); } @Override public void onInsideBubbleColumn(boolean dragDown) { double yd = dragDown ? -0.03 : 0.06; this.setDeltaMovement(this.getDeltaMovement().add(0.0, yd, 0.0)); this.resetFallDistance(); } public static T spawnProjectileFromRotation(ProjectileFactory creator, ServerLevel serverLevel, ItemStack itemStack, LivingEntity source, float yOffset, float pow, float uncertainty) { return (T)Projectile.spawnProjectile(creator.create(serverLevel, source, itemStack), serverLevel, itemStack, projectile -> projectile.shootFromRotation(source, source.getXRot(), source.getYRot(), yOffset, pow, uncertainty)); } public static T spawnProjectileUsingShoot(ProjectileFactory creator, ServerLevel serverLevel, ItemStack itemStack, LivingEntity source, double targetX, double targetY, double targetZ, float pow, float uncertainty) { return (T)Projectile.spawnProjectile(creator.create(serverLevel, source, itemStack), serverLevel, itemStack, projectile -> projectile.shoot(targetX, targetY, targetZ, pow, uncertainty)); } public static T spawnProjectileUsingShoot(T projectile, ServerLevel serverLevel, ItemStack itemStack, double targetX, double targetY, double targetZ, float pow, float uncertainty) { return (T)Projectile.spawnProjectile(projectile, serverLevel, itemStack, i -> projectile.shoot(targetX, targetY, targetZ, pow, uncertainty)); } public static T spawnProjectile(T projectile, ServerLevel serverLevel, ItemStack itemStack) { return (T)Projectile.spawnProjectile(projectile, serverLevel, itemStack, ignored -> {}); } public static T spawnProjectile(T projectile, ServerLevel serverLevel, ItemStack itemStack, Consumer shootFunction) { shootFunction.accept(projectile); serverLevel.addFreshEntity(projectile); projectile.applyOnProjectileSpawned(serverLevel, itemStack); return projectile; } public void applyOnProjectileSpawned(ServerLevel serverLevel, ItemStack pickupItemStack) { AbstractArrow arrow; ItemStack weapon; EnchantmentHelper.onProjectileSpawned(serverLevel, pickupItemStack, this, item -> {}); Projectile projectile = this; if (projectile instanceof AbstractArrow && (weapon = (arrow = (AbstractArrow)projectile).getWeaponItem()) != null && !weapon.isEmpty() && !pickupItemStack.getItem().equals(weapon.getItem())) { EnchantmentHelper.onProjectileSpawned(serverLevel, weapon, this, arrow::onItemBreak); } } protected ProjectileDeflection hitTargetOrDeflectSelf(HitResult hitResult) { ProjectileDeflection deflection; BlockHitResult blockHit; if (hitResult.getType() == HitResult.Type.ENTITY) { EntityHitResult entityHitResult = (EntityHitResult)hitResult; Entity entity = entityHitResult.getEntity(); ProjectileDeflection deflection2 = entity.deflection(this); if (deflection2 != ProjectileDeflection.NONE) { if (entity != this.lastDeflectedBy && this.deflect(deflection2, entity, this.owner, false)) { this.lastDeflectedBy = entity; } return deflection2; } } else if (this.shouldBounceOnWorldBorder() && hitResult instanceof BlockHitResult && (blockHit = (BlockHitResult)hitResult).isWorldBorderHit() && this.deflect(deflection = ProjectileDeflection.REVERSE, null, this.owner, false)) { this.setDeltaMovement(this.getDeltaMovement().scale(0.2)); return deflection; } this.onHit(hitResult); return ProjectileDeflection.NONE; } protected boolean shouldBounceOnWorldBorder() { return false; } public boolean deflect(ProjectileDeflection deflection, @Nullable Entity deflectingEntity, @Nullable EntityReference newOwner, boolean byAttack) { deflection.deflect(this, deflectingEntity, this.random); if (!this.level().isClientSide()) { this.setOwner(newOwner); this.onDeflection(byAttack); } return true; } protected void onDeflection(boolean byAttack) { } protected void onItemBreak(Item item) { } protected void onHit(HitResult hitResult) { HitResult.Type type = hitResult.getType(); if (type == HitResult.Type.ENTITY) { EntityHitResult entityHitResult = (EntityHitResult)hitResult; Entity entityHit = entityHitResult.getEntity(); if (entityHit.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entityHit instanceof Projectile) { Projectile projectile = (Projectile)entityHit; projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this.getOwner(), this.owner, true); } this.onHitEntity(entityHitResult); this.level().gameEvent(GameEvent.PROJECTILE_LAND, hitResult.getLocation(), GameEvent.Context.of(this, null)); } else if (type == HitResult.Type.BLOCK) { BlockHitResult blockHit = (BlockHitResult)hitResult; this.onHitBlock(blockHit); BlockPos target = blockHit.getBlockPos(); this.level().gameEvent(GameEvent.PROJECTILE_LAND, target, GameEvent.Context.of(this, this.level().getBlockState(target))); } } protected void onHitEntity(EntityHitResult hitResult) { } protected void onHitBlock(BlockHitResult hitResult) { BlockState state = this.level().getBlockState(hitResult.getBlockPos()); state.onProjectileHit(this.level(), state, hitResult, this); } protected boolean canHitEntity(Entity entity) { if (!entity.canBeHitByProjectile()) { return false; } Entity owner = this.getOwner(); return owner == null || this.leftOwner || !owner.isPassengerOfSameVehicle(entity); } protected void updateRotation() { Vec3 movement = this.getDeltaMovement(); double sd = movement.horizontalDistance(); this.setXRot(Projectile.lerpRotation(this.xRotO, (float)(Mth.atan2(movement.y, sd) * 57.2957763671875))); this.setYRot(Projectile.lerpRotation(this.yRotO, (float)(Mth.atan2(movement.x, movement.z) * 57.2957763671875))); } protected static float lerpRotation(float rotO, float rot) { while (rot - rotO < -180.0f) { rotO -= 360.0f; } while (rot - rotO >= 180.0f) { rotO += 360.0f; } return Mth.lerp(0.2f, rotO, rot); } @Override public Packet getAddEntityPacket(ServerEntity serverEntity) { Entity owner = this.getOwner(); return new ClientboundAddEntityPacket((Entity)this, serverEntity, owner == null ? 0 : owner.getId()); } @Override public void recreateFromPacket(ClientboundAddEntityPacket packet) { super.recreateFromPacket(packet); Entity owner = this.level().getEntity(packet.getData()); if (owner != null) { this.setOwner(owner); } } @Override public boolean mayInteract(ServerLevel level, BlockPos pos) { Entity owner = this.getOwner(); if (owner instanceof Player) { return owner.mayInteract(level, pos); } return owner == null || level.getGameRules().get(GameRules.MOB_GRIEFING) != false; } public boolean mayBreak(ServerLevel level) { return this.getType().is(EntityTypeTags.IMPACT_PROJECTILES) && level.getGameRules().get(GameRules.PROJECTILES_CAN_BREAK_BLOCKS) != false; } @Override public boolean isPickable() { return this.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE); } @Override public float getPickRadius() { return this.isPickable() ? 1.0f : 0.0f; } public DoubleDoubleImmutablePair calculateHorizontalHurtKnockbackDirection(LivingEntity hurtEntity, DamageSource damageSource) { double dx = this.getDeltaMovement().x; double dz = this.getDeltaMovement().z; return DoubleDoubleImmutablePair.of((double)dx, (double)dz); } @Override public int getDimensionChangingDelay() { return 2; } @Override public boolean hurtServer(ServerLevel level, DamageSource source, float damage) { if (!this.isInvulnerableToBase(source)) { this.markHurt(); } return false; } @FunctionalInterface public static interface ProjectileFactory { public T create(ServerLevel var1, LivingEntity var2, ItemStack var3); } }