Complete phase 3 geological activity
This commit is contained in:
@@ -33,6 +33,7 @@ import net.minecraft.world.entity.animal.Squid;
|
|||||||
import net.minecraft.world.entity.monster.Monster;
|
import net.minecraft.world.entity.monster.Monster;
|
||||||
import net.minecraft.world.item.Items;
|
import net.minecraft.world.item.Items;
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.CampfireBlockEntity;
|
import net.minecraft.world.level.block.entity.CampfireBlockEntity;
|
||||||
@@ -223,6 +224,10 @@ public class LivingWorldMod {
|
|||||||
|| biome.is(BiomeTags.IS_DEEP_OCEAN) || downfall > 0.8f) {
|
|| biome.is(BiomeTags.IS_DEEP_OCEAN) || downfall > 0.8f) {
|
||||||
bootstrap.markCoastalOrWetlandRegion(coord);
|
bootstrap.markCoastalOrWetlandRegion(coord);
|
||||||
}
|
}
|
||||||
|
int caveY = Math.max(level.getMinBuildHeight() + 1, by - 32);
|
||||||
|
if (level.getBiome(new BlockPos(bx, caveY, bz)).is(Biomes.DRIPSTONE_CAVES)) {
|
||||||
|
bootstrap.markGeothermalRegion(coord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
NeoForge.EVENT_BUS.addListener(ServerStoppingEvent.class, event -> {
|
NeoForge.EVENT_BUS.addListener(ServerStoppingEvent.class, event -> {
|
||||||
@@ -525,6 +530,14 @@ public class LivingWorldMod {
|
|||||||
player.addEffect(new MobEffectInstance(
|
player.addEffect(new MobEffectInstance(
|
||||||
MobEffects.MOVEMENT_SLOWDOWN, 60, 0, true, false));
|
MobEffects.MOVEMENT_SLOWDOWN, 60, 0, true, false));
|
||||||
}
|
}
|
||||||
|
if (bootstrap.isClimateEventActive(
|
||||||
|
coord, com.livingworld.climate.ClimateEventType.EARTHQUAKE)) {
|
||||||
|
var movement = player.getDeltaMovement();
|
||||||
|
player.setDeltaMovement(
|
||||||
|
movement.x + (random.nextDouble() - 0.5) * 0.35,
|
||||||
|
Math.max(movement.y, 0.12),
|
||||||
|
movement.z + (random.nextDouble() - 0.5) * 0.35);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -784,6 +797,10 @@ public class LivingWorldMod {
|
|||||||
|| biome.is(BiomeTags.IS_DEEP_OCEAN) || downfall > 0.8f) {
|
|| biome.is(BiomeTags.IS_DEEP_OCEAN) || downfall > 0.8f) {
|
||||||
bootstrap.markCoastalOrWetlandRegion(coord);
|
bootstrap.markCoastalOrWetlandRegion(coord);
|
||||||
}
|
}
|
||||||
|
int caveY = Math.max(level.getMinBuildHeight() + 1, cy - 32);
|
||||||
|
if (level.getBiome(new BlockPos(cx, caveY, cz)).is(Biomes.DRIPSTONE_CAVES)) {
|
||||||
|
bootstrap.markGeothermalRegion(coord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int REGION_BLOCKS =
|
private static final int REGION_BLOCKS =
|
||||||
|
|||||||
@@ -130,12 +130,16 @@ public final class LivingWorldBootstrap {
|
|||||||
private final Map<RegionCoordinate, Float> regionDownfall = new HashMap<>();
|
private final Map<RegionCoordinate, Float> regionDownfall = new HashMap<>();
|
||||||
private final Set<RegionCoordinate> desertRegions = new HashSet<>();
|
private final Set<RegionCoordinate> desertRegions = new HashSet<>();
|
||||||
private final Set<RegionCoordinate> coastalWetlandRegions = new HashSet<>();
|
private final Set<RegionCoordinate> coastalWetlandRegions = new HashSet<>();
|
||||||
|
private final Set<RegionCoordinate> geothermalRegions = new HashSet<>();
|
||||||
private final Map<RegionCoordinate, Integer> oceanPollutionCycles = new HashMap<>();
|
private final Map<RegionCoordinate, Integer> oceanPollutionCycles = new HashMap<>();
|
||||||
private final Set<RegionCoordinate> deadZoneRegions = new HashSet<>();
|
private final Set<RegionCoordinate> deadZoneRegions = new HashSet<>();
|
||||||
private final Set<RegionCoordinate> ventedRegions = new HashSet<>();
|
private final Set<RegionCoordinate> ventedRegions = new HashSet<>();
|
||||||
/** Regions whose terrain has changed significantly and need an elevation re-sample. */
|
/** Regions whose terrain has changed significantly and need an elevation re-sample. */
|
||||||
private final Set<RegionCoordinate> pendingElevationResample = new HashSet<>();
|
private final Set<RegionCoordinate> pendingElevationResample = new HashSet<>();
|
||||||
private int volcanicActivityTick = 0;
|
private int volcanicActivityTick = 0;
|
||||||
|
private record GeyserSite(int cycleTick, boolean active) {}
|
||||||
|
private final Map<RegionCoordinate, GeyserSite> geyserSites = new HashMap<>();
|
||||||
|
private int geologicalActivityTick = 0;
|
||||||
|
|
||||||
private PlatformAdapter platformAdapter;
|
private PlatformAdapter platformAdapter;
|
||||||
private Path worldSaveDirectory;
|
private Path worldSaveDirectory;
|
||||||
@@ -297,11 +301,14 @@ public final class LivingWorldBootstrap {
|
|||||||
regionDownfall.clear();
|
regionDownfall.clear();
|
||||||
desertRegions.clear();
|
desertRegions.clear();
|
||||||
coastalWetlandRegions.clear();
|
coastalWetlandRegions.clear();
|
||||||
|
geothermalRegions.clear();
|
||||||
oceanPollutionCycles.clear();
|
oceanPollutionCycles.clear();
|
||||||
deadZoneRegions.clear();
|
deadZoneRegions.clear();
|
||||||
ventedRegions.clear();
|
ventedRegions.clear();
|
||||||
pendingElevationResample.clear();
|
pendingElevationResample.clear();
|
||||||
volcanicActivityTick = 0;
|
volcanicActivityTick = 0;
|
||||||
|
geyserSites.clear();
|
||||||
|
geologicalActivityTick = 0;
|
||||||
simSpeedMultiplier = 1;
|
simSpeedMultiplier = 1;
|
||||||
serverReady = false;
|
serverReady = false;
|
||||||
LivingWorldLogger.info(
|
LivingWorldLogger.info(
|
||||||
@@ -464,6 +471,95 @@ public final class LivingWorldBootstrap {
|
|||||||
if (++volcanicActivityTick % VOLCANO_TICK_INTERVAL == 0) {
|
if (++volcanicActivityTick % VOLCANO_TICK_INTERVAL == 0) {
|
||||||
applyVolcanicActivity();
|
applyVolcanicActivity();
|
||||||
}
|
}
|
||||||
|
if (++geologicalActivityTick % 4 == 0) {
|
||||||
|
applyGeologicalActivity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyGeologicalActivity() {
|
||||||
|
Collection<Region> active = regionManager.getActiveRegions();
|
||||||
|
if (active.isEmpty() || worldEffectsModule == null) return;
|
||||||
|
long simTick = simulationManager.getSimulationTickCounter();
|
||||||
|
Map<RegionCoordinate, Region> byCoord = new HashMap<>();
|
||||||
|
for (Region region : active) byCoord.put(region.getCoordinate(), region);
|
||||||
|
int[][] offsets = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||||
|
|
||||||
|
for (Region region : active) {
|
||||||
|
RegionCoordinate coord = region.getCoordinate();
|
||||||
|
double elevation = regionElevations.getOrDefault(coord, 64.0);
|
||||||
|
VolcanoPhase phase = volcanoPhase.get(coord);
|
||||||
|
|
||||||
|
boolean volcanicAdjacent = volcanicRegions.contains(coord);
|
||||||
|
for (int[] offset : offsets) {
|
||||||
|
RegionCoordinate neighbour = new RegionCoordinate(
|
||||||
|
coord.dimensionId(), coord.x() + offset[0], coord.z() + offset[1]);
|
||||||
|
volcanicAdjacent |= volcanicRegions.contains(neighbour);
|
||||||
|
}
|
||||||
|
if (!geyserSites.containsKey(coord)
|
||||||
|
&& (volcanicAdjacent || elevation > 100.0 || geothermalRegions.contains(coord))
|
||||||
|
&& windRandom.nextDouble() < 0.02) {
|
||||||
|
geyserSites.put(coord, new GeyserSite(0, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserSite site = geyserSites.get(coord);
|
||||||
|
if (site != null) {
|
||||||
|
int cycle = (site.cycleTick() + 1) % 165;
|
||||||
|
boolean activeNow = cycle >= 150;
|
||||||
|
geyserSites.put(coord, new GeyserSite(cycle, activeNow));
|
||||||
|
if (activeNow) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
WorldEffectType.GEYSER_ERUPT, coord, 0.8));
|
||||||
|
if (cycle == 150) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
WorldEffectType.TRAVERTINE_DEPOSIT, coord, 0.7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double quakeChance = phase == VolcanoPhase.BUILDING || phase == VolcanoPhase.ERUPTING
|
||||||
|
? 0.003 : elevation > 90.0 ? 0.001 : 0.0;
|
||||||
|
if (quakeChance > 0 && windRandom.nextDouble() < quakeChance
|
||||||
|
&& !isClimateEventActive(coord, ClimateEventType.EARTHQUAKE)) {
|
||||||
|
double magnitude = 1.0 + windRandom.nextDouble() * 4.0;
|
||||||
|
ClimateEvent quake = new ClimateEvent(
|
||||||
|
ClimateEventType.EARTHQUAKE, coord, simTick, magnitude / 5.0);
|
||||||
|
activeClimateEvents.add(quake);
|
||||||
|
pendingEventMessages.add("[LW] Earthquake magnitude "
|
||||||
|
+ String.format("%.1f", magnitude) + " at region ("
|
||||||
|
+ coord.x() + "," + coord.z() + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
double groundwater = groundwaterLevel.getOrDefault(coord, 0.0);
|
||||||
|
if (groundwater > 80.0 && windRandom.nextDouble() < 0.0005
|
||||||
|
&& !isClimateEventActive(coord, ClimateEventType.SINKHOLE)) {
|
||||||
|
ClimateEvent sinkhole = new ClimateEvent(
|
||||||
|
ClimateEventType.SINKHOLE, coord, simTick, 0.8);
|
||||||
|
activeClimateEvents.add(sinkhole);
|
||||||
|
pendingEventMessages.add("[LW] Sinkhole collapse at region ("
|
||||||
|
+ coord.x() + "," + coord.z() + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
AtmosphereRegionData atmosphere = region.getModuleData()
|
||||||
|
.get(AtmosphereModule.MODULE_ID, AtmosphereRegionData.class).orElse(null);
|
||||||
|
if (atmosphere != null && atmosphere.getRainLevel() > 0.55) {
|
||||||
|
for (int[] offset : offsets) {
|
||||||
|
RegionCoordinate neighbour = new RegionCoordinate(
|
||||||
|
coord.dimensionId(), coord.x() + offset[0], coord.z() + offset[1]);
|
||||||
|
Double neighbourElevation = regionElevations.get(neighbour);
|
||||||
|
if (neighbourElevation != null && elevation - neighbourElevation > 15.0
|
||||||
|
&& windRandom.nextDouble() < 0.01) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
WorldEffectType.LANDSLIDE, coord, 0.7));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phase == VolcanoPhase.COOLING && windRandom.nextDouble() < 0.001) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
WorldEffectType.LAVA_TUBE_COLLAPSE, coord, 0.8));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the simulation speed multiplier (1 = real-time, max 100). */
|
/** Sets the simulation speed multiplier (1 = real-time, max 100). */
|
||||||
@@ -900,6 +996,10 @@ public final class LivingWorldBootstrap {
|
|||||||
if (coord != null) coastalWetlandRegions.add(coord);
|
if (coord != null) coastalWetlandRegions.add(coord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markGeothermalRegion(RegionCoordinate coord) {
|
||||||
|
if (coord != null) geothermalRegions.add(coord);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDesertRegion(RegionCoordinate coord) {
|
public boolean isDesertRegion(RegionCoordinate coord) {
|
||||||
return coord != null && desertRegions.contains(coord);
|
return coord != null && desertRegions.contains(coord);
|
||||||
}
|
}
|
||||||
@@ -1675,6 +1775,26 @@ public final class LivingWorldBootstrap {
|
|||||||
WorldEffectType.WILDFIRE, coord, 0.25));
|
WorldEffectType.WILDFIRE, coord, 0.25));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case EARTHQUAKE -> {
|
||||||
|
if (ev.getTicksActive() == 1 && worldEffectsModule != null) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
ev.getSeverity() >= 0.8
|
||||||
|
? WorldEffectType.FISSURE_OPENS : WorldEffectType.GROUND_CRACK,
|
||||||
|
coord, ev.getSeverity()));
|
||||||
|
if (ev.getSeverity() >= 0.8) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
WorldEffectType.LANDSLIDE, coord, ev.getSeverity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev.getTicksActive() >= 3) shouldResolve = true;
|
||||||
|
}
|
||||||
|
case SINKHOLE -> {
|
||||||
|
if (ev.getTicksActive() == 1 && worldEffectsModule != null) {
|
||||||
|
worldEffectsModule.queueEffect(new WorldEffectRequest(
|
||||||
|
WorldEffectType.SINKHOLE_COLLAPSES, coord, ev.getSeverity()));
|
||||||
|
}
|
||||||
|
if (ev.getTicksActive() >= 2) shouldResolve = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ public enum ClimateEventType {
|
|||||||
ACID_RAIN("Acid Rain", "Polluted rainfall damaging soil, stone and vegetation"),
|
ACID_RAIN("Acid Rain", "Polluted rainfall damaging soil, stone and vegetation"),
|
||||||
BLIZZARD("Blizzard", "Wind-driven snow accumulation and freezing water"),
|
BLIZZARD("Blizzard", "Wind-driven snow accumulation and freezing water"),
|
||||||
SANDSTORM("Sandstorm", "Dry high winds stripping plants and depositing sand"),
|
SANDSTORM("Sandstorm", "Dry high winds stripping plants and depositing sand"),
|
||||||
LIGHTNING_STORM("Lightning Storm", "Dry thunderstorm producing isolated ignition strikes");
|
LIGHTNING_STORM("Lightning Storm", "Dry thunderstorm producing isolated ignition strikes"),
|
||||||
|
EARTHQUAKE("Earthquake", "Seismic rupture opening cracks and unstable slopes"),
|
||||||
|
SINKHOLE("Sinkhole", "Groundwater-driven collapse into a shallow cave");
|
||||||
|
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|||||||
@@ -145,4 +145,25 @@ public enum WorldEffectType {
|
|||||||
|
|
||||||
/** Dry high winds deposit sand and strip fragile surface plants. */
|
/** Dry high winds deposit sand and strip fragile surface plants. */
|
||||||
SAND_DEPOSIT,
|
SAND_DEPOSIT,
|
||||||
|
|
||||||
|
/** A registered geothermal site ejects a temporary column of water. */
|
||||||
|
GEYSER_ERUPT,
|
||||||
|
|
||||||
|
/** Repeated geyser eruptions build a calcite and tuff travertine ring. */
|
||||||
|
TRAVERTINE_DEPOSIT,
|
||||||
|
|
||||||
|
/** Low-magnitude seismic activity removes a short line of surface blocks. */
|
||||||
|
GROUND_CRACK,
|
||||||
|
|
||||||
|
/** High-magnitude seismic activity opens a narrow, deep fissure. */
|
||||||
|
FISSURE_OPENS,
|
||||||
|
|
||||||
|
/** Saturated ground collapses into a detected shallow cave. */
|
||||||
|
SINKHOLE_COLLAPSES,
|
||||||
|
|
||||||
|
/** Unstable steep terrain transfers loose surface material downhill. */
|
||||||
|
LANDSLIDE,
|
||||||
|
|
||||||
|
/** A cooling volcanic roof collapses to reveal a lava-adjacent cavity. */
|
||||||
|
LAVA_TUBE_COLLAPSE,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import com.livingworld.debug.LivingWorldLogger;
|
|||||||
import com.livingworld.modules.worldeffects.WorldEffectConsumer;
|
import com.livingworld.modules.worldeffects.WorldEffectConsumer;
|
||||||
import com.livingworld.modules.worldeffects.WorldEffectRequest;
|
import com.livingworld.modules.worldeffects.WorldEffectRequest;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
@@ -38,6 +42,8 @@ public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {
|
|||||||
|
|
||||||
private final Supplier<MinecraftServer> serverSupplier;
|
private final Supplier<MinecraftServer> serverSupplier;
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
private record TimedWater(BlockPos pos, long expiresAt) {}
|
||||||
|
private final Map<ResourceKey<Level>, List<TimedWater>> geyserWater = new HashMap<>();
|
||||||
|
|
||||||
public NeoForgeWorldEffectExecutor(Supplier<MinecraftServer> serverSupplier) {
|
public NeoForgeWorldEffectExecutor(Supplier<MinecraftServer> serverSupplier) {
|
||||||
this.serverSupplier = serverSupplier;
|
this.serverSupplier = serverSupplier;
|
||||||
@@ -58,6 +64,7 @@ public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {
|
|||||||
}
|
}
|
||||||
int baseX = request.region().x() * REGION_BLOCKS;
|
int baseX = request.region().x() * REGION_BLOCKS;
|
||||||
int baseZ = request.region().z() * REGION_BLOCKS;
|
int baseZ = request.region().z() * REGION_BLOCKS;
|
||||||
|
cleanExpiredGeyserWater(level, dimensionKey);
|
||||||
|
|
||||||
switch (request.type()) {
|
switch (request.type()) {
|
||||||
case GRASS_DEGRADES_TO_DIRT ->
|
case GRASS_DEGRADES_TO_DIRT ->
|
||||||
@@ -113,6 +120,20 @@ public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {
|
|||||||
meltSnow(level, baseX, baseZ, request.intensity());
|
meltSnow(level, baseX, baseZ, request.intensity());
|
||||||
case SAND_DEPOSIT ->
|
case SAND_DEPOSIT ->
|
||||||
depositSand(level, baseX, baseZ, request.intensity());
|
depositSand(level, baseX, baseZ, request.intensity());
|
||||||
|
case GEYSER_ERUPT ->
|
||||||
|
eruptGeyser(level, dimensionKey, baseX, baseZ, request.intensity());
|
||||||
|
case TRAVERTINE_DEPOSIT ->
|
||||||
|
depositTravertine(level, baseX, baseZ, request.intensity());
|
||||||
|
case GROUND_CRACK ->
|
||||||
|
openGroundCrack(level, baseX, baseZ, request.intensity(), false);
|
||||||
|
case FISSURE_OPENS ->
|
||||||
|
openGroundCrack(level, baseX, baseZ, request.intensity(), true);
|
||||||
|
case SINKHOLE_COLLAPSES ->
|
||||||
|
collapseSinkhole(level, baseX, baseZ, request.intensity());
|
||||||
|
case LANDSLIDE ->
|
||||||
|
landslide(level, baseX, baseZ, request.intensity());
|
||||||
|
case LAVA_TUBE_COLLAPSE ->
|
||||||
|
collapseLavaTube(level, baseX, baseZ, request.intensity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,6 +832,165 @@ public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanExpiredGeyserWater(ServerLevel level, ResourceKey<Level> dimensionKey) {
|
||||||
|
List<TimedWater> entries = geyserWater.get(dimensionKey);
|
||||||
|
if (entries == null) return;
|
||||||
|
long now = level.getGameTime();
|
||||||
|
entries.removeIf(entry -> {
|
||||||
|
if (entry.expiresAt() > now) return false;
|
||||||
|
if (level.isLoaded(entry.pos()) && level.getBlockState(entry.pos()).is(Blocks.WATER)) {
|
||||||
|
level.setBlock(entry.pos(), Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (entries.isEmpty()) geyserWater.remove(dimensionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eruptGeyser(ServerLevel level, ResourceKey<Level> dimensionKey,
|
||||||
|
int baseX, int baseZ, double intensity) {
|
||||||
|
int x = baseX + REGION_BLOCKS / 2;
|
||||||
|
int z = baseZ + REGION_BLOCKS / 2;
|
||||||
|
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z);
|
||||||
|
List<TimedWater> entries = geyserWater.computeIfAbsent(dimensionKey, ignored -> new ArrayList<>());
|
||||||
|
int height = 3 + random.nextInt(3);
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
BlockPos pos = new BlockPos(x, y + i, z);
|
||||||
|
if (!level.isLoaded(pos) || (!level.getBlockState(pos).isAir()
|
||||||
|
&& !level.getBlockState(pos).is(Blocks.WATER))) break;
|
||||||
|
level.setBlock(pos, Blocks.WATER.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
entries.add(new TimedWater(pos, level.getGameTime() + 40));
|
||||||
|
level.sendParticles(ParticleTypes.BUBBLE_COLUMN_UP,
|
||||||
|
x + 0.5, y + i + 0.5, z + 0.5, 3, 0.2, 0.3, 0.2, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void depositTravertine(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||||
|
int x = baseX + REGION_BLOCKS / 2;
|
||||||
|
int z = baseZ + REGION_BLOCKS / 2;
|
||||||
|
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;
|
||||||
|
int radius = 2 + random.nextInt(3);
|
||||||
|
int writes = 0;
|
||||||
|
for (int dx = -radius; dx <= radius && writes < 10; dx++) {
|
||||||
|
for (int dz = -radius; dz <= radius && writes < 10; dz++) {
|
||||||
|
if (Math.abs(Math.sqrt(dx * dx + dz * dz) - radius) > 0.75) continue;
|
||||||
|
BlockPos pos = new BlockPos(x + dx, y, z + dz);
|
||||||
|
if (!level.isLoaded(pos)) continue;
|
||||||
|
var state = level.getBlockState(pos);
|
||||||
|
if (state.is(Blocks.DIRT) || state.is(Blocks.GRASS_BLOCK)
|
||||||
|
|| state.is(Blocks.STONE) || state.is(Blocks.GRAVEL)) {
|
||||||
|
level.setBlock(pos, random.nextBoolean() ? Blocks.CALCITE.defaultBlockState()
|
||||||
|
: Blocks.TUFF.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
writes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openGroundCrack(ServerLevel level, int baseX, int baseZ,
|
||||||
|
double intensity, boolean fissure) {
|
||||||
|
int x = baseX + random.nextInt(REGION_BLOCKS);
|
||||||
|
int z = baseZ + random.nextInt(REGION_BLOCKS);
|
||||||
|
int dx = random.nextBoolean() ? 1 : 0;
|
||||||
|
int dz = dx == 0 ? 1 : 0;
|
||||||
|
int writes = 0;
|
||||||
|
int length = fissure ? 5 : 3;
|
||||||
|
int depth = fissure ? 8 + random.nextInt(8) : 1;
|
||||||
|
for (int step = 0; step < length && writes < 10; step++) {
|
||||||
|
int surfaceY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
|
||||||
|
x + dx * step, z + dz * step) - 1;
|
||||||
|
for (int d = 0; d < depth && writes < 10; d++) {
|
||||||
|
BlockPos pos = new BlockPos(x + dx * step, surfaceY - d, z + dz * step);
|
||||||
|
if (!level.isLoaded(pos) || !isNaturalTerrain(level.getBlockState(pos))) continue;
|
||||||
|
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
writes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collapseSinkhole(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||||
|
int centerX = baseX + random.nextInt(REGION_BLOCKS);
|
||||||
|
int centerZ = baseZ + random.nextInt(REGION_BLOCKS);
|
||||||
|
int surfaceY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, centerX, centerZ) - 1;
|
||||||
|
int caveY = Integer.MIN_VALUE;
|
||||||
|
for (int y = surfaceY - 3; y >= Math.max(level.getMinBuildHeight(), surfaceY - 15); y--) {
|
||||||
|
BlockPos probe = new BlockPos(centerX, y, centerZ);
|
||||||
|
if (level.isLoaded(probe) && level.getBlockState(probe).isAir()) {
|
||||||
|
caveY = y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (caveY == Integer.MIN_VALUE) return;
|
||||||
|
int radius = 1 + random.nextInt(2);
|
||||||
|
int writes = 0;
|
||||||
|
for (int dx = -radius; dx <= radius && writes < 10; dx++) {
|
||||||
|
for (int dz = -radius; dz <= radius && writes < 10; dz++) {
|
||||||
|
for (int y = surfaceY; y > caveY && writes < 10; y--) {
|
||||||
|
BlockPos pos = new BlockPos(centerX + dx, y, centerZ + dz);
|
||||||
|
if (!level.isLoaded(pos) || !isNaturalTerrain(level.getBlockState(pos))) continue;
|
||||||
|
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
writes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void landslide(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||||
|
BlockPos highest = null;
|
||||||
|
BlockPos lowest = null;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
BlockPos sample = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
|
||||||
|
baseZ + random.nextInt(REGION_BLOCKS));
|
||||||
|
if (sample == null) continue;
|
||||||
|
if (highest == null || sample.getY() > highest.getY()) highest = sample;
|
||||||
|
if (lowest == null || sample.getY() < lowest.getY()) lowest = sample;
|
||||||
|
}
|
||||||
|
if (highest == null || lowest == null || highest.getY() - lowest.getY() < 8) return;
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
BlockPos source = highest.below(i);
|
||||||
|
var state = level.getBlockState(source);
|
||||||
|
if (!state.is(Blocks.DIRT) && !state.is(Blocks.GRAVEL) && !state.is(Blocks.SAND)) continue;
|
||||||
|
BlockPos target = lowest.above(i + 1);
|
||||||
|
if (!level.isLoaded(target) || !level.getBlockState(target).isAir()) continue;
|
||||||
|
level.setBlock(source, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
level.setBlock(target, state, Block.UPDATE_ALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collapseLavaTube(ServerLevel level, int baseX, int baseZ, double intensity) {
|
||||||
|
for (int attempt = 0; attempt < 10; attempt++) {
|
||||||
|
int x = baseX + random.nextInt(REGION_BLOCKS);
|
||||||
|
int z = baseZ + random.nextInt(REGION_BLOCKS);
|
||||||
|
int surfaceY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;
|
||||||
|
for (int y = surfaceY - 2; y >= Math.max(level.getMinBuildHeight(), surfaceY - 20); y--) {
|
||||||
|
BlockPos air = new BlockPos(x, y, z);
|
||||||
|
if (!level.isLoaded(air) || !level.getBlockState(air).isAir()) continue;
|
||||||
|
boolean lavaAdjacent = false;
|
||||||
|
for (Direction direction : Direction.values()) {
|
||||||
|
if (level.getBlockState(air.relative(direction)).is(Blocks.LAVA)) {
|
||||||
|
lavaAdjacent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lavaAdjacent) {
|
||||||
|
BlockPos ceiling = air.above();
|
||||||
|
if (isNaturalTerrain(level.getBlockState(ceiling))) {
|
||||||
|
level.setBlock(ceiling, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNaturalTerrain(net.minecraft.world.level.block.state.BlockState state) {
|
||||||
|
return state.is(Blocks.STONE) || state.is(Blocks.DEEPSLATE)
|
||||||
|
|| state.is(Blocks.DIRT) || state.is(Blocks.GRASS_BLOCK)
|
||||||
|
|| state.is(Blocks.GRAVEL) || state.is(Blocks.SAND)
|
||||||
|
|| state.is(Blocks.TUFF) || state.is(Blocks.ANDESITE)
|
||||||
|
|| state.is(Blocks.DIORITE) || state.is(Blocks.GRANITE)
|
||||||
|
|| state.is(Blocks.BASALT);
|
||||||
|
}
|
||||||
|
|
||||||
private BlockPos surfaceAt(ServerLevel level, int x, int z) {
|
private BlockPos surfaceAt(ServerLevel level, int x, int z) {
|
||||||
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;
|
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;
|
||||||
if (y < level.getMinBuildHeight()) {
|
if (y < level.getMinBuildHeight()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user