package net.minecraft.world.entity; import com.google.common.collect.Sets; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import javax.annotation.Nullable; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.world.Difficulty; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseFireBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LightningRodBlock; import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public class LightningBolt extends Entity { private static final int START_LIFE = 2; private static final double DAMAGE_RADIUS = 3.0; private static final double DETECTION_RADIUS = 15.0; private int life; public long seed; private int flashes; private boolean visualOnly; @Nullable private ServerPlayer cause; private final Set hitEntities = Sets.newHashSet(); private int blocksSetOnFire; public LightningBolt(EntityType p_20865_, Level p_20866_) { super(p_20865_, p_20866_); this.life = 2; this.seed = this.random.nextLong(); this.flashes = this.random.nextInt(3) + 1; } public void setVisualOnly(boolean p_20875_) { this.visualOnly = p_20875_; } @Override public SoundSource getSoundSource() { return SoundSource.WEATHER; } @Nullable public ServerPlayer getCause() { return this.cause; } public void setCause(@Nullable ServerPlayer p_20880_) { this.cause = p_20880_; } private void powerLightningRod() { BlockPos blockpos = this.getStrikePosition(); BlockState blockstate = this.level().getBlockState(blockpos); if (blockstate.is(Blocks.LIGHTNING_ROD)) { ((LightningRodBlock)blockstate.getBlock()).onLightningStrike(blockstate, this.level(), blockpos); } } @Override public void tick() { super.tick(); if (this.life == 2) { if (this.level().isClientSide()) { this.level() .playLocalSound( this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_THUNDER, SoundSource.WEATHER, 10000.0F, 0.8F + this.random.nextFloat() * 0.2F, false ); this.level() .playLocalSound( this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_IMPACT, SoundSource.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F, false ); } else { Difficulty difficulty = this.level().getDifficulty(); if (difficulty == Difficulty.NORMAL || difficulty == Difficulty.HARD) { this.spawnFire(4); } this.powerLightningRod(); clearCopperOnLightningStrike(this.level(), this.getStrikePosition()); this.gameEvent(GameEvent.LIGHTNING_STRIKE); } } this.life--; if (this.life < 0) { if (this.flashes == 0) { if (this.level() instanceof ServerLevel) { List list = this.level() .getEntities( this, new AABB( this.getX() - 15.0, this.getY() - 15.0, this.getZ() - 15.0, this.getX() + 15.0, this.getY() + 6.0 + 15.0, this.getZ() + 15.0 ), p_147140_ -> p_147140_.isAlive() && !this.hitEntities.contains(p_147140_) ); for (ServerPlayer serverplayer : ((ServerLevel)this.level()).getPlayers(p_326774_ -> p_326774_.distanceTo(this) < 256.0F)) { CriteriaTriggers.LIGHTNING_STRIKE.trigger(serverplayer, this, list); } } this.discard(); } else if (this.life < -this.random.nextInt(10)) { this.flashes--; this.life = 1; this.seed = this.random.nextLong(); this.spawnFire(0); } } if (this.life >= 0) { if (!(this.level() instanceof ServerLevel)) { this.level().setSkyFlashTime(2); } else if (!this.visualOnly) { List list1 = this.level() .getEntities( this, new AABB( this.getX() - 3.0, this.getY() - 3.0, this.getZ() - 3.0, this.getX() + 3.0, this.getY() + 6.0 + 3.0, this.getZ() + 3.0 ), Entity::isAlive ); for (Entity entity : list1) { entity.thunderHit((ServerLevel)this.level(), this); } this.hitEntities.addAll(list1); if (this.cause != null) { CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, list1); } } } } private BlockPos getStrikePosition() { Vec3 vec3 = this.position(); return BlockPos.containing(vec3.x, vec3.y - 1.0E-6, vec3.z); } private void spawnFire(int p_20871_) { if (!this.visualOnly && this.level() instanceof ServerLevel serverlevel && serverlevel.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) { BlockPos blockpos1 = this.blockPosition(); BlockState blockstate = BaseFireBlock.getState(this.level(), blockpos1); if (this.level().getBlockState(blockpos1).isAir() && blockstate.canSurvive(this.level(), blockpos1)) { this.level().setBlockAndUpdate(blockpos1, blockstate); this.blocksSetOnFire++; } for (int i = 0; i < p_20871_; i++) { BlockPos blockpos = blockpos1.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); blockstate = BaseFireBlock.getState(this.level(), blockpos); if (this.level().getBlockState(blockpos).isAir() && blockstate.canSurvive(this.level(), blockpos)) { this.level().setBlockAndUpdate(blockpos, blockstate); this.blocksSetOnFire++; } } } } private static void clearCopperOnLightningStrike(Level p_147151_, BlockPos p_147152_) { BlockState blockstate = p_147151_.getBlockState(p_147152_); BlockPos blockpos; BlockState blockstate1; if (blockstate.is(Blocks.LIGHTNING_ROD)) { blockpos = p_147152_.relative(blockstate.getValue(LightningRodBlock.FACING).getOpposite()); blockstate1 = p_147151_.getBlockState(blockpos); } else { blockpos = p_147152_; blockstate1 = blockstate; } if (blockstate1.getBlock() instanceof WeatheringCopper) { p_147151_.setBlockAndUpdate(blockpos, WeatheringCopper.getFirst(p_147151_.getBlockState(blockpos))); BlockPos.MutableBlockPos blockpos$mutableblockpos = p_147152_.mutable(); int i = p_147151_.random.nextInt(3) + 3; for (int j = 0; j < i; j++) { int k = p_147151_.random.nextInt(8) + 1; randomWalkCleaningCopper(p_147151_, blockpos, blockpos$mutableblockpos, k); } } } private static void randomWalkCleaningCopper(Level p_147146_, BlockPos p_147147_, BlockPos.MutableBlockPos p_147148_, int p_147149_) { p_147148_.set(p_147147_); for (int i = 0; i < p_147149_; i++) { Optional optional = randomStepCleaningCopper(p_147146_, p_147148_); if (optional.isEmpty()) { break; } p_147148_.set(optional.get()); } } private static Optional randomStepCleaningCopper(Level p_147154_, BlockPos p_147155_) { for (BlockPos blockpos : BlockPos.randomInCube(p_147154_.random, 10, p_147155_, 1)) { BlockState blockstate = p_147154_.getBlockState(blockpos); if (blockstate.getBlock() instanceof WeatheringCopper) { WeatheringCopper.getPrevious(blockstate).ifPresent(p_147144_ -> p_147154_.setBlockAndUpdate(blockpos, p_147144_)); p_147154_.levelEvent(3002, blockpos, -1); return Optional.of(blockpos); } } return Optional.empty(); } @Override public boolean shouldRenderAtSqrDistance(double p_20869_) { double d0 = 64.0 * getViewScale(); return p_20869_ < d0 * d0; } @Override protected void defineSynchedData(SynchedEntityData.Builder p_336100_) { } @Override protected void readAdditionalSaveData(CompoundTag p_20873_) { } @Override protected void addAdditionalSaveData(CompoundTag p_20877_) { } public int getBlocksSetOnFire() { return this.blocksSetOnFire; } public Stream getHitEntities() { return this.hitEntities.stream().filter(Entity::isAlive); } @Override public final boolean hurtServer(ServerLevel p_368015_, DamageSource p_364945_, float p_364228_) { return false; } }