Meteorite

This commit is contained in:
Sebastian Hartte
2020-07-07 02:13:10 +02:00
parent 5eece068ba
commit f8ee4bb1f2
17 changed files with 93 additions and 304 deletions
@@ -0,0 +1,27 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite;
public class Constants {
public static final String TAG_POS = "c";
public static final String TAG_RADIUS = "r";
public static final String TAG_CRATER = "t";
public static final String TAG_FALLOUT = "f";
public static final String TAG_PURE = "p";
public static final String TAG_LAKE = "l";
}
@@ -0,0 +1,74 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
/**
* IMPORTANT: DO NOT CHANGE THE ORDER. Only append. No removals.
*/
public enum CraterType {
/**
* No crater at all.
*/
NONE(null),
/**
* Just the default. Nothing
*/
NORMAL(Blocks.AIR),
/**
* A crater lake filled with lava.
*/
LAVA(Blocks.LAVA),
/**
* A lava crater lake cooled down to obsidian.
*/
OBSIDIAN(Blocks.OBSIDIAN),
/**
* A crater filled with water by rain
*/
WATER(Blocks.WATER),
/**
* A crater filled with snow by snowing.
*/
SNOW(Blocks.SNOW_BLOCK),
/**
* A frozen water filled crater.
*/
ICE(Blocks.ICE);
private final Block filler;
CraterType(Block filler) {
this.filler = filler;
}
public Block getFiller() {
return filler;
}
}
@@ -0,0 +1,38 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
public class MeteoriteBlockPutter {
public boolean put(final WorldAccess w, BlockPos pos, final BlockState blk) {
final BlockState original = w.getBlockState(pos);
if (original.getBlock() == Blocks.BEDROCK || original == blk) {
return false;
}
w.setBlockState(pos, blk, 0);
return true;
}
}
@@ -0,0 +1,429 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockBox;
import net.minecraft.world.WorldAccess;
import appeng.api.AEApi;
import appeng.api.definitions.IBlockDefinition;
import appeng.api.definitions.IBlocks;
import appeng.api.definitions.IMaterials;
import appeng.api.features.AEFeature;
import appeng.core.AEConfig;
import appeng.core.worlddata.WorldData;
import appeng.util.InventoryAdaptor;
import appeng.util.Platform;
import appeng.worldgen.meteorite.fallout.Fallout;
import appeng.worldgen.meteorite.fallout.FalloutCopy;
import appeng.worldgen.meteorite.fallout.FalloutMode;
import appeng.worldgen.meteorite.fallout.FalloutSand;
import appeng.worldgen.meteorite.fallout.FalloutSnow;
public final class MeteoritePlacer {
private static final double PRESSES_SPAWN_CHANCE = 0.7;
private static final int SKYSTONE_SPAWN_LIMIT = 12;
private final IBlockDefinition skyChestDefinition;
private final BlockState skyStone;
private final Item skyStoneItem;
private final MeteoriteBlockPutter putter = new MeteoriteBlockPutter();
private final WorldAccess world;
private final Fallout type;
private final BlockPos pos;
private final int x;
private final int y;
private final int z;
private final double meteoriteSize;
private final double realCrater;
private final double squaredMeteoriteSize;
private final double crater;
private final boolean placeCrater;
private final CraterType craterType;
private final boolean pureCrater;
private final boolean craterLake;
private final BlockBox boundingBox;
public MeteoritePlacer(WorldAccess world, PlacedMeteoriteSettings settings, BlockBox boundingBox) {
this.boundingBox = boundingBox;
this.world = world;
this.pos = settings.getPos();
this.x = settings.getPos().getX();
this.y = settings.getPos().getY();
this.z = settings.getPos().getZ();
this.meteoriteSize = settings.getMeteoriteRadius();
this.realCrater = this.meteoriteSize * 2 + 5;
this.placeCrater = settings.shouldPlaceCrater();
this.craterType = settings.getCraterType();
this.pureCrater = settings.isPureCrater();
this.craterLake = settings.isCraterLake();
this.squaredMeteoriteSize = this.meteoriteSize * this.meteoriteSize;
this.crater = this.realCrater * this.realCrater;
final IBlocks blocks = AEApi.instance().definitions().blocks();
this.skyChestDefinition = blocks.skyStoneChest();
this.skyStone = blocks.skyStoneBlock().block().getDefaultState();
this.skyStoneItem = blocks.skyStoneBlock().item();
this.type = getFallout(world, settings.getPos(), settings.getFallout());
}
public void place() {
// creator
if (placeCrater) {
this.placeCrater();
}
this.placeMeteorite();
// collapse blocks...
if (placeCrater) {
this.decay();
}
if (craterLake) {
this.placeCraterLake();
}
}
private int minX(int x) {
if (x < boundingBox.minX) {
return boundingBox.minX;
} else if (x > boundingBox.maxX) {
return boundingBox.maxX;
}
return x;
}
private int minZ(int x) {
if (x < boundingBox.minZ) {
return boundingBox.minZ;
} else if (x > boundingBox.maxZ) {
return boundingBox.maxZ;
}
return x;
}
private int maxX(int x) {
if (x < boundingBox.minX) {
return boundingBox.minX;
} else if (x > boundingBox.maxX) {
return boundingBox.maxX;
}
return x;
}
private int maxZ(int x) {
if (x < boundingBox.minZ) {
return boundingBox.minZ;
} else if (x > boundingBox.maxZ) {
return boundingBox.maxZ;
}
return x;
}
private void placeCrater() {
final int maxY = 255;
BlockPos.Mutable blockPos = new BlockPos.Mutable();
BlockState filler = craterType.getFiller().getDefaultState();
for (int j = y - 5; j <= maxY; j++) {
blockPos.setY(j);
for (int i = boundingBox.minX; i <= boundingBox.maxX; i++) {
blockPos.setX(i);
for (int k = boundingBox.minZ; k <= boundingBox.maxZ; k++) {
blockPos.setZ(k);
final double dx = i - x;
final double dz = k - z;
final double h = y - this.meteoriteSize + 1 + this.type.adjustCrater();
final double distanceFrom = dx * dx + dz * dz;
if (j > h + distanceFrom * 0.02) {
BlockState currentBlock = world.getBlockState(blockPos);
if (craterType != CraterType.NORMAL && j < y && currentBlock.getMaterial().isSolid()) {
if (j > h + distanceFrom * 0.02) {
this.putter.put(world, blockPos, filler);
}
} else {
this.putter.put(world, blockPos, Blocks.AIR.getDefaultState());
}
}
}
}
}
for (final Object o : world.getEntities(ItemEntity.class, new Box(minX(x - 30), y - 5, minZ(z - 30), maxX(x + 30), y + 30, maxZ(z + 30)), null)) {
final Entity e = (Entity) o;
e.remove();
}
}
private void placeMeteorite() {
// spawn meteor
this.placeMeteoriteSkyStone();
placeChest();
}
private void placeChest() {
if (AEConfig.instance().isFeatureEnabled(AEFeature.SPAWN_PRESSES_IN_METEORITES)) {
this.putter.put(world, pos, this.skyChestDefinition.block().getDefaultState());
final BlockEntity te = world.getBlockEntity(pos); // FIXME: this is also probably a band-aid for another issue
final InventoryAdaptor ap = InventoryAdaptor.getAdaptor(te, Direction.UP);
if (ap != null && !ap.containsItems()) // FIXME: band-aid for meteorites being generated multiple times
{
// TODO: loot tables would be better
int primary = Math.max(1, (int) (Math.random() * 4));
if (primary > 3) // in case math breaks...
{
primary = 3;
}
for (int zz = 0; zz < primary; zz++) {
int r;
boolean duplicate;
do {
duplicate = false;
if (Math.random() > PRESSES_SPAWN_CHANCE) {
r = WorldData.instance().storageData().getNextOrderedValue("presses", 0);
} else {
r = (int) (Math.random() * 1000);
}
ItemStack toAdd = ItemStack.EMPTY;
final IMaterials materials = AEApi.instance().definitions().materials();
switch (r % 4) {
case 0:
toAdd = materials.calcProcessorPress().maybeStack(1).orElse(ItemStack.EMPTY);
break;
case 1:
toAdd = materials.engProcessorPress().maybeStack(1).orElse(ItemStack.EMPTY);
break;
case 2:
toAdd = materials.logicProcessorPress().maybeStack(1).orElse(ItemStack.EMPTY);
break;
case 3:
toAdd = materials.siliconPress().maybeStack(1).orElse(ItemStack.EMPTY);
break;
default:
}
if (!toAdd.isEmpty()) {
if (ap.simulateRemove(1, toAdd, null).isEmpty()) {
ap.addItems(toAdd);
} else {
duplicate = true;
}
}
} while (duplicate);
}
final int secondary = Math.max(1, (int) (Math.random() * 3));
for (int zz = 0; zz < secondary; zz++) {
switch ((int) (Math.random() * 1000) % 3) {
case 0:
final int amount = (int) ((Math.random() * SKYSTONE_SPAWN_LIMIT) + 1);
ap.addItems(new ItemStack(skyStoneItem, amount));
break;
case 1:
final List<ItemStack> possibles = new ArrayList<>();
possibles.add(new ItemStack(net.minecraft.item.Items.GOLD_NUGGET));
ItemStack nugget = Platform.pickRandom(possibles);
if (nugget != null && !nugget.isEmpty()) {
nugget = nugget.copy();
nugget.setCount((int) (Math.random() * 12) + 1);
ap.addItems(nugget);
}
break;
}
}
}
}
}
private void placeMeteoriteSkyStone() {
final int meteorXLength = minX(x - 8);
final int meteorXHeight = maxX(x + 8);
final int meteorZLength = minZ(z - 8);
final int meteorZHeight = maxZ(z + 8);
BlockPos.Mutable pos = new BlockPos.Mutable();
for (int i = meteorXLength; i <= meteorXHeight; i++) {
pos.setX(i);
for (int j = y - 8; j < y + 8; j++) {
pos.setY(j);
for (int k = meteorZLength; k <= meteorZHeight; k++) {
pos.setZ(k);
final double dx = i - x;
final double dy = j - y;
final double dz = k - z;
if (dx * dx * 0.7 + dy * dy * (j > y ? 1.4 : 0.8) + dz * dz * 0.7 < this.squaredMeteoriteSize) {
this.putter.put(world, pos, skyStone);
}
}
}
}
}
private void decay() {
double randomShit = 0;
final int meteorXLength = minX(x - 30);
final int meteorXHeight = maxX(x + 30);
final int meteorZLength = minZ(z - 30);
final int meteorZHeight = maxZ(z + 30);
BlockPos.Mutable blockPos = new BlockPos.Mutable();
BlockPos.Mutable blockPosUp = new BlockPos.Mutable();
BlockPos.Mutable blockPosDown = new BlockPos.Mutable();
for (int i = meteorXLength; i <= meteorXHeight; i++) {
blockPos.setX(i);
blockPosUp.setX(i);
blockPosDown.setX(i);
for (int k = meteorZLength; k <= meteorZHeight; k++) {
blockPos.setZ(k);
blockPosUp.setZ(k);
blockPosDown.setZ(k);
for (int j = y - 9; j < y + 30; j++) {
blockPos.setY(j);
blockPosUp.setY(j + 1);
blockPosDown.setY(j - 1);
BlockState state = world.getBlockState(blockPos);
Block blk = world.getBlockState(blockPos).getBlock();
if (this.pureCrater && blk == craterType.getFiller()) {
continue;
}
// TODO reconsider
if (state.getMaterial().isReplaceable()) {
if (!world.isAir(blockPosUp)) {
final BlockState stateUp = world.getBlockState(blockPosUp);
world.setBlockState(blockPos, stateUp, 3);
} else if (randomShit < 100 * this.crater) {
final double dx = i - x;
final double dy = j - y;
final double dz = k - z;
final double dist = dx * dx + dy * dy + dz * dz;
final BlockState xf = world.getBlockState(blockPosDown);
if (!xf.getMaterial().isReplaceable()) {
final double extraRange = Math.random() * 0.6;
final double height = this.crater * (extraRange + 0.2)
- Math.abs(dist - this.crater * 1.7);
if (!xf.isAir() && height > 0 && Math.random() > 0.6) {
randomShit++;
this.type.getRandomFall(world, blockPos);
}
}
}
} else {
// decay.
if (world.isAir(blockPosUp)) {
if (Math.random() > 0.4) {
final double dx = i - x;
final double dy = j - y;
final double dz = k - z;
if (dx * dx + dy * dy + dz * dz < this.crater * 1.6) {
this.type.getRandomInset(world, blockPos);
}
}
}
}
}
}
}
}
/**
* If it finds a single water block at y62, it will replace any air blocks below
* the sea level with water.
*/
private void placeCraterLake() {
final int maxY = world.getSeaLevel() - 1;
BlockPos.Mutable blockPos = new BlockPos.Mutable();
for (int j = y - 5; j <= maxY; j++) {
blockPos.setY(j);
for (int i = boundingBox.minX; i <= boundingBox.maxX; i++) {
blockPos.setX(i);
for (int k = boundingBox.minZ; k <= boundingBox.maxZ; k++) {
blockPos.setZ(k);
final double dx = i - x;
final double dz = k - z;
final double h = y - this.meteoriteSize + 1 + this.type.adjustCrater();
final double distanceFrom = dx * dx + dz * dz;
if (j > h + distanceFrom * 0.02) {
BlockState currentBlock = world.getBlockState(blockPos);
if (currentBlock.getBlock() == Blocks.AIR) {
this.putter.put(world, blockPos, Blocks.WATER.getDefaultState());
}
}
}
}
}
}
private Fallout getFallout(WorldAccess w, BlockPos pos, FalloutMode mode) {
switch (mode) {
case SAND:
return new FalloutSand(w, pos, this.putter, this.skyStone);
case TERRACOTTA:
return new FalloutCopy(w, pos, this.putter, this.skyStone);
case ICE_SNOW:
return new FalloutSnow(w, pos, this.putter, this.skyStone);
case DEFAULT:
default:
return new Fallout(this.putter, this.skyStone);
}
}
}
@@ -0,0 +1,43 @@
package appeng.worldgen.meteorite;
import appeng.core.AppEng;
import com.mojang.serialization.Codec;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.gen.ChunkRandom;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
public class MeteoriteStructure extends StructureFeature<DefaultFeatureConfig> {
public static final Identifier ID = AppEng.makeId("meteorite");
public static final StructureFeature<DefaultFeatureConfig> INSTANCE = new MeteoriteStructure(DefaultFeatureConfig.CODEC);
public MeteoriteStructure(Codec<DefaultFeatureConfig> configCodec) {
super(configCodec);
}
@Override
public boolean shouldStartAt(ChunkGenerator generator, BiomeSource biomeSource, long seed, ChunkRandom randIn, int chunkX, int chunkZ, Biome biome, ChunkPos chunkPos2, DefaultFeatureConfig featureConfig) {
int i = chunkX >> 4;
int j = chunkZ >> 4;
randIn.setSeed((long) (i ^ j << 4) ^ seed);
randIn.nextInt();
return randIn.nextBoolean();
}
@Override
public String getName() {
return ID.toString();
}
@Override
public StructureStartFactory<DefaultFeatureConfig> getStructureStartFactory() {
return MeteoriteStructureStart::new;
}
}
@@ -0,0 +1,97 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite;
import java.util.Random;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.structure.StructurePiece;
import net.minecraft.structure.StructurePieceType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.BlockBox;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.gen.StructureAccessor;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.structure.StructureManager;
import appeng.core.worlddata.WorldData;
import appeng.worldgen.meteorite.fallout.FalloutMode;
public class MeteoriteStructurePiece extends StructurePiece {
public static final StructurePieceType TYPE = StructurePieceType.register(MeteoriteStructurePiece::new,
"AE2MTRT");
private final PlacedMeteoriteSettings settings;
protected MeteoriteStructurePiece(BlockPos center, float coreRadius, CraterType craterType, FalloutMode fallout,
boolean pureCrater, boolean craterLake) {
super(TYPE, 0);
this.settings = new PlacedMeteoriteSettings(center, coreRadius, craterType, fallout, pureCrater, craterLake);
// Assume a normal max height of 128 blocks for most biomes,
// meteors spawned at about y64 are 9x9 chunks large at most.
int range = 4 * 16;
ChunkPos chunkPos = new ChunkPos(center);
this.boundingBox = new BlockBox(chunkPos.getStartX() - range, center.getY(),
chunkPos.getStartZ() - range, chunkPos.getEndX() + range, center.getY(), chunkPos.getEndZ() + range);
}
public MeteoriteStructurePiece(StructureManager templateManager, CompoundTag tag) {
super(TYPE, tag);
BlockPos center = BlockPos.fromLong(tag.getLong(Constants.TAG_POS));
float coreRadius = tag.getFloat(Constants.TAG_RADIUS);
CraterType craterType = CraterType.values()[tag.getByte(Constants.TAG_CRATER)];
FalloutMode fallout = FalloutMode.values()[tag.getByte(Constants.TAG_FALLOUT)];
boolean pureCrater = tag.getBoolean(Constants.TAG_PURE);
boolean craterLake = tag.getBoolean(Constants.TAG_LAKE);
this.settings = new PlacedMeteoriteSettings(center, coreRadius, craterType, fallout, pureCrater, craterLake);
}
public boolean isFinalized() {
return settings.getCraterType() != null;
}
public PlacedMeteoriteSettings getSettings() {
return settings;
}
@Override
protected void toNbt(CompoundTag tag) {
tag.putFloat(Constants.TAG_RADIUS, settings.getMeteoriteRadius());
tag.putLong(Constants.TAG_POS, settings.getPos().asLong());
tag.putByte(Constants.TAG_CRATER, (byte) settings.getCraterType().ordinal());
tag.putByte(Constants.TAG_FALLOUT, (byte) settings.getFallout().ordinal());
tag.putBoolean(Constants.TAG_PURE, settings.isPureCrater());
tag.putBoolean(Constants.TAG_LAKE, settings.isCraterLake());
}
@Override
public boolean generate(ServerWorldAccess world, StructureAccessor structureAccessor, ChunkGenerator chunkGenerator, Random random, BlockBox bounds, ChunkPos chunkPos, BlockPos blockPos) {
MeteoritePlacer placer = new MeteoritePlacer(world, settings, bounds);
placer.place();
WorldData.instance().compassData().service().tryUpdateArea(world, chunkPos); // FIXME: We know the y-range here...
return true;
}
}
@@ -0,0 +1,227 @@
package appeng.worldgen.meteorite;
import java.util.Set;
import com.google.common.math.StatsAccumulator;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.structure.StructureStart;
import net.minecraft.tag.BlockTags;
import net.minecraft.tag.Tag;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockBox;
import net.minecraft.world.Heightmap;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.Biome.Category;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.Heightmap.Type;
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.StructureFeature;
import net.minecraft.structure.StructureManager;
import appeng.worldgen.meteorite.fallout.FalloutMode;
public class MeteoriteStructureStart extends StructureStart<DefaultFeatureConfig> {
private final Tag<Block> sandTag = BlockTags.getContainer().getOrCreate(new Identifier("minecraft:sand"));
private final Tag<Block> terracottaTag = BlockTags.getContainer()
.getOrCreate(new Identifier("forge:terracotta"));
public MeteoriteStructureStart(StructureFeature<DefaultFeatureConfig> feature, int chunkX, int chunkZ, BlockBox box, int references, long seed) {
super(feature, chunkX, chunkZ, box, references, seed);
}
@Override
public void init(ChunkGenerator generator, StructureManager templateManagerIn, int chunkX, int chunkZ,
Biome biome, DefaultFeatureConfig config) {
final int centerX = chunkX * 16 + this.random.nextInt(16);
final int centerZ = chunkZ * 16 + this.random.nextInt(16);
final float meteoriteRadius = (this.random.nextFloat() * 6.0f) + 2;
final int yOffset = (int) Math.ceil(meteoriteRadius) + 1;
final Set<Biome> t2 = generator.getBiomeSource().getBiomesInArea(centerX, 0, centerZ, 0);
final Biome spawnBiome = t2.stream().findFirst().orElse(biome);
final boolean isOcean = spawnBiome.getCategory() == Category.OCEAN;
final Type heightmapType = isOcean ? Heightmap.Type.OCEAN_FLOOR_WG : Heightmap.Type.WORLD_SURFACE_WG;
// Accumulate stats about the surrounding heightmap
StatsAccumulator stats = new StatsAccumulator();
int scanRadius = (int) Math.max(1, meteoriteRadius * 2);
for (int x = -scanRadius; x <= scanRadius; x++) {
for (int z = -scanRadius; z <= scanRadius; z++) {
int h = generator.getHeight(centerX + x, centerZ + z, heightmapType);
stats.add(h);
}
}
int centerY = (int) stats.mean();
// Spawn it down a bit further with a high variance.
if (stats.populationVariance() > 5) {
centerY -= (stats.mean() - stats.min()) * .75;
}
// Offset caused by the meteorsize
centerY -= yOffset;
// Limit to not spawn below y32
centerY = Math.max(32, centerY);
BlockPos actualPos = new BlockPos(centerX, centerY, centerZ);
boolean craterLake = this.locateWaterAroundTheCrater(generator, actualPos, meteoriteRadius);
CraterType craterType = this.determineCraterType(spawnBiome);
boolean pureCrater = this.random.nextFloat() > .9f;
FalloutMode fallout = getFalloutFromBaseBlock(spawnBiome.getSurfaceConfig().getTopMaterial());
children.add(
new MeteoriteStructurePiece(actualPos, meteoriteRadius, craterType, fallout, pureCrater, craterLake));
this.setBoundingBoxFromChildren();
}
/**
* Scan for water about 1 block further than the crater radius 333 1174
*
* @return true, if it found a single block of water
*/
private boolean locateWaterAroundTheCrater(ChunkGenerator generator, BlockPos pos, float radius) {
final int seaLevel = generator.getSeaLevel();
final int maxY = seaLevel - 1;
BlockPos.Mutable blockPos = new BlockPos.Mutable();
blockPos.setY(maxY);
for (int i = pos.getX() - 32; i <= pos.getX() + 32; i++) {
blockPos.setX(i);
for (int k = pos.getZ() - 32; k <= pos.getZ() + 32; k++) {
blockPos.setZ(k);
final double dx = i - pos.getX();
final double dz = k - pos.getZ();
final double h = pos.getY() - radius + 1;
final double distanceFrom = dx * dx + dz * dz;
if (maxY > h + distanceFrom * 0.0175 && maxY < h + distanceFrom * 0.02) {
int heigth = generator.getHeight(blockPos.getX(), blockPos.getZ(), Heightmap.Type.OCEAN_FLOOR);
if (heigth < seaLevel) {
return true;
}
}
}
}
return false;
}
private CraterType determineCraterType(Biome biome) {
final Biome.TemperatureGroup temp = biome.getTemperatureGroup();
final Category category = biome.getCategory();
// No craters in oceans
if (category == Category.OCEAN) {
return CraterType.NONE;
}
// 50% chance for a special meteor
final boolean specialMeteor = random.nextFloat() > .5f;
// Just a normal one
if (!specialMeteor) {
return CraterType.NORMAL;
}
// Warm biomes, higher chance for lava
if (temp == Biome.TemperatureGroup.WARM) {
// 50% chance to actually spawn as lava
final boolean lava = random.nextFloat() > .5f;
switch (biome.getPrecipitation()) {
// No rainfall, only lava
case NONE:
return lava ? CraterType.LAVA : CraterType.NORMAL;
// 25% chance to convert a lava to obsidian
case RAIN:
final boolean obsidian = random.nextFloat() > .75f;
final CraterType alternativObsidian = obsidian ? CraterType.OBSIDIAN : CraterType.LAVA;
return lava ? alternativObsidian : CraterType.NORMAL;
// Nothing for now.
default:
break;
}
}
// Temperate biomes. Water or maybe lava
if (temp == Biome.TemperatureGroup.MEDIUM) {
// 75% chance to actually spawn with a crater lake
final boolean lake = random.nextFloat() > .25f;
// 20% to spawn with lava
final boolean lava = random.nextFloat() > .8f;
switch (biome.getPrecipitation()) {
// No rainfall, water how?
case NONE:
return lava ? CraterType.LAVA : CraterType.NORMAL;
// Rainfall, can also turn lava to obsidian
case RAIN:
final boolean obsidian = random.nextFloat() > .75f;
final CraterType alternativObsidian = obsidian ? CraterType.OBSIDIAN : CraterType.LAVA;
final CraterType craterLake = lake ? CraterType.WATER : CraterType.NORMAL;
return lava ? alternativObsidian : craterLake;
// No lava, but snow
case SNOW:
final boolean snow = random.nextFloat() > .75f;
final CraterType water = lake ? CraterType.WATER : CraterType.NORMAL;
return snow ? CraterType.SNOW : water;
}
}
// Cold biomes, Snow or Ice, maybe water and very rarely lava.
if (temp == Biome.TemperatureGroup.COLD) {
// 75% chance to actually spawn with a crater lake
final boolean lake = random.nextFloat() > .25f;
// 5% to spawn with lava
final boolean lava = random.nextFloat() > .95f;
// 75% chance to freeze
final boolean frozen = random.nextFloat() > .25f;
switch (biome.getPrecipitation()) {
// No rainfall, water how?
case NONE:
return lava ? CraterType.LAVA : CraterType.NORMAL;
case RAIN:
final CraterType frozenLake = frozen ? CraterType.ICE : CraterType.WATER;
final CraterType craterLake = lake ? frozenLake : CraterType.NORMAL;
return lava ? CraterType.LAVA : craterLake;
case SNOW:
final CraterType snowCovered = lake ? CraterType.SNOW : CraterType.NORMAL;
return lava ? CraterType.LAVA : snowCovered;
}
}
return CraterType.NORMAL;
}
private FalloutMode getFalloutFromBaseBlock(BlockState blockState) {
final Block block = blockState.getBlock();
if (block.isIn(sandTag)) {
return FalloutMode.SAND;
} else if (block.isIn(terracottaTag)) {
return FalloutMode.TERRACOTTA;
} else if (block == Blocks.SNOW || block == Blocks.SNOW || block == Blocks.SNOW_BLOCK || block == Blocks.ICE
|| block == Blocks.PACKED_ICE) {
return FalloutMode.ICE_SNOW;
} else {
return FalloutMode.DEFAULT;
}
}
}
@@ -0,0 +1,102 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.BlockPos;
import appeng.worldgen.meteorite.fallout.FalloutMode;
public final class PlacedMeteoriteSettings {
private final BlockPos pos;
private final float meteoriteRadius;
private final CraterType craterType;
private final FalloutMode fallout;
private final boolean pureCrater;
private final boolean craterLake;
public PlacedMeteoriteSettings(BlockPos pos, float meteoriteRadius, CraterType craterType, FalloutMode fallout,
boolean pureCrater, boolean craterLake) {
this.pos = pos;
this.craterType = craterType;
this.meteoriteRadius = meteoriteRadius;
this.fallout = fallout;
this.pureCrater = pureCrater;
this.craterLake = craterLake;
}
public BlockPos getPos() {
return pos;
}
public CraterType getCraterType() {
return this.craterType;
}
public float getMeteoriteRadius() {
return meteoriteRadius;
}
public FalloutMode getFallout() {
return fallout;
}
public boolean shouldPlaceCrater() {
return this.craterType != CraterType.NONE;
}
public boolean isPureCrater() {
return this.pureCrater;
}
public boolean isCraterLake() {
return craterLake;
}
public CompoundTag write(CompoundTag tag) {
tag.putLong(Constants.TAG_POS, pos.toLong());
tag.putFloat(Constants.TAG_RADIUS, meteoriteRadius);
tag.putByte(Constants.TAG_CRATER, (byte) craterType.ordinal());
tag.putByte(Constants.TAG_FALLOUT, (byte) fallout.ordinal());
tag.putBoolean(Constants.TAG_PURE, this.pureCrater);
tag.putBoolean(Constants.TAG_LAKE, this.craterLake);
return tag;
}
public static PlacedMeteoriteSettings read(CompoundTag tag) {
BlockPos pos = BlockPos.fromLong(tag.getLong(Constants.TAG_POS));
float meteoriteRadius = tag.getFloat(Constants.TAG_RADIUS);
CraterType craterType = CraterType.values()[tag.getByte(Constants.TAG_CRATER)];
FalloutMode fallout = FalloutMode.values()[tag.getByte(Constants.TAG_FALLOUT)];
boolean pureCrater = tag.getBoolean(Constants.TAG_PURE);
boolean craterLake = tag.getBoolean(Constants.TAG_LAKE);
return new PlacedMeteoriteSettings(pos, meteoriteRadius, craterType, fallout, pureCrater, craterLake);
}
@Override
public String toString() {
return "PlacedMeteoriteSettings [pos=" + pos + ", meteoriteRadius=" + meteoriteRadius + ", craterType="
+ craterType + ", fallout=" + fallout + ", pureCrater=" + pureCrater + ", craterLake=" + craterLake
+ "]";
}
}
@@ -0,0 +1,145 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2020, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite.debug;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldView;
import appeng.worldgen.meteorite.CraterType;
import appeng.worldgen.meteorite.PlacedMeteoriteSettings;
/**
* Makes decisions about spawning meteorites in the world.
*/
public class MeteoriteSpawner {
public MeteoriteSpawner() {
}
public PlacedMeteoriteSettings trySpawnMeteoriteAtSuitableHeight(WorldView world, BlockPos startPos,
float coreRadius, CraterType craterType, boolean pureCrater, boolean worldGen) {
int stepSize = Math.min(5, (int) Math.ceil(coreRadius) + 1);
int minY = 10 + stepSize;
BlockPos.Mutable mutablePos = startPos.mutableCopy();
mutablePos.move(Direction.DOWN, stepSize);
while (mutablePos.getY() > minY) {
PlacedMeteoriteSettings spawned = trySpawnMeteorite(world, mutablePos, coreRadius, craterType, pureCrater);
if (spawned != null) {
return spawned;
}
mutablePos.setY(mutablePos.getY() - stepSize);
}
return null;
}
@Nullable
public PlacedMeteoriteSettings trySpawnMeteorite(WorldView world, BlockPos pos, float coreRadius,
CraterType craterType, boolean pureCrater) {
if (!areSurroundingsSuitable(world, pos)) {
return null;
}
// we can spawn here!
int skyMode = countBlockWithSkyLight(world, pos);
boolean placeCrater = skyMode > 10;
boolean solid = !isAirBelowSpawnPoint(world, pos);
if (!solid || placeCrater) {
// return null;
}
// FalloutMode fallout = getFalloutFromBaseBlock(world.getBlockState(pos));
boolean craterLake = false;
return new PlacedMeteoriteSettings(pos, coreRadius, craterType, null, pureCrater, craterLake);
}
private static boolean isAirBelowSpawnPoint(WorldView w, BlockPos pos) {
BlockPos.Mutable testPos = pos.mutableCopy();
for (int j = pos.getY() - 15; j < pos.getY() - 1; j++) {
testPos.setY(j);
if (w.isAir(testPos)) {
return true;
}
}
return false;
}
private int countBlockWithSkyLight(WorldView w, BlockPos pos) {
int skyMode = 0;
BlockPos.Mutable testPos = new BlockPos.Mutable();
for (int i = pos.getX() - 15; i < pos.getX() + 15; i++) {
testPos.setX(i);
for (int j = pos.getY() - 15; j < pos.getY() + 11; j++) {
testPos.setY(j);
for (int k = pos.getZ() - 15; k < pos.getZ() + 15; k++) {
testPos.setZ(k);
if (w.isSkyVisibleAllowingSea(testPos)) {
skyMode++;
}
}
}
}
return skyMode;
}
private boolean areSurroundingsSuitable(WorldView w, BlockPos pos) {
int realValidBlocks = 0;
BlockPos.Mutable testPos = new BlockPos.Mutable();
for (int i = pos.getX() - 6; i < pos.getX() + 6; i++) {
testPos.setX(i);
for (int j = pos.getY() - 6; j < pos.getY() + 6; j++) {
testPos.setY(j);
for (int k = pos.getZ() - 6; k < pos.getZ() + 6; k++) {
testPos.setZ(k);
Block block = w.getBlockState(testPos).getBlock();
realValidBlocks++;
}
}
}
int validBlocks = 0;
for (int i = pos.getX() - 15; i < pos.getX() + 15; i++) {
testPos.setX(i);
for (int j = pos.getY() - 15; j < pos.getY() + 15; j++) {
testPos.setY(j);
for (int k = pos.getZ() - 15; k < pos.getZ() + 15; k++) {
testPos.setZ(k);
Block testBlk = w.getBlockState(testPos).getBlock();
validBlocks++;
}
}
}
final int minBlocks = 200;
return validBlocks > minBlocks && realValidBlocks > 80;
}
}
@@ -0,0 +1,70 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite.fallout;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import appeng.worldgen.meteorite.MeteoriteBlockPutter;
public class Fallout {
private final MeteoriteBlockPutter putter;
private final BlockState skyStone;
public Fallout(final MeteoriteBlockPutter putter, final BlockState skyStone) {
this.putter = putter;
this.skyStone = skyStone;
}
public int adjustCrater() {
return 0;
}
public void getRandomFall(final WorldAccess w, BlockPos pos) {
final double a = Math.random();
if (a > 0.9) {
this.putter.put(w, pos, Blocks.STONE.getDefaultState());
} else if (a > 0.8) {
this.putter.put(w, pos, Blocks.COBBLESTONE.getDefaultState());
} else if (a > 0.7) {
this.putter.put(w, pos, Blocks.DIRT.getDefaultState());
} else {
this.putter.put(w, pos, Blocks.GRAVEL.getDefaultState());
}
}
public void getRandomInset(final WorldAccess w, BlockPos pos) {
final double a = Math.random();
if (a > 0.9) {
this.putter.put(w, pos, Blocks.COBBLESTONE.getDefaultState());
} else if (a > 0.8) {
this.putter.put(w, pos, Blocks.STONE.getDefaultState());
} else if (a > 0.7) {
this.putter.put(w, pos, Blocks.GRASS_BLOCK.getDefaultState());
} else if (a > 0.6) {
this.putter.put(w, pos, this.skyStone);
} else if (a > 0.5) {
this.putter.put(w, pos, Blocks.GRAVEL.getDefaultState());
} else {
this.putter.put(w, pos, Blocks.AIR.getDefaultState());
}
}
}
@@ -0,0 +1,67 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite.fallout;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import appeng.worldgen.meteorite.MeteoriteBlockPutter;
public class FalloutCopy extends Fallout {
private static final double SPECIFIED_BLOCK_THRESHOLD = 0.9;
private static final double AIR_BLOCK_THRESHOLD = 0.8;
private static final double BLOCK_THRESHOLD_STEP = 0.1;
private final BlockState block;
private final MeteoriteBlockPutter putter;
public FalloutCopy(final WorldAccess w, BlockPos pos, final MeteoriteBlockPutter putter, final BlockState skyStone) {
super(putter, skyStone);
this.putter = putter;
this.block = w.getBiome(pos).getSurfaceConfig().getTopMaterial();
}
@Override
public void getRandomFall(final WorldAccess w, BlockPos pos) {
final double a = Math.random();
if (a > SPECIFIED_BLOCK_THRESHOLD) {
this.putter.put(w, pos, this.block);
} else {
this.getOther(w, pos, a);
}
}
public void getOther(final WorldAccess w, BlockPos pos, final double a) {
}
@Override
public void getRandomInset(final WorldAccess w, BlockPos pos) {
final double a = Math.random();
if (a > SPECIFIED_BLOCK_THRESHOLD) {
this.putter.put(w, pos, this.block);
} else if (a > AIR_BLOCK_THRESHOLD) {
this.putter.put(w, pos, Blocks.AIR.getDefaultState());
} else {
this.getOther(w, pos, a - BLOCK_THRESHOLD_STEP);
}
}
}
@@ -0,0 +1,29 @@
package appeng.worldgen.meteorite.fallout;
public enum FalloutMode {
/**
* No fallout, e.g. when without a crater.
*/
NONE,
/**
* Default
*/
DEFAULT,
/**
* For sandy terrain
*/
SAND,
/**
* For terracotta (mesa)
*/
TERRACOTTA,
/**
* Icy/snowy terrain
*/
ICE_SNOW;
}
@@ -0,0 +1,48 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite.fallout;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import appeng.worldgen.meteorite.MeteoriteBlockPutter;
public class FalloutSand extends FalloutCopy {
private static final double GLASS_THRESHOLD = 0.66;
private final MeteoriteBlockPutter putter;
public FalloutSand(final WorldAccess w, BlockPos pos, final MeteoriteBlockPutter putter, final BlockState skyStone) {
super(w, pos, putter, skyStone);
this.putter = putter;
}
@Override
public int adjustCrater() {
return 2;
}
@Override
public void getOther(final WorldAccess w, BlockPos pos, final double a) {
if (a > GLASS_THRESHOLD) {
this.putter.put(w, pos, Blocks.GLASS.getDefaultState());
}
}
}
@@ -0,0 +1,51 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.worldgen.meteorite.fallout;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import appeng.worldgen.meteorite.MeteoriteBlockPutter;
public class FalloutSnow extends FalloutCopy {
private static final double SNOW_THRESHOLD = 0.7;
private static final double ICE_THRESHOLD = 0.5;
private final MeteoriteBlockPutter putter;
public FalloutSnow(final WorldAccess w, BlockPos pos, final MeteoriteBlockPutter putter, final BlockState skyStone) {
super(w, pos, putter, skyStone);
this.putter = putter;
}
@Override
public int adjustCrater() {
return 2;
}
@Override
public void getOther(final WorldAccess w, BlockPos pos, final double a) {
if (a > SNOW_THRESHOLD) {
this.putter.put(w, pos, Blocks.SNOW.getDefaultState());
} else if (a > ICE_THRESHOLD) {
this.putter.put(w, pos, Blocks.ICE.getDefaultState());
}
}
}