254 lines
8.1 KiB
Java
254 lines
8.1 KiB
Java
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 net.minecraft.advancements.CriteriaTriggers;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.syncher.SynchedEntityData.Builder;
|
|
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;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
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<Entity> hitEntities = Sets.<Entity>newHashSet();
|
|
private int blocksSetOnFire;
|
|
|
|
public LightningBolt(EntityType<? extends LightningBolt> entityType, Level level) {
|
|
super(entityType, level);
|
|
this.life = 2;
|
|
this.seed = this.random.nextLong();
|
|
this.flashes = this.random.nextInt(3) + 1;
|
|
}
|
|
|
|
public void setVisualOnly(boolean visualOnly) {
|
|
this.visualOnly = visualOnly;
|
|
}
|
|
|
|
@Override
|
|
public SoundSource getSoundSource() {
|
|
return SoundSource.WEATHER;
|
|
}
|
|
|
|
@Nullable
|
|
public ServerPlayer getCause() {
|
|
return this.cause;
|
|
}
|
|
|
|
public void setCause(@Nullable ServerPlayer cause) {
|
|
this.cause = cause;
|
|
}
|
|
|
|
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<Entity> 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),
|
|
entityx -> entityx.isAlive() && !this.hitEntities.contains(entityx)
|
|
);
|
|
|
|
for (ServerPlayer serverPlayer : ((ServerLevel)this.level()).getPlayers(serverPlayerx -> serverPlayerx.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<Entity> list = 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 : list) {
|
|
entity.thunderHit((ServerLevel)this.level(), this);
|
|
}
|
|
|
|
this.hitEntities.addAll(list);
|
|
if (this.cause != null) {
|
|
CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, list);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private BlockPos getStrikePosition() {
|
|
Vec3 vec3 = this.position();
|
|
return BlockPos.containing(vec3.x, vec3.y - 1.0E-6, vec3.z);
|
|
}
|
|
|
|
private void spawnFire(int extraIgnitions) {
|
|
if (!this.visualOnly && this.level() instanceof ServerLevel serverLevel && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOFIRETICK)) {
|
|
BlockPos blockPos = this.blockPosition();
|
|
BlockState blockState = BaseFireBlock.getState(this.level(), blockPos);
|
|
if (this.level().getBlockState(blockPos).isAir() && blockState.canSurvive(this.level(), blockPos)) {
|
|
this.level().setBlockAndUpdate(blockPos, blockState);
|
|
this.blocksSetOnFire++;
|
|
}
|
|
|
|
for (int i = 0; i < extraIgnitions; i++) {
|
|
BlockPos blockPos2 = blockPos.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1);
|
|
blockState = BaseFireBlock.getState(this.level(), blockPos2);
|
|
if (this.level().getBlockState(blockPos2).isAir() && blockState.canSurvive(this.level(), blockPos2)) {
|
|
this.level().setBlockAndUpdate(blockPos2, blockState);
|
|
this.blocksSetOnFire++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void clearCopperOnLightningStrike(Level level, BlockPos pos) {
|
|
BlockState blockState = level.getBlockState(pos);
|
|
BlockPos blockPos;
|
|
BlockState blockState2;
|
|
if (blockState.is(Blocks.LIGHTNING_ROD)) {
|
|
blockPos = pos.relative(((Direction)blockState.getValue(LightningRodBlock.FACING)).getOpposite());
|
|
blockState2 = level.getBlockState(blockPos);
|
|
} else {
|
|
blockPos = pos;
|
|
blockState2 = blockState;
|
|
}
|
|
|
|
if (blockState2.getBlock() instanceof WeatheringCopper) {
|
|
level.setBlockAndUpdate(blockPos, WeatheringCopper.getFirst(level.getBlockState(blockPos)));
|
|
BlockPos.MutableBlockPos mutableBlockPos = pos.mutable();
|
|
int i = level.random.nextInt(3) + 3;
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
int k = level.random.nextInt(8) + 1;
|
|
randomWalkCleaningCopper(level, blockPos, mutableBlockPos, k);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void randomWalkCleaningCopper(Level level, BlockPos pos, BlockPos.MutableBlockPos mutable, int steps) {
|
|
mutable.set(pos);
|
|
|
|
for (int i = 0; i < steps; i++) {
|
|
Optional<BlockPos> optional = randomStepCleaningCopper(level, mutable);
|
|
if (optional.isEmpty()) {
|
|
break;
|
|
}
|
|
|
|
mutable.set((Vec3i)optional.get());
|
|
}
|
|
}
|
|
|
|
private static Optional<BlockPos> randomStepCleaningCopper(Level level, BlockPos pos) {
|
|
for (BlockPos blockPos : BlockPos.randomInCube(level.random, 10, pos, 1)) {
|
|
BlockState blockState = level.getBlockState(blockPos);
|
|
if (blockState.getBlock() instanceof WeatheringCopper) {
|
|
WeatheringCopper.getPrevious(blockState).ifPresent(blockStatex -> level.setBlockAndUpdate(blockPos, blockStatex));
|
|
level.levelEvent(3002, blockPos, -1);
|
|
return Optional.of(blockPos);
|
|
}
|
|
}
|
|
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldRenderAtSqrDistance(double distance) {
|
|
double d = 64.0 * getViewScale();
|
|
return distance < d * d;
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData(Builder builder) {
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(CompoundTag tag) {
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(CompoundTag tag) {
|
|
}
|
|
|
|
public int getBlocksSetOnFire() {
|
|
return this.blocksSetOnFire;
|
|
}
|
|
|
|
public Stream<Entity> getHitEntities() {
|
|
return this.hitEntities.stream().filter(Entity::isAlive);
|
|
}
|
|
|
|
@Override
|
|
public final boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) {
|
|
return false;
|
|
}
|
|
}
|