package net.minecraft.world.entity.projectile; import it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair; import java.util.List; import java.util.function.Predicate; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponents; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.AreaEffectCloud; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.animal.axolotl.Axolotl; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.item.alchemy.PotionContents; import net.minecraft.world.item.alchemy.Potions; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.AbstractCandleBlock; import net.minecraft.world.level.block.CampfireBlock; import net.minecraft.world.level.block.state.BlockState; 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.HitResult.Type; import org.jetbrains.annotations.Nullable; public class ThrownPotion extends ThrowableItemProjectile { public static final double SPLASH_RANGE = 4.0; private static final double SPLASH_RANGE_SQ = 16.0; public static final Predicate WATER_SENSITIVE_OR_ON_FIRE = livingEntity -> livingEntity.isSensitiveToWater() || livingEntity.isOnFire(); public ThrownPotion(EntityType entityType, Level level) { super(entityType, level); } public ThrownPotion(Level level, LivingEntity livingEntity, ItemStack itemStack) { super(EntityType.POTION, livingEntity, level, itemStack); } public ThrownPotion(Level level, double d, double e, double f, ItemStack itemStack) { super(EntityType.POTION, d, e, f, level, itemStack); } @Override protected Item getDefaultItem() { return Items.SPLASH_POTION; } @Override protected double getDefaultGravity() { return 0.05; } @Override protected void onHitBlock(BlockHitResult result) { super.onHitBlock(result); if (!this.level().isClientSide) { ItemStack itemStack = this.getItem(); Direction direction = result.getDirection(); BlockPos blockPos = result.getBlockPos(); BlockPos blockPos2 = blockPos.relative(direction); PotionContents potionContents = itemStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); if (potionContents.is(Potions.WATER)) { this.dowseFire(blockPos2); this.dowseFire(blockPos2.relative(direction.getOpposite())); for (Direction direction2 : Direction.Plane.HORIZONTAL) { this.dowseFire(blockPos2.relative(direction2)); } } } } @Override protected void onHit(HitResult result) { super.onHit(result); if (this.level() instanceof ServerLevel serverLevel) { ItemStack itemStack = this.getItem(); PotionContents potionContents = itemStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY); if (potionContents.is(Potions.WATER)) { this.applyWater(serverLevel); } else if (potionContents.hasEffects()) { if (this.isLingering()) { this.makeAreaOfEffectCloud(potionContents); } else { this.applySplash(serverLevel, potionContents.getAllEffects(), result.getType() == Type.ENTITY ? ((EntityHitResult)result).getEntity() : null); } } int i = potionContents.potion().isPresent() && ((Potion)((Holder)potionContents.potion().get()).value()).hasInstantEffects() ? 2007 : 2002; serverLevel.levelEvent(i, this.blockPosition(), potionContents.getColor()); this.discard(); } } private void applyWater(ServerLevel serverLevel) { AABB aABB = this.getBoundingBox().inflate(4.0, 2.0, 4.0); for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, aABB, WATER_SENSITIVE_OR_ON_FIRE)) { double d = this.distanceToSqr(livingEntity); if (d < 16.0) { if (livingEntity.isSensitiveToWater()) { livingEntity.hurtServer(serverLevel, this.damageSources().indirectMagic(this, this.getOwner()), 1.0F); } if (livingEntity.isOnFire() && livingEntity.isAlive()) { livingEntity.extinguishFire(); } } } for (Axolotl axolotl : this.level().getEntitiesOfClass(Axolotl.class, aABB)) { axolotl.rehydrate(); } } private void applySplash(ServerLevel serverLevel, Iterable iterable, @Nullable Entity entity) { AABB aABB = this.getBoundingBox().inflate(4.0, 2.0, 4.0); List list = serverLevel.getEntitiesOfClass(LivingEntity.class, aABB); if (!list.isEmpty()) { Entity entity2 = this.getEffectSource(); for (LivingEntity livingEntity : list) { if (livingEntity.isAffectedByPotions()) { double d = this.distanceToSqr(livingEntity); if (d < 16.0) { double e; if (livingEntity == entity) { e = 1.0; } else { e = 1.0 - Math.sqrt(d) / 4.0; } for (MobEffectInstance mobEffectInstance : iterable) { Holder holder = mobEffectInstance.getEffect(); if (holder.value().isInstantenous()) { holder.value().applyInstantenousEffect(serverLevel, this, this.getOwner(), livingEntity, mobEffectInstance.getAmplifier(), e); } else { int i = mobEffectInstance.mapDuration(ix -> (int)(e * ix + 0.5)); MobEffectInstance mobEffectInstance2 = new MobEffectInstance( holder, i, mobEffectInstance.getAmplifier(), mobEffectInstance.isAmbient(), mobEffectInstance.isVisible() ); if (!mobEffectInstance2.endsWithin(20)) { livingEntity.addEffect(mobEffectInstance2, entity2); } } } } } } } } private void makeAreaOfEffectCloud(PotionContents potionContents) { AreaEffectCloud areaEffectCloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ()); if (this.getOwner() instanceof LivingEntity livingEntity) { areaEffectCloud.setOwner(livingEntity); } areaEffectCloud.setRadius(3.0F); areaEffectCloud.setRadiusOnUse(-0.5F); areaEffectCloud.setWaitTime(10); areaEffectCloud.setRadiusPerTick(-areaEffectCloud.getRadius() / areaEffectCloud.getDuration()); areaEffectCloud.setPotionContents(potionContents); this.level().addFreshEntity(areaEffectCloud); } private boolean isLingering() { return this.getItem().is(Items.LINGERING_POTION); } private void dowseFire(BlockPos pos) { BlockState blockState = this.level().getBlockState(pos); if (blockState.is(BlockTags.FIRE)) { this.level().destroyBlock(pos, false, this); } else if (AbstractCandleBlock.isLit(blockState)) { AbstractCandleBlock.extinguish(null, blockState, this.level(), pos); } else if (CampfireBlock.isLitCampfire(blockState)) { this.level().levelEvent(null, 1009, pos, 0); CampfireBlock.dowse(this.getOwner(), this.level(), pos, blockState); this.level().setBlockAndUpdate(pos, blockState.setValue(CampfireBlock.LIT, false)); } } @Override public DoubleDoubleImmutablePair calculateHorizontalHurtKnockbackDirection(LivingEntity entity, DamageSource damageSource) { double d = entity.position().x - this.position().x; double e = entity.position().z - this.position().z; return DoubleDoubleImmutablePair.of(d, e); } }