Complete phase 2 atmospheric events
This commit is contained in:
@@ -216,6 +216,13 @@ public class LivingWorldMod {
|
||||
if (biome.is(BiomeTags.IS_OCEAN) || biome.is(BiomeTags.IS_DEEP_OCEAN)) {
|
||||
bootstrap.markOceanicRegion(coord);
|
||||
}
|
||||
if (biome.is(BiomeTags.IS_BADLANDS) || temp >= 2.0f) {
|
||||
bootstrap.markDesertRegion(coord);
|
||||
}
|
||||
if (biome.is(BiomeTags.IS_BEACH) || biome.is(BiomeTags.IS_OCEAN)
|
||||
|| biome.is(BiomeTags.IS_DEEP_OCEAN) || downfall > 0.8f) {
|
||||
bootstrap.markCoastalOrWetlandRegion(coord);
|
||||
}
|
||||
}
|
||||
});
|
||||
NeoForge.EVENT_BUS.addListener(ServerStoppingEvent.class, event -> {
|
||||
@@ -488,6 +495,36 @@ public class LivingWorldMod {
|
||||
player.getX(), player.getY() + 1.0, player.getZ(),
|
||||
4, 5.0, 2.0, 5.0, 0.01);
|
||||
}
|
||||
double humidity = bootstrap.getRegionHumidity(coord);
|
||||
double temperature = bootstrap.getRegionTemperature(coord);
|
||||
double pollution = metricsOpt.map(RegionMetrics::getPollutionScore).orElse(100.0);
|
||||
long localTime = ambLevel.getDayTime() % 24000L;
|
||||
if (bootstrap.isCoastalOrWetlandRegion(coord) && humidity > 0.70
|
||||
&& (atm == null || atm.getThunderLevel() < 0.15)) {
|
||||
ambLevel.sendParticles(player, ParticleTypes.CLOUD, false,
|
||||
player.getX(), player.getY() + 0.5, player.getZ(),
|
||||
5, 10.0, 1.5, 10.0, 0.005);
|
||||
}
|
||||
if ((bootstrap.isDesertRegion(coord) || pollution > 60.0)
|
||||
&& localTime < 12000L) {
|
||||
ambLevel.sendParticles(player, ParticleTypes.FLAME, false,
|
||||
player.getX(), player.getY() + 0.2, player.getZ(),
|
||||
2, 5.0, 0.1, 5.0, 0.01);
|
||||
}
|
||||
if (temperature < 0.15 && localTime > 13000L
|
||||
&& pollution < 10.0 && (atm == null || atm.getRainLevel() < 0.1)) {
|
||||
ambLevel.sendParticles(player, ParticleTypes.END_ROD, false,
|
||||
player.getX(), Math.min(220.0, Math.max(180.0, player.getY() + 100.0)),
|
||||
player.getZ(), 5, 24.0, 4.0, 24.0, 0.01);
|
||||
}
|
||||
if (bootstrap.isClimateEventActive(coord, com.livingworld.climate.ClimateEventType.BLIZZARD)) {
|
||||
double angle = bootstrap.getWindAngle();
|
||||
var movement = player.getDeltaMovement();
|
||||
player.setDeltaMovement(movement.x + Math.cos(angle) * 0.025,
|
||||
movement.y, movement.z + Math.sin(angle) * 0.025);
|
||||
player.addEffect(new MobEffectInstance(
|
||||
MobEffects.MOVEMENT_SLOWDOWN, 60, 0, true, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -737,6 +774,16 @@ public class LivingWorldMod {
|
||||
float temp = biome.value().getBaseTemperature();
|
||||
float downfall = biome.value().getModifiedClimateSettings().downfall();
|
||||
bootstrap.initializeRegionBiomeValues(coord, temp, downfall);
|
||||
if (biome.is(BiomeTags.IS_OCEAN) || biome.is(BiomeTags.IS_DEEP_OCEAN)) {
|
||||
bootstrap.markOceanicRegion(coord);
|
||||
}
|
||||
if (biome.is(BiomeTags.IS_BADLANDS) || temp >= 2.0f) {
|
||||
bootstrap.markDesertRegion(coord);
|
||||
}
|
||||
if (biome.is(BiomeTags.IS_BEACH) || biome.is(BiomeTags.IS_OCEAN)
|
||||
|| biome.is(BiomeTags.IS_DEEP_OCEAN) || downfall > 0.8f) {
|
||||
bootstrap.markCoastalOrWetlandRegion(coord);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int REGION_BLOCKS =
|
||||
|
||||
@@ -127,6 +127,9 @@ public final class LivingWorldBootstrap {
|
||||
/** Ocean-floor regions; can develop submarine volcanoes regardless of surface elevation. */
|
||||
private final Set<RegionCoordinate> oceanicRegions = new HashSet<>();
|
||||
private final Map<RegionCoordinate, Float> regionTemperatures = new HashMap<>();
|
||||
private final Map<RegionCoordinate, Float> regionDownfall = new HashMap<>();
|
||||
private final Set<RegionCoordinate> desertRegions = new HashSet<>();
|
||||
private final Set<RegionCoordinate> coastalWetlandRegions = new HashSet<>();
|
||||
private final Map<RegionCoordinate, Integer> oceanPollutionCycles = new HashMap<>();
|
||||
private final Set<RegionCoordinate> deadZoneRegions = new HashSet<>();
|
||||
private final Set<RegionCoordinate> ventedRegions = new HashSet<>();
|
||||
@@ -291,6 +294,9 @@ public final class LivingWorldBootstrap {
|
||||
volcanicRegions.clear();
|
||||
oceanicRegions.clear();
|
||||
regionTemperatures.clear();
|
||||
regionDownfall.clear();
|
||||
desertRegions.clear();
|
||||
coastalWetlandRegions.clear();
|
||||
oceanPollutionCycles.clear();
|
||||
deadZoneRegions.clear();
|
||||
ventedRegions.clear();
|
||||
@@ -886,6 +892,38 @@ public final class LivingWorldBootstrap {
|
||||
return coord != null && oceanicRegions.contains(coord);
|
||||
}
|
||||
|
||||
public void markDesertRegion(RegionCoordinate coord) {
|
||||
if (coord != null) desertRegions.add(coord);
|
||||
}
|
||||
|
||||
public void markCoastalOrWetlandRegion(RegionCoordinate coord) {
|
||||
if (coord != null) coastalWetlandRegions.add(coord);
|
||||
}
|
||||
|
||||
public boolean isDesertRegion(RegionCoordinate coord) {
|
||||
return coord != null && desertRegions.contains(coord);
|
||||
}
|
||||
|
||||
public boolean isCoastalOrWetlandRegion(RegionCoordinate coord) {
|
||||
return coord != null && coastalWetlandRegions.contains(coord);
|
||||
}
|
||||
|
||||
public float getRegionTemperature(RegionCoordinate coord) {
|
||||
return regionTemperatures.getOrDefault(coord, 0.8f);
|
||||
}
|
||||
|
||||
public float getRegionHumidity(RegionCoordinate coord) {
|
||||
return regionDownfall.getOrDefault(coord, 0.4f);
|
||||
}
|
||||
|
||||
public boolean isClimateEventActive(RegionCoordinate coord, ClimateEventType type) {
|
||||
if (coord == null || type == null) return false;
|
||||
for (ClimateEvent event : activeClimateEvents) {
|
||||
if (event.getType() == type && event.getAffectedRegions().contains(coord)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances reefs, kelp forests, blooms and dead zones. All physical work is queued
|
||||
* through the bounded world-effect executor.
|
||||
@@ -1374,6 +1412,8 @@ public final class LivingWorldBootstrap {
|
||||
RecoveryRegionData recovery = region.getModuleData().get(RecoveryModule.MODULE_ID, RecoveryRegionData.class).orElse(null);
|
||||
PollutionRegionData poll = region.getModuleData().get(PollutionModule.MODULE_ID, PollutionRegionData.class).orElse(null);
|
||||
if (water == null || atm == null || recovery == null) continue;
|
||||
float temperature = regionTemperatures.getOrDefault(coord, 0.8f);
|
||||
float humidity = regionDownfall.getOrDefault(coord, 0.4f);
|
||||
|
||||
// --- DROUGHT trigger ---
|
||||
if (water.getWaterAvailability() < 20 && water.getDroughtRisk() > 65) {
|
||||
@@ -1426,6 +1466,52 @@ public final class LivingWorldBootstrap {
|
||||
+ coord.x() + "," + coord.z() + ") overflowing into lowlands.");
|
||||
}
|
||||
}
|
||||
|
||||
if (poll != null && poll.getAirPollution() > 70.0 && atm.getRainLevel() > 0.25) {
|
||||
ClimateEvent acidRain = new ClimateEvent(
|
||||
ClimateEventType.ACID_RAIN, coord, simTick,
|
||||
Math.min(1.0, poll.getAirPollution() / 100.0));
|
||||
activeClimateEvents.add(acidRain);
|
||||
coveredByEvent.add(coord);
|
||||
pendingEventMessages.add("[LW] Acid rain falling at region ("
|
||||
+ coord.x() + "," + coord.z() + ").");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (temperature < 0.15f && humidity > 0.6f
|
||||
&& atm.getRainLevel() > 0.45 && atm.getThunderLevel() > 0.25) {
|
||||
ClimateEvent blizzard = new ClimateEvent(
|
||||
ClimateEventType.BLIZZARD, coord, simTick,
|
||||
Math.min(1.0, 0.4 + atm.getThunderLevel()));
|
||||
activeClimateEvents.add(blizzard);
|
||||
coveredByEvent.add(coord);
|
||||
pendingEventMessages.add("[LW] Blizzard conditions at region ("
|
||||
+ coord.x() + "," + coord.z() + ").");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desertRegions.contains(coord) && humidity < 0.25f
|
||||
&& atm.getThunderLevel() > 0.25) {
|
||||
ClimateEvent sandstorm = new ClimateEvent(
|
||||
ClimateEventType.SANDSTORM, coord, simTick,
|
||||
Math.min(1.0, 0.35 + atm.getThunderLevel()));
|
||||
activeClimateEvents.add(sandstorm);
|
||||
coveredByEvent.add(coord);
|
||||
pendingEventMessages.add("[LW] Sandstorm crossing region ("
|
||||
+ coord.x() + "," + coord.z() + ").");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (humidity < 0.30f && atm.getThunderLevel() > 0.55
|
||||
&& windRandom.nextDouble() < 0.20) {
|
||||
ClimateEvent lightning = new ClimateEvent(
|
||||
ClimateEventType.LIGHTNING_STORM, coord, simTick,
|
||||
Math.min(1.0, atm.getThunderLevel()));
|
||||
activeClimateEvents.add(lightning);
|
||||
coveredByEvent.add(coord);
|
||||
pendingEventMessages.add("[LW] Dry lightning storm at region ("
|
||||
+ coord.x() + "," + coord.z() + ").");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1517,6 +1603,78 @@ public final class LivingWorldBootstrap {
|
||||
WorldEffectType.ALGAE_BLOOM, coord, ev.getSeverity()));
|
||||
}
|
||||
}
|
||||
case ACID_RAIN -> {
|
||||
AtmosphereRegionData atm = region.getModuleData()
|
||||
.get(AtmosphereModule.MODULE_ID, AtmosphereRegionData.class).orElse(null);
|
||||
PollutionRegionData pollution = region.getModuleData()
|
||||
.get(PollutionModule.MODULE_ID, PollutionRegionData.class).orElse(null);
|
||||
SoilRegionData soil = region.getModuleData()
|
||||
.get(SoilModule.MODULE_ID, SoilRegionData.class).orElse(null);
|
||||
if (atm == null || pollution == null
|
||||
|| atm.getRainLevel() < 0.1 || pollution.getAirPollution() < 70.0) {
|
||||
shouldResolve = true;
|
||||
break;
|
||||
}
|
||||
if (soil != null) {
|
||||
soil.setContamination(Math.min(100, soil.getContamination() + 5.0));
|
||||
region.getModuleData().put(SoilModule.MODULE_ID, soil);
|
||||
}
|
||||
if (worldEffectsModule != null) {
|
||||
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||
WorldEffectType.ACID_RAIN_DAMAGE, coord, ev.getSeverity()));
|
||||
}
|
||||
regionManager.markDirty(region);
|
||||
}
|
||||
case BLIZZARD -> {
|
||||
AtmosphereRegionData atm = region.getModuleData()
|
||||
.get(AtmosphereModule.MODULE_ID, AtmosphereRegionData.class).orElse(null);
|
||||
float temperature = regionTemperatures.getOrDefault(coord, 0.8f);
|
||||
if (temperature > 0.15f || atm == null || atm.getRainLevel() < 0.2) {
|
||||
shouldResolve = true;
|
||||
if (temperature > 0.0f && worldEffectsModule != null) {
|
||||
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||
WorldEffectType.SNOW_MELTS, coord, 0.7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (worldEffectsModule != null) {
|
||||
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||
WorldEffectType.SNOW_ACCUMULATION, coord, ev.getSeverity()));
|
||||
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||
WorldEffectType.WATER_FREEZES, coord, ev.getSeverity()));
|
||||
}
|
||||
}
|
||||
case SANDSTORM -> {
|
||||
AtmosphereRegionData atm = region.getModuleData()
|
||||
.get(AtmosphereModule.MODULE_ID, AtmosphereRegionData.class).orElse(null);
|
||||
if (!desertRegions.contains(coord) || atm == null || atm.getThunderLevel() < 0.15) {
|
||||
shouldResolve = true;
|
||||
break;
|
||||
}
|
||||
if (worldEffectsModule != null) {
|
||||
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||
WorldEffectType.SAND_DEPOSIT, coord, ev.getSeverity()));
|
||||
}
|
||||
SoilRegionData soil = region.getModuleData()
|
||||
.get(SoilModule.MODULE_ID, SoilRegionData.class).orElse(null);
|
||||
if (soil != null) {
|
||||
soil.setContamination(Math.min(100, soil.getContamination() + 0.1));
|
||||
region.getModuleData().put(SoilModule.MODULE_ID, soil);
|
||||
regionManager.markDirty(region);
|
||||
}
|
||||
}
|
||||
case LIGHTNING_STORM -> {
|
||||
AtmosphereRegionData atm = region.getModuleData()
|
||||
.get(AtmosphereModule.MODULE_ID, AtmosphereRegionData.class).orElse(null);
|
||||
if (atm == null || atm.getThunderLevel() < 0.25) {
|
||||
shouldResolve = true;
|
||||
break;
|
||||
}
|
||||
if (worldEffectsModule != null && windRandom.nextDouble() < 0.45) {
|
||||
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||
WorldEffectType.WILDFIRE, coord, 0.25));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1568,6 +1726,7 @@ public final class LivingWorldBootstrap {
|
||||
public void initializeRegionBiomeValues(RegionCoordinate coord, float temp, float downfall) {
|
||||
if (!serverReady || coord == null) return;
|
||||
regionTemperatures.put(coord, temp);
|
||||
regionDownfall.put(coord, downfall);
|
||||
regionManager.resolve(coord).ifPresent(region -> {
|
||||
SoilRegionData soil = region.getModuleData()
|
||||
.get(SoilModule.MODULE_ID, SoilRegionData.class).orElse(null);
|
||||
|
||||
@@ -6,7 +6,11 @@ public enum ClimateEventType {
|
||||
WILDFIRE("Wildfire", "Fire spreading through drought-stressed forest regions"),
|
||||
FLOOD("Flood", "Heavy rainfall overwhelming low-lying terrain"),
|
||||
VOLCANIC_ERUPTION("Volcanic Eruption", "Lava flows and ash clouds from an active volcano"),
|
||||
ALGAE_BLOOM("Algae Bloom", "Nutrient pollution causing a bloom and aquatic dead zone");
|
||||
ALGAE_BLOOM("Algae Bloom", "Nutrient pollution causing a bloom and aquatic dead zone"),
|
||||
ACID_RAIN("Acid Rain", "Polluted rainfall damaging soil, stone and vegetation"),
|
||||
BLIZZARD("Blizzard", "Wind-driven snow accumulation and freezing water"),
|
||||
SANDSTORM("Sandstorm", "Dry high winds stripping plants and depositing sand"),
|
||||
LIGHTNING_STORM("Lightning Storm", "Dry thunderstorm producing isolated ignition strikes");
|
||||
|
||||
private final String displayName;
|
||||
private final String description;
|
||||
|
||||
@@ -130,4 +130,19 @@ public enum WorldEffectType {
|
||||
|
||||
/** Volcanic seafloor activity creates a permanent bubbling vent field. */
|
||||
HYDROTHERMAL_VENT,
|
||||
|
||||
/** Polluted rainfall weathers exposed rock and kills surface vegetation. */
|
||||
ACID_RAIN_DAMAGE,
|
||||
|
||||
/** Blizzard snowfall builds bounded snow layers on exposed terrain. */
|
||||
SNOW_ACCUMULATION,
|
||||
|
||||
/** Blizzard conditions freeze exposed standing water. */
|
||||
WATER_FREEZES,
|
||||
|
||||
/** Warm conditions remove accumulated seasonal snow one layer at a time. */
|
||||
SNOW_MELTS,
|
||||
|
||||
/** Dry high winds deposit sand and strip fragile surface plants. */
|
||||
SAND_DEPOSIT,
|
||||
}
|
||||
|
||||
@@ -103,6 +103,16 @@ public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {
|
||||
deadZone(level, baseX, baseZ, request.intensity());
|
||||
case HYDROTHERMAL_VENT ->
|
||||
hydrothermalVent(level, baseX, baseZ, request.intensity());
|
||||
case ACID_RAIN_DAMAGE ->
|
||||
acidRainDamage(level, baseX, baseZ, request.intensity());
|
||||
case SNOW_ACCUMULATION ->
|
||||
accumulateSnow(level, baseX, baseZ, request.intensity());
|
||||
case WATER_FREEZES ->
|
||||
freezeWater(level, baseX, baseZ, request.intensity());
|
||||
case SNOW_MELTS ->
|
||||
meltSnow(level, baseX, baseZ, request.intensity());
|
||||
case SAND_DEPOSIT ->
|
||||
depositSand(level, baseX, baseZ, request.intensity());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,6 +721,96 @@ public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
private void acidRainDamage(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||
if (random.nextDouble() > 0.25) return;
|
||||
for (int i = 0; i < Math.max(2, (int) (intensity * 8)); i++) {
|
||||
BlockPos pos = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
|
||||
baseZ + random.nextInt(REGION_BLOCKS));
|
||||
if (pos == null || !level.canSeeSky(pos.above())) continue;
|
||||
var state = level.getBlockState(pos);
|
||||
if (state.is(Blocks.STONE) || state.is(Blocks.COBBLESTONE)) {
|
||||
level.setBlock(pos, Blocks.GRAVEL.defaultBlockState(), Block.UPDATE_ALL);
|
||||
} else if (state.is(Blocks.GRASS_BLOCK)) {
|
||||
level.setBlock(pos, Blocks.DIRT.defaultBlockState(), Block.UPDATE_ALL);
|
||||
}
|
||||
BlockPos above = pos.above();
|
||||
if (level.getBlockState(above).is(BlockTags.FLOWERS)
|
||||
|| level.getBlockState(above).is(Blocks.SHORT_GRASS)
|
||||
|| level.getBlockState(above).is(BlockTags.SAPLINGS)) {
|
||||
level.setBlock(above, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void accumulateSnow(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||
if (random.nextDouble() > 0.35) return;
|
||||
for (int i = 0; i < Math.max(2, (int) (intensity * 8)); i++) {
|
||||
BlockPos surface = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
|
||||
baseZ + random.nextInt(REGION_BLOCKS));
|
||||
if (surface == null) continue;
|
||||
BlockPos above = surface.above();
|
||||
if (!level.canSeeSky(above)) continue;
|
||||
var state = level.getBlockState(above);
|
||||
if (state.isAir()) {
|
||||
level.setBlock(above, Blocks.SNOW.defaultBlockState(), Block.UPDATE_ALL);
|
||||
} else if (state.is(Blocks.SNOW)
|
||||
&& state.getValue(net.minecraft.world.level.block.SnowLayerBlock.LAYERS) < 4) {
|
||||
level.setBlock(above, state.setValue(
|
||||
net.minecraft.world.level.block.SnowLayerBlock.LAYERS,
|
||||
state.getValue(net.minecraft.world.level.block.SnowLayerBlock.LAYERS) + 1),
|
||||
Block.UPDATE_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void freezeWater(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||
if (random.nextDouble() > 0.25) return;
|
||||
for (int i = 0; i < Math.max(1, (int) (intensity * 6)); i++) {
|
||||
int x = baseX + random.nextInt(REGION_BLOCKS);
|
||||
int z = baseZ + random.nextInt(REGION_BLOCKS);
|
||||
int y = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z) - 1;
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
if (level.isLoaded(pos) && level.getBlockState(pos).is(Blocks.WATER)
|
||||
&& level.getFluidState(pos).isSource() && level.canSeeSky(pos.above())) {
|
||||
level.setBlock(pos, Blocks.ICE.defaultBlockState(), Block.UPDATE_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void meltSnow(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||
for (int i = 0; i < Math.max(2, (int) (intensity * 8)); i++) {
|
||||
BlockPos pos = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
|
||||
baseZ + random.nextInt(REGION_BLOCKS));
|
||||
if (pos == null || !level.getBlockState(pos).is(Blocks.SNOW)) continue;
|
||||
var state = level.getBlockState(pos);
|
||||
int layers = state.getValue(net.minecraft.world.level.block.SnowLayerBlock.LAYERS);
|
||||
level.setBlock(pos, layers > 1
|
||||
? state.setValue(net.minecraft.world.level.block.SnowLayerBlock.LAYERS, layers - 1)
|
||||
: Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
private void depositSand(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||
if (random.nextDouble() > 0.30) return;
|
||||
for (int i = 0; i < Math.max(2, (int) (intensity * 8)); i++) {
|
||||
BlockPos surface = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
|
||||
baseZ + random.nextInt(REGION_BLOCKS));
|
||||
if (surface == null) continue;
|
||||
BlockPos above = surface.above();
|
||||
var aboveState = level.getBlockState(above);
|
||||
if (aboveState.is(BlockTags.FLOWERS) || aboveState.is(Blocks.SHORT_GRASS)
|
||||
|| aboveState.is(Blocks.DEAD_BUSH)) {
|
||||
level.setBlock(above, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||
} else if (aboveState.isAir() && level.canSeeSky(above)
|
||||
&& (level.getBlockState(surface).is(Blocks.SAND)
|
||||
|| level.getBlockState(surface).is(Blocks.RED_SAND)
|
||||
|| level.getBlockState(surface).is(Blocks.SANDSTONE)
|
||||
|| level.getBlockState(surface).is(Blocks.TERRACOTTA))) {
|
||||
level.setBlock(above, Blocks.SAND.defaultBlockState(), Block.UPDATE_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BlockPos surfaceAt(ServerLevel level, int x, int z) {
|
||||
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;
|
||||
if (y < level.getMinBuildHeight()) {
|
||||
|
||||
Reference in New Issue
Block a user