Tree cutter fixes and compat improvements (issue #54, issue #59). Experimental Fluid Collection Funnel implementation.
This commit is contained in:
parent
8746491095
commit
7592c9d494
50 changed files with 1718 additions and 96 deletions
|
@ -4,4 +4,4 @@ org.gradle.jvmargs=-Xmx8G
|
|||
version_minecraft=1.12.2
|
||||
version_forge=14.23.5.2768
|
||||
version_jei=4.10.0.198
|
||||
version_engineersdecor=1.0.15-b2
|
||||
version_engineersdecor=1.0.15
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
|
||||
"1.12.2": {
|
||||
"1.0.15": "[R] Release based on v1.0.15-b2. Release-to-release changes: * Added Small Block Breaker * Small Tree Cutter fixes and compatability improved. * Crafting table compat fixes.\n[M] Small Tree Cutter log detection bug fixed (issue #59).\n[M] Small Tree Cutter supports Menril chopping (issue #54).",
|
||||
"1.0.15-b2": "[A] Added Small Block Breaker\n[M] Crafting Table: Allowing NBT \"Damage\" mismatch only items that are declared damagable (issue #56).\n[M] Tree Cutter: Loosened the strict mod namespace requirement for Dynamic Trees log detection (issue #52) to enable checking DT compat mod log blocks.",
|
||||
"1.0.15-b1": "[A] Added Floor Edge Light.\n[A] Added Factory Block Placer and Planter.",
|
||||
"1.0.14": "[R] Release based on v1.0.14-b1. Release-to-release changes: * Factory Hopper added. * Small Waste Incinerator improved. * Lang updates. * Recipe fixes.",
|
||||
|
@ -64,7 +65,7 @@
|
|||
"1.0.0-b1": "[A] Initial structure.\n[A] Added clinker bricks and clinker brick stairs.\n[A] Added slag bricks and slag brick stairs.\n[A] Added metal rung ladder.\n[A] Added staggered metal steps ladder.\n[A] Added treated wood ladder.\n[A] Added treated wood pole.\n[A] Added treated wood table."
|
||||
},
|
||||
"promos": {
|
||||
"1.12.2-recommended": "1.0.14",
|
||||
"1.12.2-latest": "1.0.15-b2"
|
||||
"1.12.2-recommended": "1.0.15",
|
||||
"1.12.2-latest": "1.0.15"
|
||||
}
|
||||
}
|
|
@ -10,6 +10,15 @@ Mod sources for Minecraft version 1.12.2.
|
|||
----
|
||||
## Version history
|
||||
|
||||
-------------------------------------------------------------------
|
||||
- v1.0.15 [R] Release based on v1.0.15-b2. Release-to-release changes:
|
||||
* Added Small Block Breaker
|
||||
* Small Tree Cutter fixes and compatability improved.
|
||||
* Crafting table compat fixes.
|
||||
-------------------------------------------------------------------
|
||||
[M] Small Tree Cutter log detection bug fixed (issue #59).
|
||||
[M] Small Tree Cutter supports Menril chopping (issue #54).
|
||||
|
||||
- v1.0.15-b2 [A] Added Small Block Breaker
|
||||
[M] Crafting Table: Allowing NBT "Damage" mismatch only
|
||||
items that are declared damagable (issue #56).
|
||||
|
|
|
@ -193,6 +193,13 @@ public class ModContent
|
|||
ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16)
|
||||
);
|
||||
|
||||
public static final BlockDecorFluidFunnel SMALL_FLUID_FUNNEL = new BlockDecorFluidFunnel(
|
||||
"small_fluid_funnel",
|
||||
BlockDecor.CFG_CUTOUT|BlockDecor.CFG_ELECTRICAL|BlockDecor.CFG_REDSTONE_CONTROLLED,
|
||||
Material.IRON, 1f, 15f, SoundType.METAL,
|
||||
ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16)
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
public static final BlockDecorStraightPole TREATED_WOOD_POLE = new BlockDecorStraightPole(
|
||||
|
@ -468,6 +475,9 @@ public class ModContent
|
|||
private static final TileEntityRegistrationData PASSIVE_FLUID_ACCUMULATOR_TEI = new TileEntityRegistrationData(
|
||||
BlockDecorPassiveFluidAccumulator.BTileEntity.class, "te_passive_fluid_accumulator"
|
||||
);
|
||||
private static final TileEntityRegistrationData SMALL_FLUID_FUNNEL_TEI = new TileEntityRegistrationData(
|
||||
BlockDecorFluidFunnel.BTileEntity.class, "te_small_fluid_funnel"
|
||||
);
|
||||
private static final TileEntityRegistrationData WASTE_INCINERATOR_TEI = new TileEntityRegistrationData(
|
||||
BlockDecorWasteIncinerator.BTileEntity.class, "te_small_waste_incinerator"
|
||||
);
|
||||
|
@ -568,6 +578,7 @@ public class ModContent
|
|||
PANZERGLASS_SLAB, // @todo: check if another class is needed due to is_side_visible
|
||||
TREATED_WOOD_FLOOR, // @todo: check if textures need improvement
|
||||
TEST_BLOCK,TEST_BLOCK_TEI,
|
||||
SMALL_FLUID_FUNNEL,SMALL_FLUID_FUNNEL_TEI
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -116,6 +116,7 @@ public class ModEngineersDecor
|
|||
if(RecipeCondModSpecific.num_skipped > 0) logger.info("Excluded " + RecipeCondModSpecific.num_skipped + " recipes due to config opt-out.");
|
||||
if(ModConfig.zmisc.with_experimental) logger.info("Included experimental features due to mod config.");
|
||||
ExtItems.onPostInit();
|
||||
BlockCategories.reload();
|
||||
TreeCutting.reload();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* @file BlockDecorFluidFunnel.java
|
||||
* @author Stefan Wilhelm (wile)
|
||||
* @copyright (C) 2019 Stefan Wilhelm
|
||||
* @license MIT (see https://opensource.org/licenses/MIT)
|
||||
*
|
||||
* A device that collects and stores fluid blocks above it.
|
||||
* Tracks flowing fluid to their source blocks. Compatible
|
||||
* with vanilla infinite water source.
|
||||
*/
|
||||
package wile.engineersdecor.blocks;
|
||||
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.properties.PropertyInteger;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ITickable;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.fluids.*;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||
import net.minecraftforge.fluids.capability.IFluidTankProperties;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class BlockDecorFluidFunnel extends BlockDecor
|
||||
{
|
||||
public static final int FILL_LEVEL_MAX = 3;
|
||||
public static final PropertyInteger FILL_LEVEL = PropertyInteger.create("level", 0, FILL_LEVEL_MAX);
|
||||
|
||||
public BlockDecorFluidFunnel(@Nonnull String registryName, long config, @Nullable Material material, float hardness, float resistance, @Nullable SoundType sound, @Nonnull AxisAlignedBB unrotatedAABB)
|
||||
{ super(registryName, config, material, hardness, resistance, sound, unrotatedAABB); }
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState()
|
||||
{ return new BlockStateContainer(this, FILL_LEVEL); }
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int meta)
|
||||
{ return super.getStateFromMeta(meta).withProperty(FILL_LEVEL, meta & 0x3); }
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state)
|
||||
{ return super.getMetaFromState(state) | (state.getValue(FILL_LEVEL)); }
|
||||
|
||||
@Override
|
||||
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer, EnumHand hand)
|
||||
{ return super.getStateForPlacement(world, pos, facing, hitX, hitY, hitZ, meta, placer, hand).withProperty(FILL_LEVEL, 0); }
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public int getComparatorInputOverride(IBlockState state, World world, BlockPos pos)
|
||||
{ return MathHelper.clamp((state.getValue(FILL_LEVEL)*5), 0, 15); }
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state)
|
||||
{ return true; }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TileEntity createTileEntity(World world, IBlockState state)
|
||||
{ return new BTileEntity(); }
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
|
||||
{
|
||||
if(world.isRemote) return;
|
||||
if((!stack.hasTagCompound()) || (!stack.getTagCompound().hasKey("tedata"))) return;
|
||||
NBTTagCompound te_nbt = stack.getTagCompound().getCompoundTag("tedata");
|
||||
if(te_nbt.isEmpty()) return;
|
||||
final TileEntity te = world.getTileEntity(pos);
|
||||
if(!(te instanceof BTileEntity)) return;
|
||||
((BTileEntity)te).readnbt(te_nbt, false);
|
||||
((BTileEntity)te).markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest)
|
||||
{
|
||||
if(world.isRemote) return true;
|
||||
TileEntity te = world.getTileEntity(pos);
|
||||
if(!(te instanceof BTileEntity)) return super.removedByPlayer(state, world, pos, player, willHarvest);
|
||||
ItemStack stack = new ItemStack(this, 1);
|
||||
NBTTagCompound te_nbt = new NBTTagCompound();
|
||||
((BTileEntity) te).writenbt(te_nbt, false);
|
||||
if(!te_nbt.isEmpty()) {
|
||||
NBTTagCompound nbt = new NBTTagCompound();
|
||||
nbt.setTag("tedata", te_nbt);
|
||||
stack.setTagCompound(nbt);
|
||||
}
|
||||
world.spawnEntity(new EntityItem(world, pos.getX()+0.5, pos.getY()+0.5, pos.getZ()+0.5, stack));
|
||||
world.setBlockToAir(pos);
|
||||
world.removeTileEntity(pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
|
||||
{
|
||||
if(world.isRemote) return true;
|
||||
TileEntity te = world.getTileEntity(pos);
|
||||
if(!(te instanceof BTileEntity)) return false;
|
||||
return FluidUtil.interactWithFluidHandler(player, hand, world, pos, facing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos fromPos)
|
||||
{ TileEntity te = world.getTileEntity(pos); if(te instanceof BTileEntity) ((BTileEntity)te).block_changed(); }
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
// Tile entity
|
||||
//--------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
public static class BTileEntity extends TileEntity implements IFluidHandler, IFluidTankProperties, ICapabilityProvider, ITickable
|
||||
{
|
||||
public static final int TANK_CAPACITY = 3000;
|
||||
public static final int TICK_INTERVAL = 10; // ca 500ms
|
||||
public static final int COLLECTION_INTERVAL = 40; // ca 2000ms, simulates suction delay and saves CPU when not drained.
|
||||
public static final int MAX_TRACK_RADIUS = 16;
|
||||
public static final int MAX_TRACKING_STEPS_PER_CYCLE = 72;
|
||||
public static final int MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE = 1024;
|
||||
public static final int MAX_TRACK_RADIUS_SQ = MAX_TRACK_RADIUS*MAX_TRACK_RADIUS;
|
||||
public static final int INTENSIVE_SEARCH_TRIGGER_THRESHOLD = 16;
|
||||
private final IFluidTankProperties[] fluid_props_ = {this};
|
||||
private FluidStack tank_ = null;
|
||||
private int tick_timer_ = 0;
|
||||
private int collection_timer_ = 0;
|
||||
private BlockPos last_pick_pos_ = BlockPos.ORIGIN;
|
||||
private ArrayList<Vec3i> search_offsets_ = null;
|
||||
private int no_fluid_found_counter_ = 0;
|
||||
private int intensive_search_counter_ = 0;
|
||||
private int total_pick_counter_ = 0;
|
||||
|
||||
public BTileEntity()
|
||||
{}
|
||||
|
||||
public void block_changed()
|
||||
{ tick_timer_ = TICK_INTERVAL; } // collect after flowing fluid has a stable state, otherwise it looks odd.
|
||||
|
||||
public void readnbt(NBTTagCompound nbt, boolean update_packet)
|
||||
{
|
||||
tank_ = (!nbt.hasKey("tank")) ? (null) : (FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag("tank")));
|
||||
}
|
||||
|
||||
protected void writenbt(NBTTagCompound nbt, boolean update_packet)
|
||||
{
|
||||
if(tank_ != null) nbt.setTag("tank", tank_.writeToNBT(new NBTTagCompound()));
|
||||
}
|
||||
|
||||
// TileEntity ------------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
|
||||
{
|
||||
block_changed();
|
||||
return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorFluidFunnel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFromNBT(NBTTagCompound nbt)
|
||||
{ super.readFromNBT(nbt); readnbt(nbt, false); }
|
||||
|
||||
@Override
|
||||
public NBTTagCompound writeToNBT(NBTTagCompound nbt)
|
||||
{ super.writeToNBT(nbt); writenbt(nbt, false); return nbt; }
|
||||
|
||||
// ICapabilityProvider --------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing)
|
||||
{ return ((capability==CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY)) || super.hasCapability(capability, facing); }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing)
|
||||
{
|
||||
if(capability != CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return super.getCapability(capability, facing);
|
||||
return ((T)this);
|
||||
}
|
||||
|
||||
// IFluidHandler of the output port --------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public IFluidTankProperties[] getTankProperties()
|
||||
{ return fluid_props_; }
|
||||
|
||||
@Override
|
||||
public int fill(FluidStack resource, boolean doFill)
|
||||
{ return 0; }
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FluidStack drain(FluidStack resource, boolean doDrain)
|
||||
{
|
||||
if((resource==null) || (tank_==null)) return null;
|
||||
return (!(tank_.isFluidEqual(resource))) ? (null) : drain(resource.amount, doDrain);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public FluidStack drain(int maxDrain, boolean doDrain)
|
||||
{
|
||||
if(tank_==null) return null;
|
||||
maxDrain = MathHelper.clamp(maxDrain ,0 , tank_.amount);
|
||||
FluidStack res = tank_.copy();
|
||||
res.amount = maxDrain;
|
||||
if(doDrain) tank_.amount -= maxDrain;
|
||||
if(tank_.amount <= 0) tank_= null;
|
||||
return res;
|
||||
}
|
||||
|
||||
// IFluidTankProperties --------------------------------------------------------------------
|
||||
|
||||
@Override @Nullable public FluidStack getContents() { return (tank_==null) ? (null) : (tank_.copy()); }
|
||||
@Override public int getCapacity() { return TANK_CAPACITY; }
|
||||
@Override public boolean canFill() { return false; }
|
||||
@Override public boolean canDrain() { return true; }
|
||||
@Override public boolean canFillFluidType(FluidStack fluidStack) { return false; }
|
||||
@Override public boolean canDrainFluidType(FluidStack fluidStack) { return true; }
|
||||
|
||||
// ITickable--------------------------------------------------------------------------------
|
||||
|
||||
private Fluid get_fluid(BlockPos pos)
|
||||
{ return FluidRegistry.lookupFluidForBlock(world.getBlockState(pos).getBlock()); }
|
||||
|
||||
private boolean try_pick(BlockPos pos)
|
||||
{
|
||||
IFluidHandler hnd = FluidUtil.getFluidHandler(world, pos, null);
|
||||
if(hnd == null) return false;
|
||||
FluidStack fs = hnd.drain((tank_==null) ? (TANK_CAPACITY) : (TANK_CAPACITY-tank_.amount), true);
|
||||
if(fs == null) return false;
|
||||
if(tank_ == null) {
|
||||
tank_ = fs.copy();
|
||||
} else if(tank_.isFluidEqual(fs)) {
|
||||
tank_.amount = MathHelper.clamp(tank_.amount+fs.amount, 0, TANK_CAPACITY);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
world.setBlockToAir(pos);
|
||||
world.notifyNeighborsOfStateChange(pos, world.getBlockState(pos).getBlock(), true); // explicitly update neighbours to allow these start flowing
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean can_pick(BlockPos pos, Fluid fluid)
|
||||
{
|
||||
IFluidHandler hnd = FluidUtil.getFluidHandler(world, pos, null);
|
||||
if(hnd == null) return false;
|
||||
FluidStack fs = hnd.drain((tank_==null) ? (TANK_CAPACITY) : (TANK_CAPACITY-tank_.amount), false);
|
||||
return (fs != null) && (fs.getFluid().equals(fluid));
|
||||
}
|
||||
|
||||
private void rebuild_search_offsets(boolean intensive)
|
||||
{
|
||||
search_offsets_ = new ArrayList<>(9);
|
||||
search_offsets_.add(new Vec3i(0, 1, 0)); // up first
|
||||
{
|
||||
ArrayList<Vec3i> ofs = new ArrayList<Vec3i>(Arrays.asList(new Vec3i(-1, 0, 0), new Vec3i( 1, 0, 0), new Vec3i( 0, 0,-1), new Vec3i( 0, 0, 1)));
|
||||
if(intensive || (total_pick_counter_ > 50)) Collections.shuffle(ofs);
|
||||
search_offsets_.addAll(ofs);
|
||||
}
|
||||
if(intensive) {
|
||||
ArrayList<Vec3i> ofs = new ArrayList<Vec3i>(Arrays.asList(new Vec3i(-1, 1, 0), new Vec3i( 1, 1, 0), new Vec3i( 0, 1,-1), new Vec3i( 0, 1, 1)));
|
||||
Collections.shuffle(ofs);
|
||||
search_offsets_.addAll(ofs);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean try_collect(BlockPos collection_pos)
|
||||
{
|
||||
final Block collection_block = world.getBlockState(collection_pos).getBlock();
|
||||
if((!(collection_block instanceof IFluidBlock)) && (!(collection_block instanceof BlockLiquid))) return false;
|
||||
final Fluid fluid_to_collect = FluidRegistry.lookupFluidForBlock(collection_block);
|
||||
if(fluid_to_collect == null) return false; // not sure if that can return null
|
||||
if((tank_!=null) && (!tank_.getFluid().equals(fluid_to_collect))) return false;
|
||||
if(try_pick(collection_pos)) { last_pick_pos_ = collection_pos; return true; } // Blocks directly always first. Allows water source blocks to recover/reflow to source blocks.
|
||||
// Not picked, not a source block -> search highest allowed source block to pick
|
||||
// ---------------------------------------------------------------------------------------------------------------
|
||||
// Plan is to pick preferably surface blocks whilst not wasting much mem and cpu. Blocks will dynamically change due to flowing.
|
||||
// Basic assumptions: fluid flows straight (not diagonal) and not up the hill. Only falling fluid streams are long, lateral streams are about max 16 blocks (water 8).
|
||||
// Problem resolving: Threre are fluids going all over the place, and do not sometimes continue flowing without a source block.
|
||||
// - Stack based trail tracking is used
|
||||
// - Turtle motion with reset on fail, preferrs up, remaining motion order is shuffled to pick not in the same direction.
|
||||
// - Calculation time capping, reset on fail, extended search for far blocks (e.g. stream from a high position) on many fails.
|
||||
// - Preferrs fluid surface blocks if possible and anough calculation time left.
|
||||
// - On fail, replace last flowing block with air and cause a block update, in case previous block updates or strange fluid block behaviour does not prevent advancing.
|
||||
// - Assumption is: The search can go much further than fluids can flow, except top-bottom falling streams. so
|
||||
if((last_pick_pos_==null) || (last_pick_pos_.distanceSq(collection_pos) > MAX_TRACK_RADIUS_SQ)) { last_pick_pos_ = collection_pos; search_offsets_ = null; }
|
||||
BlockPos pos = last_pick_pos_;
|
||||
HashSet<BlockPos> checked = new HashSet<>();
|
||||
Stack<BlockPos> trail = new Stack<BlockPos>();
|
||||
trail.add(pos);
|
||||
checked.add(pos);
|
||||
int steps=0;
|
||||
boolean intensive = (no_fluid_found_counter_ >= INTENSIVE_SEARCH_TRIGGER_THRESHOLD);
|
||||
if(intensive) { no_fluid_found_counter_ = 0; ++intensive_search_counter_; }
|
||||
if(search_offsets_ == null) rebuild_search_offsets(intensive);
|
||||
int max = intensive ? MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE : MAX_TRACKING_STEPS_PER_CYCLE;
|
||||
while(++steps <= max) {
|
||||
int num_adjacent = 0;
|
||||
for(int i=0; i<search_offsets_.size(); ++i) {
|
||||
BlockPos p = pos.add(search_offsets_.get(i));
|
||||
if(checked.contains(p)) continue;
|
||||
checked.add(p);
|
||||
++steps;
|
||||
if(fluid_to_collect.equals(get_fluid(p))) {
|
||||
++num_adjacent;
|
||||
pos = p;
|
||||
trail.push(pos);
|
||||
if(steps < MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2) {
|
||||
// check for same fluid above (only source blocks)
|
||||
final int max_surface_search = (MAX_TRACKING_STEPS_PER_CYCLE_INTENSIVE/2)-steps;
|
||||
for(int k=0; k<max_surface_search; ++k) {
|
||||
if(!can_pick(pos.up(), fluid_to_collect)) break;
|
||||
pos = pos.up();
|
||||
trail.push(pos);
|
||||
}
|
||||
}
|
||||
if(try_pick(pos)) {
|
||||
last_pick_pos_ = pos;
|
||||
no_fluid_found_counter_ = 0;
|
||||
search_offsets_ = null;
|
||||
// probability reset, so it's not turteling too far away, mainly for large nether lava seas, not desert lakes.
|
||||
if((++total_pick_counter_ > 50) && world.rand.nextInt(10)==0) last_pick_pos_ = collection_pos;
|
||||
//println("PASS " + steps + " - " + (pos.subtract(collection_pos)));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(trail.isEmpty()) break; // reset search
|
||||
if(num_adjacent==0) pos = trail.pop();
|
||||
}
|
||||
//println("FAIL=" + steps + " - " + (pos.subtract(collection_pos))); String s = new String(); for(BlockPos p:checked) s += "\n" + p; System.out.println(s);
|
||||
if(intensive_search_counter_ > 2) world.setBlockToAir(pos);
|
||||
last_pick_pos_ = collection_pos;
|
||||
search_offsets_ = null; // try other search order
|
||||
++no_fluid_found_counter_;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void update()
|
||||
{
|
||||
if((world.isRemote) || (--tick_timer_ > 0)) return;
|
||||
tick_timer_ = TICK_INTERVAL;
|
||||
collection_timer_ += TICK_INTERVAL;
|
||||
boolean dirty = false;
|
||||
// Collection
|
||||
if((collection_timer_ >= COLLECTION_INTERVAL) && ((tank_==null) || (tank_.amount <= (TANK_CAPACITY-1000)))) {
|
||||
collection_timer_ = 0;
|
||||
if(!world.isBlockPowered(pos)) { // redstone disable feature
|
||||
if(last_pick_pos_==null) last_pick_pos_ = pos.up();
|
||||
if(try_collect(pos.up())) dirty = true;
|
||||
}
|
||||
}
|
||||
// Gravity fluid transfer
|
||||
if((tank_!=null) && (tank_.amount >= 1000)) {
|
||||
IFluidHandler fh = FluidUtil.getFluidHandler(world, pos.down(), EnumFacing.UP);
|
||||
if(fh != null) {
|
||||
FluidStack fs = new FluidStack(tank_.getFluid(), 1000);
|
||||
int nfilled = MathHelper.clamp(fh.fill(fs, true), 0, 1000);
|
||||
tank_.amount -= nfilled;
|
||||
if(tank_.amount <= 0) tank_ = null;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
// Block state
|
||||
int fill_level = (tank_==null) ? 0 : (MathHelper.clamp(tank_.amount/1000,0,FILL_LEVEL_MAX));
|
||||
final IBlockState funnel_state = world.getBlockState(pos);
|
||||
if(funnel_state.getValue(FILL_LEVEL) != fill_level) world.setBlockState(pos, funnel_state.withProperty(FILL_LEVEL, fill_level), 2|16);
|
||||
if(dirty) markDirty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -124,7 +124,7 @@ public class BlockDecorPassiveFluidAccumulator extends BlockDecorDirected
|
|||
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
|
||||
{
|
||||
block_changed();
|
||||
return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorPipeValve));
|
||||
return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorPassiveFluidAccumulator));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,7 @@ public class BlockCategories
|
|||
{
|
||||
private static Set<Block> logs_ = new HashSet<Block>();
|
||||
private static Set<Block> leaves_ = new HashSet<Block>();
|
||||
private static Set<Block> variant_logs_ = new HashSet<Block>(); // logs that are not checked for log equivalence
|
||||
|
||||
public static final Set<Block> logs()
|
||||
{ return logs_; } // wrap in case immutable needed one time.
|
||||
|
@ -33,7 +34,6 @@ public class BlockCategories
|
|||
public static final Set<Block> leaves()
|
||||
{ return leaves_; }
|
||||
|
||||
|
||||
public static boolean isLog(IBlockState state)
|
||||
{
|
||||
final Block block = state.getBlock();
|
||||
|
@ -48,23 +48,27 @@ public class BlockCategories
|
|||
}
|
||||
|
||||
public static final boolean isSameLeaves(IBlockState a, IBlockState b)
|
||||
{ return (a.getBlock() == b.getBlock()); }
|
||||
{
|
||||
if(!isLeaves(a)) return false;
|
||||
final Block block = a.getBlock();
|
||||
if(block != b.getBlock()) return false;
|
||||
if(block instanceof BlockNewLeaf) return a.getValue(BlockNewLeaf.VARIANT) == b.getValue(BlockNewLeaf.VARIANT);
|
||||
if(block instanceof BlockOldLeaf) return a.getValue(BlockOldLeaf.VARIANT) == b.getValue(BlockOldLeaf.VARIANT);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final boolean isSameLog(IBlockState a, IBlockState b)
|
||||
{
|
||||
// very strange ...
|
||||
if(a.getBlock()!=b.getBlock()) {
|
||||
return false;
|
||||
} else if(a.getBlock() instanceof BlockNewLog) {
|
||||
return a.getValue(BlockNewLog.VARIANT) == b.getValue(BlockNewLog.VARIANT);
|
||||
} else if(a.getBlock() instanceof BlockOldLog) {
|
||||
return a.getValue(BlockOldLog.VARIANT) == b.getValue(BlockOldLog.VARIANT);
|
||||
} else {
|
||||
// Uagh, that hurts the heart of performance ...
|
||||
final IProperty<?> prop = a.getPropertyKeys().stream().filter( (IProperty<?> p) -> (p.getName().contains("variant") || p.getName().contains("type"))).findFirst().orElse(null);
|
||||
if(prop==null) return false;
|
||||
return a.getValue(prop).equals(b.getValue(prop));
|
||||
}
|
||||
if((!isLog(a)) || (!isLog(b))) return false;
|
||||
if(variant_logs_.contains(a.getBlock()) || (variant_logs_.contains(b.getBlock()))) return true;
|
||||
if(a.getBlock()!=b.getBlock()) return false;
|
||||
if(a.getBlock() instanceof BlockNewLog) return a.getValue(BlockNewLog.VARIANT) == b.getValue(BlockNewLog.VARIANT);
|
||||
if(a.getBlock() instanceof BlockOldLog) return a.getValue(BlockOldLog.VARIANT)==b.getValue(BlockOldLog.VARIANT);
|
||||
// Uagh, that hurts the heart of performance ...
|
||||
final IProperty<?> prop = a.getPropertyKeys().stream().filter( (IProperty<?> p) -> (p.getName().contains("variant") || p.getName().contains("type"))).findFirst().orElse(null);
|
||||
if(prop!=null) return a.getValue(prop).equals(b.getValue(prop));
|
||||
// All other: We have to assume that there are no variants for this block, and the block type denotes the log type unambiguously.
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final void reload()
|
||||
|
@ -77,11 +81,17 @@ public class BlockCategories
|
|||
for(ItemStack stack : stacks) {
|
||||
final Item item = stack.getItem();
|
||||
if(!(item instanceof ItemBlock)) continue;
|
||||
logs.add(((ItemBlock)item).getBlock());
|
||||
Block block = ((ItemBlock)item).getBlock();
|
||||
logs.add(block);
|
||||
// @todo: make this configurable
|
||||
if(block.getRegistryName().getPath().contains("menril")) variant_logs_.add(block);
|
||||
}
|
||||
}
|
||||
logs_ = logs;
|
||||
ModEngineersDecor.logger.info("Found "+logs.size()+" types of 'choppable' log.");
|
||||
if(ModConfig.zmisc.with_experimental) {
|
||||
for(Block b:logs_) ModEngineersDecor.logger.info(" - choppable log: " + b.getRegistryName());
|
||||
}
|
||||
}
|
||||
{
|
||||
HashSet<Block> leaves = new HashSet<Block>();
|
||||
|
@ -96,6 +106,9 @@ public class BlockCategories
|
|||
}
|
||||
leaves_ = leaves;
|
||||
ModEngineersDecor.logger.info("Found "+leaves.size()+" types of leaves.");
|
||||
if(ModConfig.zmisc.with_experimental) {
|
||||
for(Block b:leaves_) ModEngineersDecor.logger.info(" - choppable leaf: " + b.getRegistryName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ import java.util.*;
|
|||
public class TreeCutting
|
||||
{
|
||||
private static org.apache.logging.log4j.Logger LOGGER = ModEngineersDecor.logger;
|
||||
private static int max_log_tracing_steps = 128;
|
||||
private static int max_cutting_height = 128;
|
||||
private static int max_cutting_radius = 12;
|
||||
|
||||
private static class Compat
|
||||
{
|
||||
|
@ -39,27 +42,28 @@ public class TreeCutting
|
|||
choppable_states.clear();
|
||||
if(ModAuxiliaries.isModLoaded("dynamictrees")) {
|
||||
ForgeRegistries.BLOCKS.getKeys().forEach((regname)->{
|
||||
//if("dynamictrees".equals(regname.getNamespace())) { ... let's see if that also work with dyntrees compat mods
|
||||
if(!regname.getPath().contains("branch")) return;
|
||||
try {
|
||||
Block block = ForgeRegistries.BLOCKS.getValue(regname);
|
||||
IBlockState state = block.getDefaultState();
|
||||
for(IProperty<?> vaprop: state.getProperties().keySet()) {
|
||||
if(!("radius".equals(vaprop.getName())) || (vaprop.getValueClass() != Integer.class)) continue;
|
||||
@SuppressWarnings("unchecked")
|
||||
IProperty<Integer> prop = (IProperty<Integer>)vaprop;
|
||||
Integer max = ((Collection<Integer>)prop.getAllowedValues()).stream().max(Integer::compare).orElse(0);
|
||||
if(max<7) continue;
|
||||
for(int r=7; r<=max; ++r) choppable_states.put(state.withProperty(prop, r), ChoppingMethod.RootBlockBreaking);
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
LOGGER.warn("Failed to register chopping for " + regname.toString());
|
||||
return;
|
||||
if(!regname.getPath().contains("branch")) return;
|
||||
try {
|
||||
Block block = ForgeRegistries.BLOCKS.getValue(regname);
|
||||
IBlockState state = block.getDefaultState();
|
||||
for(IProperty<?> vaprop: state.getProperties().keySet()) {
|
||||
if(!("radius".equals(vaprop.getName())) || (vaprop.getValueClass() != Integer.class)) continue;
|
||||
@SuppressWarnings("unchecked")
|
||||
IProperty<Integer> prop = (IProperty<Integer>)vaprop;
|
||||
Integer max = ((Collection<Integer>)prop.getAllowedValues()).stream().max(Integer::compare).orElse(0);
|
||||
if(max<7) continue;
|
||||
for(int r=7; r<=max; ++r) choppable_states.put(state.withProperty(prop, r), ChoppingMethod.RootBlockBreaking);
|
||||
}
|
||||
//}
|
||||
} catch(Throwable e) {
|
||||
LOGGER.warn("Failed to register chopping for " + regname.toString());
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
LOGGER.info("Dynamic Trees chopping compat: " + choppable_states.size() + " choppable states found.");
|
||||
if(ModConfig.zmisc.with_experimental) {
|
||||
for(IBlockState b:choppable_states.keySet()) ModEngineersDecor.logger.info(" - dynamic tree state: " + b);
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
choppable_states.clear();
|
||||
LOGGER.warn("Failed to determine choppings for dynamic trees compat, skipping that:" + e);
|
||||
|
@ -100,7 +104,13 @@ public class TreeCutting
|
|||
);
|
||||
|
||||
public static void reload()
|
||||
{ Compat.reload(); }
|
||||
{
|
||||
Compat.reload();
|
||||
// later config, now keep IJ from suggesting that a private variable should be used.
|
||||
max_log_tracing_steps = 128;
|
||||
max_cutting_height = 128;
|
||||
max_cutting_radius = 12;
|
||||
}
|
||||
|
||||
private static List<BlockPos> findBlocksAround(final World world, final BlockPos centerPos, final IBlockState leaf_type_state, final Set<BlockPos> checked, int recursion_left)
|
||||
{
|
||||
|
@ -121,6 +131,17 @@ public class TreeCutting
|
|||
return to_decay;
|
||||
}
|
||||
|
||||
private static boolean too_far(BlockPos start, BlockPos pos)
|
||||
{
|
||||
int dy = pos.getY()-start.getY();
|
||||
if((dy < 0) || (dy>max_cutting_height)) return true;
|
||||
final int dx = Math.abs(pos.getX()-start.getX());
|
||||
final int dz = Math.abs(pos.getZ()-start.getZ());
|
||||
if(dy > max_cutting_radius) dy = max_cutting_radius;
|
||||
if((dx >= dy+3) || (dz >= dy+3)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canChop(IBlockState state)
|
||||
{ return BlockCategories.isLog(state) || Compat.canChop(state); }
|
||||
|
||||
|
@ -144,11 +165,13 @@ public class TreeCutting
|
|||
LinkedList<BlockPos> upqueue = new LinkedList<BlockPos>();
|
||||
queue.add(startPos);
|
||||
int cutlevel = 0;
|
||||
int steps_left = 64;
|
||||
int steps_left = max_log_tracing_steps;
|
||||
IBlockState tracked_leaves_state = null;
|
||||
while(!queue.isEmpty() && (--steps_left >= 0)) {
|
||||
final BlockPos pos = queue.removeFirst();
|
||||
// Vertical search
|
||||
final BlockPos uppos = pos.up();
|
||||
if(too_far(startPos, uppos)) { checked.add(uppos); continue; }
|
||||
final IBlockState upstate = world.getBlockState(uppos);
|
||||
if(!checked.contains(uppos)) {
|
||||
checked.add(uppos);
|
||||
|
@ -156,11 +179,18 @@ public class TreeCutting
|
|||
// Up is log
|
||||
upqueue.add(uppos);
|
||||
to_break.add(uppos);
|
||||
steps_left = 64;
|
||||
steps_left = max_log_tracing_steps;
|
||||
} else {
|
||||
boolean isleaf = BlockCategories.isLeaves(upstate);
|
||||
if(isleaf || world.isAirBlock(uppos) || (upstate.getBlock() instanceof BlockVine)) {
|
||||
if(isleaf) to_decay.add(uppos);
|
||||
if(isleaf) {
|
||||
if(tracked_leaves_state==null) {
|
||||
tracked_leaves_state=upstate;
|
||||
to_decay.add(uppos);
|
||||
} else if(BlockCategories.isSameLeaves(upstate, tracked_leaves_state)) {
|
||||
to_decay.add(uppos);
|
||||
}
|
||||
}
|
||||
// Up is air, check adjacent for diagonal up (e.g. Accacia)
|
||||
for(Vec3i v:hoffsets) {
|
||||
final BlockPos p = uppos.add(v);
|
||||
|
@ -172,7 +202,9 @@ public class TreeCutting
|
|||
queue.add(p);
|
||||
to_break.add(p);
|
||||
} else if(BlockCategories.isLeaves(st)) {
|
||||
to_decay.add(p);
|
||||
if((tracked_leaves_state==null) || (BlockCategories.isSameLeaves(st, tracked_leaves_state))) {
|
||||
to_decay.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,14 +215,15 @@ public class TreeCutting
|
|||
final BlockPos p = pos.add(v);
|
||||
if(checked.contains(p)) continue;
|
||||
checked.add(p);
|
||||
if(p.distanceSq(new BlockPos(startPos.getX(), p.getY(), startPos.getZ())) > (3+cutlevel*cutlevel)) continue;
|
||||
final IBlockState st = world.getBlockState(p);
|
||||
final Block bl = st.getBlock();
|
||||
if(BlockCategories.isSameLog(st, broken_state)) {
|
||||
queue.add(p);
|
||||
to_break.add(p);
|
||||
} else if(BlockCategories.isLeaves(st)) {
|
||||
to_decay.add(p);
|
||||
if((tracked_leaves_state==null) || (BlockCategories.isSameLeaves(st, tracked_leaves_state))) {
|
||||
to_decay.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(queue.isEmpty() && (!upqueue.isEmpty())) {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"forge_marker": 1,
|
||||
"defaults": { "model": "engineersdecor:device/small_fluid_funnel_model_s0" },
|
||||
"variants": {
|
||||
"normal": [{}],
|
||||
"inventory": [{}],
|
||||
"level": {
|
||||
"0":{},
|
||||
"1":{"model": "engineersdecor:device/small_fluid_funnel_model_s1"},
|
||||
"2":{"model": "engineersdecor:device/small_fluid_funnel_model_s2"},
|
||||
"3":{"model": "engineersdecor:device/small_fluid_funnel_model_s3"}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -150,6 +150,11 @@ tile.engineersdecor.straight_pipe_valve_redstone_analog.help=§6Straight fluid p
|
|||
tile.engineersdecor.passive_fluid_accumulator.name=Passive Fluid Accumulator
|
||||
tile.engineersdecor.passive_fluid_accumulator.help=§6Vacuum suction based fluid collector.§r Has one output, all other sides are input. \
|
||||
Drains fluids from adjacent tanks when being drained from the output port by a pump.
|
||||
tile.engineersdecor.small_fluid_funnel.name=Small Fluid Collection Funnel
|
||||
tile.engineersdecor.small_fluid_funnel.help=§6Collects fluids above it.§r Has an internal tank with three buckets capacity. Traces \
|
||||
flowing fluids to nearby source blocks. The fluid can be obtained with fluid transfer systems \
|
||||
or a bucket. Fills only tanks below (gravity transfer). Compatible with vanilla \
|
||||
infinite-water-source creation.
|
||||
#-----------------------------------------------------------------------------------------------------------
|
||||
tile.engineersdecor.factory_dropper.name=Factory Dropper
|
||||
tile.engineersdecor.factory_dropper.help=§6Dropper suitable for advanced factory automation.§r Has twelve round-robin selected slots. \
|
||||
|
|
|
@ -145,6 +145,11 @@ tile.engineersdecor.straight_pipe_valve_redstone_analog.help=§6Фрагмент
|
|||
tile.engineersdecor.passive_fluid_accumulator.name=Пассивный жидкостный накопитель
|
||||
tile.engineersdecor.passive_fluid_accumulator.help=§6Вакуумный всасывающий жидкостный коллектор.§r Имеет один выход, все остальные стороны входные. \
|
||||
Сливает жидкости из соседних резервуаров при выкачивании жидкости из выходного порта.
|
||||
tile.engineersdecor.small_fluid_funnel.name=Small Fluid Collection Funnel
|
||||
#tile.engineersdecor.small_fluid_funnel.help=§6Collects fluids above it.§r Has an internal tank with three buckets capacity. Traces \
|
||||
flowing fluids to nearby source blocks. The fluid can be obtained with fluid transfer systems \
|
||||
or a bucket. Fills only tanks below (gravity transfer). Compatible with vanilla \
|
||||
infinite-water-source creation.
|
||||
#-----------------------------------------------------------------------------------------------------------
|
||||
tile.engineersdecor.factory_dropper.name=Фабричный выбрасыватель
|
||||
tile.engineersdecor.factory_dropper.help=§6Выбрасыватель подходит для продвинутой автоматизации производства.§r Имеет 12 выборочных слотов. \
|
||||
|
|
|
@ -149,6 +149,11 @@ tile.engineersdecor.straight_pipe_valve_redstone_analog.help=§6一段直输液
|
|||
tile.engineersdecor.passive_fluid_accumulator.name=被动流体累积器。
|
||||
tile.engineersdecor.passive_fluid_accumulator.help=§6基于真空吸力的流体收集器。§r有一个输出面,其他面都是输入。\
|
||||
当从输出面被泵抽取时,从输入面邻接储罐抽取液体。
|
||||
tile.engineersdecor.small_fluid_funnel.name=Small Fluid Collection Funnel
|
||||
#tile.engineersdecor.small_fluid_funnel.help=§6Collects fluids above it.§r Has an internal tank with three buckets capacity. Traces \
|
||||
flowing fluids to nearby source blocks. The fluid can be obtained with fluid transfer systems \
|
||||
or a bucket. Fills only tanks below (gravity transfer). Compatible with vanilla \
|
||||
infinite-water-source creation.
|
||||
#-----------------------------------------------------------------------------------------------------------
|
||||
tile.engineersdecor.factory_dropper.name=工厂掉落器
|
||||
tile.engineersdecor.factory_dropper.help=§6适用于高级工厂自动化的掉落器。§r有十二个轮询选择的储物格。\
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
{
|
||||
"parent": "block/cube",
|
||||
"textures": {
|
||||
"top": "engineersdecor:blocks/device/small_fluid_funnel_top",
|
||||
"bottom": "engineersdecor:blocks/device/small_fluid_funnel_bottom",
|
||||
"side": "engineersdecor:blocks/device/small_fluid_funnel_side_s0",
|
||||
"particle": "engineersdecor:blocks/device/small_fluid_funnel_side_s0"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [0, 0, 0],
|
||||
"to": [16, 14, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 2, 16, 16], "texture": "#side"},
|
||||
"east": {"uv": [0, 2, 16, 16], "texture": "#side"},
|
||||
"south": {"uv": [0, 2, 16, 16], "texture": "#side"},
|
||||
"west": {"uv": [0, 2, 16, 16], "texture": "#side"},
|
||||
"up": {"uv": [0, 0, 16, 16], "texture": "#top"},
|
||||
"down": {"uv": [0, 0, 16, 16], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [14, 15, 0],
|
||||
"to": [16, 16, 16],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 2, 1], "texture": "#side"},
|
||||
"east": {"uv": [0, 0, 16, 1], "texture": "#side"},
|
||||
"south": {"uv": [14, 0, 16, 1], "texture": "#side"},
|
||||
"west": {"uv": [0, 0, 16, 1], "texture": "#side"},
|
||||
"up": {"uv": [14, 0, 16, 16], "texture": "#top"},
|
||||
"down": {"uv": [14, 0, 16, 16], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [13, 14, 2],
|
||||
"to": [15, 15, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [7, 20, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 1, 3, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 1, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [13, 1, 15, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 1, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [13, 2, 15, 14], "texture": "#top"},
|
||||
"down": {"uv": [13, 2, 15, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 15, 0],
|
||||
"to": [15, 16, 2],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [5, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 0, 14, 1], "texture": "#side"},
|
||||
"east": {"uv": [14, 0, 16, 1], "texture": "#side"},
|
||||
"south": {"uv": [2, 0, 15, 1], "texture": "#side"},
|
||||
"west": {"uv": [0, 0, 2, 1], "texture": "#side"},
|
||||
"up": {"uv": [2, 0, 15, 2], "texture": "#top"},
|
||||
"down": {"uv": [2, 14, 15, 16], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3, 14, 1],
|
||||
"to": [13, 15, 3],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [5, 20, 9]},
|
||||
"faces": {
|
||||
"north": {"uv": [3, 1, 13, 2], "texture": "#side"},
|
||||
"east": {"uv": [13, 1, 15, 2], "texture": "#side"},
|
||||
"south": {"uv": [3, 1, 13, 2], "texture": "#side"},
|
||||
"west": {"uv": [1, 1, 3, 2], "texture": "#side"},
|
||||
"up": {"uv": [3, 1, 13, 3], "texture": "#top"},
|
||||
"down": {"uv": [3, 13, 13, 15], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 15, 14],
|
||||
"to": [15, 16, 16],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [5, 22, 22]},
|
||||
"faces": {
|
||||
"north": {"uv": [1, 0, 14, 1], "texture": "#side"},
|
||||
"east": {"uv": [0, 0, 2, 1], "texture": "#side"},
|
||||
"south": {"uv": [2, 0, 15, 1], "texture": "#side"},
|
||||
"west": {"uv": [14, 0, 16, 1], "texture": "#side"},
|
||||
"up": {"uv": [2, 14, 15, 16], "texture": "#top"},
|
||||
"down": {"uv": [2, 0, 15, 2], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3, 14, 13],
|
||||
"to": [13, 15, 15],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [5, 20, 21]},
|
||||
"faces": {
|
||||
"north": {"uv": [3, 1, 13, 2], "texture": "#side"},
|
||||
"east": {"uv": [1, 1, 3, 2], "texture": "#side"},
|
||||
"south": {"uv": [3, 1, 13, 2], "texture": "#side"},
|
||||
"west": {"uv": [13, 1, 15, 2], "texture": "#side"},
|
||||
"up": {"uv": [3, 13, 13, 15], "texture": "#top"},
|
||||
"down": {"uv": [3, 1, 13, 3], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [2, 14, 2],
|
||||
"to": [2.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [-4, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [13.5, 0, 14, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [2, 0, 2.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [2, 2, 2.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [2, 2, 2.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [3, 14, 2],
|
||||
"to": [3.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [-3, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [12.5, 0, 13, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [3, 0, 3.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [3, 2, 3.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [3, 2, 3.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [4, 14, 2],
|
||||
"to": [4.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [-1, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [11.5, 0, 12, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [4, 0, 4.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [4, 2, 4.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [4, 2, 4.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [5, 14, 2],
|
||||
"to": [5.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [0, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [10.5, 0, 11, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [5, 0, 5.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [5, 2, 5.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [5, 2, 5.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [6, 14, 2],
|
||||
"to": [6.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [1, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [9.5, 0, 10, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [6, 0, 6.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [6, 2, 6.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [6, 2, 6.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [7, 14, 2],
|
||||
"to": [7.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [2, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [8.5, 0, 9, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [7, 0, 7.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [7, 2, 7.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [7, 2, 7.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, 14, 2],
|
||||
"to": [8.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [3, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [7.5, 0, 8, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [8, 0, 8.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [8, 2, 8.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [8, 2, 8.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [9, 14, 2],
|
||||
"to": [9.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [4, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [6.5, 0, 7, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [9, 0, 9.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [9, 2, 9.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [9, 2, 9.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [10, 14, 2],
|
||||
"to": [10.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [5, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [5.5, 0, 6, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [10, 0, 10.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [10, 2, 10.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [10, 2, 10.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [11, 14, 2],
|
||||
"to": [11.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [6, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [4.5, 0, 5, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [11, 0, 11.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [11, 2, 11.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [11, 2, 11.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [12, 14, 2],
|
||||
"to": [12.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [7, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [3.5, 0, 4, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [12, 0, 12.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [12, 2, 12.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [12, 2, 12.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [13, 14, 2],
|
||||
"to": [13.5, 16, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [2.5, 0, 3, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [13, 0, 13.5, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 0, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [13, 2, 13.5, 14], "texture": "#top"},
|
||||
"down": {"uv": [13, 2, 13.5, 14], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [0, 15, 0],
|
||||
"to": [2, 16, 16],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [-6, 22, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [14, 0, 16, 1], "texture": "#side"},
|
||||
"east": {"uv": [0, 0, 16, 1], "texture": "#side"},
|
||||
"south": {"uv": [0, 0, 2, 1], "texture": "#side"},
|
||||
"west": {"uv": [0, 0, 16, 1], "texture": "#side"},
|
||||
"up": {"uv": [0, 0, 2, 16], "texture": "#top"},
|
||||
"down": {"uv": [0, 0, 2, 16], "texture": "#bottom"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [1, 14, 2],
|
||||
"to": [3, 15, 14],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [-5, 20, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [13, 1, 15, 2], "texture": "#side"},
|
||||
"east": {"uv": [2, 1, 14, 2], "texture": "#side"},
|
||||
"south": {"uv": [1, 1, 3, 2], "texture": "#side"},
|
||||
"west": {"uv": [2, 1, 14, 2], "texture": "#side"},
|
||||
"up": {"uv": [1, 2, 3, 14], "texture": "#top"},
|
||||
"down": {"uv": [1, 2, 3, 14], "texture": "#bottom"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"ground": {
|
||||
"scale": [0.2, 0.2, 0.2]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, 225, 0],
|
||||
"scale": [0.625, 0.625, 0.625]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
|
||||
"textures": { "side": "engineersdecor:blocks/device/small_fluid_funnel_side_s1" }
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
|
||||
"textures": { "side": "engineersdecor:blocks/device/small_fluid_funnel_side_s2" }
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
|
||||
"textures": { "side": "engineersdecor:blocks/device/small_fluid_funnel_side_s3" }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "engineersdecor:grc",
|
||||
"result": "engineersdecor:small_fluid_funnel",
|
||||
"missing": ["immersiveengineering:metal_device1"]
|
||||
}
|
||||
],
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
"HHH",
|
||||
"IBI",
|
||||
"III"
|
||||
],
|
||||
"key": {
|
||||
"B": {
|
||||
"item": "minecraft:bucket",
|
||||
"data": 0
|
||||
},
|
||||
"I": {
|
||||
"item": "#ingotIron",
|
||||
"data": 0
|
||||
},
|
||||
"H": {
|
||||
"item": "#anyHopper",
|
||||
"data": 0
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "engineersdecor:small_fluid_funnel",
|
||||
"count": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"conditions": [
|
||||
{
|
||||
"type": "engineersdecor:grc",
|
||||
"result": "engineersdecor:small_fluid_funnel",
|
||||
"required": ["immersiveengineering:metal_device1"]
|
||||
}
|
||||
],
|
||||
"type": "minecraft:crafting_shaped",
|
||||
"pattern": [
|
||||
"HHH",
|
||||
"PBP",
|
||||
"PPP"
|
||||
],
|
||||
"key": {
|
||||
"B": {
|
||||
"item": "#itemSteelBarrel",
|
||||
"data": 0
|
||||
},
|
||||
"P": {
|
||||
"item": "#plateAnyMetal",
|
||||
"data": 0
|
||||
},
|
||||
"H": {
|
||||
"item": "#anyHopper",
|
||||
"data": 0
|
||||
}
|
||||
},
|
||||
"result": {
|
||||
"item": "engineersdecor:small_fluid_funnel",
|
||||
"count": 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 618 B |
Binary file not shown.
After Width: | Height: | Size: 572 B |
Binary file not shown.
After Width: | Height: | Size: 579 B |
Binary file not shown.
After Width: | Height: | Size: 575 B |
Binary file not shown.
After Width: | Height: | Size: 572 B |
Binary file not shown.
After Width: | Height: | Size: 431 B |
Loading…
Add table
Add a link
Reference in a new issue