234 lines
9.4 KiB
Java
234 lines
9.4 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Sets
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
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.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.entity.Entity;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.item.HoneycombItem;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.BaseFireBlock;
|
|
import net.minecraft.world.level.block.Block;
|
|
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.level.storage.ValueInput;
|
|
import net.minecraft.world.level.storage.ValueOutput;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jspecify.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 = 2;
|
|
public long seed;
|
|
private int flashes;
|
|
private boolean visualOnly;
|
|
private @Nullable ServerPlayer cause;
|
|
private final Set<Entity> hitEntities = Sets.newHashSet();
|
|
private int blocksSetOnFire;
|
|
|
|
public LightningBolt(EntityType<? extends LightningBolt> type, Level level) {
|
|
super(type, level);
|
|
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;
|
|
}
|
|
|
|
public @Nullable ServerPlayer getCause() {
|
|
return this.cause;
|
|
}
|
|
|
|
public void setCause(@Nullable ServerPlayer cause) {
|
|
this.cause = cause;
|
|
}
|
|
|
|
private void powerLightningRod() {
|
|
BlockPos strikePosition = this.getStrikePosition();
|
|
BlockState stateBelow = this.level().getBlockState(strikePosition);
|
|
Block block = stateBelow.getBlock();
|
|
if (block instanceof LightningRodBlock) {
|
|
LightningRodBlock lightningRodBlock = (LightningRodBlock)block;
|
|
lightningRodBlock.onLightningStrike(stateBelow, this.level(), strikePosition);
|
|
}
|
|
}
|
|
|
|
@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();
|
|
LightningBolt.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> viewers = 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), entity -> entity.isAlive() && !this.hitEntities.contains(entity));
|
|
for (ServerPlayer player2 : ((ServerLevel)this.level()).getPlayers(player -> player.distanceTo(this) < 256.0f)) {
|
|
CriteriaTriggers.LIGHTNING_STRIKE.trigger(player2, this, viewers);
|
|
}
|
|
}
|
|
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> entities = 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 entity2 : entities) {
|
|
entity2.thunderHit((ServerLevel)this.level(), this);
|
|
}
|
|
this.hitEntities.addAll(entities);
|
|
if (this.cause != null) {
|
|
CriteriaTriggers.CHANNELED_LIGHTNING.trigger(this.cause, entities);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private BlockPos getStrikePosition() {
|
|
Vec3 position = this.position();
|
|
return BlockPos.containing(position.x, position.y - 1.0E-6, position.z);
|
|
}
|
|
|
|
private void spawnFire(int additionalSources) {
|
|
Level level;
|
|
if (this.visualOnly || !((level = this.level()) instanceof ServerLevel)) {
|
|
return;
|
|
}
|
|
ServerLevel level2 = (ServerLevel)level;
|
|
BlockPos pos = this.blockPosition();
|
|
if (!level2.canSpreadFireAround(pos)) {
|
|
return;
|
|
}
|
|
BlockState fire = BaseFireBlock.getState(level2, pos);
|
|
if (level2.getBlockState(pos).isAir() && fire.canSurvive(level2, pos)) {
|
|
level2.setBlockAndUpdate(pos, fire);
|
|
++this.blocksSetOnFire;
|
|
}
|
|
for (int i = 0; i < additionalSources; ++i) {
|
|
BlockPos nearbyPos = pos.offset(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1);
|
|
fire = BaseFireBlock.getState(level2, nearbyPos);
|
|
if (!level2.getBlockState(nearbyPos).isAir() || !fire.canSurvive(level2, nearbyPos)) continue;
|
|
level2.setBlockAndUpdate(nearbyPos, fire);
|
|
++this.blocksSetOnFire;
|
|
}
|
|
}
|
|
|
|
private static void clearCopperOnLightningStrike(Level level, BlockPos struckPos) {
|
|
BlockState struckState = level.getBlockState(struckPos);
|
|
boolean isWaxed = HoneycombItem.WAX_OFF_BY_BLOCK.get().get((Object)struckState.getBlock()) != null;
|
|
boolean isWeatheringCopper = struckState.getBlock() instanceof WeatheringCopper;
|
|
if (!isWeatheringCopper && !isWaxed) {
|
|
return;
|
|
}
|
|
if (isWeatheringCopper) {
|
|
level.setBlockAndUpdate(struckPos, WeatheringCopper.getFirst(level.getBlockState(struckPos)));
|
|
}
|
|
BlockPos.MutableBlockPos workPos = struckPos.mutable();
|
|
int strikesCount = level.random.nextInt(3) + 3;
|
|
for (int strike = 0; strike < strikesCount; ++strike) {
|
|
int stepCount = level.random.nextInt(8) + 1;
|
|
LightningBolt.randomWalkCleaningCopper(level, struckPos, workPos, stepCount);
|
|
}
|
|
}
|
|
|
|
private static void randomWalkCleaningCopper(Level level, BlockPos originalStrikePos, BlockPos.MutableBlockPos workPos, int stepCount) {
|
|
Optional<BlockPos> stepPos;
|
|
workPos.set(originalStrikePos);
|
|
for (int step = 0; step < stepCount && !(stepPos = LightningBolt.randomStepCleaningCopper(level, workPos)).isEmpty(); ++step) {
|
|
workPos.set(stepPos.get());
|
|
}
|
|
}
|
|
|
|
private static Optional<BlockPos> randomStepCleaningCopper(Level level, BlockPos pos) {
|
|
for (BlockPos candidate : BlockPos.randomInCube(level.random, 10, pos, 1)) {
|
|
BlockState state = level.getBlockState(candidate);
|
|
if (!(state.getBlock() instanceof WeatheringCopper)) continue;
|
|
WeatheringCopper.getPrevious(state).ifPresent(s -> level.setBlockAndUpdate(candidate, (BlockState)s));
|
|
level.levelEvent(3002, candidate, -1);
|
|
return Optional.of(candidate);
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldRenderAtSqrDistance(double distance) {
|
|
double size = 64.0 * LightningBolt.getViewScale();
|
|
return distance < size * size;
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData(SynchedEntityData.Builder entityData) {
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(ValueInput input) {
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(ValueOutput output) {
|
|
}
|
|
|
|
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 source, float damage) {
|
|
return false;
|
|
}
|
|
}
|
|
|