Files
Applied-Energistics-2/src/main/java/appeng/worldgen/MeteoritePlacer.java
T
2023-12-20 20:01:21 -06:00

486 lines
19 KiB
Java

/*
* 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;
import appeng.api.AEApi;
import appeng.api.definitions.IBlockDefinition;
import appeng.api.definitions.IBlocks;
import appeng.api.definitions.IMaterials;
import appeng.block.storage.BlockSkyChest;
import appeng.core.AEConfig;
import appeng.core.features.AEFeature;
import appeng.core.worlddata.WorldData;
import appeng.util.InventoryAdaptor;
import appeng.util.Platform;
import appeng.worldgen.meteorite.*;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.oredict.OreDictionary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public final class MeteoritePlacer {
private static final double PRESSES_SPAWN_CHANCE = 0.7;
private static final int SKYSTONE_SPAWN_LIMIT = 12;
private final Collection<Block> validSpawn = new HashSet<>();
private final Collection<Block> invalidSpawn = new HashSet<>();
private final IBlockDefinition skyChestDefinition;
private final IBlockDefinition skyStoneDefinition;
private final MeteoriteBlockPutter putter = new MeteoriteBlockPutter();
private double meteoriteSize = (Math.random() * 6.0) + 2;
private double realCrater = this.meteoriteSize * 2 + 5;
private double squaredMeteoriteSize = this.meteoriteSize * this.meteoriteSize;
private double crater = this.realCrater * this.realCrater;
private NBTTagCompound settings;
private Fallout type;
public MeteoritePlacer() {
final IBlocks blocks = AEApi.instance().definitions().blocks();
this.skyChestDefinition = blocks.skyStoneChest();
this.skyStoneDefinition = blocks.skyStoneBlock();
this.validSpawn.add(Blocks.STONE);
this.validSpawn.add(Blocks.COBBLESTONE);
this.validSpawn.add(Blocks.GRASS);
this.validSpawn.add(Blocks.SAND);
this.validSpawn.add(Blocks.DIRT);
this.validSpawn.add(Blocks.GRAVEL);
this.validSpawn.add(Blocks.NETHERRACK);
this.validSpawn.add(Blocks.IRON_ORE);
this.validSpawn.add(Blocks.GOLD_ORE);
this.validSpawn.add(Blocks.DIAMOND_ORE);
this.validSpawn.add(Blocks.REDSTONE_ORE);
this.validSpawn.add(Blocks.HARDENED_CLAY);
this.validSpawn.add(Blocks.ICE);
this.validSpawn.add(Blocks.SNOW);
this.validSpawn.add(Blocks.STAINED_HARDENED_CLAY);
this.skyStoneDefinition.maybeBlock().ifPresent(this.invalidSpawn::add);
this.invalidSpawn.add(Blocks.PLANKS);
this.invalidSpawn.add(Blocks.IRON_DOOR);
this.invalidSpawn.add(Blocks.IRON_BARS);
this.invalidSpawn.add(Blocks.OAK_DOOR);
this.invalidSpawn.add(Blocks.ACACIA_DOOR);
this.invalidSpawn.add(Blocks.BIRCH_DOOR);
this.invalidSpawn.add(Blocks.DARK_OAK_DOOR);
this.invalidSpawn.add(Blocks.IRON_DOOR);
this.invalidSpawn.add(Blocks.JUNGLE_DOOR);
this.invalidSpawn.add(Blocks.SPRUCE_DOOR);
this.invalidSpawn.add(Blocks.BRICK_BLOCK);
this.invalidSpawn.add(Blocks.CLAY);
this.invalidSpawn.add(Blocks.WATER);
this.invalidSpawn.add(Blocks.LOG);
this.invalidSpawn.add(Blocks.LOG2);
this.type = new Fallout(this.putter, this.skyStoneDefinition);
}
boolean spawnMeteorite(final IMeteoriteWorld w, final NBTTagCompound meteoriteBlob) {
this.settings = meteoriteBlob;
final int x = this.settings.getInteger("x");
final int y = this.settings.getInteger("y");
final int z = this.settings.getInteger("z");
this.meteoriteSize = this.settings.getDouble("real_sizeOfMeteorite");
this.realCrater = this.settings.getDouble("realCrater");
this.squaredMeteoriteSize = this.settings.getDouble("sizeOfMeteorite");
this.crater = this.settings.getDouble("crater");
final Block blk = Block.getBlockById(this.settings.getInteger("blk"));
if (blk == Blocks.SAND) {
this.type = new FalloutSand(w, x, y, z, this.putter, this.skyStoneDefinition);
} else if (blk == Blocks.HARDENED_CLAY) {
this.type = new FalloutCopy(w, x, y, z, this.putter, this.skyStoneDefinition);
} else if (blk == Blocks.ICE || blk == Blocks.SNOW) {
this.type = new FalloutSnow(w, x, y, z, this.putter, this.skyStoneDefinition);
}
final int skyMode = this.settings.getInteger("skyMode");
// creator
if (skyMode > 10) {
this.placeCrater(w, x, y, z);
}
this.placeMeteorite(w, x, y, z);
// collapse blocks...
if (skyMode > 3) {
this.decay(w, x, y, z);
}
w.done();
return true;
}
private void placeCrater(final IMeteoriteWorld w, final int x, final int y, final int z) {
final boolean lava = this.settings.getBoolean("lava");
final int maxY = 255;
final int minX = w.minX(x - 200);
final int maxX = w.maxX(x + 200);
final int minZ = w.minZ(z - 200);
final int maxZ = w.maxZ(z + 200);
for (int j = y - 5; j < maxY; j++) {
boolean changed = false;
for (int i = minX; i < maxX; i++) {
for (int k = minZ; k < maxZ; 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) {
if (lava && j < y && w.getBlockState(i, j, k).getMaterial().isSolid()) {
if (j > h + distanceFrom * 0.02) {
this.putter.put(w, i, j, k, Blocks.LAVA);
}
} else {
changed = this.putter.put(w, i, j, k, Platform.AIR_BLOCK) || changed;
}
}
}
}
}
for (final Object o : w.getWorld()
.getEntitiesWithinAABB(EntityItem.class,
new AxisAlignedBB(w.minX(x - 30), y - 5, w.minZ(z - 30), w.maxX(x + 30), y + 30, w.maxZ(z + 30)))) {
final Entity e = (Entity) o;
e.setDead();
}
}
private void placeMeteorite(final IMeteoriteWorld w, final int x, final int y, final int z) {
// spawn meteor
this.skyStoneDefinition.maybeBlock().ifPresent(block -> this.placeMeteoriteSkyStone(w, x, y, z, block));
if (AEConfig.instance().isFeatureEnabled(AEFeature.SPAWN_PRESSES_IN_METEORITES)) {
this.skyChestDefinition.maybeBlock().ifPresent(block -> this.putter.put(w, x, y, z,
block.getDefaultState().withProperty(BlockSkyChest.NATURAL, true)));
final TileEntity te = w.getTileEntity(x, y, z);
final InventoryAdaptor ap = InventoryAdaptor.getAdaptor(te, EnumFacing.UP);
if (ap != null) {
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");
} 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);
this.skyStoneDefinition.maybeStack(amount).ifPresent(ap::addItems);
break;
case 1:
final List<ItemStack> possibles = new ArrayList<>();
possibles.addAll(OreDictionary.getOres("nuggetIron"));
possibles.addAll(OreDictionary.getOres("nuggetCopper"));
possibles.addAll(OreDictionary.getOres("nuggetTin"));
possibles.addAll(OreDictionary.getOres("nuggetSilver"));
possibles.addAll(OreDictionary.getOres("nuggetLead"));
possibles.addAll(OreDictionary.getOres("nuggetPlatinum"));
possibles.addAll(OreDictionary.getOres("nuggetNickel"));
possibles.addAll(OreDictionary.getOres("nuggetAluminium"));
possibles.addAll(OreDictionary.getOres("nuggetElectrum"));
possibles.add(new ItemStack(net.minecraft.init.Items.GOLD_NUGGET));
ItemStack nugget = Platform.pickRandom(possibles);
if (!nugget.isEmpty()) {
nugget = nugget.copy();
nugget.setCount((int) (Math.random() * 12) + 1);
ap.addItems(nugget);
}
break;
}
}
}
}
}
private void placeMeteoriteSkyStone(IMeteoriteWorld w, int x, int y, int z, Block block) {
final int meteorXLength = w.minX(x - 8);
final int meteorXHeight = w.maxX(x + 8);
final int meteorZLength = w.minZ(z - 8);
final int meteorZHeight = w.maxZ(z + 8);
for (int i = meteorXLength; i < meteorXHeight; i++) {
for (int j = y - 8; j < y + 8; j++) {
for (int k = meteorZLength; k < meteorZHeight; 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(w, i, j, k, block);
}
}
}
}
}
private void decay(final IMeteoriteWorld w, final int x, final int y, final int z) {
double randomShit = 0;
final int meteorXLength = w.minX(x - 30);
final int meteorXHeight = w.maxX(x + 30);
final int meteorZLength = w.minZ(z - 30);
final int meteorZHeight = w.maxZ(z + 30);
for (int i = meteorXLength; i < meteorXHeight; i++) {
for (int k = meteorZLength; k < meteorZHeight; k++) {
for (int j = y - 9; j < y + 30; j++) {
Block blk = w.getBlock(i, j, k);
if (blk == Blocks.LAVA) {
continue;
}
if (blk.isReplaceable(w.getWorld(), new BlockPos(i, j, k))) {
blk = Platform.AIR_BLOCK;
final Block blk_b = w.getBlock(i, j + 1, k);
if (blk_b != blk) {
final IBlockState meta_b = w.getBlockState(i, j + 1, k);
w.setBlock(i, j, k, meta_b, 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 Block xf = w.getBlock(i, j - 1, k);
if (!xf.isReplaceable(w.getWorld(), new BlockPos(i, j - 1, k))) {
final double extraRange = Math.random() * 0.6;
final double height = this.crater * (extraRange + 0.2) - Math.abs(dist - this.crater * 1.7);
if (xf != blk && height > 0 && Math.random() > 0.6) {
randomShit++;
this.type.getRandomFall(w, i, j, k);
}
}
}
} else {
// decay.
final Block blk_b = w.getBlock(i, j + 1, k);
if (blk_b == Platform.AIR_BLOCK) {
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(w, i, j, k);
}
}
}
}
}
}
}
}
double getSqDistance(final int x, final int z) {
final int chunkX = this.settings.getInteger("x") - x;
final int chunkZ = this.settings.getInteger("z") - z;
return chunkX * chunkX + chunkZ * chunkZ;
}
public boolean spawnMeteorite(final IMeteoriteWorld w, final int x, final int y, final int z) {
if (!w.isNether()) {
return false;
}
Block blk = w.getBlock(x, y, z);
if (!this.validSpawn.contains(blk)) {
return false; // must spawn on a valid block..
}
this.settings = new NBTTagCompound();
this.settings.setInteger("x", x);
this.settings.setInteger("y", y);
this.settings.setInteger("z", z);
this.settings.setInteger("blk", Block.getIdFromBlock(blk));
this.settings.setDouble("real_sizeOfMeteorite", this.meteoriteSize);
this.settings.setDouble("realCrater", this.realCrater);
this.settings.setDouble("sizeOfMeteorite", this.squaredMeteoriteSize);
this.settings.setDouble("crater", this.crater);
this.settings.setBoolean("lava", Math.random() > 0.9);
if (blk == Blocks.SAND) {
this.type = new FalloutSand(w, x, y, z, this.putter, this.skyStoneDefinition);
} else if (blk == Blocks.HARDENED_CLAY) {
this.type = new FalloutCopy(w, x, y, z, this.putter, this.skyStoneDefinition);
} else if (blk == Blocks.ICE || blk == Blocks.SNOW) {
this.type = new FalloutSnow(w, x, y, z, this.putter, this.skyStoneDefinition);
}
int realValidBlocks = 0;
for (int i = x - 6; i < x + 6; i++) {
for (int j = y - 6; j < y + 6; j++) {
for (int k = z - 6; k < z + 6; k++) {
blk = w.getBlock(i, j, k);
if (this.validSpawn.contains(blk)) {
realValidBlocks++;
}
}
}
}
int validBlocks = 0;
for (int i = x - 15; i < x + 15; i++) {
for (int j = y - 15; j < y + 15; j++) {
for (int k = z - 15; k < z + 15; k++) {
blk = w.getBlock(i, j, k);
if (this.invalidSpawn.contains(blk)) {
return false;
}
if (this.validSpawn.contains(blk)) {
validBlocks++;
}
}
}
}
final int minBlocks = 200;
if (validBlocks > minBlocks && realValidBlocks > 80) {
// we can spawn here!
int skyMode = 0;
for (int i = x - 15; i < x + 15; i++) {
for (int j = y - 15; j < y + 11; j++) {
for (int k = z - 15; k < z + 15; k++) {
if (w.canBlockSeeTheSky(i, j, k)) {
skyMode++;
}
}
}
}
boolean solid = true;
for (int j = y - 15; j < y - 1; j++) {
if (w.getBlock(x, j, z) == Platform.AIR_BLOCK) {
solid = false;
}
}
if (!solid) {
skyMode = 0;
}
// creator
if (skyMode > 10) {
this.placeCrater(w, x, y, z);
}
this.placeMeteorite(w, x, y, z);
// collapse blocks...
if (skyMode > 3) {
this.decay(w, x, y, z);
}
this.settings.setInteger("skyMode", skyMode);
w.done();
WorldData.instance().spawnData().addNearByMeteorites(w.getWorld().provider.getDimension(), x >> 4, z >> 4, this.settings);
return true;
}
return false;
}
NBTTagCompound getSettings() {
return this.settings;
}
}