Implemented schematic structure generation, and Mining laser fluid pumping

- finalized schematic support in structure generation, including blocks replacement and items insertions
note: implementation is functional but not optimized.
- added blockstate support for schematic insertions and replacements
- added maxRetries to item Insertion to try other slots.
- added fluid storage to mining laser pump upgrades, will still evaporate fluid if no tank was found.
This commit is contained in:
n507 2023-01-31 13:22:41 -05:00 committed by LemADEC
parent 474c756442
commit b691306a71
8 changed files with 491 additions and 83 deletions

View file

@ -7,9 +7,11 @@ import cr0s.warpdrive.block.TileEntityAbstractLaser;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.data.FluidWrapper;
import cr0s.warpdrive.data.InventoryWrapper;
import cr0s.warpdrive.data.TankWrapper;
import cr0s.warpdrive.data.Vector3;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nonnull;
@ -27,8 +29,9 @@ import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
public abstract class TileEntityAbstractMiner extends TileEntityAbstractLaser {
@ -55,11 +58,30 @@ public abstract class TileEntityAbstractMiner extends TileEntityAbstractLaser {
if (blockState.getBlock().isAir(blockState, world, blockPos)) {
return;
}
if (FluidWrapper.isFluid(blockState)) {
final Fluid fluid = FluidWrapper.getFluid(blockState);
if (fluid != null) {// (this is a fluid block)
if ( WarpDriveConfig.MINING_LASER_PUMP_UPGRADE_HARVEST_FLUID
&& FluidWrapper.isSourceBlock(world, blockPos, blockState) ) {// (fluid collection is enabled & it's a source block)
final Collection<Object> connectedTanks = TankWrapper.getConnectedTanks(world, pos);
if (!connectedTanks.isEmpty()) {// (at least 1 tank is connected)
final FluidStack fluidStack = new FluidStack(fluid, 1000);
final boolean fluidOverflowed = TankWrapper.addToTanks(world, pos, connectedTanks, fluidStack);
if (fluidOverflowed) {
// assume player wants to collect the fluid, hence stop the mining in case of overflow
setIsEnabled(false);
}
// Collect Fluid
world.playSound(null, blockPos, fluid.getFillSound(fluidStack), SoundCategory.BLOCKS, 0.5F,
2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F);
}
} else {
// Evaporate fluid
world.playSound(null, blockPos, net.minecraft.init.SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F,
2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F);
}
// remove without updating neighbours
world.setBlockState(blockPos, Blocks.AIR.getDefaultState(), 2);
@ -78,14 +100,14 @@ public abstract class TileEntityAbstractMiner extends TileEntityAbstractLaser {
// try to replant the crop
if ( itemStackDrops != null
&& blockState.getBlock() instanceof IGrowable) {
&& blockState.getBlock() instanceof IGrowable ) {
for (final ItemStack itemStackPlant : itemStackDrops) {
if (itemStackPlant.getItem() instanceof IPlantable) {
final IPlantable plantable = (IPlantable) itemStackPlant.getItem();
final IBlockState blockStatePlant = plantable.getPlant(world, blockPos);
if (WarpDriveConfig.LOGGING_COLLECTION) {
WarpDrive.logger.info(String.format("Drop includes %s which is plantable %s as block %s",
itemStackPlant, plantable, blockStatePlant ));
itemStackPlant, plantable, blockStatePlant));
}
final BlockPos blockPosSoil = blockPos.down();
final IBlockState blockStateSoil = getWorld().getBlockState(blockPosSoil);

View file

@ -430,6 +430,7 @@ public class WarpDriveConfig {
public static double MINING_LASER_MINE_SILKTOUCH_ENERGY_FACTOR = 1.5;
public static int MINING_LASER_MINE_SILKTOUCH_DEUTERIUM_MB = 0;
public static double MINING_LASER_MINE_FORTUNE_ENERGY_FACTOR = 1.5;
public static boolean MINING_LASER_PUMP_UPGRADE_HARVEST_FLUID = false;
// Laser tree farm
// oak tree height is 8 to 11 logs + 2 leaves
@ -1258,6 +1259,7 @@ public class WarpDriveConfig {
MINING_LASER_MINE_FORTUNE_ENERGY_FACTOR = Commons.clamp(0.01D, 1000.0D,
config.get("mining_laser", "fortune_energy_factor", MINING_LASER_MINE_FORTUNE_ENERGY_FACTOR, "Energy cost multiplier per fortune level").getDouble(MINING_LASER_MINE_FORTUNE_ENERGY_FACTOR));
}
MINING_LASER_PUMP_UPGRADE_HARVEST_FLUID = config.get("mining_laser", "pump_upgrade_harvest_fluid", MINING_LASER_PUMP_UPGRADE_HARVEST_FLUID, "Pump upgrade will actually pump fluid source if a tank is found, instead of just evaporating it").getBoolean(false);
// Tree Farm
TREE_FARM_MAX_MEDIUMS_COUNT = Commons.clamp(1, 10,

View file

@ -7,12 +7,16 @@ import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.Loot;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlFileManager;
import cr0s.warpdrive.data.JumpBlock;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
@ -21,7 +25,7 @@ import org.w3c.dom.Element;
public class Schematic extends AbstractStructure {
protected String filename;
protected HashMap<String, Integer> filenames;
protected Replacement[] replacements;
protected Insertion[] insertions;
@ -29,10 +33,57 @@ public class Schematic extends AbstractStructure {
super(group, name);
}
public String getRandomFileName(final Random random) {
// In loadFromXmlElement, it's already checked that there must be at least 1 "schematic" xml node
// therefore, this should not be possible
assert(!filenames.isEmpty());
int totalWeight = 0;
for (final int weight : filenames.values()) {
totalWeight += weight;
}
int result = random.nextInt(totalWeight);
for (final Map.Entry<String, Integer> entry : filenames.entrySet()) {
result -= entry.getValue();
if (result <= 0) {
return entry.getKey();
}
}
return filenames.keySet().iterator().next();
}
@Override
public boolean generate(@Nonnull final World world, @Nonnull final Random random, @Nonnull final BlockPos blockPos) {
return instantiate(random).generate(world, random, blockPos);
}
@Override
public AbstractStructureInstance instantiate(final Random random) {
return new SchematicInstance(this, random);
}
@Override
public boolean loadFromXmlElement(final Element element) throws InvalidXmlException {
super.loadFromXmlElement(element);
final List<Element> fileNameList = XmlFileManager.getChildrenElementByTagName(element, "schematic");
if (fileNameList.isEmpty()) {
throw new InvalidXmlException("Must have one schematic node with file name!");
}
this.filenames = new HashMap<>(fileNameList.size());
for (final Element entry : fileNameList) {
final String filename = entry.getAttribute("filename");
int weight = 1;
try {
weight = Integer.parseInt(entry.getAttribute("weight"));
} catch (final NumberFormatException numberFormatException) {
throw new InvalidXmlException(String.format("Invalid weight in schematic %s of structure %s:%s",
filename, group, name));
}
this.filenames.put(filename, weight);
}
// load all replacement elements
final List<Element> listReplacements = XmlFileManager.getChildrenElementByTagName(element, "replacement");
replacements = new Replacement[listReplacements.size()];
@ -72,21 +123,143 @@ public class Schematic extends AbstractStructure {
return true;
}
@Override
public boolean generate(@Nonnull final World world, @Nonnull final Random random, @Nonnull final BlockPos blockPos) {
return instantiate(random).generate(world, random, blockPos);
static class BlockMatcher {
IBlockState blockState;
public static BlockMatcher fromXmlElement(final Element element, final GenericSet<?> caller) throws InvalidXmlException{
final String blockStateString = element.getAttribute("blockState");
final String blockNameString = element.getAttribute("block");
final String metaString = element.getAttribute("metadata");
final BlockMatcher blockMatcher;
if(blockNameString.isEmpty()){
blockMatcher = BlockMatcher.fromBlockStateString(blockStateString);
}else {
blockMatcher = BlockMatcher.fromBlockAndMeta(blockNameString, metaString);
}
if (blockMatcher == null){
WarpDrive.logger.warn(String.format("Invalid matching scheme %s found for %s",
blockStateString.isEmpty() ? blockNameString + "@" + metaString : blockStateString,
caller.getFullName()));
}
return blockMatcher;
}
public static BlockMatcher fromBlockStateString(final String blockStateString) {
// TODO: allow different data input type for meta: range (e.g. 2-13), comma separated list (e.g. 1,2,3..), multiple property (e.g. variant=oak,half=bottom)
final BlockMatcher result = new BlockMatcher();
String blockNameString = "";
String metaString = "*";
if (blockStateString.contains("@")) {// (with metadata)
final String[] blockStateParts = blockStateString.split("@");
blockNameString = blockStateParts[0].trim();
metaString = blockStateParts[1].trim();
} else {// (without metadata)
blockNameString = blockStateString;
}
final Block block = Block.getBlockFromName(blockNameString);
if (block == null) {
WarpDrive.logger.warn(String.format("Ignoring invalid block with name %s.", blockNameString));
return null;
}
if (metaString.equals("*")) {// (no metadata or explicit wildcard)
result.blockState = block.getDefaultState();
} else if (metaString.contains("=")) {// (in string format (e.g. "color=red"))
final String[] metaParts = metaString.split("=");
final String propertyKey = metaParts[0].trim();
final String propertyValue = metaParts[1].trim();
final IProperty<? extends Comparable<?>> property = block.getBlockState().getProperty(propertyKey);
if (property == null) {
WarpDrive.logger.warn(String.format("Found invalid block property %s for block %s", propertyKey, blockNameString));
return null;
}
/*
Note: the below code was attempted but not succeeded.
IBlockState#WithProperty require (T extends Comparable<?> property , V extend T value).
It is impossible to ensure V extend T because value returned from parseValue is itself <? extends Comparable<?>>
Therefore, T may only be determined at runtime, and V extend T may not be enforced.
Optional<? extends Comparable<?>> parsedValue = property.parseValue(propertyValue);
if (result.isPresent()){
result.blockState = block.getDefaultState().withProperty(property, parsedValue.get());
}else{
WarpDrive.logger.warn(String.format("Value %s is not allowed for property %s for block %s", propertyValue, propertyKey, state));
}
*/
boolean found = false;
for (int i = 0; i < 16; i++) {// not efficient, but this would work (and since it's load time, it should not be a problem)
final IBlockState tmpState = block.getStateFromMeta(i);
if (tmpState.getProperties().get(property).equals(property.parseValue(propertyValue).orNull())) {
result.blockState = tmpState;
found = true;
break;
}
}
if (!found) {
WarpDrive.logger.warn(String.format("Failed to find metadata value that represent block property %s for block %s", propertyKey, blockNameString));
return null;
}
} else {// (metadata)
final int metadata;
try {
metadata = Integer.parseInt(metaString);
} catch (final NumberFormatException numberFormatException) {
WarpDrive.logger.warn(String.format("%s is not a valid number for metadata of block %s", metaString, blockNameString));
return null;
}
result.blockState = block.getStateFromMeta(metadata);
}
return result;
}
public static BlockMatcher fromBlockAndMeta(final String blockName, final String metaString) {
final BlockMatcher result = new BlockMatcher();
final Block block = Block.getBlockFromName(blockName);
if (block == null) {
WarpDrive.logger.warn(String.format("Found invalid block %s", blockName));
return null;
}
if (!metaString.isEmpty()) {
try {
final int meta = Integer.parseInt(metaString);
result.blockState = block.getStateFromMeta(meta);
} catch (final NumberFormatException numberFormatException) {
WarpDrive.logger.warn(String.format("%s is not a valid number for meta of block %s", metaString, blockName));
return null;
}
}
return result;
}
public boolean isMatching(final IBlockState blockStateIn) {
return blockStateIn.equals(blockState);
}
public boolean isMatching(final JumpBlock jumpBlockIn) {
return blockState != null && jumpBlockIn != null && jumpBlockIn.blockMeta == blockState.getBlock().getMetaFromState(blockState);
}
@Override
public AbstractStructureInstance instantiate(final Random random) {
return new SchematicInstance(this, random);
public String toString() {
return "BlockMatcher{" + (blockState == null ? "null" : blockState.toString()) + "}";
}
}
public class Replacement extends GenericSet<Filler> {
public static class Replacement extends GenericSet<Filler> {
private final String parentFullName;
protected Block block;
protected IBlockState blockState;
protected BlockMatcher matcher;
public Replacement(final String parentFullName, final String name) {
super(null, name, Filler.DEFAULT, "filler");
@ -95,13 +268,15 @@ public class Schematic extends AbstractStructure {
@Override
public boolean loadFromXmlElement(final Element element) throws InvalidXmlException {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(String.format(" + found replacement %s",
element.getAttribute("name")));
}
super.loadFromXmlElement(element);
matcher = BlockMatcher.fromXmlElement(element, this);
if ( WarpDriveConfig.LOGGING_WORLD_GENERATION
&& matcher != null ) {
WarpDrive.logger.info(String.format(" + found replacement for block %s", matcher));
}
// resolve static imports
for (final String importGroupName : getImportGroupNames()) {
final GenericSet<Filler> fillerSet = WarpDriveConfig.FillerManager.getGenericSet(importGroupName);
@ -126,8 +301,7 @@ public class Schematic extends AbstractStructure {
public Replacement instantiate(final Random random) {
final Replacement replacement = new Replacement(parentFullName, name);
replacement.block = block;
replacement.blockState = blockState;
replacement.matcher = this.matcher;
try {
replacement.loadFrom(this);
for (final String importGroup : getImportGroups()) {
@ -159,18 +333,21 @@ public class Schematic extends AbstractStructure {
}
public boolean isMatching(final IBlockState blockStateIn) {
return (block != null && block == blockStateIn.getBlock())
|| blockState.equals(blockStateIn);
return matcher != null && matcher.isMatching(blockStateIn);
}
public boolean isMatching(final JumpBlock jumpBlockIn) {
return matcher != null && matcher.isMatching(jumpBlockIn);
}
}
public class Insertion extends GenericSet<Loot> {
public static class Insertion extends GenericSet<Loot> {
private final String parentFullName;
protected BlockMatcher matcher;
private int minQuantity;
private int maxQuantity;
protected Block block;
protected IBlockState blockState;
private int maxRetries;
public Insertion(final String parentFullName, final String name) {
super(null, name, Loot.DEFAULT, "loot");
@ -179,13 +356,15 @@ public class Schematic extends AbstractStructure {
@Override
public boolean loadFromXmlElement(final Element element) throws InvalidXmlException {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(String.format(" + found insertion %s",
element.getAttribute("name")));
}
super.loadFromXmlElement(element);
matcher = BlockMatcher.fromXmlElement(element, this);
if ( WarpDriveConfig.LOGGING_WORLD_GENERATION
&& matcher != null ) {
WarpDrive.logger.info(String.format(" + found insertion for block %s", matcher));
}
// get optional minQuantity attribute, defaulting to 0
minQuantity = 0;
final String stringMinQuantity = element.getAttribute("minQuantity");
@ -200,6 +379,13 @@ public class Schematic extends AbstractStructure {
maxQuantity = Integer.parseInt(stringMaxQuantity);
}
// get optional maxTries attribute, defaulting to 3 according to WorldGenStructure#fillInventoryWithLoot
maxRetries = 3;
final String stringMaxTries = element.getAttribute("maxRetries");
if (!stringMaxTries.isEmpty()) {
maxRetries = Integer.parseInt(stringMaxTries);
}
// resolve static imports
for (final String importGroupName : getImportGroupNames()) {
final GenericSet<Loot> lootSet = WarpDriveConfig.LootManager.getGenericSet(importGroupName);
@ -226,8 +412,8 @@ public class Schematic extends AbstractStructure {
final Insertion insertion = new Insertion(parentFullName, name);
insertion.minQuantity = minQuantity;
insertion.maxQuantity = maxQuantity;
insertion.block = block;
insertion.blockState = blockState;
insertion.maxRetries = maxRetries;
insertion.matcher = matcher;
try {
insertion.loadFrom(this);
for (final String importGroup : getImportGroups()) {
@ -238,7 +424,7 @@ public class Schematic extends AbstractStructure {
continue;
}
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(String.format("Filling %s:%s with %s:%s",
WarpDrive.logger.info(String.format("Inserting %s:%s with %s:%s",
parentFullName, name, importGroup, lootSet.getName()));
}
insertion.loadFrom(lootSet);
@ -258,9 +444,24 @@ public class Schematic extends AbstractStructure {
return insertion;
}
public int getMinQuantity() {
return minQuantity;
}
public int getMaxQuantity() {
return maxQuantity;
}
public int getMaxRetries() {
return maxRetries;
}
public boolean isMatching(final IBlockState blockStateIn) {
return (block != null && block == blockStateIn.getBlock())
|| blockState.equals(blockStateIn);
return matcher != null && matcher.isMatching(blockStateIn);
}
public boolean isMatching(final JumpBlock jumpBlockIn) {
return matcher != null && matcher.isMatching(jumpBlockIn);
}
}
}

View file

@ -3,9 +3,11 @@ package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.api.WarpDriveText;
import cr0s.warpdrive.config.Filler;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.structures.Schematic.Insertion;
import cr0s.warpdrive.config.structures.Schematic.Replacement;
import cr0s.warpdrive.data.JumpBlock;
import cr0s.warpdrive.data.JumpShip;
import cr0s.warpdrive.world.WorldGenStructure;
@ -26,7 +28,7 @@ public class SchematicInstance extends AbstractStructureInstance {
super(schematic, random);
final WarpDriveText reason = new WarpDriveText();
jumpShip = JumpShip.createFromFile(schematic.filename, reason);
jumpShip = JumpShip.createFromFile(schematic.getRandomFileName(random), reason);
if (jumpShip == null) {
WarpDrive.logger.error(String.format("Failed to instantiate schematic structure %s due to %s",
schematic.getFullName(), reason));
@ -45,7 +47,7 @@ public class SchematicInstance extends AbstractStructureInstance {
insertions = new Insertion[schematic.insertions.length];
int insertionIndexOut = 0;
for (int insertionIndexIn = 0; insertionIndexIn < schematic.replacements.length; insertionIndexIn++) {
for (int insertionIndexIn = 0; insertionIndexIn < schematic.insertions.length; insertionIndexIn++) {
final Insertion insertion = schematic.insertions[insertionIndexIn].instantiate(random);
if (insertion != null) {
insertions[insertionIndexOut] = insertion;
@ -72,11 +74,26 @@ public class SchematicInstance extends AbstractStructureInstance {
return false;
}
for (final Replacement replacement : this.replacements) {
// Pick a common replacement block to get an homogenous result
final Filler filler = replacement.getRandomUnit(random);
// loop through the structure and see if a block need to be replaced
for (int i = 0; i < jumpShip.jumpBlocks.length; i++) {
if (replacement.isMatching(jumpShip.jumpBlocks[i])) {
jumpShip.jumpBlocks[i] = new JumpBlock(filler,
jumpShip.jumpBlocks[i].x,
jumpShip.jumpBlocks[i].y,
jumpShip.jumpBlocks[i].z );
}
}
}
final int y2 = Commons.clamp(
WarpDriveConfig.SPACE_GENERATOR_Y_MIN_BORDER + (jumpShip.core.getY() - jumpShip.minY),
WarpDriveConfig.SPACE_GENERATOR_Y_MAX_BORDER - (jumpShip.maxY - jumpShip.core.getY()),
blockPos.getY() );
new WorldGenStructure(random.nextFloat() < 0.2F, random).deployShip(world, jumpShip, blockPos.getX(), y2, blockPos.getZ(), (byte) 0);
new WorldGenStructure(random.nextFloat() < 0.2F, random).deployShip(world, jumpShip, blockPos.getX(), y2, blockPos.getZ(), (byte) 0, insertions);
return true;
}
}

View file

@ -127,8 +127,11 @@ public class InventoryWrapper {
} else if (inventory instanceof IItemHandler) {
qtyLeft = addToInventory(itemStack, (IItemHandler) inventory);
} else {
WarpDrive.logger.error(String.format("Invalid inventory type %s, please report to mod author: %s",
Commons.format(world, blockPos), inventory ));
if (Commons.throttleMe("addToInventories")){
WarpDrive.logger.error(String.format("Invalid inventory type %s of class %s at %s, please report to mod author",
inventory, inventory.getClass(), Commons.format(world, blockPos) ));
break;
}
}
if (qtyLeft > 0) {
if (itemStackLeft == itemStack) {
@ -141,7 +144,7 @@ public class InventoryWrapper {
}
if (qtyLeft > 0) {
if (WarpDriveConfig.LOGGING_COLLECTION) {
WarpDrive.logger.info(String.format("Overflow detected %s",
WarpDrive.logger.info(String.format("Overflow detected at %s",
Commons.format(world, blockPos) ));
}
overflow = true;

View file

@ -0,0 +1,132 @@
package cr0s.warpdrive.data;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.WarpDriveConfig;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockPos.MutableBlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
// this is almost a copy of InventoryWrapper, but for fluids.
public class TankWrapper {
// WarpDrive methods
public static boolean isTank(final TileEntity tileEntity, final EnumFacing facing) {
boolean isTank = false;
if (tileEntity instanceof IFluidTank) {
isTank = true;
}
if (!isTank
&& tileEntity.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, facing)) {
isTank = true;
}
return isTank;
}
public static Object getTank(final TileEntity tileEntity, final EnumFacing facing) {
if (tileEntity instanceof IFluidTank) {
return tileEntity;
}
if (tileEntity != null) {
return tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, facing);
}
return null;
}
public static @Nonnull Collection<Object> getConnectedTanks(final World world, final BlockPos blockPos) {
final Collection<Object> result = new ArrayList<>(6);
final Collection<IFluidHandler> resultCapabilities = new ArrayList<>(6);
final MutableBlockPos mutableBlockPos = new MutableBlockPos();
for (final EnumFacing side : EnumFacing.VALUES) {
mutableBlockPos.setPos(blockPos.getX() + side.getXOffset(),
blockPos.getY() + side.getYOffset(),
blockPos.getZ() + side.getZOffset());
final TileEntity tileEntity = world.getTileEntity(mutableBlockPos);
if (tileEntity instanceof IFluidTank) {
result.add(tileEntity);
} else if (tileEntity != null) {
final IFluidHandler fluidHandler = tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side);
if (fluidHandler != null) {
resultCapabilities.add(fluidHandler);
}
}
}
result.addAll(resultCapabilities);
return result;
}
public static boolean addToConnectedTanks(final World world, final BlockPos blockPos, final FluidStack fluidStack) {
final List<FluidStack> fluidStacks = new ArrayList<>(1);
fluidStacks.add(fluidStack);
return addToConnectedTanks(world, blockPos, fluidStacks);
}
public static boolean addToConnectedTanks(final World world, final BlockPos blockPos, final List<FluidStack> fluidStacks) {
final Collection<Object> inventories = getConnectedTanks(world, blockPos);
return addToTanks(world, blockPos, inventories, fluidStacks);
}
public static boolean addToTanks(final World world, final BlockPos blockPos,
final Collection<Object> tanks, final FluidStack fluidStack) {
final List<FluidStack> fluidStacks = new ArrayList<>(1);
fluidStacks.add(fluidStack);
return addToTanks(world, blockPos, tanks, fluidStacks);
}
public static boolean addToTanks(final World world, final BlockPos blockPos,
final Collection<Object> tanks, final List<FluidStack> fluidStacks) {
boolean overflow = false;
if (fluidStacks != null) {
for (final FluidStack fluidStack : fluidStacks) {
int qtyFilled = 0;
for (final Object tank : tanks) {
if (tank instanceof IFluidTank) {
qtyFilled = ((IFluidTank) tank).fill(fluidStack, true);
} else if (tank instanceof IFluidHandler) {
qtyFilled = ((IFluidHandler) tank).fill(fluidStack, true);
} else {
if (Commons.throttleMe("addToTanks")){
WarpDrive.logger.error(String.format("Invalid fluid tank type %s of class %s at %s, please report to mod author",
tank, tank.getClass(), Commons.format(world, blockPos) ));
break;
}
}
if (fluidStack.amount > qtyFilled) {
fluidStack.amount -= qtyFilled;
} else {
break;
}
}
if (fluidStack.amount > qtyFilled) {
if (WarpDriveConfig.LOGGING_COLLECTION) {
WarpDrive.logger.info(String.format("Tank overflow detected at %s",
Commons.format(world, blockPos)));
}
overflow = true;
}
}
}
return overflow;
}
}

View file

@ -8,6 +8,7 @@ import cr0s.warpdrive.config.Filler;
import cr0s.warpdrive.config.GenericSet;
import cr0s.warpdrive.config.Loot;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.structures.Schematic.Insertion;
import cr0s.warpdrive.data.InventoryWrapper;
import cr0s.warpdrive.data.JumpBlock;
import cr0s.warpdrive.data.JumpShip;
@ -18,6 +19,8 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.Random;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
@ -200,29 +203,6 @@ public class WorldGenStructure {
public void fillInventoryWithLoot(final World world, final Random rand, final BlockPos blockPos, final String group,
final int quantityMin, final int quantityRandom1, final int quantityRandom2,
final int maxRetries) {
// validate context
final TileEntity tileEntity = world.getTileEntity(blockPos);
final Object inventory = InventoryWrapper.getInventory(tileEntity, null);
if (inventory == null) {
WarpDrive.logger.warn(String.format("Unable to fill inventory with LootSet %s %s: %s has no inventory",
group,
Commons.format(world, blockPos),
tileEntity ));
return;
}
if (tileEntity.isInvalid()) {
WarpDrive.logger.warn(String.format("Unable to fill inventory with LootSet %s %s: %s is Invalid",
group,
Commons.format(world, blockPos),
tileEntity ));
return;
}
// evaluate parameters: quantity of loot, actual loot set
final int size = InventoryWrapper.getSize(inventory);
final int countLoots = Math.min(quantityMin + rand.nextInt(quantityRandom1) + rand.nextInt(quantityRandom2), size);
final GenericSet<Loot> lootSet = WarpDriveConfig.LootManager.getRandomSetFromGroup(rand, group);
if (lootSet == null) {
@ -233,6 +213,36 @@ public class WorldGenStructure {
return;
}
fillInventoryWithLoot(world, rand, blockPos, lootSet, quantityMin, quantityRandom1, quantityRandom2, maxRetries);
}
public void fillInventoryWithLoot(final World world, final Random rand, final BlockPos blockPos, final GenericSet<Loot> lootSet,
final int quantityMin, final int quantityRandom1, final int quantityRandom2,
final int maxRetries) {
// validate context
final TileEntity tileEntity = world.getTileEntity(blockPos);
final Object inventory = InventoryWrapper.getInventory(tileEntity, null);
if (inventory == null) {
WarpDrive.logger.warn(String.format("Unable to fill inventory with LootSet %s %s: %s has no inventory",
lootSet.getFullName(),
Commons.format(world, blockPos),
tileEntity ));
return;
}
if (tileEntity.isInvalid()) {
WarpDrive.logger.warn(String.format("Unable to fill inventory with LootSet %s %s: %s is Invalid",
lootSet.getFullName(),
Commons.format(world, blockPos),
tileEntity ));
return;
}
// evaluate parameters: quantity of loot, actual loot set
final int size = InventoryWrapper.getSize(inventory);
final int countLoots = Math.min(quantityMin + rand.nextInt(quantityRandom1) + rand.nextInt(quantityRandom2), size);
// shuffle slot indexes to reduce random calls and loops later on
final ArrayList<Integer> indexSlots = new ArrayList<>(size);
for (int indexSlot = 0; indexSlot < size; indexSlot++) {
@ -243,6 +253,14 @@ public class WorldGenStructure {
}
Collections.shuffle(indexSlots);
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.debug(String.format("About to add %d loots from set %s into inventory %s at %s with max retries %d for each",
countLoots,
lootSet.getFullName(),
inventory,
Commons.format(world, blockPos),
maxRetries));
}
// for all loots to add
ItemStack itemStackLoot;
boolean isAdded;
@ -267,23 +285,15 @@ public class WorldGenStructure {
try {
InventoryWrapper.insertItem(inventory, indexSlot, itemStackLoot);
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.debug(String.format("Filling inventory with LootSet %s %s: loot %s from %s in slot %d of inventory %s",
group,
Commons.format(world, blockPos),
WarpDrive.logger.debug(String.format(" + placed %s into slot %d",
Commons.format(itemStackLoot),
lootSet.getFullName(),
indexSlot,
inventory ));
indexSlot));
}
} catch (final Exception exception) {
exception.printStackTrace(WarpDrive.printStreamError);
WarpDrive.logger.error(String.format("Exception while filling inventory with LootSet %s %s: loot %s from %s in slot %d of inventory %s reported %s",
group,
Commons.format(world, blockPos),
WarpDrive.logger.error(String.format(" ! Exception while placing %s into slot %d: %s",
Commons.format(itemStackLoot),
lootSet.getFullName(),
indexSlot,
inventory,
exception.getMessage() ));
}
break;
@ -313,6 +323,10 @@ public class WorldGenStructure {
}
public void deployShip(final World world, final JumpShip jumpShip, final int targetX, final int targetY, final int targetZ, final byte rotationSteps) {
deployShip(world, jumpShip, targetX, targetY, targetZ, rotationSteps, null);
}
public void deployShip(final World world, final JumpShip jumpShip, final int targetX, final int targetY, final int targetZ, final byte rotationSteps, @Nullable final Insertion[] insertions) {
final Transformation transformation = new Transformation(jumpShip, world,
targetX - jumpShip.core.getX(),
@ -336,7 +350,6 @@ public class WorldGenStructure {
WarpDrive.logger.info(String.format("At index %d, skipping anchor block %s", index, jumpBlock.block));
}
} else {
index++;
if (WarpDrive.isDev && WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(String.format("At index %d, deploying %s ",
index, jumpBlock));
@ -345,6 +358,23 @@ public class WorldGenStructure {
final Block blockAtTarget = world.getBlockState(targetLocation).getBlock();
if (blockAtTarget == Blocks.AIR || Dictionary.BLOCKS_EXPANDABLE.contains(blockAtTarget)) {
jumpBlock.deploy(null, world, transformation);
// Apply insertions as defined
if (insertions != null) {
for(final Insertion insertion : insertions){
if (insertion.isMatching(jumpBlock)){
final BlockPos deployedLocation = transformation.apply(jumpBlock.x, jumpBlock.y, jumpBlock.z);
fillInventoryWithLoot(world, rand, deployedLocation,
insertion,
insertion.getMinQuantity(),
insertion.getMaxQuantity(),
0,
insertion.getMaxRetries() );
}
}
}
} else {
if (WarpDrive.isDev && WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(String.format("Deployment collision detected %s with %s during world generation, skipping this block...",

View file

@ -107,6 +107,7 @@
<xs:attribute name="blockState" type="xs:string" use="required" />
<xs:attribute name="minQuantity" type="xs:string" use="optional" />
<xs:attribute name="maxQuantity" type="xs:string" use="optional" />
<xs:attribute name="maxRetries" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>