1.12: v1.0.15 release merge.

This commit is contained in:
stfwi 2019-11-03 13:01:27 +01:00
commit 3834c7573c
121 changed files with 7466 additions and 528 deletions

View file

@ -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-b1
version_engineersdecor=1.0.15

View file

@ -1,6 +1,9 @@
{
"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.",
"1.0.14-b1": "[A] Factory Hopper added (configurable hopper and item collector).\n[M] Small Waste Incinerator Fifo shifting improved.\n[M] Lang file zh_cn updated (scikirbypoke, PR#53).\n[F] Fixed conditional recipe constant for redstone pipe valve (thx @albert_ac).",
"1.0.13": "[R] Release based on v1.0.13-b2. Release-to-release changes: * Small Tree Cutter device added. * Small Solar Panel added. * Steel Mesh Fence added. * Broad Window Sill added.",
@ -62,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.14"
"1.12.2-recommended": "1.0.15",
"1.12.2-latest": "1.0.15"
}
}

View file

@ -10,7 +10,24 @@ Mod sources for Minecraft version 1.12.2.
----
## Version history
~ v1.0.15-b1 [A] Added Floor Edge Light.
-------------------------------------------------------------------
- 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).
[M] Tree Cutter: Loosened the strict mod namespace
requirement for Dynamic Trees log detection (issue #52)
to enable checking DT compat mod log blocks.
- v1.0.15-b1 [A] Added Floor Edge Light.
[A] Added Factory Block Placer and Planter.
-------------------------------------------------------------------
- v1.0.14 [R] Release based on v1.0.14-b1. Release-to-release changes:

View file

@ -32,34 +32,7 @@ import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import wile.engineersdecor.blocks.BlockDecor;
import wile.engineersdecor.blocks.BlockDecorChair;
import wile.engineersdecor.blocks.BlockDecorCraftingTable;
import wile.engineersdecor.blocks.BlockDecorDirected;
import wile.engineersdecor.blocks.BlockDecorDropper;
import wile.engineersdecor.blocks.BlockDecorFence;
import wile.engineersdecor.blocks.BlockDecorFloorGrating;
import wile.engineersdecor.blocks.BlockDecorFull;
import wile.engineersdecor.blocks.BlockDecorFurnace;
import wile.engineersdecor.blocks.BlockDecorFurnaceElectrical;
import wile.engineersdecor.blocks.BlockDecorGlassBlock;
import wile.engineersdecor.blocks.BlockDecorHalfSlab;
import wile.engineersdecor.blocks.BlockDecorHopper;
import wile.engineersdecor.blocks.BlockDecorHorizontalSupport;
import wile.engineersdecor.blocks.BlockDecorLadder;
import wile.engineersdecor.blocks.BlockDecorMineralSmelter;
import wile.engineersdecor.blocks.BlockDecorPassiveFluidAccumulator;
import wile.engineersdecor.blocks.BlockDecorPipeValve;
import wile.engineersdecor.blocks.BlockDecorSlab;
import wile.engineersdecor.blocks.BlockDecorSolarPanel;
import wile.engineersdecor.blocks.BlockDecorStairs;
import wile.engineersdecor.blocks.BlockDecorStraightPole;
import wile.engineersdecor.blocks.BlockDecorTest;
import wile.engineersdecor.blocks.BlockDecorTreeCutter;
import wile.engineersdecor.blocks.BlockDecorWall;
import wile.engineersdecor.blocks.BlockDecorWasteIncinerator;
import wile.engineersdecor.blocks.BlockDecorWindow;
import wile.engineersdecor.blocks.BlockDecorWindowSill;
import wile.engineersdecor.blocks.*;
import wile.engineersdecor.detail.ModAuxiliaries;
import wile.engineersdecor.detail.ModConfig;
import wile.engineersdecor.detail.ModTesrs;
@ -144,6 +117,13 @@ public class ModContent
ModAuxiliaries.getPixeledAABB(2,2,2, 14,14,14)
);
public static final BlockDecorPlacer FACTORY_PLACER = new BlockDecorPlacer(
"factory_placer",
BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK|BlockDecor.CFG_REDSTONE_CONTROLLED,
Material.IRON, 1f, 15f, SoundType.METAL,
ModAuxiliaries.getPixeledAABB(2,2,2, 14,14,14)
);
public static final BlockDecorWasteIncinerator SMALL_WASTE_INCINERATOR = new BlockDecorWasteIncinerator(
"small_waste_incinerator",
BlockDecor.CFG_DEFAULT|BlockDecor.CFG_ELECTRICAL,
@ -172,6 +152,13 @@ public class ModContent
ModAuxiliaries.getPixeledAABB(0,0,0, 16,8,16)
);
public static final BlockDecorBreaker SMALL_BLOCK_BREAKER = new BlockDecorBreaker(
"small_block_breaker",
BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK,
Material.IRON, 1f, 15f, SoundType.METAL,
ModAuxiliaries.getPixeledAABB(0,0,0, 16,12,16)
);
//--------------------------------------------------------------------------------------------------------------------
public static final BlockDecorPipeValve STRAIGHT_CHECK_VALVE = new BlockDecorPipeValve(
@ -206,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(
@ -481,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"
);
@ -490,6 +487,9 @@ public class ModContent
private static final TileEntityRegistrationData FACTORY_HOPPER_TEI = new TileEntityRegistrationData(
BlockDecorHopper.BTileEntity.class, "te_factory_hopper"
);
private static final TileEntityRegistrationData FACTORY_PLACER_TEI = new TileEntityRegistrationData(
BlockDecorPlacer.BTileEntity.class, "te_factory_placer"
);
private static final TileEntityRegistrationData SMALL_MINERAL_SMELTER_TEI = new TileEntityRegistrationData(
BlockDecorMineralSmelter.BTileEntity.class, "te_small_mineral_smelter"
);
@ -499,6 +499,9 @@ public class ModContent
private static final TileEntityRegistrationData SMALL_TREE_CUTTER_TEI = new TileEntityRegistrationData(
BlockDecorTreeCutter.BTileEntity.class, "te_small_tree_cutter"
);
private static final TileEntityRegistrationData SMALL_BLOCK_BREAKER_TEI = new TileEntityRegistrationData(
BlockDecorBreaker.BTileEntity.class, "te_small_block_breaker"
);
private static final TileEntityRegistrationData TEST_BLOCK_TEI = new TileEntityRegistrationData(
BlockDecorTest.BTileEntity.class, "te_testblock"
);
@ -513,12 +516,14 @@ public class ModContent
SMALL_ELECTRICAL_FURNACE, SMALL_ELECTRICAL_FURNACE_TEI,
FACTORY_HOPPER,FACTORY_HOPPER_TEI,
FACTORY_DROPPER, FACTORY_DROPPER_TEI,
FACTORY_PLACER, FACTORY_PLACER_TEI,
SMALL_BLOCK_BREAKER,SMALL_BLOCK_BREAKER_TEI,
SMALL_TREE_CUTTER,SMALL_TREE_CUTTER_TEI,
SMALL_WASTE_INCINERATOR, WASTE_INCINERATOR_TEI,
SMALL_SOLAR_PANEL,SMALL_SOLAR_PANEL_TEI,
SMALL_MINERAL_SMELTER, SMALL_MINERAL_SMELTER_TEI,
STRAIGHT_CHECK_VALVE, STRAIGHT_REDSTONE_VALVE, STRAIGHT_REDSTONE_ANALOG_VALVE, STRAIGHT_PIPE_VALVE_TEI,
PASSIVE_FLUID_ACCUMULATOR, PASSIVE_FLUID_ACCUMULATOR_TEI,
SMALL_MINERAL_SMELTER, SMALL_MINERAL_SMELTER_TEI,
SMALL_SOLAR_PANEL,SMALL_SOLAR_PANEL_TEI,
SMALL_TREE_CUTTER,SMALL_TREE_CUTTER_TEI,
CLINKER_BRICK_BLOCK,
CLINKER_BRICK_SLAB,
CLINKER_BRICK_STAIRS,
@ -572,7 +577,8 @@ public class ModContent
SIGN_MINDSTEP,
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
TEST_BLOCK,TEST_BLOCK_TEI,
SMALL_FLUID_FUNNEL,SMALL_FLUID_FUNNEL_TEI
};
//--------------------------------------------------------------------------------------------------------------------

View file

@ -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();
}
@ -159,6 +160,7 @@ public class ModEngineersDecor
public static final int GUIID_SMALL_WASTE_INCINERATOR = 213104;
public static final int GUIID_FACTORY_DROPPER = 213105;
public static final int GUIID_FACTORY_HOPPER = 213106;
public static final int GUIID_FACTORY_PLACER = 213107;
@Override
public Object getServerGuiElement(final int guiid, final EntityPlayer player, final World world, int x, int y, int z)
@ -172,6 +174,7 @@ public class ModEngineersDecor
case GUIID_SMALL_WASTE_INCINERATOR: return BlockDecorWasteIncinerator.getServerGuiElement(player, world, pos, te);
case GUIID_FACTORY_DROPPER: return BlockDecorDropper.getServerGuiElement(player, world, pos, te);
case GUIID_FACTORY_HOPPER: return BlockDecorHopper.getServerGuiElement(player, world, pos, te);
case GUIID_FACTORY_PLACER: return BlockDecorPlacer.getServerGuiElement(player, world, pos, te);
}
return null;
}
@ -189,6 +192,7 @@ public class ModEngineersDecor
case GUIID_SMALL_WASTE_INCINERATOR: return BlockDecorWasteIncinerator.getClientGuiElement(player, world, pos, te);
case GUIID_FACTORY_DROPPER: return BlockDecorDropper.getClientGuiElement(player, world, pos, te);
case GUIID_FACTORY_HOPPER: return BlockDecorHopper.getClientGuiElement(player, world, pos, te);
case GUIID_FACTORY_PLACER: return BlockDecorPlacer.getClientGuiElement(player, world, pos, te);
}
return null;
}

View file

@ -0,0 +1,275 @@
/*
* @file BlockDecorBreaker.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Small Block Breaker
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.world.World;
import net.minecraft.block.Block;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.init.Blocks;
import net.minecraft.init.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.*;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Random;
public class BlockDecorBreaker extends BlockDecorDirected
{
public static final PropertyBool ACTIVE = PropertyBool.create("active");
public BlockDecorBreaker(@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);
setLightOpacity(0);
}
@Override
protected BlockStateContainer createBlockState()
{ return new BlockStateContainer(this, FACING, ACTIVE); }
@Override
public IBlockState getStateFromMeta(int meta)
{ return getDefaultState().withProperty(FACING, EnumFacing.byHorizontalIndex(meta & 0x7)).withProperty(ACTIVE, (meta & 0x8)!=0); }
@Override
public int getMetaFromState(IBlockState state)
{ return (state.getValue(FACING).getHorizontalIndex() & 0x7) | (state.getValue(ACTIVE) ? 8 : 0); }
@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(ACTIVE, false); }
@Override
public boolean hasTileEntity(IBlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(World world, IBlockState state)
{ return new BTileEntity(); }
@Override
@SideOnly(Side.CLIENT)
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rnd)
{
if((state.getBlock()!=this) || (!state.getValue(ACTIVE))) return;
final double rv = rnd.nextDouble();
if(rv > 0.8) return;
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2);
switch(state.getValue(FACING)) {
case WEST: world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, x-xc, yr, z+xr, 0.0, 0.0, 0.0); break;
case EAST: world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, x+xc, yr, z+xr, 0.0, 0.0, 0.0); break;
case NORTH: world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, x+xr, yr, z-xc, 0.0, 0.0, 0.0); break;
default: world.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, x+xr, yr, z+xc, 0.0, 0.0, 0.0); break;
}
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighborPos)
{
if(!(world instanceof World) || (((World) world).isRemote)) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
((BTileEntity)te).block_updated();
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements ITickable, IEnergyStorage
{
public static final int IDLE_TICK_INTERVAL = 40;
public static final int TICK_INTERVAL = 5;
public static final int BOOST_FACTOR = 8;
public static final int DEFAULT_BOOST_ENERGY = 64;
public static final int DEFAULT_BREAKING_RELUCTANCE = 17;
public static final int DEFAULT_MIN_BREAKING_TIME = 15;
public static final int MAX_BREAKING_TIME = 800;
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE;
private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME;
private int tick_timer_;
private int proc_time_elapsed_;
private int boost_energy_;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 16, 512);
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50);
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100);
ModEngineersDecor.logger.info("Config block breaker: Boost energy consumption:" + boost_energy_consumption + "rf/t, reluctance=" + breaking_reluctance + "/hrdn, break time offset=" + min_breaking_time );
}
public BTileEntity()
{}
public void block_updated()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
// TileEntity ------------------------------------------------------------------------------
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
{ return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorBreaker)); }
// IEnergyStorage ----------------------------------------------------------------------------
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return boost_energy_consumption; }
@Override
public int getEnergyStored()
{ return boost_energy_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{
if((boost_energy_ >= boost_energy_consumption) || (maxReceive < boost_energy_consumption)) return 0;
if(!simulate) boost_energy_ = boost_energy_consumption;
return boost_energy_consumption;
}
// Capability export ----------------------------------------------------------------------------
@Override
public boolean hasCapability(Capability<?> cap, EnumFacing facing)
{ return ((cap==CapabilityEnergy.ENERGY)) || super.hasCapability(cap, facing); }
@Override
@SuppressWarnings("unchecked")
@Nullable
public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing)
{
if(capability == CapabilityEnergy.ENERGY) {
return (T)this;
} else {
return super.getCapability(capability, facing);
}
}
// ITickable ------------------------------------------------------------------------------------
private static HashSet<Block> blacklist = new HashSet<>();
static {
blacklist.add(Blocks.AIR);
blacklist.add(Blocks.BEDROCK);
blacklist.add(Blocks.FIRE);
blacklist.add(Blocks.END_PORTAL);
blacklist.add(Blocks.END_GATEWAY);
blacklist.add(Blocks.END_PORTAL_FRAME);
}
private static boolean isBreakable(IBlockState state, BlockPos pos, World world)
{
final Block block = state.getBlock();
if(blacklist.contains(block)) return false;
if(state.getMaterial().isLiquid()) return false;
if(block.isAir(state, world, pos)) return false;
float bh = state.getBlockHardness(world, pos);
if((bh<0) || (bh>55)) return false;
return true;
}
private boolean breakBlock(IBlockState state, BlockPos pos, World world)
{
if(world.isRemote || world.restoringBlockSnapshots) return false; // retry next cycle
NonNullList<ItemStack> drops = NonNullList.create();
state.getBlock().getDrops(drops, world, pos, state, 0);
world.setBlockToAir(pos);
for(ItemStack drop:drops) spawnAsEntity(world, pos, drop);
SoundType stype = state.getBlock().getSoundType(state, world, pos, null);
if(stype != null) world.playSound(null, pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
return true;
}
@Override
@SuppressWarnings("deprecation")
public void update()
{
if(--tick_timer_ > 0) return;
if(world.isRemote) {
IBlockState state = world.getBlockState(pos);
if(!state.getValue(ACTIVE)) {
tick_timer_ = TICK_INTERVAL;
} else {
tick_timer_ = 1;
// not sure if is so cool to do this each tick ... may be simplified/removed again.
SoundEvent sound = SoundEvents.BLOCK_WOOD_HIT;
SoundType stype = world.getBlockState(pos.offset(state.getValue(FACING))).getBlock().getSoundType();
if((stype == SoundType.CLOTH) || (stype == SoundType.PLANT) || (stype == SoundType.SNOW)) {
sound = SoundEvents.BLOCK_CLOTH_HIT;
} else if((stype == SoundType.GROUND) || (stype == SoundType.SAND)) {
sound = SoundEvents.BLOCK_GRAVEL_HIT;
}
world.playSound(pos.getX(), pos.getY(), pos.getZ(), sound, SoundCategory.BLOCKS, 0.1f, 1.2f, false);
}
} else {
tick_timer_ = TICK_INTERVAL;
final IBlockState device_state = world.getBlockState(pos);
final BlockPos target_pos = pos.offset(device_state.getValue(FACING));
final IBlockState target_state = world.getBlockState(target_pos);
if((world.isBlockPowered(pos)) || (!isBreakable(target_state, target_pos, world))) {
if(device_state.getValue(ACTIVE)) world.setBlockState(pos, device_state.withProperty(ACTIVE, false), 1|2);
proc_time_elapsed_ = 0;
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
proc_time_elapsed_ += TICK_INTERVAL;
boolean active = true;
int time_needed = (int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time;
if(boost_energy_ >= boost_energy_consumption) {
boost_energy_ = 0;
proc_time_elapsed_ += TICK_INTERVAL * BOOST_FACTOR;
time_needed += min_breaking_time * (3*BOOST_FACTOR/5);
}
time_needed = MathHelper.clamp(time_needed, min_breaking_time, MAX_BREAKING_TIME);
if(proc_time_elapsed_ >= time_needed) {
proc_time_elapsed_ = 0;
breakBlock(target_state, target_pos, world);
active = false;
}
if(device_state.getValue(ACTIVE) != active) {
world.setBlockState(pos, device_state.withProperty(ACTIVE, active), 1|2);
}
}
}
}
}

View file

@ -900,7 +900,7 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
// first iteration: fillup existing stacks
for(int i = slot_begin; i < slot_end; ++i) {
final ItemStack stack = inventory.getStackInSlot(i);
if((stack.isEmpty()) || (!stack.isItemEqual(mvstack))) continue;
if((stack.isEmpty()) || (!isItemExactlyEqual(stack,mvstack))) continue;
int nmax = stack.getMaxStackSize() - stack.getCount();
if(mvstack.getCount() <= nmax) {
stack.setCount(stack.getCount()+mvstack.getCount());
@ -936,6 +936,24 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
return mvstack;
}
private boolean isItemExactlyEqual(ItemStack stack1, ItemStack stack2)
{
if(!stack1.isItemEqual(stack2)) return false;
if(stack1.hasTagCompound()) {
final NBTTagCompound nbt = stack1.getTagCompound();
int n = stack1.getTagCompound().getSize();
if((n > 0) && stack1.getItem().isDamageable() && (stack1.getTagCompound().hasKey("Damage"))) --n;
if(n > 0) return false;
}
if(stack2.hasTagCompound()) {
final NBTTagCompound nbt = stack2.getTagCompound();
int n = stack2.getTagCompound().getSize();
if((n > 0) && stack2.getItem().isDamageable() && (stack2.getTagCompound().hasKey("Damage"))) --n;
if(n > 0) return false;
}
return true;
}
/**
* Moves as much items from the slots in range [first_slot, last_slot] of the inventory into a new stack.
* Implicitly shrinks the inventory stacks and the `request_stack`.
@ -950,16 +968,10 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
int smallest_stack_index = -1;
for(int i = slot_begin; i < slot_end; ++i) {
final ItemStack stack = inventory.getStackInSlot(i);
if((!stack.isEmpty()) && (stack.isItemEqual(request_stack))) {
if((!stack.isEmpty()) && (isItemExactlyEqual(stack, request_stack))) {
// Never automatically place stuff with nbt (except a few allowed like "Damage"),
// as this could be a full crate, a valuable tool item, etc. For these recipes
// the user has to place this item manually.
if(stack.hasTagCompound()) {
final NBTTagCompound nbt = stack.getTagCompound();
int n = stack.getTagCompound().getSize();
if((n > 0) && (stack.getTagCompound().hasKey("Damage"))) --n;
if(n > 0) continue;
}
fetched_stack = stack.copy(); // copy exact stack with nbt and tool damage, otherwise we have an automagical repair of items.
fetched_stack.setCount(0);
int n = stack.getCount();
@ -1067,12 +1079,12 @@ public class BlockDecorCraftingTable extends BlockDecorDirected
for(int i=0; i<9; ++i) {
final ItemStack grid_stack = getStackInSlot(i + CRAFTING_SLOTS_BEGIN);
final ItemStack refab_stack = to_refab.isEmpty() ? ItemStack.EMPTY : to_refab.get(i).copy();
if((!grid_stack.isEmpty()) && (grid_stack.isItemEqual(to_distribute))) {
if((!grid_stack.isEmpty()) && (isItemExactlyEqual(grid_stack, to_distribute))) {
matching_grid_stack_sizes[i] = grid_stack.getCount();
total_num_missing_stacks += grid_stack.getMaxStackSize()-grid_stack.getCount();
if(max_matching_stack_size < matching_grid_stack_sizes[i]) max_matching_stack_size = matching_grid_stack_sizes[i];
if(min_matching_stack_size > matching_grid_stack_sizes[i]) min_matching_stack_size = matching_grid_stack_sizes[i];
} else if((!refab_stack.isEmpty()) && (refab_stack.isItemEqual(to_distribute))) {
} else if((!refab_stack.isEmpty()) && (isItemExactlyEqual(refab_stack,to_distribute))) {
matching_grid_stack_sizes[i] = 0;
total_num_missing_stacks += grid_stack.getMaxStackSize();
if(max_matching_stack_size < matching_grid_stack_sizes[i]) max_matching_stack_size = matching_grid_stack_sizes[i];

View file

@ -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();
}
}
}

View file

@ -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

View file

@ -0,0 +1,709 @@
/*
* @file BlockDecorPlacer.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Block placer and planter, factory automation suitable.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.Networking;
import net.minecraft.init.Blocks;
import net.minecraft.init.SoundEvents;
import net.minecraft.world.IBlockAccess;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.world.World;
import net.minecraft.world.Explosion;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.item.*;
import net.minecraft.inventory.*;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.math.*;
import net.minecraft.util.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
public class BlockDecorPlacer extends BlockDecorDirected
{
public BlockDecorPlacer(@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
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face)
{ return BlockFaceShape.SOLID; }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(IBlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(IBlockState blockState, World world, BlockPos pos)
{ return Container.calcRedstone(world.getTileEntity(pos)); }
@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).reset_rtstate();
((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 void onBlockExploded(World world, BlockPos pos, Explosion explosion)
{
if(world.isRemote) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
for(ItemStack stack: ((BTileEntity)te).stacks_) {
if(!stack.isEmpty()) world.spawnEntity(new EntityItem(world, pos.getX(), pos.getY(), pos.getZ(), stack));
}
((BTileEntity)te).reset_rtstate();
super.onBlockExploded(world, pos, explosion);
}
@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;
player.openGui(ModEngineersDecor.instance, ModEngineersDecor.GuiHandler.GUIID_FACTORY_PLACER, world, pos.getX(), pos.getY(), pos.getZ());
return true;
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighborPos)
{
if(!(world instanceof World) || (((World) world).isRemote)) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
((BTileEntity)te).block_updated();
}
//--------------------------------------------------------------------------------------------------------------------
// ModEngineersDecor.GuiHandler connectors
//--------------------------------------------------------------------------------------------------------------------
public static Object getServerGuiElement(final EntityPlayer player, final World world, final BlockPos pos, final TileEntity te)
{ return (te instanceof BTileEntity) ? (new BContainer(player.inventory, world, pos, (BTileEntity)te)) : null; }
public static Object getClientGuiElement(final EntityPlayer player, final World world, final BlockPos pos, final TileEntity te)
{ return (te instanceof BTileEntity) ? (new BGui(player.inventory, world, pos, (BTileEntity)te)) : null; }
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@SideOnly(Side.CLIENT)
private static class BGui extends GuiContainer
{
private final BTileEntity te;
public BGui(InventoryPlayer playerInventory, World world, BlockPos pos, BTileEntity te)
{ super(new BContainer(playerInventory, world, pos, te)); this.te = te; }
@Override
public void initGui()
{ super.initGui(); }
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks)
{
drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
renderHoveredToolTip(mouseX, mouseY);
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException
{
super.mouseClicked(mouseX, mouseY, mouseButton);
BContainer container = (BContainer)inventorySlots;
if(container.fields_.length != 3) return;
int mx = mouseX - getGuiLeft(), my = mouseY - getGuiTop();
if(!isPointInRegion(126, 1, 49, 60, mouseX, mouseY)) {
return;
} else if(isPointInRegion(133, 49, 9, 9, mouseX, mouseY)) {
NBTTagCompound nbt = new NBTTagCompound();
nbt.setInteger("manual_trigger", 1);
Networking.PacketTileNotify.sendToServer(te, nbt);
} else if(isPointInRegion(145, 49, 9, 9, mouseX, mouseY)) {
NBTTagCompound nbt = new NBTTagCompound();
nbt.setInteger("logic", container.fields_[0] ^ BTileEntity.LOGIC_INVERTED);
Networking.PacketTileNotify.sendToServer(te, nbt);
} else if(isPointInRegion(159, 49, 7, 9, mouseX, mouseY)) {
NBTTagCompound nbt = new NBTTagCompound();
nbt.setInteger("logic", container.fields_[0] ^ BTileEntity.LOGIC_CONTINUOUS);
Networking.PacketTileNotify.sendToServer(te, nbt);
}
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
{
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
mc.getTextureManager().bindTexture(new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/factory_placer_gui.png"));
final int x0=getGuiLeft(), y0=getGuiTop(), w=getXSize(), h=getYSize();
drawTexturedModalRect(x0, y0, 0, 0, w, h);
BContainer container = (BContainer)inventorySlots;
if(container.fields_.length != 3) return; // no init, no cake.
// active slot
{
int slot_index = container.fields_[2];
if((slot_index < 0) || (slot_index >= BTileEntity.NUM_OF_SLOTS)) slot_index = 0;
int x = (x0+10+((slot_index % 6) * 18));
int y = (y0+8+((slot_index / 6) * 17));
drawTexturedModalRect(x, y, 200, 8, 18, 18);
}
// redstone input
{
if(container.fields_[1] != 0) {
drawTexturedModalRect(x0+133, y0+49, 217, 49, 9, 9);
}
}
// trigger logic
{
int inverter_offset = ((container.fields_[0] & BTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0;
drawTexturedModalRect(x0+145, y0+49, 177+inverter_offset, 49, 9, 9);
int pulse_mode_offset = ((container.fields_[0] & BTileEntity.LOGIC_CONTINUOUS ) != 0) ? 9 : 0;
drawTexturedModalRect(x0+159, y0+49, 199+pulse_mode_offset, 49, 9, 9);
}
}
}
//--------------------------------------------------------------------------------------------------------------------
// container
//--------------------------------------------------------------------------------------------------------------------
public static class BContainer extends Container
{
private static final int PLAYER_INV_START_SLOTNO = BTileEntity.NUM_OF_SLOTS;
private final World world;
private final BlockPos pos;
private final EntityPlayer player;
private final BTileEntity te;
private int fields_[] = new int[3];
public BContainer(InventoryPlayer playerInventory, World world, BlockPos pos, BTileEntity te)
{
this.player = playerInventory.player;
this.world = world;
this.pos = pos;
this.te = te;
int i=-1;
// device slots (stacks 0 to 17)
for(int y=0; y<3; ++y) {
for(int x=0; x<6; ++x) {
int xpos = 11+x*18, ypos = 9+y*17;
addSlotToContainer(new Slot(te, ++i, xpos, ypos));
}
}
// player slots
for(int x=0; x<9; ++x) {
addSlotToContainer(new Slot(playerInventory, x, 8+x*18, 129)); // player slots: 0..8
}
for(int y=0; y<3; ++y) {
for(int x=0; x<9; ++x) {
addSlotToContainer(new Slot(playerInventory, x+y*9+9, 8+x*18, 71+y*18)); // player slots: 9..35
}
}
}
public BlockPos getPos()
{ return pos; }
@Override
public void addListener(IContainerListener listener)
{ super.addListener(listener); listener.sendAllWindowProperties(this, te); }
@Override
public void detectAndSendChanges()
{
super.detectAndSendChanges();
for(int il=0; il<listeners.size(); ++il) {
IContainerListener lis = listeners.get(il);
for(int k=0; k<fields_.length; ++k) {
int f = te.getField(k);
if(fields_[k] != f) {
fields_[k] = f;
lis.sendWindowProperty(this, k, f);
}
}
}
}
@Override
@SideOnly(Side.CLIENT)
public void updateProgressBar(int id, int value)
{
if((id < 0) || (id >= fields_.length)) return;
fields_[id] = value;
te.setField(id, value);
}
@Override
public boolean canInteractWith(EntityPlayer player)
{ return (world.getBlockState(pos).getBlock() instanceof BlockDecorPlacer) && (player.getDistanceSq(pos) <= 64); }
@Override
public ItemStack transferStackInSlot(EntityPlayer player, int index)
{
Slot slot = inventorySlots.get(index);
if((slot==null) || (!slot.getHasStack())) return ItemStack.EMPTY;
ItemStack slot_stack = slot.getStack();
ItemStack transferred = slot_stack.copy();
if((index>=0) && (index<PLAYER_INV_START_SLOTNO)) {
// Device slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) {
// Player slot
if(!mergeItemStack(slot_stack, 0, BTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY;
} else {
// invalid slot
return ItemStack.EMPTY;
}
if(slot_stack.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY;
slot.onTake(player, slot_stack);
return transferred;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements ITickable, ISidedInventory, Networking.IPacketReceiver
{
public static final int TICK_INTERVAL = 40;
public static final int NUM_OF_SLOTS = 18;
///
public static final int LOGIC_INVERTED = 0x01;
public static final int LOGIC_CONTINUOUS = 0x02;
///
private boolean block_power_signal_ = false;
private boolean block_power_updated_ = false;
private int logic_ = LOGIC_INVERTED|LOGIC_CONTINUOUS;
private int current_slot_index_ = 0;
private int tick_timer_ = 0;
protected NonNullList<ItemStack> stacks_;
public static void on_config(int cooldown_ticks)
{
// ModEngineersDecor.logger.info("Config factory placer:");
}
public BTileEntity()
{
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
reset_rtstate();
}
public void reset_rtstate()
{
block_power_signal_ = false;
block_power_updated_ = false;
}
public void readnbt(NBTTagCompound nbt, boolean update_packet)
{
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
ItemStackHelper.loadAllItems(nbt, stacks_);
while(stacks_.size() < NUM_OF_SLOTS) stacks_.add(ItemStack.EMPTY);
block_power_signal_ = nbt.getBoolean("powered");
current_slot_index_ = nbt.getInteger("act_slot_index");
logic_ = nbt.getInteger("logic");
}
protected void writenbt(NBTTagCompound nbt, boolean update_packet)
{
ItemStackHelper.saveAllItems(nbt, stacks_);
nbt.setBoolean("powered", block_power_signal_);
nbt.setInteger("act_slot_index", current_slot_index_);
nbt.setInteger("logic", logic_);
}
public void block_updated()
{
boolean powered = world.isBlockPowered(pos);
if(block_power_signal_ != powered) block_power_updated_ = true;
block_power_signal_ = powered;
if(block_power_updated_) {
tick_timer_ = 1;
} else if(tick_timer_ > 4) {
tick_timer_ = 4;
}
}
public boolean is_input_slot(int index)
{ return (index >= 0) && (index < NUM_OF_SLOTS); }
// TileEntity ------------------------------------------------------------------------------
@Override
public boolean shouldRefresh(World world, BlockPos pos, IBlockState os, IBlockState ns)
{ return (os.getBlock() != ns.getBlock()) || (!(ns.getBlock() instanceof BlockDecorPlacer)); }
@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; }
// IWorldNamable ---------------------------------------------------------------------------
@Override
public String getName()
{ final Block block=getBlockType(); return (block!=null) ? (block.getTranslationKey() + ".name") : (""); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getDisplayName()
{ return new TextComponentTranslation(getName(), new Object[0]); }
// IInventory ------------------------------------------------------------------------------
@Override
public int getSizeInventory()
{ return stacks_.size(); }
@Override
public boolean isEmpty()
{ for(ItemStack stack: stacks_) { if(!stack.isEmpty()) return false; } return true; }
@Override
public ItemStack getStackInSlot(int index)
{ return (index < getSizeInventory()) ? stacks_.get(index) : ItemStack.EMPTY; }
@Override
public ItemStack decrStackSize(int index, int count)
{ return ItemStackHelper.getAndSplit(stacks_, index, count); }
@Override
public ItemStack removeStackFromSlot(int index)
{ return ItemStackHelper.getAndRemove(stacks_, index); }
@Override
public void setInventorySlotContents(int index, ItemStack stack)
{
stacks_.set(index, stack);
if(stack.getCount() > getInventoryStackLimit()) stack.setCount(getInventoryStackLimit());
if(tick_timer_ > 8) tick_timer_ = 8;
markDirty();
}
@Override
public int getInventoryStackLimit()
{ return 64; }
@Override
public void markDirty()
{ super.markDirty(); }
@Override
public boolean isUsableByPlayer(EntityPlayer player)
{ return ((world.getTileEntity(pos) == this) && (player.getDistanceSq(pos.getX()+0.5d, pos.getY()+0.5d, pos.getZ()+0.5d) <= 64.0d)); }
@Override
public void openInventory(EntityPlayer player)
{}
@Override
public void closeInventory(EntityPlayer player)
{ markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return true; }
@Override
public int getField(int id)
{
switch(id) {
case 0: return logic_;
case 1: return block_power_signal_ ? 1 : 0;
case 2: return current_slot_index_;
default: return 0;
}
}
@Override
public void setField(int id, int value)
{
switch(id) {
case 0: logic_ = value; return;
case 1: block_power_signal_ = (value != 0); return;
case 2: current_slot_index_ = MathHelper.clamp(value, 0, NUM_OF_SLOTS-1); return;
default: return;
}
}
@Override
public int getFieldCount()
{ return 3; }
@Override
public void clear()
{ stacks_.clear(); }
// ISidedInventory ----------------------------------------------------------------------------
private final IItemHandler item_handler_ = new SidedInvWrapper(this, EnumFacing.UP);
private static final int[] SIDED_INV_SLOTS;
static {
SIDED_INV_SLOTS = new int[NUM_OF_SLOTS];
for(int i=0; i<NUM_OF_SLOTS; ++i) SIDED_INV_SLOTS[i] = i;
}
@Override
public int[] getSlotsForFace(EnumFacing side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack stack, EnumFacing direction)
{ return is_input_slot(index) && isItemValidForSlot(index, stack); }
@Override
public boolean canExtractItem(int index, ItemStack stack, EnumFacing direction)
{ return false; }
// Capability export ----------------------------------------------------------------------------
@Override
public boolean hasCapability(Capability<?> cap, EnumFacing facing)
{ return (cap==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) || super.hasCapability(cap, facing); }
@Override
@SuppressWarnings("unchecked")
@Nullable
public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing)
{
if(capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return (T)item_handler_;
return super.getCapability(capability, facing);
}
// IPacketReceiver -------------------------------------------------------------------------------
@Override
public void onServerPacketReceived(NBTTagCompound nbt)
{}
@Override
public void onClientPacketReceived(EntityPlayer player, NBTTagCompound nbt)
{
if(nbt.hasKey("logic")) logic_ = nbt.getInteger("logic");
if(nbt.hasKey("manual_trigger") && (nbt.getInteger("manual_trigger")!=0)) { block_power_signal_=true; block_power_updated_=true; tick_timer_=1; }
markDirty();
}
// ITickable and aux methods ---------------------------------------------------------------------
private static int next_slot(int i)
{ return (i<NUM_OF_SLOTS-1) ? (i+1) : 0; }
private boolean spit_out(EnumFacing facing)
{
ItemStack stack = stacks_.get(current_slot_index_);
ItemStack drop = stack.copy();
stack.shrink(1);
stacks_.set(current_slot_index_, stack);
drop.setCount(1);
for(int i=0; i<8; ++i) {
BlockPos p = pos.offset(facing, i);
if(!world.isAirBlock(p)) continue;
world.spawnEntity(new EntityItem(world, (p.getX()+0.5), (p.getY()+0.5), (p.getZ()+0.5), drop));
world.playSound(null, p, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 0.7f, 0.8f);
break;
}
return true;
}
private boolean try_place(EnumFacing facing)
{
if(world.isRemote) return false;
BlockPos placement_pos = pos.offset(facing);
if(world.getTileEntity(placement_pos) != null) return false;
ItemStack current_stack = ItemStack.EMPTY;
for(int i=0; i<NUM_OF_SLOTS; ++i) {
if(current_slot_index_ >= NUM_OF_SLOTS) current_slot_index_ = 0;
current_stack = stacks_.get(current_slot_index_);
if(!current_stack.isEmpty()) break;
current_slot_index_ = next_slot(current_slot_index_);
}
if(current_stack.isEmpty()) { current_slot_index_ = 0; return false; }
boolean no_space = false;
final Item item = current_stack.getItem();
Block block = (item instanceof IPlantable) ? (((IPlantable)item).getPlant(world, pos).getBlock()) : Block.getBlockFromItem(item);
if(block == Blocks.AIR) {
if(item != null) {
return spit_out(facing); // Item not accepted
} else {
// try next slot
}
} else if(block instanceof IPlantable) {
if(world.isAirBlock(placement_pos)) {
// plant here, block below has to be valid soil.
IBlockState soilstate = world.getBlockState(placement_pos.down());
if(!soilstate.getBlock().canSustainPlant(soilstate, world, pos, EnumFacing.UP, (IPlantable)block)) {
block = Blocks.AIR;
}
} else {
// adjacent block is the soil, plant above if the soil is valid.
IBlockState soilstate = world.getBlockState(placement_pos);
if(soilstate.getBlock() == block) {
// The plant is already planted from the case above.
block = Blocks.AIR;
no_space = true;
} else if(!world.isAirBlock(placement_pos.up())) {
// If this is the soil an air block is needed above, if that is blocked we can't plant.
block = Blocks.AIR;
no_space = true;
} else if(!soilstate.getBlock().canSustainPlant(soilstate, world, pos, EnumFacing.UP, (IPlantable)block)) {
// Would be space above, but it's not the right soil for the plant.
block = Blocks.AIR;
} else {
// Ok, plant above.
placement_pos = placement_pos.up();
}
}
} else if(!world.getBlockState(placement_pos).getBlock().isReplaceable(world, placement_pos)) {
block = Blocks.AIR;
no_space = true;
}
// System.out.println("PLACE " + current_stack + " --> " + block + " at " + placement_pos.subtract(pos) + "( item=" + item + ")");
if(block != Blocks.AIR) {
try {
final FakePlayer placer = net.minecraftforge.common.util.FakePlayerFactory.getMinecraft((net.minecraft.world.WorldServer)world);
final IBlockState placement_state = (placer==null) ? (block.getDefaultState()) : (block.getStateForPlacement(world, placement_pos, EnumFacing.DOWN,0.5f,0.5f,0f, 0, placer, EnumHand.MAIN_HAND));
if(placement_state == null) {
return spit_out(facing);
} else if(item instanceof ItemBlock) {
ItemStack placement_stack = current_stack.copy();
placement_stack.setCount(1);
((ItemBlock)item).placeBlockAt(placement_stack, placer, world, placement_pos, EnumFacing.DOWN, 0.5f,0.5f,0f, placement_state);
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
} else {
if(world.setBlockState(placement_pos, placement_state, 1|2|8)) {
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
}
}
current_stack.shrink(1);
stacks_.set(current_slot_index_, current_stack);
return true;
} catch(Throwable e) {
// The block really needs a player or other issues happened during placement.
// A hard crash should not be fired here, instead spit out the item to indicated that this
// block is not compatible.
System.out.println("Exception while trying to place " + e);
world.setBlockToAir(placement_pos);
return spit_out(facing);
}
}
if((!no_space) && (!current_stack.isEmpty())) {
// There is space, but the current plant cannot be planted there, so try next.
for(int i=0; i<NUM_OF_SLOTS; ++i) {
current_slot_index_ = next_slot(current_slot_index_);
if(!stacks_.get(current_slot_index_).isEmpty()) break;
}
}
return false;
}
@Override
public void update()
{
// Tick cycle pre-conditions
if(world.isRemote) return;
if(--tick_timer_ > 0) return;
tick_timer_ = TICK_INTERVAL;
// Cycle init
boolean dirty = block_power_updated_;
boolean rssignal = ((logic_ & LOGIC_INVERTED)!=0)==(!block_power_signal_);
boolean trigger = (rssignal && ((block_power_updated_) || ((logic_ & LOGIC_CONTINUOUS)!=0)));
final IBlockState state = world.getBlockState(pos);
if(state == null) { block_power_signal_= false; return; }
final EnumFacing placer_facing = state.getValue(FACING);
// Trigger edge detection for next cycle
{
boolean tr = world.isBlockPowered(pos);
block_power_updated_ = (block_power_signal_ != tr);
block_power_signal_ = tr;
if(block_power_updated_) dirty = true;
}
// Placing
if(trigger && try_place(placer_facing)) dirty = true;
if(dirty) markDirty();
if(trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL;
}
}
}

View file

@ -1,10 +1,10 @@
/*
* @file BlockFurnace.java
* @file BlockDecorTreeCutter.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* ED Lab furnace.
* Small Tree Cutter
*/
package wile.engineersdecor.blocks;

View file

@ -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());
}
}
}
}

View file

@ -168,6 +168,11 @@ public class ModConfig
@Config.RequiresMcRestart
public boolean without_factory_hopper = false;
@Config.Comment({"Disable the Factory Block Placer."})
@Config.Name("Without factory placer")
@Config.RequiresMcRestart
public boolean without_factory_placer = false;
@Config.Comment({"Disable horizontal half-block slab."})
@Config.Name("Without slabs")
@Config.RequiresMcRestart
@ -411,6 +416,7 @@ public class ModConfig
if(block instanceof BlockDecorWasteIncinerator) return optout.without_waste_incinerator;
if(block instanceof BlockDecorDropper) return optout.without_factory_dropper;
if(block instanceof BlockDecorHopper) return optout.without_factory_hopper;
if(block instanceof BlockDecorPlacer) return optout.without_factory_placer;
if(block instanceof BlockDecorHalfSlab) return optout.without_halfslabs;
if(block instanceof BlockDecorLadder) return optout.without_ladders;
if(block instanceof BlockDecorWindow) return optout.without_windows;

View file

@ -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())) {
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())) {

View file

@ -0,0 +1,11 @@
{
"forge_marker": 1,
"defaults": {
"model": "engineersdecor:device/factory_placer_model"
},
"variants": {
"normal": [{}],
"inventory": [{}],
"facing": { "north":{"y":0}, "south":{"y":180}, "west":{"y":270}, "east":{"y":90}, "up": {"x":-90}, "down": {"x":90} }
}
}

View file

@ -0,0 +1,12 @@
{
"forge_marker": 1,
"defaults": {
"model": "engineersdecor:device/small_block_breaker_model"
},
"variants": {
"normal": [{}],
"inventory": [{}],
"facing": { "north":{"y":0}, "south":{"y":180}, "west":{"y":270}, "east":{"y":90}, "up": {"x":-90}, "down": {"x":90} },
"active": { "true":{ "model": "engineersdecor:device/small_block_breaker_model_active" }, "false":{}}
}
}

View file

@ -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"}
}
}
}

View file

@ -150,21 +150,40 @@ 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. \
Drop force, angle, stack size, and cool-down delay adjustable in the GUI. Three stack compare \
slots with logical AND or OR can be used as internal trigger source. Internal trigger can be \
AND'ed or OR'ed with the external redstone signal trigger. Trigger simulation buttons for testing. \
Pre-opens shutter door when internal trigger conditions are met. Drops all matching stacks \
simultaneously. Click on all elements in the GUI to see how it works.
Drop force, angle, stack size, and cool-down delay adjustable using sliders in the GUI. Three stack compare \
slots (below the inventory slots) with logical AND or OR can be used as internal trigger source. The \
internal trigger can be AND'ed or OR'ed with the external redstone signal trigger. Trigger simulation \
buttons for testing. Pre-opens shutter door when internal trigger conditions are met. Drops all matching \
stacks simultaneously. Simply click on all elements in the GUI to see how it works.
tile.engineersdecor.factory_hopper.name=Factory Hopper
tile.engineersdecor.factory_hopper.help=§6Hopper suitable for advanced factory automation.§r
tile.engineersdecor.factory_hopper.help=§6Hopper suitable for advanced factory automation.§r Can transfer half-stacks, max collection range 9x9.\n\
GUI Slider controls: Collection range (0 to 4), insertion delay (0.5s to 10s), insertion stack size (1 to 32).\n\
GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).
tile.engineersdecor.factory_placer.name=Factory Block Placer
tile.engineersdecor.factory_placer.help=§6Allows placing blocks and planting crops or trees.§r\n\
GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).\n\
Also supports spike planing from underneath the soil. Spits out items that it cannot place or plant.
tile.engineersdecor.small_block_breaker.name=Small Block Breaker
tile.engineersdecor.small_block_breaker.help=§6Breaks blocks in front of it.§r\n\
Can be disabled by applying a redstone signal. \
The time needed to destroy a block depends on the hardness of that block. \
Provide RF/FE power to speed up the breaking process (massively).
tile.engineersdecor.small_mineral_smelter.name=Small Mineral Melting Furnace
tile.engineersdecor.small_mineral_smelter.help=§6High temperature, high insulation electrical stone melting furnace.§r\n\
Heats up mineral blocks to magma blocks, and finally to lava. Due to the \
miniturized device size the process is rather inefficient - much time and \
energy is needed to liquefy a stone block.
Heats up mineral blocks to magma blocks, and finally to lava. Click with a \
mineral block (stone, cobble, etc) or use item insertion to place a block in \
the melter. Use bucket or fluid extraction to retrieve the lava. When cooling \
down lava, obsidian is generated. Remove the RF power or apply a redstone \
signal to disable the furnace. For automation, use a redstone comparator to \
see in which melting phase the mineral is a moment.
tile.engineersdecor.small_solar_panel.name=Small Solar Panel
tile.engineersdecor.small_solar_panel.help=§6Produces a small amount of power when exposed to sunlight.§r\n\
Useful for charging LF capacitors in remote systems with low consumption. The \

View file

@ -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 выборочных слотов. \
@ -155,12 +160,26 @@ tile.engineersdecor.factory_dropper.help=§6Выбрасыватель подх
когда выполняются условия внутреннего запуска. Сбрасывает все соответствующие стеки одновременно. \
Нажмите на все элементы в GUI, чтобы увидеть, как это работает.
tile.engineersdecor.factory_hopper.name=Factory Hopper
#tile.engineersdecor.factory_hopper.help=§6Hopper suitable for advanced factory automation.§r
#tile.engineersdecor.factory_hopper.help=§6Hopper suitable for advanced factory automation.§r Can transfer half-stacks, max collection range 9x9.\n\
GUI Slider controls: Collection range (0 to 4), insertion delay (0.5s to 10s), insertion stack size (1 to 32).\n\
GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).
tile.engineersdecor.factory_placer.name=Factory Block Placer
#tile.engineersdecor.factory_placer.help=§6Allows placing blocks and planting crops or trees.§r\n\
GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).\n\
Also supports spike planing from underneath the soil. Spits out items that it cannot place or plant.
tile.engineersdecor.small_block_breaker.name=Factory Block Breaker
#tile.engineersdecor.small_block_breaker.help=§6Breaks blocks in front of it.§r\n\
Can be disabled by applying a redstone signal. \
The time needed to destroy a block depends on the hardness of that block. \
Provide RF/FE power to speed up the breaking process (massively).
tile.engineersdecor.small_mineral_smelter.name=Small Mineral Melting Furnace
#tile.engineersdecor.small_mineral_smelter.help=§6High temperature, high insulation electrical stone melting furnace.§r\n\
Heats up mineral blocks to magma blocks, and finally to lava. Due to the \
miniturized device size the process is rather inefficient - much time and \
energy is needed to liquefy a stone block.
Heats up mineral blocks to magma blocks, and finally to lava. Click with a \
mineral block (stone, cobble, etc) or use item insertion to place a block in \
the melter. Use bucket or fluid extraction to retrieve the lava. When cooling \
down lava, obsidian is generated. Remove the RF power or apply a redstone \
signal to disable the furnace. For automation, use a redstone comparator to \
see in which melting phase the mineral is a moment.
tile.engineersdecor.small_solar_panel.name=Small Solar Panel
#tile.engineersdecor.small_solar_panel.help=§6Produces a small amount of power when exposed to sunlight.§r\n\
Useful for charging LF capacitors in remote systems with low consumption. The \

View file

@ -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有十二个轮询选择的储物格。\
@ -158,7 +163,18 @@ tile.engineersdecor.factory_dropper.help=§6适用于高级工厂自动化的掉
当内部触发条件满足时,预先打开卷帘门。所有符合条件的物品\
会同时掉落。点击GUI的各处来了解如何运作。
tile.engineersdecor.factory_hopper.name=Factory Hopper
#tile.engineersdecor.factory_hopper.help=§6Hopper suitable for advanced factory automation.§r
#tile.engineersdecor.factory_hopper.help=§6Hopper suitable for advanced factory automation.§r Can transfer half-stacks, max collection range 9x9.\n\
GUI Slider controls: Collection range (0 to 4), insertion delay (0.5s to 10s), insertion stack size (1 to 32).\n\
GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).
tile.engineersdecor.factory_placer.name=Factory Block Placer
#tile.engineersdecor.factory_placer.help=§6Allows placing blocks and planting crops or trees.§r\n\
GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).\n\
Also supports spike planing from underneath the soil. Spits out items that it cannot place or plant.
tile.engineersdecor.small_block_breaker.name=Factory Block Breaker
#tile.engineersdecor.small_block_breaker.help=§6Breaks blocks in front of it.§r\n\
Can be disabled by applying a redstone signal. \
The time needed to destroy a block depends on the hardness of that block. \
Provide RF/FE power to speed up the breaking process (massively).
tile.engineersdecor.small_mineral_smelter.name=小型矿物熔炼炉
tile.engineersdecor.small_mineral_smelter.help=§6高温、高绝缘电熔石炉。§r\n\
把矿物块加热成岩浆块,最后变成熔岩。由于\

View file

@ -0,0 +1,170 @@
{
"parent": "block/cube",
"textures": {
"top": "engineersdecor:blocks/device/factory_placer_top",
"bottom": "engineersdecor:blocks/device/factory_placer_bottom",
"side": "engineersdecor:blocks/device/factory_placer_side",
"particle": "engineersdecor:blocks/device/factory_placer_side"
},
"elements": [
{
"from": [0, 0, 3],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 16], "texture": "#bottom"},
"east": {"uv": [3, 0, 16, 16], "rotation": 180, "texture": "#side"},
"south": {"uv": [0, 0, 16, 16], "texture": "#top"},
"west": {"uv": [3, 0, 16, 16], "texture": "#side"},
"up": {"uv": [3, 0, 16, 16], "rotation": 90, "texture": "#side"},
"down": {"uv": [3, 0, 16, 16], "rotation": 270, "texture": "#side"}
}
},
{
"from": [15, 0, 0],
"to": [16, 16, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 7]},
"faces": {
"north": {"uv": [0, 0, 1, 16], "texture": "#bottom"},
"east": {"uv": [15, 0, 16, 16], "texture": "#side"},
"south": {"uv": [14, 0, 16, 16], "texture": "#top"},
"west": {"uv": [0, 0, 1, 16], "texture": "#side"},
"up": {"uv": [14, 0, 15, 1], "rotation": 90, "texture": "#side"},
"down": {"uv": [14, 15, 16, 16], "texture": "#side"}
}
},
{
"from": [15, 0, 2],
"to": [16, 16, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [0, 0, 1, 16], "texture": "#bottom"},
"east": {"uv": [2, 0, 3, 16], "rotation": 180, "texture": "#side"},
"south": {"uv": [15, 0, 16, 16], "texture": "#top"},
"west": {"uv": [2, 0, 4, 16], "texture": "#side"},
"up": {"uv": [15, 2, 16, 4], "rotation": 90, "texture": "#side"},
"down": {"uv": [15, 12, 16, 14], "rotation": 270, "texture": "#side"}
}
},
{
"from": [0, 0, 0],
"to": [1, 16, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 7]},
"faces": {
"north": {"uv": [15, 0, 16, 16], "texture": "#bottom"},
"east": {"uv": [15, 0, 16, 16], "texture": "#side"},
"south": {"uv": [0, 0, 2, 16], "texture": "#top"},
"west": {"uv": [0, 0, 1, 16], "texture": "#side"},
"up": {"uv": [0, 0, 1, 1], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 15, 2, 16], "texture": "#side"}
}
},
{
"from": [0, 0, 2],
"to": [1, 16, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [15, 0, 16, 16], "texture": "#bottom"},
"east": {"uv": [2, 0, 3, 16], "texture": "#side"},
"south": {"uv": [0, 0, 1, 16], "texture": "#top"},
"west": {"uv": [2, 0, 3, 16], "texture": "#side"},
"up": {"uv": [0, 2, 1, 3], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 13, 1, 14], "rotation": 270, "texture": "#side"}
}
},
{
"from": [0, 15, 1],
"to": [1, 16, 2],
"faces": {
"north": {"uv": [15, 0, 16, 1], "texture": "#bottom"},
"east": {"uv": [14, 0, 15, 1], "texture": "#side"},
"south": {"uv": [0, 0, 1, 1], "texture": "#top"},
"west": {"uv": [1, 0, 2, 1], "texture": "#side"},
"up": {"uv": [1, 0, 2, 1], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 14, 1, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [15, 15, 1],
"to": [16, 16, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [23, 8, 8]},
"faces": {
"north": {"uv": [0, 0, 1, 1], "texture": "#bottom"},
"east": {"uv": [14, 0, 15, 1], "texture": "#side"},
"south": {"uv": [15, 0, 16, 1], "texture": "#top"},
"west": {"uv": [1, 0, 2, 1], "texture": "#side"},
"up": {"uv": [15, 1, 16, 2], "rotation": 90, "texture": "#side"},
"down": {"uv": [15, 14, 16, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [15, 0, 1],
"to": [16, 1, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [23, -7, 8]},
"faces": {
"north": {"uv": [0, 15, 1, 16], "texture": "#bottom"},
"east": {"uv": [14, 15, 15, 16], "texture": "#side"},
"south": {"uv": [15, 15, 16, 16], "texture": "#top"},
"west": {"uv": [1, 15, 2, 16], "texture": "#side"},
"up": {"uv": [15, 1, 16, 2], "rotation": 90, "texture": "#side"},
"down": {"uv": [15, 14, 16, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [0, 0, 1],
"to": [1, 1, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -7, 8]},
"faces": {
"north": {"uv": [15, 15, 16, 16], "texture": "#bottom"},
"east": {"uv": [14, 15, 15, 16], "texture": "#side"},
"south": {"uv": [0, 15, 1, 16], "texture": "#top"},
"west": {"uv": [1, 15, 2, 16], "texture": "#side"},
"up": {"uv": [0, 1, 1, 2], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 14, 1, 15], "texture": "#side"}
}
},
{
"from": [1, 15, 2],
"to": [15, 16, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [1, 0, 15, 1], "texture": "#bottom"},
"east": {"uv": [12, 0, 14, 1], "texture": "#side"},
"south": {"uv": [1, 0, 15, 1], "texture": "#top"},
"west": {"uv": [2, 0, 4, 1], "texture": "#side"},
"up": {"uv": [2, 1, 3, 15], "rotation": 90, "texture": "#side"},
"down": {"uv": [2, 1, 3, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [1, 0, 2],
"to": [15, 1, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [1, 15, 15, 16], "texture": "#bottom"},
"east": {"uv": [12, 15, 14, 16], "texture": "#side"},
"south": {"uv": [1, 15, 15, 16], "texture": "#top"},
"west": {"uv": [2, 15, 4, 16], "texture": "#side"},
"up": {"uv": [2, 1, 3, 15], "rotation": 180, "texture": "#side"},
"down": {"uv": [2, 1, 3, 15], "rotation": 270, "texture": "#side"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -0,0 +1,383 @@
{
"parent": "block/cube",
"textures": {
"t": "engineersdecor:blocks/device/small_block_breaker_top",
"b": "engineersdecor:blocks/device/small_block_breaker_bottom",
"l": "engineersdecor:blocks/device/small_block_breaker_left",
"particle": "engineersdecor:blocks/device/small_block_breaker_left",
"k": "engineersdecor:blocks/device/small_block_breaker_back",
"r": "engineersdecor:blocks/device/small_block_breaker_right",
"f": "engineersdecor:blocks/device/small_block_breaker_front",
"sh": "engineersdecor:blocks/device/small_block_breaker_shaft"
},
"elements": [
{
"from": [0, 0, 4],
"to": [1, 12, 16],
"faces": {
"north": {"uv": [15, 4, 16, 16], "texture": "#f"},
"east": {"uv": [4, 0, 16, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [0, 4, 1, 16], "texture": "#k"},
"west": {"uv": [4, 4, 16, 16], "texture": "#l"},
"up": {"uv": [0, 4, 1, 16], "texture": "#t"},
"down": {"uv": [0, 0, 1, 12], "texture": "#b"}
}
},
{
"from": [15, 0, 4],
"to": [16, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [23, 8, 8]},
"faces": {
"north": {"uv": [0, 4, 1, 16], "texture": "#f"},
"east": {"uv": [0, 4, 12, 16], "texture": "#r"},
"south": {"uv": [15, 4, 16, 16], "texture": "#k"},
"west": {"uv": [0, 0, 12, 12], "texture": "#l"},
"up": {"uv": [15, 4, 16, 16], "texture": "#t"},
"down": {"uv": [15, 0, 16, 12], "texture": "#b"}
}
},
{
"from": [7.5, 4, 6],
"to": [8.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15.5, 12, -5]},
"faces": {
"north": {"uv": [7, 4, 8, 12], "texture": "#f"},
"east": {"uv": [9, 4, 11, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [7.5, 4, 8.5, 12], "texture": "#t"},
"west": {"uv": [5, 4, 7, 12], "texture": "#f"},
"up": {"uv": [12, 7, 13, 9], "texture": "#t"},
"down": {"uv": [7.5, 9, 8.5, 11], "rotation": 270, "texture": "#l"}
}
},
{
"from": [8.5, 4, 6],
"to": [9, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [16.5, 12, -5]},
"faces": {
"north": {"uv": [7, 4, 7.5, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [8.5, 4, 9, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#l"},
"up": {"uv": [8.5, 7, 9, 8], "texture": "#t"},
"down": {"uv": [8.5, 9, 9, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [7, 4, 6],
"to": [7.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15, 12, -5]},
"faces": {
"north": {"uv": [8.5, 4, 9, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#l"},
"south": {"uv": [7, 4, 7.5, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#f"},
"up": {"uv": [7.5, 7, 8.5, 8], "texture": "#t"},
"down": {"uv": [7, 9, 7.5, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [15, 0, 0],
"to": [16, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [23, -2, 8]},
"faces": {
"north": {"uv": [0, 11, 1, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#r"},
"south": {"uv": [15, 11, 16, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [15, 0, 16, 4], "texture": "#t"},
"down": {"uv": [15, 12, 16, 16], "texture": "#b"}
}
},
{
"from": [0, 0, 0],
"to": [1, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -2, 8]},
"faces": {
"north": {"uv": [15, 11, 16, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#l"},
"south": {"uv": [0, 11, 1, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [0, 0, 1, 4], "texture": "#t"},
"down": {"uv": [0, 12, 1, 16], "texture": "#b"}
}
},
{
"from": [1, 0, 0],
"to": [15, 4, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 24]},
"faces": {
"north": {"uv": [1, 12, 15, 16], "texture": "#f"},
"east": {"uv": [0, 12, 16, 16], "texture": "#l"},
"south": {"uv": [1, 12, 15, 16], "texture": "#k"},
"west": {"uv": [0, 12, 16, 16], "texture": "#l"},
"up": {"uv": [1, 0, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 16], "texture": "#b"}
}
},
{
"from": [1, 4, 7],
"to": [15, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 12, 24]},
"faces": {
"north": {"uv": [1, 4, 15, 12], "texture": "#f"},
"east": {"uv": [0, 4, 9, 12], "texture": "#l"},
"south": {"uv": [1, 4, 15, 12], "texture": "#k"},
"west": {"uv": [7, 4, 16, 12], "texture": "#l"},
"up": {"uv": [1, 7, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 9], "rotation": 270, "texture": "#l"}
}
},
{
"from": [4, 9.5, 5],
"to": [5, 10.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, 2.5, 12.5]},
"faces": {
"north": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"east": {"uv": [9.5, 5.5, 11, 6.5], "texture": "#sh"},
"south": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"west": {"uv": [5, 5.5, 6.5, 6.5], "texture": "#sh"},
"up": {"uv": [4, 5, 5, 6.5], "texture": "#sh"},
"down": {"uv": [4, 9.5, 5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, 6.5],
"to": [6.5, 10, 7.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12.5, 1, 13.5]},
"faces": {
"north": {"uv": [12, 12, 16, 16], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [2.5, 6, 6.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [2, 6, 6, 7], "texture": "#sh"},
"down": {"uv": [2.5, 15.5, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 9, 5],
"to": [3.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, 2, 12.5]},
"faces": {
"north": {"uv": [12, 6, 13, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [2.5, 6, 3.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [2, 5, 3, 6.5], "texture": "#sh"},
"down": {"uv": [2.5, 9.5, 3.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2, 7.5, 5],
"to": [3, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 0.5, 12.5]},
"faces": {
"north": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [2, 5, 3, 6.5], "texture": "#sh"},
"down": {"uv": [2, 9.5, 3, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, 5],
"to": [3.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, -1, 12.5]},
"faces": {
"north": {"uv": [12, 9, 13, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [2.5, 9, 3.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [2.5, 5, 3.5, 6.5], "texture": "#sh"},
"down": {"uv": [2.5, 9.5, 3.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [4, 5.5, 5],
"to": [5, 6.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, -1.5, 12.5]},
"faces": {
"north": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"east": {"uv": [9.5, 9.5, 11, 10.5], "texture": "#sh"},
"south": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"west": {"uv": [5, 9.5, 6.5, 10.5], "texture": "#sh"},
"up": {"uv": [4, 5, 5, 6.5], "texture": "#sh"},
"down": {"uv": [4, 9.5, 5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 6, 5],
"to": [6.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, -1, 12.5]},
"faces": {
"north": {"uv": [9, 9, 10, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [5.5, 9, 6.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [5, 5, 6, 6.5], "texture": "#sh"},
"down": {"uv": [5.5, 9.5, 6.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 9, 5],
"to": [6.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, 2, 12.5]},
"faces": {
"north": {"uv": [9, 6, 10, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [5.5, 6, 6.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [5, 5, 6, 6.5], "texture": "#sh"},
"down": {"uv": [5.5, 9.5, 6.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [6, 7.5, 5],
"to": [7, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [14, 0.5, 12.5]},
"faces": {
"north": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [6, 5, 7, 6.5], "texture": "#sh"},
"down": {"uv": [6, 9.5, 7, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, 6.5],
"to": [13.5, 10, 7.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19.5, 1, 13.5]},
"faces": {
"north": {"uv": [10, 12, 14, 16], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [9.5, 6, 13.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [10, 7, 14, 8], "texture": "#sh"},
"down": {"uv": [9.5, 15.5, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 9, 5],
"to": [13.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, 2, 12.5]},
"faces": {
"north": {"uv": [2, 6, 3, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [12.5, 6, 13.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [12, 5, 13, 6.5], "texture": "#sh"},
"down": {"uv": [12.5, 9.5, 13.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 9.5, 5],
"to": [12, 10.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, 2.5, 12.5]},
"faces": {
"north": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"east": {"uv": [9.5, 5.5, 11, 6.5], "texture": "#sh"},
"south": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"west": {"uv": [5, 5.5, 6.5, 6.5], "texture": "#sh"},
"up": {"uv": [11, 5, 12, 6.5], "texture": "#sh"},
"down": {"uv": [11, 9.5, 12, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 9, 5],
"to": [10.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, 2, 12.5]},
"faces": {
"north": {"uv": [5, 6, 6, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [9.5, 6, 10.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [9, 5, 10, 6.5], "texture": "#sh"},
"down": {"uv": [9.5, 9.5, 10.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9, 7.5, 5],
"to": [10, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17, 0.5, 12.5]},
"faces": {
"north": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [9, 5, 10, 6.5], "texture": "#sh"},
"down": {"uv": [9, 9.5, 10, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, 5],
"to": [10.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, -1, 12.5]},
"faces": {
"north": {"uv": [5, 9, 6, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [9.5, 9, 10.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [9, 5, 10, 6.5], "texture": "#sh"},
"down": {"uv": [9.5, 9.5, 10.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 5.5, 5],
"to": [12, 6.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, -1.5, 12.5]},
"faces": {
"north": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"east": {"uv": [9.5, 9.5, 11, 10.5], "texture": "#sh"},
"south": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"west": {"uv": [5, 9.5, 6.5, 10.5], "texture": "#sh"},
"up": {"uv": [11, 5, 12, 6.5], "texture": "#sh"},
"down": {"uv": [11, 9.5, 12, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 6, 5],
"to": [13.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, -1, 12.5]},
"faces": {
"north": {"uv": [2, 9, 3, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [12.5, 9, 13.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [12, 5, 13, 6.5], "texture": "#sh"},
"down": {"uv": [12.5, 9.5, 13.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [13, 7.5, 5],
"to": [14, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [21, 0.5, 12.5]},
"faces": {
"north": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [13, 5, 14, 6.5], "texture": "#sh"},
"down": {"uv": [13, 9.5, 14, 11], "rotation": 270, "texture": "#sh"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -0,0 +1,461 @@
{
"parent": "block/cube",
"textures": {
"t": "engineersdecor:blocks/device/small_block_breaker_top",
"b": "engineersdecor:blocks/device/small_block_breaker_bottom",
"l": "engineersdecor:blocks/device/small_block_breaker_left",
"particle": "engineersdecor:blocks/device/small_block_breaker_left",
"k": "engineersdecor:blocks/device/small_block_breaker_back",
"r": "engineersdecor:blocks/device/small_block_breaker_right",
"f": "engineersdecor:blocks/device/small_block_breaker_front",
"sh": "engineersdecor:blocks/device/small_block_breaker_shaft_active"
},
"elements": [
{
"from": [0, 0, 4],
"to": [1, 12, 16],
"faces": {
"north": {"uv": [15, 4, 16, 16], "texture": "#f"},
"east": {"uv": [4, 0, 16, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [0, 4, 1, 16], "texture": "#k"},
"west": {"uv": [4, 4, 16, 16], "texture": "#l"},
"up": {"uv": [0, 4, 1, 16], "texture": "#t"},
"down": {"uv": [0, 0, 1, 12], "texture": "#b"}
}
},
{
"from": [15, 0, 4],
"to": [16, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [23, 8, 8]},
"faces": {
"north": {"uv": [0, 4, 1, 16], "texture": "#f"},
"east": {"uv": [0, 4, 12, 16], "texture": "#r"},
"south": {"uv": [15, 4, 16, 16], "texture": "#k"},
"west": {"uv": [0, 0, 12, 12], "texture": "#l"},
"up": {"uv": [15, 4, 16, 16], "texture": "#t"},
"down": {"uv": [15, 0, 16, 12], "texture": "#b"}
}
},
{
"from": [7.5, 4, 6],
"to": [8.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15.5, 12, -5]},
"faces": {
"north": {"uv": [7.5, 4, 8.5, 12], "texture": "#f"},
"east": {"uv": [9, 4, 11, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [7.5, 4, 8.5, 12], "texture": "#t"},
"west": {"uv": [5, 4, 7, 12], "texture": "#f"},
"up": {"uv": [12, 7, 13, 9], "texture": "#t"},
"down": {"uv": [7.5, 9, 8.5, 11], "rotation": 270, "texture": "#l"}
}
},
{
"from": [8.5, 4, 6],
"to": [9, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [16.5, 12, -5]},
"faces": {
"north": {"uv": [7, 4, 7.5, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [8.5, 4, 9, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#l"},
"up": {"uv": [8, 7, 9.5, 8], "texture": "#t"},
"down": {"uv": [8.5, 9, 9, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [7, 4, 6],
"to": [7.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15, 12, -5]},
"faces": {
"north": {"uv": [8.5, 4, 9, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#l"},
"south": {"uv": [7, 4, 7.5, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#f"},
"up": {"uv": [7, 7, 7.5, 8], "texture": "#t"},
"down": {"uv": [7, 9, 7.5, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [15, 0, 0],
"to": [16, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [23, -2, 8]},
"faces": {
"north": {"uv": [0, 11, 1, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#r"},
"south": {"uv": [15, 11, 16, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [15, 0, 16, 4], "texture": "#t"},
"down": {"uv": [15, 12, 16, 16], "texture": "#b"}
}
},
{
"from": [0, 0, 0],
"to": [1, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -2, 8]},
"faces": {
"north": {"uv": [15, 11, 16, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#l"},
"south": {"uv": [0, 11, 1, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [0, 0, 1, 4], "texture": "#t"},
"down": {"uv": [0, 12, 1, 16], "texture": "#b"}
}
},
{
"from": [1, 0, 0],
"to": [15, 4, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 24]},
"faces": {
"north": {"uv": [1, 12, 15, 16], "texture": "#f"},
"east": {"uv": [0, 12, 16, 16], "texture": "#l"},
"south": {"uv": [1, 12, 15, 16], "texture": "#k"},
"west": {"uv": [0, 12, 16, 16], "texture": "#l"},
"up": {"uv": [1, 0, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 16], "texture": "#b"}
}
},
{
"from": [1, 4, 7],
"to": [15, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 12, 24]},
"faces": {
"north": {"uv": [1, 4, 15, 12], "texture": "#f"},
"east": {"uv": [0, 4, 9, 12], "texture": "#l"},
"south": {"uv": [1, 4, 15, 12], "texture": "#k"},
"west": {"uv": [7, 4, 16, 12], "texture": "#l"},
"up": {"uv": [1, 7, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 9], "rotation": 270, "texture": "#l"}
}
},
{
"from": [4, 9.5, -2],
"to": [5, 10.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, 2.5, 5.5]},
"faces": {
"north": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"east": {"uv": [16, 5.5, 16, 6.5], "texture": "#sh"},
"south": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"west": {"uv": [0, 5.5, 0, 6.5], "texture": "#sh"},
"up": {"uv": [4, 0, 5, 0], "texture": "#sh"},
"down": {"uv": [4, 16, 5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [3, 7, 0.5],
"to": [3.5, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [2.5, 1, 5]},
"faces": {
"north": {"uv": [12.5, 7, 13, 9], "texture": "#sh"},
"east": {"uv": [9, 7, 15.5, 9], "texture": "#sh"},
"south": {"uv": [3, 7, 3.5, 9], "texture": "#sh"},
"west": {"uv": [1, 0, 7.5, 2], "texture": "#sh"},
"up": {"uv": [3, 0.5, 9.5, 2], "rotation": 90, "texture": "#sh"},
"down": {"uv": [3, 9, 3.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, -0.5],
"to": [6.5, 10, 0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12.5, 1, 6.5]},
"faces": {
"north": {"uv": [11, 11, 15, 15], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [2.5, 6, 6.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [3, 2, 7, 2.5], "texture": "#sh"},
"down": {"uv": [2.5, 15.5, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 9, -2],
"to": [3.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, 2, 5.5]},
"faces": {
"north": {"uv": [12.5, 6, 13.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [2.5, 6, 3.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [3, 2, 4, 3], "texture": "#sh"},
"down": {"uv": [2.5, 16, 3.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2, 7.5, -2],
"to": [3, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 0.5, 5.5]},
"faces": {
"north": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [2, 0, 3, 0], "texture": "#sh"},
"down": {"uv": [2, 16, 3, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, -2],
"to": [3.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, -1, 5.5]},
"faces": {
"north": {"uv": [12.5, 9, 13.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [2.5, 9, 3.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [2.5, 0, 3.5, 0], "texture": "#sh"},
"down": {"uv": [2.5, 16, 3.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [4, 5.5, -2],
"to": [5, 6.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, -1.5, 5.5]},
"faces": {
"north": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"east": {"uv": [16, 9.5, 16, 10.5], "texture": "#sh"},
"south": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"west": {"uv": [0, 9.5, 0, 10.5], "texture": "#sh"},
"up": {"uv": [4, 0, 5, 0], "texture": "#sh"},
"down": {"uv": [4, 16, 5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 6, -2],
"to": [6.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, -1, 5.5]},
"faces": {
"north": {"uv": [9.5, 9, 10.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [5.5, 9, 6.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [5.5, 0, 6.5, 0], "texture": "#sh"},
"down": {"uv": [5.5, 16, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 9, -2],
"to": [6.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, 2, 5.5]},
"faces": {
"north": {"uv": [9.5, 6, 10.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [5.5, 6, 6.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [5.5, 0, 6.5, 0], "texture": "#sh"},
"down": {"uv": [5.5, 16, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [6, 7.5, -2],
"to": [7, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [14, 0.5, 5.5]},
"faces": {
"north": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [6, 0, 7, 0], "texture": "#sh"},
"down": {"uv": [6, 16, 7, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 7, 0.5],
"to": [6, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [5.5, 1, 5]},
"faces": {
"north": {"uv": [10, 7, 10.5, 9], "texture": "#sh"},
"east": {"uv": [8, 7, 14.5, 9], "texture": "#sh"},
"south": {"uv": [5.5, 7, 6, 9], "texture": "#sh"},
"west": {"uv": [0.5, 7, 7, 9], "texture": "#sh"},
"up": {"uv": [5, 2, 11.5, 2.5], "texture": "#sh"},
"down": {"uv": [5.5, 9, 6, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [3.5, 6.5, 0.5],
"to": [5.5, 9.5, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [4.5, 1, 5]},
"faces": {
"north": {"uv": [10.5, 6.5, 12.5, 9.5], "texture": "#sh"},
"east": {"uv": [9, 6.5, 15.5, 9.5], "texture": "#sh"},
"south": {"uv": [3.5, 6.5, 5.5, 9.5], "texture": "#sh"},
"west": {"uv": [0.5, 6.5, 7, 9.5], "texture": "#sh"},
"up": {"uv": [4, 1, 11, 3.5], "rotation": 90, "texture": "#sh"},
"down": {"uv": [3.5, 9, 5.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 7, 0.5],
"to": [13, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [12.5, 1, 5]},
"faces": {
"north": {"uv": [3, 7, 3.5, 9], "texture": "#sh"},
"east": {"uv": [9, 7, 15.5, 9], "texture": "#sh"},
"south": {"uv": [12.5, 7, 13, 9], "texture": "#sh"},
"west": {"uv": [0.5, 7, 7, 9], "texture": "#sh"},
"up": {"uv": [3, 0, 11.5, 0.5], "rotation": 90, "texture": "#sh"},
"down": {"uv": [12.5, 9, 13, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [10.5, 6.5, 0.5],
"to": [12.5, 9.5, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [11.5, 1, 5]},
"faces": {
"north": {"uv": [3.5, 6.5, 5.5, 9.5], "texture": "#sh"},
"east": {"uv": [9, 6.5, 15.5, 9.5], "texture": "#sh"},
"south": {"uv": [10.5, 6.5, 12.5, 9.5], "texture": "#sh"},
"west": {"uv": [0.5, 6.5, 7, 9.5], "texture": "#sh"},
"up": {"uv": [6, 0, 12, 2.5], "rotation": 90, "texture": "#sh"},
"down": {"uv": [10.5, 9, 12.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [10, 7, 0.5],
"to": [10.5, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [9.5, 1, 5]},
"faces": {
"north": {"uv": [5.5, 7, 6, 9], "texture": "#sh"},
"east": {"uv": [9, 7, 15.5, 9], "texture": "#sh"},
"south": {"uv": [10, 7, 10.5, 9], "texture": "#sh"},
"west": {"uv": [0.5, 7, 7, 9], "texture": "#sh"},
"up": {"uv": [10, 0.5, 15.5, 1], "rotation": 90, "texture": "#sh"},
"down": {"uv": [10, 9, 10.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, -0.5],
"to": [13.5, 10, 0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19.5, 1, 6.5]},
"faces": {
"north": {"uv": [10, 12, 14, 16], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [9.5, 6, 13.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [9.5, 0, 13.5, 0.5], "texture": "#sh"},
"down": {"uv": [9.5, 15.5, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 9, -2],
"to": [13.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, 2, 5.5]},
"faces": {
"north": {"uv": [2.5, 6, 3.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [12.5, 6, 13.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [12.5, 0, 13.5, 0], "texture": "#sh"},
"down": {"uv": [12.5, 16, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 9.5, -2],
"to": [12, 10.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, 2.5, 5.5]},
"faces": {
"north": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"east": {"uv": [16, 5.5, 16, 6.5], "texture": "#sh"},
"south": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"west": {"uv": [0, 5.5, 0, 6.5], "texture": "#sh"},
"up": {"uv": [11, 0, 12, 0], "texture": "#sh"},
"down": {"uv": [11, 16, 12, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 9, -2],
"to": [10.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, 2, 5.5]},
"faces": {
"north": {"uv": [5.5, 6, 6.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [9.5, 6, 10.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [9.5, 0, 10.5, 0], "texture": "#sh"},
"down": {"uv": [9.5, 16, 10.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9, 7.5, -2],
"to": [10, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17, 0.5, 5.5]},
"faces": {
"north": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [9, 0, 10, 0], "texture": "#sh"},
"down": {"uv": [9, 16, 10, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, -2],
"to": [10.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, -1, 5.5]},
"faces": {
"north": {"uv": [5.5, 9, 6.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [9.5, 9, 10.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [9.5, 0, 10.5, 0], "texture": "#sh"},
"down": {"uv": [9.5, 16, 10.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 5.5, -2],
"to": [12, 6.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, -1.5, 5.5]},
"faces": {
"north": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"east": {"uv": [16, 9.5, 16, 10.5], "texture": "#sh"},
"south": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"west": {"uv": [0, 9.5, 0, 10.5], "texture": "#sh"},
"up": {"uv": [11, 0, 12, 0], "texture": "#sh"},
"down": {"uv": [11, 16, 12, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 6, -2],
"to": [13.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, -1, 5.5]},
"faces": {
"north": {"uv": [2.5, 9, 3.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [12.5, 9, 13.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [12.5, 0, 13.5, 0], "texture": "#sh"},
"down": {"uv": [12.5, 16, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [13, 7.5, -2],
"to": [14, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [21, 0.5, 5.5]},
"faces": {
"north": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [13, 0, 14, 0], "texture": "#sh"},
"down": {"uv": [13, 16, 14, 16], "rotation": 270, "texture": "#sh"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -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]
}
}
}

View file

@ -0,0 +1,4 @@
{
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
"textures": { "side": "engineersdecor:blocks/device/small_fluid_funnel_side_s1" }
}

View file

@ -0,0 +1,4 @@
{
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
"textures": { "side": "engineersdecor:blocks/device/small_fluid_funnel_side_s2" }
}

View file

@ -0,0 +1,4 @@
{
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
"textures": { "side": "engineersdecor:blocks/device/small_fluid_funnel_side_s3" }
}

View file

@ -0,0 +1,33 @@
{
"conditions": [
{
"type": "engineersdecor:grc",
"result": "engineersdecor:factory_placer",
"missing": ["immersiveengineering:material"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"WWW",
"WDP",
"WWW"
],
"key": {
"D": {
"item": "minecraft:dispenser",
"data": 0
},
"P": {
"item": "#ingotIron",
"data": 0
},
"W": {
"item": "#plankWood",
"data": 0
}
},
"result": {
"item": "engineersdecor:factory_placer",
"count": 1
}
}

View file

@ -0,0 +1,37 @@
{
"conditions": [
{
"type": "engineersdecor:grc",
"result": "engineersdecor:small_block_breaker",
"missing": ["immersiveengineering:material"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"PPP",
"PAO",
"PRP"
],
"key": {
"O": {
"item": "minecraft:observer",
"data": 0
},
"P": {
"item": "#ingotIron",
"data": 0
},
"A": {
"item": "minecraft:iron_pickaxe",
"data": 0
},
"R": {
"item": "minecraft:redstone_block",
"data": 0
}
},
"result": {
"item": "engineersdecor:small_block_breaker",
"count": 1
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,33 @@
{
"conditions": [
{
"type": "engineersdecor:grc",
"result": "engineersdecor:factory_placer",
"required": ["immersiveengineering:material"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"WWW",
"WDP",
"WWW"
],
"key": {
"D": {
"item": "minecraft:dispenser",
"data": 0
},
"P": {
"item": "#plateIron",
"data": 0
},
"W": {
"item": "#plankTreatedWood",
"data": 0
}
},
"result": {
"item": "engineersdecor:factory_placer",
"count": 1
}
}

View file

@ -0,0 +1,41 @@
{
"conditions": [
{
"type": "engineersdecor:grc",
"result": "engineersdecor:small_block_breaker",
"required": ["immersiveengineering:material"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"PPP",
"PAO",
"CRC"
],
"key": {
"O": {
"item": "minecraft:observer",
"data": 0
},
"P": {
"item": "#plateAnyFerroMetal",
"data": 0
},
"A": {
"item": "minecraft:iron_pickaxe",
"data": 0
},
"R": {
"item": "minecraft:redstone_block",
"data": 0
},
"C": {
"item": "#anyMechanicalComponent",
"data": 0
}
},
"result": {
"item": "engineersdecor:small_block_breaker",
"count": 1
}
}

View file

@ -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: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

View file

@ -0,0 +1 @@
{ "animation":{ "frames": [0,1], "frametime":2, "interpolate":false }}

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -5,4 +5,4 @@ version_minecraft=1.14.4
version_forge_minecraft=1.14.4-28.1.68
version_fml_mappings=20190719-1.14.3
version_jei=1.14.4:6.0.0.10
version_engineersdecor=1.0.15-b1
version_engineersdecor=1.0.15-b4

View file

@ -1,6 +1,8 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.14.4": {
"1.0.15-b3": "[A] Added Small Block Breaker.\n[M] Mineral Smelter fluid handler/transfer added.",
"1.0.15-b2": "[!] Forge version requirement set to 1.14.4-28.1.68 or higher.\n[A] Added Factory Block Placer and Planter.\n[A] Added Small Tree Cutter.",
"1.0.15-b1": "[A] Added Floor Edge Light.\n[U] Updated to Forge1.14.4-28.1.68/20190719-1.14.3.",
"1.0.14-b1": "[U] Updated to Forge 1.14.4-28.1.40/20190719-1.14.3.\n[A] Factory Hopper added (configurable hopper and item collector).\n[M] Switched to integrated loot table generation.\n[M] Lang file zh_cn updated (scikirbypoke, PR#53).",
"1.0.13-b2": "[A] Added Steel Mesh Fence.\n[A] Added Broad Window Sill.",
@ -28,6 +30,6 @@
},
"promos": {
"1.14.4-recommended": "",
"1.14.4-latest": "1.0.15-b1"
"1.14.4-latest": "1.0.15-b3"
}
}

View file

@ -11,6 +11,15 @@ Mod sources for Minecraft version 1.14.4.
## Version history
~ v1.0.15-b4 [A] Added Fluid Collection Funnel.
- v1.0.15-b3 [A] Added Small Block Breaker.
[M] Mineral Smelter fluid handler/transfer added.
- v1.0.15-b2 [!] Forge version requirement set to 1.14.4-28.1.68 or higher.
[A] Added Factory Block Placer and Planter.
[A] Added Small Tree Cutter.
- v1.0.15-b1 [A] Added Floor Edge Light.
[U] Updated to Forge1.14.4-28.1.68/20190719-1.14.3.

View file

@ -368,6 +368,18 @@ public class ModContent
ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,15)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "factory_dropper"));
public static final BlockDecorPlacer FACTORY_PLACER = (BlockDecorPlacer)(new BlockDecorPlacer(
BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK|BlockDecor.CFG_REDSTONE_CONTROLLED,
Block.Properties.create(Material.WOOD, MaterialColor.WOOD).hardnessAndResistance(2f, 15f).sound(SoundType.METAL),
ModAuxiliaries.getPixeledAABB(2,2,2, 14,14,14)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "factory_placer"));
public static final BlockDecorBreaker SMALL_BLOCK_BREAKER = (BlockDecorBreaker)(new BlockDecorBreaker(
BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK,
Block.Properties.create(Material.WOOD, MaterialColor.WOOD).hardnessAndResistance(2f, 15f).sound(SoundType.METAL),
ModAuxiliaries.getPixeledAABB(0,0,0, 16,12,16)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "small_block_breaker"));
public static final BlockDecorHopper FACTORY_HOPPER = (BlockDecorHopper)(new BlockDecorHopper(
BlockDecor.CFG_CUTOUT|BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_REDSTONE_CONTROLLED,
Block.Properties.create(Material.IRON, MaterialColor.IRON).hardnessAndResistance(2f, 15f).sound(SoundType.METAL),
@ -392,6 +404,12 @@ public class ModContent
ModAuxiliaries.getPixeledAABB(0,0,0, 16,11.5,16)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "small_solar_panel"));
public static final BlockDecorTreeCutter SMALL_TREE_CUTTER = (BlockDecorTreeCutter)(new BlockDecorTreeCutter(
BlockDecor.CFG_CUTOUT|BlockDecor.CFG_HORIZIONTAL|BlockDecor.CFG_LOOK_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK,
Block.Properties.create(Material.IRON, MaterialColor.IRON).hardnessAndResistance(2f, 15f).sound(SoundType.METAL),
ModAuxiliaries.getPixeledAABB(0,0,0, 16,8,16)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "small_tree_cutter"));
public static final BlockDecorPipeValve STRAIGHT_CHECK_VALVE = (BlockDecorPipeValve)(new BlockDecorPipeValve(
BlockDecor.CFG_FACING_PLACEMENT|BlockDecor.CFG_OPPOSITE_PLACEMENT|BlockDecor.CFG_FLIP_PLACEMENT_SHIFTCLICK|BlockDecor.CFG_CUTOUT,
Block.Properties.create(Material.IRON, MaterialColor.IRON).hardnessAndResistance(2f, 15f).sound(SoundType.METAL),
@ -416,6 +434,12 @@ public class ModContent
ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "passive_fluid_accumulator"));
public static final BlockDecorFluidFunnel SMALL_FLUID_FUNNEL = (BlockDecorFluidFunnel)(new BlockDecorFluidFunnel(
BlockDecor.CFG_CUTOUT|BlockDecor.CFG_REDSTONE_CONTROLLED,
Block.Properties.create(Material.IRON, MaterialColor.IRON).hardnessAndResistance(2f, 15f).sound(SoundType.METAL),
ModAuxiliaries.getPixeledAABB(0,0,0, 16,16,16)
)).setRegistryName(new ResourceLocation(ModEngineersDecor.MODID, "small_fluid_funnel"));
// -------------------------------------------------------------------------------------------------------------------
public static final BlockDecorWall CONCRETE_WALL = (BlockDecorWall)(new BlockDecorWall(
@ -470,12 +494,20 @@ public class ModContent
private static final Block modBlocks[] = {
TREATED_WOOD_CRAFTING_TABLE,
SMALL_LAB_FURNACE,
FACTORY_DROPPER,
FACTORY_HOPPER,
SMALL_ELECTRICAL_FURNACE,
FACTORY_HOPPER,
FACTORY_DROPPER,
FACTORY_PLACER,
SMALL_BLOCK_BREAKER,
SMALL_TREE_CUTTER,
SMALL_SOLAR_PANEL,
SMALL_WASTE_INCINERATOR,
SMALL_MINERAL_SMELTER,
SMALL_SOLAR_PANEL,
STRAIGHT_CHECK_VALVE,
STRAIGHT_REDSTONE_VALVE,
STRAIGHT_REDSTONE_ANALOG_VALVE,
PASSIVE_FLUID_ACCUMULATOR,
SMALL_FLUID_FUNNEL,
CLINKER_BRICK_BLOCK,
CLINKER_BRICK_SLAB,
CLINKER_BRICK_STAIRS,
@ -537,10 +569,6 @@ public class ModContent
};
private static final Block devBlocks[] = {
STRAIGHT_CHECK_VALVE,
STRAIGHT_REDSTONE_VALVE,
STRAIGHT_REDSTONE_ANALOG_VALVE,
PASSIVE_FLUID_ACCUMULATOR,
};
//--------------------------------------------------------------------------------------------------------------------
@ -567,6 +595,16 @@ public class ModContent
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_factory_dropper");
public static final TileEntityType<?> TET_FACTORY_PLACER = TileEntityType.Builder
.create(BlockDecorPlacer.BTileEntity::new, FACTORY_PLACER)
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_factory_placer");
public static final TileEntityType<?> TET_SMALL_BLOCK_BREAKER = TileEntityType.Builder
.create(BlockDecorBreaker.BTileEntity::new, SMALL_BLOCK_BREAKER)
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_small_block_breaker");
public static final TileEntityType<?> TET_FACTORY_HOPPER = TileEntityType.Builder
.create(BlockDecorHopper.BTileEntity::new, FACTORY_HOPPER)
.build(null)
@ -587,6 +625,11 @@ public class ModContent
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_passive_fluid_accumulator");
public static final TileEntityType<?> TET_SMALL_FLUID_FUNNEL = TileEntityType.Builder
.create(BlockDecorFluidFunnel.BTileEntity::new, SMALL_FLUID_FUNNEL)
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_small_fluid_funnel");
public static final TileEntityType<?> TET_MINERAL_SMELTER = TileEntityType.Builder
.create(BlockDecorMineralSmelter.BTileEntity::new, SMALL_MINERAL_SMELTER)
.build(null)
@ -597,18 +640,27 @@ public class ModContent
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_small_solar_panel");
public static final TileEntityType<?> TET_SMALL_TREE_CUTTER = TileEntityType.Builder
.create(BlockDecorTreeCutter.BTileEntity::new, SMALL_TREE_CUTTER)
.build(null)
.setRegistryName(ModEngineersDecor.MODID, "te_small_tree_cutter");
private static final TileEntityType<?> tile_entity_types[] = {
TET_TREATED_WOOD_CRAFTING_TABLE,
TET_SMALL_LAB_FURNACE,
TET_FACTORY_DROPPER,
TET_FACTORY_HOPPER,
TET_SMALL_ELECTRICAL_FURNACE,
TET_FACTORY_HOPPER,
TET_FACTORY_DROPPER,
TET_FACTORY_PLACER,
TET_SMALL_BLOCK_BREAKER,
TET_SMALL_TREE_CUTTER,
TET_WASTE_INCINERATOR,
TET_MINERAL_SMELTER,
TET_SMALL_SOLAR_PANEL,
TET_STRAIGHT_PIPE_VALVE,
TET_PASSIVE_FLUID_ACCUMULATOR,
TET_MINERAL_SMELTER,
TET_SMALL_SOLAR_PANEL
TET_SMALL_FLUID_FUNNEL,
};
//--------------------------------------------------------------------------------------------------------------------
@ -634,6 +686,7 @@ public class ModContent
public static final ContainerType<BlockDecorCraftingTable.BContainer> CT_TREATED_WOOD_CRAFTING_TABLE;
public static final ContainerType<BlockDecorDropper.BContainer> CT_FACTORY_DROPPER;
public static final ContainerType<BlockDecorPlacer.BContainer> CT_FACTORY_PLACER;
public static final ContainerType<BlockDecorHopper.BContainer> CT_FACTORY_HOPPER;
public static final ContainerType<BlockDecorFurnace.BContainer> CT_SMALL_LAB_FURNACE;
public static final ContainerType<BlockDecorFurnaceElectrical.BContainer> CT_SMALL_ELECTRICAL_FURNACE;
@ -644,6 +697,8 @@ public class ModContent
CT_TREATED_WOOD_CRAFTING_TABLE.setRegistryName(ModEngineersDecor.MODID,"ct_treated_wood_crafting_table");
CT_FACTORY_DROPPER = (new ContainerType<BlockDecorDropper.BContainer>(BlockDecorDropper.BContainer::new));
CT_FACTORY_DROPPER.setRegistryName(ModEngineersDecor.MODID,"ct_factory_dropper");
CT_FACTORY_PLACER = (new ContainerType<BlockDecorPlacer.BContainer>(BlockDecorPlacer.BContainer::new));
CT_FACTORY_PLACER.setRegistryName(ModEngineersDecor.MODID,"ct_factory_placer");
CT_FACTORY_HOPPER = (new ContainerType<BlockDecorHopper.BContainer>(BlockDecorHopper.BContainer::new));
CT_FACTORY_HOPPER.setRegistryName(ModEngineersDecor.MODID,"ct_factory_hopper");
CT_SMALL_LAB_FURNACE = (new ContainerType<BlockDecorFurnace.BContainer>(BlockDecorFurnace.BContainer::new));
@ -658,6 +713,7 @@ public class ModContent
private static final ContainerType<?> container_types[] = {
CT_TREATED_WOOD_CRAFTING_TABLE,
CT_FACTORY_DROPPER,
CT_FACTORY_PLACER,
CT_FACTORY_HOPPER,
CT_SMALL_LAB_FURNACE,
CT_SMALL_ELECTRICAL_FURNACE,
@ -740,6 +796,7 @@ public class ModContent
{
ScreenManager.registerFactory(CT_TREATED_WOOD_CRAFTING_TABLE, BlockDecorCraftingTable.BGui::new);
ScreenManager.registerFactory(CT_FACTORY_DROPPER, BlockDecorDropper.BGui::new);
ScreenManager.registerFactory(CT_FACTORY_PLACER, BlockDecorPlacer.BGui::new);
ScreenManager.registerFactory(CT_FACTORY_HOPPER, BlockDecorHopper.BGui::new);
ScreenManager.registerFactory(CT_SMALL_LAB_FURNACE, BlockDecorFurnace.BGui::new);
ScreenManager.registerFactory(CT_SMALL_ELECTRICAL_FURNACE, BlockDecorFurnaceElectrical.BGui::new);

View file

@ -40,6 +40,7 @@ public class ModEngineersDecor
public static final String MODID = "engineersdecor";
public static final int VERSION_DATAFIXER = 0;
private static final Logger LOGGER = LogManager.getLogger();
private static boolean config_loaded = false;
public ModEngineersDecor()
{
@ -63,6 +64,16 @@ public class ModEngineersDecor
LOGGER.info("Registering recipe condition processor ...");
CraftingHelper.register(Serializer.INSTANCE);
Networking.init();
if(config_loaded) {
try {
logger().info("Applying loaded config file.");
ModConfig.apply();
} catch(Throwable e) {
logger().error("Failed to apply config: " + e.getMessage());
}
} else {
logger().info("Cannot apply config, load event was not casted yet.");
}
}
private void onClientSetup(final FMLClientSetupEvent event)
@ -101,16 +112,9 @@ public class ModEngineersDecor
public static void onServerStarting(FMLServerStartingEvent event)
{}
// @SubscribeEvent
@SubscribeEvent
public static void onConfigLoad(net.minecraftforge.fml.config.ModConfig.Loading configEvent)
{
try {
ModEngineersDecor.logger().info("Loaded config file {}", configEvent.getConfig().getFileName());
ModConfig.apply();
} catch(Throwable e) {
ModEngineersDecor.logger().error("Failed to load config: " + e.getMessage());
}
}
{ config_loaded = true; }
@SubscribeEvent
public static void onConfigFileChange(net.minecraftforge.fml.config.ModConfig.ConfigReloading configEvent)
@ -128,7 +132,6 @@ public class ModEngineersDecor
{
event.getGenerator().addProvider(new ModLootTables(event.getGenerator()));
}
}
//

View file

@ -0,0 +1,282 @@
/*
* @file BlockDecorBreaker.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Small Block Breaker
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.block.Blocks;
import net.minecraft.block.SoundType;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
public class BlockDecorBreaker extends BlockDecorDirectedHorizontal
{
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public BlockDecorBreaker(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(ACTIVE); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(ACTIVE, false); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BTileEntity(); }
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{
if((state.getBlock()!=this) || (!state.get(ACTIVE))) return;
final double rv = rnd.nextDouble();
if(rv > 0.8) return;
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2);
switch(state.get(HORIZONTAL_FACING)) {
case WEST: world.addParticle(ParticleTypes.SMOKE, x-xc, yr, z+xr, 0.0, 0.0, 0.0); break;
case EAST: world.addParticle(ParticleTypes.SMOKE, x+xc, yr, z+xr, 0.0, 0.0, 0.0); break;
case NORTH: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z-xc, 0.0, 0.0, 0.0); break;
default: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z+xc, 0.0, 0.0, 0.0); break;
}
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{
if(!(world instanceof World) || (((World) world).isRemote)) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
((BTileEntity)te).block_updated();
}
@Override
@SuppressWarnings("deprecation")
public boolean canProvidePower(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getWeakPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage
{
public static final int IDLE_TICK_INTERVAL = 40;
public static final int TICK_INTERVAL = 5;
public static final int BOOST_FACTOR = 8;
public static final int DEFAULT_BOOST_ENERGY = 64;
public static final int DEFAULT_BREAKING_RELUCTANCE = 17;
public static final int DEFAULT_MIN_BREAKING_TIME = 15;
public static final int MAX_BREAKING_TIME = 800;
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int breaking_reluctance = DEFAULT_BREAKING_RELUCTANCE;
private static int min_breaking_time = DEFAULT_MIN_BREAKING_TIME;
private int tick_timer_;
private int proc_time_elapsed_;
private int boost_energy_;
public static void on_config(int boost_energy_per_tick, int breaking_time_per_hardness, int min_breaking_time_ticks)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 16, 512);
breaking_reluctance = MathHelper.clamp(breaking_time_per_hardness, 5, 50);
min_breaking_time = MathHelper.clamp(min_breaking_time_ticks, 10, 100);
ModEngineersDecor.logger().info("Config block breaker: Boost energy consumption:" + boost_energy_consumption + "rf/t, reluctance=" + breaking_reluctance + "/hrdn, break time offset=" + min_breaking_time );
}
public BTileEntity()
{ super(ModContent.TET_SMALL_BLOCK_BREAKER); }
public BTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void block_updated()
{ if(tick_timer_ > 2) tick_timer_ = 2; }
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return boost_energy_consumption; }
@Override
public int getEnergyStored()
{ return boost_energy_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{ // only speedup support, no buffering, not in nbt -> no markdirty
if((boost_energy_ >= boost_energy_consumption) || (maxReceive < boost_energy_consumption)) return 0;
if(!simulate) boost_energy_ = boost_energy_consumption;
return boost_energy_consumption;
}
// Capability export ----------------------------------------------------------------------------
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(!this.removed && (facing != null)) {
if(capability== CapabilityEnergy.ENERGY) {
return energy_handler_.cast();
}
}
return super.getCapability(capability, facing);
}
// ITickable ------------------------------------------------------------------------------------
private static HashSet<Block> blacklist = new HashSet<>();
static {
blacklist.add(Blocks.AIR);
blacklist.add(Blocks.BEDROCK);
blacklist.add(Blocks.FIRE);
blacklist.add(Blocks.END_PORTAL);
blacklist.add(Blocks.END_GATEWAY);
blacklist.add(Blocks.END_PORTAL_FRAME);
blacklist.add(Blocks.NETHER_PORTAL);
blacklist.add(Blocks.BARRIER);
}
private static boolean isBreakable(BlockState state, BlockPos pos, World world)
{
final Block block = state.getBlock();
if(blacklist.contains(block)) return false;
if(state.getMaterial().isLiquid()) return false;
if(block.isAir(state, world, pos)) return false;
float bh = state.getBlockHardness(world, pos);
if((bh<0) || (bh>55)) return false;
return true;
}
private boolean breakBlock(BlockState state, BlockPos pos, World world)
{
if(world.isRemote || (!(world instanceof ServerWorld)) || world.restoringBlockSnapshots) return false; // retry next cycle
List<ItemStack> drops = Block.getDrops(state, (ServerWorld)world, pos, world.getTileEntity(pos));
world.removeBlock(pos, false);
for(ItemStack drop:drops) spawnAsEntity(world, pos, drop);
SoundType stype = state.getBlock().getSoundType(state, world, pos, null);
if(stype != null) world.playSound(null, pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
return true;
}
@Override
@SuppressWarnings("deprecation")
public void tick()
{
if(--tick_timer_ > 0) return;
if(world.isRemote) {
BlockState state = world.getBlockState(pos);
if(!state.get(ACTIVE)) {
tick_timer_ = TICK_INTERVAL;
} else {
tick_timer_ = 1;
// not sure if is so cool to do this each tick ... may be simplified/removed again.
SoundEvent sound = SoundEvents.BLOCK_WOOD_HIT;
BlockState target_state = world.getBlockState(pos.offset(state.get(HORIZONTAL_FACING)));
SoundType stype = target_state.getBlock().getSoundType(target_state);
if((stype == SoundType.CLOTH) || (stype == SoundType.PLANT) || (stype == SoundType.SNOW)) {
sound = SoundEvents.BLOCK_WOOL_HIT;
} else if((stype == SoundType.GROUND) || (stype == SoundType.SAND)) {
sound = SoundEvents.BLOCK_GRAVEL_HIT;
}
world.playSound(pos.getX(), pos.getY(), pos.getZ(), sound, SoundCategory.BLOCKS, 0.1f, 1.2f, false);
}
} else {
tick_timer_ = TICK_INTERVAL;
final BlockState device_state = world.getBlockState(pos);
final BlockPos target_pos = pos.offset(device_state.get(HORIZONTAL_FACING));
final BlockState target_state = world.getBlockState(target_pos);
if((world.isBlockPowered(pos)) || (!isBreakable(target_state, target_pos, world))) {
if(device_state.get(ACTIVE)) world.setBlockState(pos, device_state.with(ACTIVE, false), 1|2);
proc_time_elapsed_ = 0;
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
proc_time_elapsed_ += TICK_INTERVAL;
boolean active = true;
int time_needed = (int)(target_state.getBlockHardness(world, pos) * breaking_reluctance) + min_breaking_time;
if(boost_energy_ >= boost_energy_consumption) {
boost_energy_ = 0;
proc_time_elapsed_ += TICK_INTERVAL * BOOST_FACTOR;
time_needed += min_breaking_time * (3*BOOST_FACTOR/5);
}
time_needed = MathHelper.clamp(time_needed, min_breaking_time, MAX_BREAKING_TIME);
if(proc_time_elapsed_ >= time_needed) {
proc_time_elapsed_ = 0;
breakBlock(target_state, target_pos, world);
active = false;
}
if(device_state.get(ACTIVE) != active) {
world.setBlockState(pos, device_state.with(ACTIVE, active), 1|2);
}
}
}
}
}

View file

@ -0,0 +1,392 @@
/*
* @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 wile.engineersdecor.ModContent;
import net.minecraft.block.*;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.fluid.IFluidState;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.*;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.*;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
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 IntegerProperty FILL_LEVEL = IntegerProperty.create("level", 0, FILL_LEVEL_MAX);
public BlockDecorFluidFunnel(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(FILL_LEVEL); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(FILL_LEVEL, 0); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BTileEntity(); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState state, World world, BlockPos pos)
{ return MathHelper.clamp((state.get(FILL_LEVEL)*5), 0, 15); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("tedata");
if(te_nbt.isEmpty()) return;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
((BTileEntity)te).readnbt(te_nbt);
((BTileEntity)te).markDirty();
world.setBlockState(pos, state.with(FILL_LEVEL, 0));
}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return stacks;
if(!explosion) {
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = new CompoundNBT();
((BTileEntity)te).writenbt(te_nbt);
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
} else {
stacks.add(new ItemStack(this, 1));
}
return stacks;
}
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return true;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return false;
return FluidUtil.interactWithFluidHandler(player, hand, world, pos, rayTraceResult.getFace());
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{ TileEntity te = world.getTileEntity(pos); if(te instanceof BTileEntity) ((BTileEntity)te).block_changed(); }
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider
{
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 FluidStack tank_ = FluidStack.EMPTY.copy();
private int tick_timer_ = 0;
private int collection_timer_ = 0;
private int no_fluid_found_counter_ = 0;
private int intensive_search_counter_ = 0;
private int total_pick_counter_ = 0;
private BlockPos last_pick_pos_ = BlockPos.ZERO;
private ArrayList<Vec3i> search_offsets_ = null;
public void block_changed()
{ tick_timer_ = TICK_INTERVAL; } // collect after flowing fluid has a stable state, otherwise it looks odd.
public BTileEntity()
{ this(ModContent.TET_SMALL_FLUID_FUNNEL); }
public BTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
public void readnbt(CompoundNBT nbt)
{
tank_ = (!nbt.contains("tank")) ? (FluidStack.EMPTY.copy()) : (FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
}
public void writenbt(CompoundNBT nbt)
{
if(!tank_.isEmpty()) nbt.put("tank", tank_.writeToNBT(new CompoundNBT()));
}
// TileEntity -----------------------------------------------------------------------------------------
@Override
public void read(CompoundNBT nbt)
{ super.read(nbt); readnbt(nbt); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt); return nbt; }
// ICapabilityProvider / Output flow handler ----------------------------------------------------------
private static class OutputFluidHandler implements IFluidHandler
{
private final BTileEntity te;
OutputFluidHandler(BTileEntity parent) { te = parent; }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return te.tank_.copy(); }
@Override public int getTankCapacity(int tank) { return TANK_CAPACITY; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public FluidStack drain(FluidStack resource, FluidAction action)
{
if((resource==null) || (te.tank_.isEmpty())) return FluidStack.EMPTY.copy();
return (!(te.tank_.isFluidEqual(resource))) ? (FluidStack.EMPTY.copy()) : drain(resource.getAmount(), action);
}
@Override public FluidStack drain(int maxDrain, FluidAction action)
{
FluidStack res = te.tank_.copy();
if(res.isEmpty()) return res;
maxDrain = MathHelper.clamp(maxDrain ,0 , te.tank_.getAmount());
res.setAmount(maxDrain);
if(action != FluidAction.EXECUTE) return res;
te.tank_.setAmount(te.tank_.getAmount()-maxDrain);
if(te.tank_.getAmount() <= 0) te.tank_ = FluidStack.EMPTY.copy();
return res;
}
}
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new OutputFluidHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) return fluid_handler_.cast();
return super.getCapability(capability, facing);
}
// ITickableTileEntity --------------------------------------------------------------------------------
private IFluidState get_fluidstate(BlockPos pos)
{
// todo: check if getFluidState() is enough
final Block collection_block = world.getBlockState(pos).getBlock();
if((!(collection_block instanceof IFluidBlock)) && (!(collection_block instanceof FlowingFluidBlock)) && (!(collection_block instanceof IWaterLoggable))) {
return Fluids.EMPTY.getDefaultState();
}
return world.getFluidState(pos);
}
private boolean try_pick(BlockPos pos, IFluidState fluidstate)
{
if(!fluidstate.isSource()) return false;
IFluidHandler hnd = FluidUtil.getFluidHandler(world, pos, null).orElse(null);
FluidStack fs;
if(hnd != null) {
fs = hnd.drain(TANK_CAPACITY, FluidAction.EXECUTE); // IFluidBlock
} else {
fs = new FluidStack(fluidstate.getFluid(), 1000);
BlockState state = world.getBlockState(pos);
if(state instanceof IBucketPickupHandler) {
((IBucketPickupHandler)state).pickupFluid(world, pos, state);
} else {
world.setBlockState(pos, Blocks.AIR.getDefaultState(), 1|2); // ok we can't leave the block, that would be an infinite source of an unknown fluid.
}
}
if((fs==null) || (fs.isEmpty())) return false; // it's marked nonnull but I don't trust every modder - including meself ...
if(tank_.isEmpty()) {
tank_ = fs.copy();
} else if(tank_.isFluidEqual(fs)) {
tank_.setAmount(MathHelper.clamp(tank_.getAmount()+fs.getAmount(), 0, TANK_CAPACITY));
} else {
return false;
}
return true;
}
private boolean can_pick(BlockPos pos, IFluidState fluidstate)
{
if(fluidstate.isSource()) return true;
IFluidHandler hnd = FluidUtil.getFluidHandler(world, pos, null).orElse(null);
if(hnd == null) return false;
FluidStack fs = hnd.drain(TANK_CAPACITY, FluidAction.SIMULATE); // don't trust that everyone returns nonnull
return ((fs!=null) && (!fs.isEmpty())) && (fluidstate.getFluid().isEquivalentTo(fs.getFluid()));
}
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(final BlockPos collection_pos)
{
IFluidState collection_fluidstate = get_fluidstate(collection_pos);
if(collection_fluidstate.isEmpty()) return false;
Fluid fluid_to_collect = collection_fluidstate.getFluid();
if((!tank_.isEmpty()) && (!tank_.getFluid().isEquivalentTo(fluid_to_collect))) return false;
if(try_pick(collection_pos, collection_fluidstate)) { last_pick_pos_ = collection_pos; return true; } // Blocks directly always first. Allows water source blocks to recover/reflow to source blocks.
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;
IFluidState fluidstate = get_fluidstate(p);
// @todo: nice thing in 1.14: the fluid level is easily readable,
// so lateral motion can be restricted to higher fill levels.
if(fluidstate.getFluid().isEquivalentTo(fluid_to_collect)) {
++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) {
IFluidState fs = get_fluidstate(pos.up());
if(!can_pick(pos.up(), fs)) break;
fluidstate = fs;
pos = pos.up();
trail.push(pos);
}
}
if(try_pick(pos, fluidstate)) {
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; println(s);
if(intensive_search_counter_ > 2) world.removeBlock(pos, false);
last_pick_pos_ = collection_pos;
search_offsets_ = null; // try other search order
++no_fluid_found_counter_;
return false;
}
public void tick()
{
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_.getAmount() <= (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_.getAmount() >= 1000)) {
IFluidHandler fh = FluidUtil.getFluidHandler(world, pos.down(), Direction.UP).orElse(null);
if(fh != null) {
FluidStack fs = new FluidStack(tank_.getFluid(), 1000);
int nfilled = MathHelper.clamp(fh.fill(fs, FluidAction.EXECUTE), 0, 1000);
tank_.shrink(nfilled);
dirty = true;
}
}
// Block state
int fill_level = (tank_==null) ? 0 : (MathHelper.clamp(tank_.getAmount()/1000,0,FILL_LEVEL_MAX));
final BlockState funnel_state = world.getBlockState(pos);
if(funnel_state.get(FILL_LEVEL) != fill_level) world.setBlockState(pos, funnel_state.with(FILL_LEVEL, fill_level), 2|16);
if(dirty) markDirty();
}
}
}

View file

@ -11,7 +11,6 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
//import wile.engineersdecor.detail.ModConfig;
import net.minecraft.entity.LivingEntity;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.inventory.ItemStackHelper;
@ -40,10 +39,8 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
//import net.minecraftforge.fluids.FluidStack;
//import net.minecraftforge.fluids.capability.IFluidHandler;
//import net.minecraftforge.fluids.capability.IFluidTankProperties;
//import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.api.distmarker.Dist;
@ -146,7 +143,7 @@ public class BlockDecorMineralSmelter extends BlockDecorDirectedHorizontal
}
}
}
} else if(stack.getItem() == Items.AIR) {
} else if(stack.isEmpty()) {
final ItemStack istack = te.getStackInSlot(1).copy();
if(te.phase() > BTileEntity.PHASE_WARMUP) player.setFire(1);
if(!istack.isEmpty()) {
@ -468,46 +465,39 @@ public class BlockDecorMineralSmelter extends BlockDecorDirectedHorizontal
// IFluidHandler --------------------------------------------------------------------------------
// private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new BFluidHandler(this));
//
// // @todo: REPLACE lava=null with whatever will work
// private static class BFluidHandler implements IFluidHandler, IFluidTankProperties
// {
// private final FluidStack lava;
// private final BTileEntity te;
// private final IFluidTankProperties[] props_ = {this};
// BFluidHandler(BTileEntity te) {
// this.te = te;
// //lava = new FluidStack(ForgeRegistries.FLUIDS.getValue(new ResourceLocation("minecraft:lava")), 1);
// // lava = new FluidStack(Blocks.LAVA.getFluidState(Blocks.LAVA.getDefaultState()).getFluid(), 1);
// // lava = new FluidStack(Fluids.EMPTY, 1);
// //new net.minecraftforge.fluids.FluidStack(net.minecraft.fluid.Fluids.LAVA, 1);
//lava=null;
// }
// @Override @Nullable public FluidStack getContents() { return new FluidStack(lava, te.fluid_level()); }
// @Override public IFluidTankProperties[] getTankProperties() { return props_; }
// @Override public int fill(FluidStack resource, boolean doFill) { return 0; }
// @Override public int getCapacity() { return 1000; }
// @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 fluidStack.isFluidEqual(lava); }
//
// @Override @Nullable public FluidStack drain(FluidStack resource, boolean doDrain)
// {
// if((te.fluid_level() <= 0) || (!resource.isFluidEqual(lava))) return null;
// FluidStack fs = getContents();
// if(doDrain) te.fluid_level_drain(fs.amount);
// return fs;
// }
//
// @Override @Nullable public FluidStack drain(int maxDrain, boolean doDrain)
// {
// if(te.fluid_level() <= 0) return null;
// maxDrain = (doDrain) ? (te.fluid_level_drain(maxDrain)) : (Math.min(maxDrain, te.fluid_level()));
// return new FluidStack(lava, maxDrain);
// }
// }
private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new BFluidHandler(this));
private static class BFluidHandler implements IFluidHandler
{
private final FluidStack lava;
private final BTileEntity te;
BFluidHandler(BTileEntity te)
{ this.te = te; lava = new net.minecraftforge.fluids.FluidStack(net.minecraft.fluid.Fluids.LAVA, 1); }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return new FluidStack(lava, te.fluid_level()); }
@Override public int getTankCapacity(int tank) { return 1000; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return (tank==0) && (stack.isFluidEqual(lava)); }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override
public FluidStack drain(FluidStack resource, FluidAction action)
{
if(!resource.isFluidEqual(lava) || (te.fluid_level() <= 0)) return FluidStack.EMPTY.copy();
FluidStack stack = new FluidStack(lava, te.fluid_level());
if(action == FluidAction.EXECUTE) te.fluid_level_drain(te.fluid_level());
return stack;
}
@Override
public FluidStack drain(int maxDrain, FluidAction action)
{
if(te.fluid_level() <= 0) return FluidStack.EMPTY.copy();
maxDrain = (action==FluidAction.EXECUTE) ? (te.fluid_level_drain(maxDrain)) : (Math.min(maxDrain, te.fluid_level()));
return new FluidStack(lava, maxDrain);
}
}
// IEnergyStorage ----------------------------------------------------------------------------

View file

@ -15,26 +15,28 @@ package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.detail.ModAuxiliaries;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import net.minecraft.world.IBlockReader;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
//import net.minecraftforge.common.util.LazyOptional;
//import net.minecraftforge.common.capabilities.ICapabilityProvider;
//import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
//import net.minecraftforge.fluids.capability.IFluidHandler;
//import net.minecraftforge.fluids.capability.IFluidTankProperties;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -67,7 +69,6 @@ public class BlockDecorPassiveFluidAccumulator extends BlockDecorDirected
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{
// @todo double check if this is actually needed
TileEntity te = world.getTileEntity(pos);
if(te instanceof BlockDecorPipeValve.BTileEntity) ((BTileEntity)te).block_changed();
}
@ -76,13 +77,12 @@ public class BlockDecorPassiveFluidAccumulator extends BlockDecorDirected
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity // implements ITickableTileEntity, IFluidHandler, IFluidTankProperties, ICapabilityProvider
public static class BTileEntity extends TileEntity implements ITickableTileEntity, ICapabilityProvider
{
protected static int tick_idle_interval = 20; // ca 1000ms, simulates suction delay and saves CPU when not drained.
protected static int max_flowrate = 1000;
//private final IFluidTankProperties[] fluid_props_ = {this};
private Direction block_facing_ = Direction.NORTH;
private FluidStack tank_ = null;
private FluidStack tank_ = FluidStack.EMPTY;
private int last_drain_request_amount_ = 0;
private int vacuum_ = 0;
private int tick_timer_ = 0;
@ -93,33 +93,13 @@ public class BlockDecorPassiveFluidAccumulator extends BlockDecorDirected
public void send_device_stats(PlayerEntity player)
{
int t_vol = (tank_==null) ? 0 : (tank_.getAmount());
int t_vol = tank_.getAmount();
ModAuxiliaries.playerChatMessage(player,"" + t_vol + "mB");
}
public void block_changed()
{ initialized_ = false; tick_timer_ = MathHelper.clamp(tick_timer_ , 0, tick_idle_interval); }
// // Output flow handler ---------------------------------------------------------------------
//
// private static class InputFillHandler implements IFluidHandler, IFluidTankProperties
// {
// private final BTileEntity parent_;
// private final IFluidTankProperties[] props_ = {this};
// InputFillHandler(BTileEntity parent) { parent_ = parent; }
// @Override public int fill(FluidStack resource, boolean doFill) { return 0; }
// @Override @Nullable public FluidStack drain(FluidStack resource, boolean doDrain) { return null; }
// @Override @Nullable public FluidStack drain(int maxDrain, boolean doDrain) { return null; }
// @Override @Nullable public FluidStack getContents() { return null; }
// @Override public IFluidTankProperties[] getTankProperties() { return props_; }
// @Override public int getCapacity() { return max_flowrate; }
// @Override public boolean canFill() { return true; }
// @Override public boolean canDrain() { return false; }
// @Override public boolean canFillFluidType(FluidStack fluidStack) { return true; }
// @Override public boolean canDrainFluidType(FluidStack fluidStack) { return false; }
// }
//
// TileEntity ------------------------------------------------------------------------------
public BTileEntity()
@ -128,143 +108,147 @@ public class BlockDecorPassiveFluidAccumulator extends BlockDecorDirected
public BTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
// @Override
// public void read(CompoundNBT nbt)
// {
// super.read(nbt);
// tank_ = (!nbt.contains("tank")) ? (null) : (FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
// }
//
// @Override
// public CompoundNBT write(CompoundNBT nbt)
// {
// super.write(nbt);
// if(tank_ != null) nbt.put("tank", tank_.writeToNBT(new CompoundNBT()));
// return nbt;
// }
//
// // ICapabilityProvider --------------------------------------------------------------------
//
// private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)this);
// private final LazyOptional<IFluidHandler> fill_handler_ = LazyOptional.of(() -> new InputFillHandler(this));
//
// @Override
// public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
// {
// if((initialized_) && (!this.removed) && (facing != null)) {
// if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
// if(facing == block_facing_) return fluid_handler_.cast();
// return fill_handler_.cast();
// }
// }
// return super.getCapability(capability, facing);
// }
//
// // 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(!initialized_) return null;
// if(doDrain && (maxDrain > 0)) last_drain_request_amount_ = maxDrain;
// if(tank_==null) return null;
// maxDrain = MathHelper.clamp(maxDrain ,0 , tank_.amount);
// if(!doDrain) return tank_.copy();
// FluidStack res = tank_.copy();
// res.amount = maxDrain;
// tank_.amount -= maxDrain;
// if(tank_.amount <= 0) tank_= null;
// total_volume_drained_ += res.amount;
// return res;
// }
//
// // IFluidTankProperties --------------------------------------------------------------------
//
// @Override @Nullable public FluidStack getContents() { return (tank_==null) ? (null) : (tank_.copy()); }
// @Override public int getCapacity() { return max_flowrate; }
// @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--------------------------------------------------------------------------------
//
// public void tick()
// {
// if((world.isRemote) || (--tick_timer_ > 0)) return;
// tick_timer_ = tick_idle_interval;
// if(!initialized_) {
// initialized_ = true;
// BlockState state = world.getBlockState(pos);
// if((state==null) || (!(state.getBlock() instanceof BlockDecorPassiveFluidAccumulator))) return;
// block_facing_ = state.get(FACING);
// }
// int n_requested = last_drain_request_amount_;
// last_drain_request_amount_ = 0;
// if(n_requested > 0) {
// vacuum_ += 2;
// if(vacuum_ > 5) vacuum_ = 5;
// } else {
// if((--vacuum_) <= 0) {
// vacuum_ = 0;
// if(tank_!=null) {
// return; // nothing to do, noone's draining.
// } else {
// n_requested = 10; // drip in
// }
// }
// }
// boolean has_refilled = false;
// n_requested += (vacuum_ * 50);
// int tank_buffer_needed = n_requested;
// if(tank_buffer_needed > max_flowrate) tank_buffer_needed = max_flowrate;
// for(int i=0; i<6; ++i) {
// if(++round_robin_ > 5) round_robin_ = 0;
// if(n_requested <= 0) break;
// if(((tank_!=null) && (tank_.amount >= tank_buffer_needed))) break;
// final Direction f = Direction.byIndex(round_robin_);
// if(f == block_facing_) continue;
// final TileEntity te = world.getTileEntity(pos.offset(f));
// if((te==null) || (te instanceof BTileEntity)) continue;
// final IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f.getOpposite()).orElse(null);
// if(fh==null) continue;
// if(tank_==null) {
// FluidStack res = fh.drain(n_requested, true);
// if((res == null) || (res.amount==0)) continue;
// total_volume_filled_ += res.amount;
// tank_ = res.copy();
// has_refilled = true;
// } else {
// if((tank_.amount + n_requested) > max_flowrate) n_requested = (max_flowrate - tank_.amount);
// FluidStack rq = tank_.copy();
// rq.amount = n_requested;
// FluidStack res = fh.drain(rq, true);
// if(res == null) continue;
// tank_.amount += res.amount;
// total_volume_filled_ += res.amount;
// has_refilled = true;
// if(tank_.amount >= max_flowrate) break;
// }
// }
// if(has_refilled) tick_timer_ = 0;
// }
@Override
public void read(CompoundNBT nbt)
{
super.read(nbt);
tank_ = (!nbt.contains("tank")) ? (FluidStack.EMPTY.copy()) : (FluidStack.loadFluidStackFromNBT(nbt.getCompound("tank")));
}
@Override
public CompoundNBT write(CompoundNBT nbt)
{
super.write(nbt);
if(!tank_.isEmpty()) nbt.put("tank", tank_.writeToNBT(new CompoundNBT()));
return nbt;
}
// Input flow handler ---------------------------------------------------------------------
private static class InputFillHandler implements IFluidHandler
{
private final BTileEntity parent_;
InputFillHandler(BTileEntity parent) { parent_ = parent; }
@Override public int getTanks() { return 0; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return max_flowrate; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY.copy(); }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY.copy(); }
}
// Output flow handler ---------------------------------------------------------------------
private static class OutputFlowHandler implements IFluidHandler
{
private final BTileEntity te;
OutputFlowHandler(BTileEntity parent) { te = parent; }
@Override public int getTanks() { return 1; }
@Override public FluidStack getFluidInTank(int tank) { return te.tank_.copy(); }
@Override public int getTankCapacity(int tank) { return max_flowrate; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public FluidStack drain(FluidStack resource, FluidAction action)
{
if((resource==null) || (te.tank_.isEmpty())) return FluidStack.EMPTY.copy();
return (!(te.tank_.isFluidEqual(resource))) ? (FluidStack.EMPTY.copy()) : drain(resource.getAmount(), action);
}
@Override public FluidStack drain(int maxDrain, FluidAction action)
{
if(!te.initialized_) FluidStack.EMPTY.copy();
if((action==FluidAction.EXECUTE) && (maxDrain > 0)) te.last_drain_request_amount_ = maxDrain;
if(te.tank_.isEmpty()) return FluidStack.EMPTY.copy();
maxDrain = MathHelper.clamp(maxDrain ,0 , te.tank_.getAmount());
FluidStack res = te.tank_.copy();
if(action!=FluidAction.EXECUTE) return res;
res.setAmount(maxDrain);
te.tank_.setAmount(te.tank_.getAmount()-maxDrain);
if(te.tank_.getAmount() <= 0) te.tank_ = FluidStack.EMPTY.copy();
te.total_volume_drained_ += res.getAmount();
return res;
}
}
// ICapabilityProvider --------------------------------------------------------------------
private final LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> new OutputFlowHandler(this));
private final LazyOptional<IFluidHandler> fill_handler_ = LazyOptional.of(() -> new InputFillHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if((initialized_) && (!this.removed) && (facing != null)) {
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
if(facing == block_facing_) return fluid_handler_.cast();
return fill_handler_.cast();
}
}
return super.getCapability(capability, facing);
}
// ITickable--------------------------------------------------------------------------------
public void tick()
{
if((world.isRemote) || (--tick_timer_ > 0)) return;
tick_timer_ = tick_idle_interval;
if(!initialized_) {
initialized_ = true;
BlockState state = world.getBlockState(pos);
if((state==null) || (!(state.getBlock() instanceof BlockDecorPassiveFluidAccumulator))) return;
block_facing_ = state.get(FACING);
}
int n_requested = last_drain_request_amount_;
last_drain_request_amount_ = 0;
if(n_requested > 0) {
vacuum_ += 2;
if(vacuum_ > 5) vacuum_ = 5;
} else {
if((--vacuum_) <= 0) {
vacuum_ = 0;
if(!tank_.isEmpty()) {
return; // nothing to do, noone's draining.
} else {
n_requested = 10; // drip in
}
}
}
boolean has_refilled = false;
n_requested += (vacuum_ * 50);
int tank_buffer_needed = n_requested;
if(tank_buffer_needed > max_flowrate) tank_buffer_needed = max_flowrate;
for(int i=0; i<6; ++i) {
if(++round_robin_ > 5) round_robin_ = 0;
if(n_requested <= 0) break;
if((tank_.getAmount() >= tank_buffer_needed)) break;
final Direction f = Direction.byIndex(round_robin_);
if(f == block_facing_) continue;
final TileEntity te = world.getTileEntity(pos.offset(f));
if((te==null) || (te instanceof BTileEntity)) continue;
final IFluidHandler fh = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, f.getOpposite()).orElse(null);
if(fh==null) continue;
if(tank_.isEmpty()) {
FluidStack res = fh.drain(n_requested, FluidAction.EXECUTE).copy();
if(res.getAmount()==0) continue;
total_volume_filled_ += res.getAmount();
tank_ = res.copy();
has_refilled = true;
} else {
if((tank_.getAmount() + n_requested) > max_flowrate) n_requested = (max_flowrate - tank_.getAmount());
FluidStack rq = tank_.copy();
rq.setAmount(n_requested);
FluidStack res = fh.drain(rq, FluidAction.EXECUTE);
if(res.isEmpty()) continue;
tank_.setAmount(tank_.getAmount()+res.getAmount());
total_volume_filled_ += res.getAmount();
has_refilled = true;
if(tank_.getAmount() >= max_flowrate) break;
}
}
if(has_refilled) tick_timer_ = 0;
}
}
}

View file

@ -9,9 +9,10 @@
*/
package wile.engineersdecor.blocks;
import net.minecraft.block.IWaterLoggable;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.block.IWaterLoggable;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.BooleanProperty;
import net.minecraft.world.IWorld;
import net.minecraft.item.BlockItemUseContext;
@ -28,12 +29,12 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
//import net.minecraftforge.common.util.LazyOptional;
//import net.minecraftforge.common.capabilities.ICapabilityProvider;
//import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
//import net.minecraftforge.fluids.capability.IFluidHandler;
//import net.minecraftforge.fluids.capability.IFluidTankProperties;
//import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -141,7 +142,7 @@ public class BlockDecorPipeValve extends BlockDecorDirected implements IWaterLog
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity // implements IFluidHandler, IFluidTankProperties, ICapabilityProvider, IFluidPipe
public static class BTileEntity extends TileEntity implements ICapabilityProvider //, IFluidPipe
{
protected static int fluid_maxflow_mb = 1000;
protected static int redstone_flow_slope_mb = 1000/15;
@ -178,164 +179,132 @@ public class BlockDecorPipeValve extends BlockDecorDirected implements IWaterLog
}
// TileEntity ------------------------------------------------------------------------------
//
// @Override
// public void onLoad()
// {
// if(!hasWorld()) return;
// final BlockState state = world.getBlockState(pos);
// if((!(state.getBlock() instanceof BlockDecorPipeValve))) return;
// block_reconfigure(state.get(FACING), block_config_);
// world.notifyNeighborsOfStateChange(pos, state.getBlock());
// }
//
// @Override
// public void read(CompoundNBT nbt)
// {
// super.read(nbt);
// int i = nbt.getInt("facing");
// if((i>=0) || (i<6)) block_facing_ = Direction.byIndex(i);
// block_config_ = nbt.getLong("conf");
// }
//
// @Override
// public CompoundNBT write(CompoundNBT nbt)
// {
// super.write(nbt);
// nbt.putInt("facing", block_facing_.getIndex());
// nbt.putLong("conf", block_config_);
// return nbt;
// }
//
// // ICapabilityProvider --------------------------------------------------------------------
//
// private static final BackFlowHandler back_flow_handler_singleton_ = new BackFlowHandler();
// private LazyOptional<IFluidHandler> back_flow_handler_ = LazyOptional.of(() -> (IFluidHandler)back_flow_handler_singleton_);
// private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)this);
//
// @Override
// public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
// {
// if(!this.removed && (facing != null)) {
// if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
// if(facing == block_facing_) return fluid_handler_.cast();
// if(facing == block_facing_.getOpposite()) return back_flow_handler_.cast();
// }
// }
// return super.getCapability(capability, facing);
// }
//
// // IFluidHandler/IFluidTankProperties ---------------------------------------------------------------
//
// @Nullable
// private IFluidHandler forward_fluid_handler()
// {
// final TileEntity te = world.getTileEntity(pos.offset(block_facing_));
// if(te == null) return null;
// return te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, block_facing_.getOpposite()).orElse(null);
// }
//
// @Override
// public int fill(FluidStack resource, boolean doFill)
// {
// if((filling_) || (!filling_enabled_)) return 0;
// if((block_config_ & CFG_REDSTONE_CONTROLLED) != 0) {
// int rs = world.getRedstonePowerFromNeighbors(pos);
// if(rs <= 0) return 0;
// if(((block_config_ & CFG_ANALOG) != 0) && (rs < 15)) resource.amount = MathHelper.clamp(rs * redstone_flow_slope_mb, 1, resource.amount);
// }
// FluidStack res = resource.copy();
// if(res.amount > fluid_maxflow_mb) res.amount = fluid_maxflow_mb;
// final IFluidHandler fh = forward_fluid_handler();
// if(fh==null) return 0;
// filling_ = true; // in case someone does not check the cap, but just "forwards back" what is beeing filled right now.
// if(res.amount > 50) {
// final TileEntity te = world.getTileEntity(pos.offset(block_facing_));
// if(te instanceof IFluidPipe) {
// // forward pressureized tag
// if(res.tag == null) res.tag = new CompoundNBT();
// res.tag.putBoolean("pressurized", true);
// }
// }
// int n_filled = forward_fluid_handler().fill(res, doFill);
// filling_ = false;
// return n_filled;
// }
//
// @Override
// @Nullable
// public FluidStack drain(FluidStack resource, boolean doDrain)
// { return null; }
//
// @Override
// @Nullable
// public FluidStack drain(int maxDrain, boolean doDrain)
// { return null; }
//
// @Override
// public IFluidTankProperties[] getTankProperties()
// { return fluid_props_; }
//
// // IFluidTankProperties --
//
// @Override
// @Nullable
// public FluidStack getContents()
// { return null; }
//
// public int getCapacity()
// { return 1000; }
//
// @Override
// public boolean canFill()
// { return true; }
//
// @Override
// public boolean canDrain()
// { return false; }
//
// @Override
// public boolean canFillFluidType(FluidStack fluidStack)
// { return true; }
//
// @Override
// public boolean canDrainFluidType(FluidStack fluidStack)
// { return false; }
//
// // Back flow prevention handler --
//
// private static class BackFlowHandler implements IFluidHandler, IFluidTankProperties
// {
// private final IFluidTankProperties[] props_ = {this};
// @Override public IFluidTankProperties[] getTankProperties() { return props_; }
// @Override public int fill(FluidStack resource, boolean doFill) { return 0; }
// @Override @Nullable public FluidStack drain(FluidStack resource, boolean doDrain) { return null; }
// @Override @Nullable public FluidStack drain(int maxDrain, boolean doDrain) { return null; }
// @Override @Nullable public FluidStack getContents() { return null; }
// @Override public int getCapacity() { return 0; }
// @Override public boolean canFill() { return false; }
// @Override public boolean canDrain() { return false; }
// @Override public boolean canFillFluidType(FluidStack fluidStack) { return false; }
// @Override public boolean canDrainFluidType(FluidStack fluidStack) { return false; }
// }
//
// // IFluidPipe
//
// @Override
// public boolean hasOutputConnection(Direction side)
// { return (side == block_facing_); }
//
// @Override
// public boolean canOutputPressurized(boolean consumePower)
// {
// if(getlocked_ || (!filling_enabled_)) return false;
// final TileEntity te = world.getTileEntity(pos.offset(block_facing_));
// if(!(te instanceof IFluidPipe)) return false;
// getlocked_ = true; // not sure if IE explicitly pre-detects loops, so let's lock recurion here, too.
// boolean r = ((IFluidPipe)te).canOutputPressurized(consumePower);
// getlocked_ = false;
// return r;
// }
@Override
public void onLoad()
{
if(!hasWorld()) return;
final BlockState state = world.getBlockState(pos);
if((!(state.getBlock() instanceof BlockDecorPipeValve))) return;
block_reconfigure(state.get(FACING), block_config_);
world.notifyNeighborsOfStateChange(pos, state.getBlock());
}
@Override
public void read(CompoundNBT nbt)
{
super.read(nbt);
int i = nbt.getInt("facing");
if((i>=0) || (i<6)) block_facing_ = Direction.byIndex(i);
block_config_ = nbt.getLong("conf");
}
@Override
public CompoundNBT write(CompoundNBT nbt)
{
super.write(nbt);
nbt.putInt("facing", block_facing_.getIndex());
nbt.putLong("conf", block_config_);
return nbt;
}
// ICapabilityProvider --------------------------------------------------------------------
private LazyOptional<IFluidHandler> back_flow_handler_ = LazyOptional.of(() -> (IFluidHandler)new BackFlowHandler());
private LazyOptional<IFluidHandler> fluid_handler_ = LazyOptional.of(() -> (IFluidHandler)new MainFlowHandler(this));
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(!this.removed && (facing != null)) {
if(capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
if(facing == block_facing_) return fluid_handler_.cast();
if(facing == block_facing_.getOpposite()) return back_flow_handler_.cast();
}
}
return super.getCapability(capability, facing);
}
// IFluidHandlers
@Nullable
private IFluidHandler forward_fluid_handler()
{
final TileEntity te = world.getTileEntity(pos.offset(block_facing_));
if(te == null) return null;
return te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, block_facing_.getOpposite()).orElse(null);
}
// Forward flow handler --
private static class MainFlowHandler implements IFluidHandler
{
private BTileEntity te;
public MainFlowHandler(BTileEntity te) { this.te = te; }
@Override public int getTanks() { return 0; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return fluid_maxflow_mb; }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY.copy(); }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY.copy(); }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return true; }
@Override public int fill(FluidStack resource, FluidAction action)
{
if((te.filling_) || (!te.filling_enabled_)) return 0;
if((te.block_config_ & CFG_REDSTONE_CONTROLLED) != 0) {
int rs = te.world.getRedstonePowerFromNeighbors(te.pos);
if(rs <= 0) return 0;
if(((te.block_config_ & CFG_ANALOG) != 0) && (rs < 15)) resource.setAmount(MathHelper.clamp(rs * redstone_flow_slope_mb, 1, resource.getAmount()));
}
FluidStack res = resource.copy();
if(res.getAmount() > fluid_maxflow_mb) res.setAmount(fluid_maxflow_mb);
final IFluidHandler fh = te.forward_fluid_handler();
if(fh==null) return 0;
te.filling_ = true; // in case someone does not check the cap, but just "forwards back" what is beeing filled right now.
//if(res.amount > 50) {
//final TileEntity te = te.world.getTileEntity(te.pos.offset(te.block_facing_));
//if(te instanceof IFluidPipe) {
// // forward pressureized tag
// if(res.tag == null) res.tag = new CompoundNBT();
// res.tag.putBoolean("pressurized", true);
//}
//}
int n_filled = fh.fill(res, action);
te.filling_ = false;
return n_filled;
}
}
// Back flow prevention handler --
private static class BackFlowHandler implements IFluidHandler
{
@Override public int getTanks() { return 0; }
@Override public FluidStack getFluidInTank(int tank) { return FluidStack.EMPTY; }
@Override public int getTankCapacity(int tank) { return 0; }
@Override public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { return false; }
@Override public int fill(FluidStack resource, FluidAction action) { return 0; }
@Override public FluidStack drain(FluidStack resource, FluidAction action) { return FluidStack.EMPTY.copy(); }
@Override public FluidStack drain(int maxDrain, FluidAction action) { return FluidStack.EMPTY.copy(); }
}
// IFluidPipe
// @Override
// public boolean hasOutputConnection(Direction side)
// { return (side == block_facing_); }
//
// @Override
// public boolean canOutputPressurized(boolean consumePower)
// {
// if(getlocked_ || (!filling_enabled_)) return false;
// final TileEntity te = world.getTileEntity(pos.offset(block_facing_));
// if(!(te instanceof IFluidPipe)) return false;
// getlocked_ = true; // not sure if IE explicitly pre-detects loops, so let's lock recurion here, too.
// boolean r = ((IFluidPipe)te).canOutputPressurized(consumePower);
// getlocked_ = false;
// return r;
// }
}
}

View file

@ -0,0 +1,747 @@
/*
* @file BlockDecorPlacer.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Block placer and planter, factory automation suitable.
*/
package wile.engineersdecor.blocks;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import wile.engineersdecor.detail.Networking;
import net.minecraft.block.*;
import net.minecraft.state.StateContainer;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.item.*;
import net.minecraft.inventory.*;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.Slot;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.util.*;
import net.minecraft.util.math.*;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.SoundEvents;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.wrapper.SidedInvWrapper;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.fml.network.NetworkHooks;
import com.mojang.blaze3d.platform.GlStateManager;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class BlockDecorPlacer extends BlockDecorDirected
{
public BlockDecorPlacer(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
@OnlyIn(Dist.CLIENT)
public BlockRenderLayer getRenderLayer()
{ return BlockRenderLayer.SOLID; }
@Override
public VoxelShape getCollisionShape(BlockState state, IBlockReader world, BlockPos pos, ISelectionContext selectionContext)
{ return VoxelShapes.fullCube(); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context); }
@Override
@SuppressWarnings("deprecation")
public boolean hasComparatorInputOverride(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getComparatorInputOverride(BlockState blockState, World world, BlockPos pos)
{ return Container.calcRedstone(world.getTileEntity(pos)); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BTileEntity(); }
@Override
public void onBlockPlacedBy(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack stack)
{
if(world.isRemote) return;
if((!stack.hasTag()) || (!stack.getTag().contains("tedata"))) return;
CompoundNBT te_nbt = stack.getTag().getCompound("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).reset_rtstate();
((BTileEntity)te).markDirty();
}
@Override
public boolean hasDynamicDropList()
{ return true; }
@Override
public List<ItemStack> dropList(BlockState state, World world, BlockPos pos, boolean explosion)
{
final List<ItemStack> stacks = new ArrayList<ItemStack>();
if(world.isRemote) return stacks;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return stacks;
if(!explosion) {
ItemStack stack = new ItemStack(this, 1);
CompoundNBT te_nbt = ((BTileEntity) te).clear_getnbt();
if(!te_nbt.isEmpty()) {
CompoundNBT nbt = new CompoundNBT();
nbt.put("tedata", te_nbt);
stack.setTag(nbt);
}
stacks.add(stack);
} else {
for(ItemStack stack: ((BTileEntity)te).stacks_) {
if(!stack.isEmpty()) stacks.add(stack);
}
((BTileEntity)te).reset_rtstate();
}
return stacks;
}
@Override
@SuppressWarnings("deprecation")
public boolean onBlockActivated(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult rayTraceResult)
{
if(world.isRemote) return true;
final TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return true;
if((!(player instanceof ServerPlayerEntity) && (!(player instanceof FakePlayer)))) return true;
NetworkHooks.openGui((ServerPlayerEntity)player,(INamedContainerProvider)te);
return true;
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, World world, BlockPos pos, Block block, BlockPos fromPos, boolean unused)
{
if(!(world instanceof World) || (((World) world).isRemote)) return;
TileEntity te = world.getTileEntity(pos);
if(!(te instanceof BTileEntity)) return;
((BTileEntity)te).block_updated();
}
@Override
@SuppressWarnings("deprecation")
public boolean canProvidePower(BlockState state)
{ return true; }
@Override
@SuppressWarnings("deprecation")
public int getWeakPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
@Override
@SuppressWarnings("deprecation")
public int getStrongPower(BlockState blockState, IBlockReader blockAccess, BlockPos pos, Direction side)
{ return 0; }
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements ITickableTileEntity, INameable, IInventory, INamedContainerProvider, ISidedInventory
{
public static final int TICK_INTERVAL = 40;
public static final int NUM_OF_SLOTS = 18;
public static final int NUM_OF_FIELDS = 3;
///
public static final int LOGIC_INVERTED = 0x01;
public static final int LOGIC_CONTINUOUS = 0x02;
///
private boolean block_power_signal_ = false;
private boolean block_power_updated_ = false;
private int logic_ = LOGIC_INVERTED|LOGIC_CONTINUOUS;
private int current_slot_index_ = 0;
private int tick_timer_ = 0;
protected NonNullList<ItemStack> stacks_;
public static void on_config(int cooldown_ticks)
{
// ModEngineersDecor.logger.info("Config factory placer:");
}
public BTileEntity()
{ this(ModContent.TET_FACTORY_PLACER); }
public BTileEntity(TileEntityType<?> te_type)
{
super(te_type);
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
reset_rtstate();
}
public CompoundNBT clear_getnbt()
{
CompoundNBT nbt = new CompoundNBT();
writenbt(nbt, false);
for(int i=0; i<stacks_.size(); ++i) stacks_.set(i, ItemStack.EMPTY);
reset_rtstate();
block_power_updated_ = false;
return nbt;
}
public void reset_rtstate()
{
block_power_signal_ = false;
block_power_updated_ = false;
}
public void readnbt(CompoundNBT nbt, boolean update_packet)
{
stacks_ = NonNullList.<ItemStack>withSize(NUM_OF_SLOTS, ItemStack.EMPTY);
ItemStackHelper.loadAllItems(nbt, stacks_);
while(stacks_.size() < NUM_OF_SLOTS) stacks_.add(ItemStack.EMPTY);
block_power_signal_ = nbt.getBoolean("powered");
current_slot_index_ = nbt.getInt("act_slot_index");
logic_ = nbt.getInt("logic");
}
protected void writenbt(CompoundNBT nbt, boolean update_packet)
{
ItemStackHelper.saveAllItems(nbt, stacks_);
nbt.putBoolean("powered", block_power_signal_);
nbt.putInt("act_slot_index", current_slot_index_);
nbt.putInt("logic", logic_);
}
public void block_updated()
{
boolean powered = world.isBlockPowered(pos);
if(block_power_signal_ != powered) block_power_updated_ = true;
block_power_signal_ = powered;
if(block_power_updated_) {
tick_timer_ = 1;
} else if(tick_timer_ > 4) {
tick_timer_ = 4;
}
}
public boolean is_input_slot(int index)
{ return (index >= 0) && (index < NUM_OF_SLOTS); }
// TileEntity ------------------------------------------------------------------------------
@Override
public void read(CompoundNBT nbt)
{ super.read(nbt); readnbt(nbt, false); }
@Override
public CompoundNBT write(CompoundNBT nbt)
{ super.write(nbt); writenbt(nbt, false); return nbt; }
// INamable ----------------------------------------------------------------------------------------------
@Override
public ITextComponent getName()
{ final Block block=getBlockState().getBlock(); return new StringTextComponent((block!=null) ? block.getTranslationKey() : "Factory placer"); }
@Override
public boolean hasCustomName()
{ return false; }
@Override
public ITextComponent getCustomName()
{ return getName(); }
// INamedContainerProvider ------------------------------------------------------------------------------
@Override
public ITextComponent getDisplayName()
{ return INameable.super.getDisplayName(); }
@Override
public Container createMenu(int id, PlayerInventory inventory, PlayerEntity player )
{ return new BContainer(id, inventory, this, IWorldPosCallable.of(world, pos), fields); }
// IInventory -------------------------------------------------------------------------------------------
@Override
public int getSizeInventory()
{ return stacks_.size(); }
@Override
public boolean isEmpty()
{ for(ItemStack stack: stacks_) { if(!stack.isEmpty()) return false; } return true; }
@Override
public ItemStack getStackInSlot(int index)
{ return (index < getSizeInventory()) ? stacks_.get(index) : ItemStack.EMPTY; }
@Override
public ItemStack decrStackSize(int index, int count)
{ return ItemStackHelper.getAndSplit(stacks_, index, count); }
@Override
public ItemStack removeStackFromSlot(int index)
{ return ItemStackHelper.getAndRemove(stacks_, index); }
@Override
public void setInventorySlotContents(int index, ItemStack stack)
{
if((index<0) || (index >= NUM_OF_SLOTS)) return;
stacks_.set(index, stack);
if(stack.getCount() > getInventoryStackLimit()) stack.setCount(getInventoryStackLimit());
if(tick_timer_ > 8) tick_timer_ = 8;
markDirty();
}
@Override
public int getInventoryStackLimit()
{ return 64; }
@Override
public void markDirty()
{ super.markDirty(); }
@Override
public boolean isUsableByPlayer(PlayerEntity player)
{ return getPos().distanceSq(player.getPosition()) < 36; }
@Override
public void openInventory(PlayerEntity player)
{}
@Override
public void closeInventory(PlayerEntity player)
{ markDirty(); }
@Override
public boolean isItemValidForSlot(int index, ItemStack stack)
{ return (index>=0) && (index<NUM_OF_SLOTS); }
@Override
public void clear()
{ for(int i=0; i<stacks_.size(); ++i) stacks_.set(i, ItemStack.EMPTY); } // should search a better vectorizing method here.
// Fields -----------------------------------------------------------------------------------------------
protected final IIntArray fields = new IntArray(BTileEntity.NUM_OF_FIELDS)
{
@Override
public int get(int id)
{
switch(id) {
case 0: return logic_;
case 1: return block_power_signal_ ? 1 : 0;
case 2: return MathHelper.clamp(current_slot_index_, 0, NUM_OF_SLOTS-1);
default: return 0;
}
}
@Override
public void set(int id, int value)
{
switch(id) {
case 0: logic_ = value; return;
case 1: block_power_signal_ = (value != 0); return;
case 2: current_slot_index_ = MathHelper.clamp(value, 0, NUM_OF_SLOTS-1); return;
default: return;
}
}
};
// ISidedInventory --------------------------------------------------------------------------------------
LazyOptional<? extends IItemHandler>[] item_handlers = SidedInvWrapper.create(this, Direction.UP);
private static final int[] SIDED_INV_SLOTS;
static {
SIDED_INV_SLOTS = new int[NUM_OF_SLOTS];
for(int i=0; i<NUM_OF_SLOTS; ++i) SIDED_INV_SLOTS[i] = i;
}
@Override
public int[] getSlotsForFace(Direction side)
{ return SIDED_INV_SLOTS; }
@Override
public boolean canInsertItem(int index, ItemStack stack, Direction direction)
{ return is_input_slot(index) && isItemValidForSlot(index, stack); }
@Override
public boolean canExtractItem(int index, ItemStack stack, Direction direction)
{ return false; }
// Capability export ------------------------------------------------------------------------------------
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(!this.removed && (facing != null)) {
if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return item_handlers[0].cast();
}
return super.getCapability(capability, facing);
}
// ITickable and aux methods ----------------------------------------------------------------------------
private static int next_slot(int i)
{ return (i<NUM_OF_SLOTS-1) ? (i+1) : 0; }
private boolean spit_out(Direction facing)
{
ItemStack stack = stacks_.get(current_slot_index_);
ItemStack drop = stack.copy();
stack.shrink(1);
stacks_.set(current_slot_index_, stack);
drop.setCount(1);
for(int i=0; i<8; ++i) {
BlockPos p = pos.offset(facing, i);
if(!world.isAirBlock(p)) continue;
world.addEntity(new ItemEntity(world, (p.getX()+0.5), (p.getY()+0.5), (p.getZ()+0.5), drop));
world.playSound(null, p, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 0.7f, 0.8f);
break;
}
return true;
}
private boolean try_place(Direction facing)
{
if(world.isRemote) return false;
BlockPos placement_pos = pos.offset(facing);
if(world.getTileEntity(placement_pos) != null) return false;
ItemStack current_stack = ItemStack.EMPTY;
for(int i=0; i<NUM_OF_SLOTS; ++i) {
if(current_slot_index_ >= NUM_OF_SLOTS) current_slot_index_ = 0;
current_stack = stacks_.get(current_slot_index_);
if(!current_stack.isEmpty()) break;
current_slot_index_ = next_slot(current_slot_index_);
}
if(current_stack.isEmpty()) { current_slot_index_ = 0; return false; }
boolean no_space = false;
final Item item = current_stack.getItem();
Block block = (item instanceof IPlantable) ? (((IPlantable)item).getPlant(world, pos).getBlock()) : Block.getBlockFromItem(item);
if(block == Blocks.AIR) {
if(item != null) {
return spit_out(facing); // Item not accepted
} else {
// try next slot
}
} else if(block instanceof IPlantable) {
if(world.isAirBlock(placement_pos)) {
// plant here, block below has to be valid soil.
BlockState soilstate = world.getBlockState(placement_pos.down());
if(!soilstate.getBlock().canSustainPlant(soilstate, world, pos, Direction.UP, (IPlantable)block)) {
block = Blocks.AIR;
}
} else {
// adjacent block is the soil, plant above if the soil is valid.
BlockState soilstate = world.getBlockState(placement_pos);
if(soilstate.getBlock() == block) {
// The plant is already planted from the case above.
block = Blocks.AIR;
no_space = true;
} else if(!world.isAirBlock(placement_pos.up())) {
// If this is the soil an air block is needed above, if that is blocked we can't plant.
block = Blocks.AIR;
no_space = true;
} else if(!soilstate.getBlock().canSustainPlant(soilstate, world, pos, Direction.UP, (IPlantable)block)) {
// Would be space above, but it's not the right soil for the plant.
block = Blocks.AIR;
} else {
// Ok, plant above.
placement_pos = placement_pos.up();
}
}
} else if(!world.getBlockState(placement_pos).getMaterial().isReplaceable()) {
block = Blocks.AIR;
no_space = true;
}
// System.out.println("PLACE " + current_stack + " --> " + block + " at " + placement_pos.subtract(pos) + "( item=" + item + ")");
if(block != Blocks.AIR) {
try {
BlockItemUseContext use_context = null;
{
final FakePlayer placer = net.minecraftforge.common.util.FakePlayerFactory.getMinecraft((ServerWorld)world);
if(placer != null) {
ItemStack placement_stack = current_stack.copy();
placement_stack.setCount(1);
ItemStack held = placer.getHeldItem(Hand.MAIN_HAND);
placer.setHeldItem(Hand.MAIN_HAND, placement_stack);
use_context = new BlockItemUseContext(new ItemUseContext(placer, Hand.MAIN_HAND, new BlockRayTraceResult(new Vec3d(0.5,0,0.5), Direction.DOWN, placement_pos, false)));
placer.setHeldItem(Hand.MAIN_HAND, held);
}
}
final BlockState placement_state = (use_context==null) ? (block.getDefaultState()) : (block.getStateForPlacement(use_context));
if(placement_state == null) {
return spit_out(facing);
} else if(item instanceof BlockItem) {
if(((BlockItem)item).tryPlace(use_context) == ActionResultType.SUCCESS) {
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
} else {
return spit_out(facing);
}
} else {
if(world.setBlockState(placement_pos, placement_state, 1|2|8)) {
SoundType stype = block.getSoundType(placement_state, world, pos, null);
if(stype != null) world.playSound(null, placement_pos, stype.getPlaceSound(), SoundCategory.BLOCKS, stype.getVolume()*0.6f, stype.getPitch());
}
}
current_stack.shrink(1);
stacks_.set(current_slot_index_, current_stack);
return true;
} catch(Throwable e) {
// The block really needs a player or other issues happened during placement.
// A hard crash should not be fired here, instead spit out the item to indicated that this
// block is not compatible.
System.out.println("Exception while trying to place " + e);
world.removeBlock(placement_pos, false);
return spit_out(facing);
}
}
if((!no_space) && (!current_stack.isEmpty())) {
// There is space, but the current plant cannot be planted there, so try next.
for(int i=0; i<NUM_OF_SLOTS; ++i) {
current_slot_index_ = next_slot(current_slot_index_);
if(!stacks_.get(current_slot_index_).isEmpty()) break;
}
}
return false;
}
@Override
public void tick()
{
// Tick cycle pre-conditions
if(world.isRemote) return;
if(--tick_timer_ > 0) return;
tick_timer_ = TICK_INTERVAL;
// Cycle init
boolean dirty = block_power_updated_;
boolean rssignal = ((logic_ & LOGIC_INVERTED)!=0)==(!block_power_signal_);
boolean trigger = (rssignal && ((block_power_updated_) || ((logic_ & LOGIC_CONTINUOUS)!=0)));
final BlockState state = world.getBlockState(pos);
if(state == null) { block_power_signal_= false; return; }
final Direction placer_facing = state.get(FACING);
// Trigger edge detection for next cycle
{
boolean tr = world.isBlockPowered(pos);
block_power_updated_ = (block_power_signal_ != tr);
block_power_signal_ = tr;
if(block_power_updated_) dirty = true;
}
// Placing
if(trigger && try_place(placer_facing)) dirty = true;
if(dirty) markDirty();
if(trigger && (tick_timer_ > TICK_INTERVAL)) tick_timer_ = TICK_INTERVAL;
}
}
//--------------------------------------------------------------------------------------------------------------------
// container
//--------------------------------------------------------------------------------------------------------------------
public static class BContainer extends Container implements Networking.INetworkSynchronisableContainer
{
private static final int PLAYER_INV_START_SLOTNO = BTileEntity.NUM_OF_SLOTS;
private final PlayerEntity player_;
private final IInventory inventory_;
private final IWorldPosCallable wpc_;
private final IIntArray fields_;
public final int field(int index) { return fields_.get(index); }
public BContainer(int cid, PlayerInventory player_inventory)
{ this(cid, player_inventory, new Inventory(BTileEntity.NUM_OF_SLOTS), IWorldPosCallable.DUMMY, new IntArray(BTileEntity.NUM_OF_FIELDS)); }
private BContainer(int cid, PlayerInventory player_inventory, IInventory block_inventory, IWorldPosCallable wpc, IIntArray fields)
{
super(ModContent.CT_FACTORY_PLACER, cid);
fields_ = fields;
wpc_ = wpc;
player_ = player_inventory.player;
inventory_ = block_inventory;
int i=-1;
// device slots (stacks 0 to 17)
for(int y=0; y<3; ++y) {
for(int x=0; x<6; ++x) {
int xpos = 11+x*18, ypos = 9+y*17;
addSlot(new Slot(inventory_, ++i, xpos, ypos));
}
}
// player slots
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x, 8+x*18, 129)); // player slots: 0..8
}
for(int y=0; y<3; ++y) {
for(int x=0; x<9; ++x) {
addSlot(new Slot(player_inventory, x+y*9+9, 8+x*18, 71+y*18)); // player slots: 9..35
}
}
this.trackIntArray(fields_); // === Add reference holders
}
@Override
public boolean canInteractWith(PlayerEntity player)
{ return inventory_.isUsableByPlayer(player); }
@Override
public ItemStack transferStackInSlot(PlayerEntity player, int index)
{
Slot slot = getSlot(index);
if((slot==null) || (!slot.getHasStack())) return ItemStack.EMPTY;
ItemStack slot_stack = slot.getStack();
ItemStack transferred = slot_stack.copy();
if((index>=0) && (index<PLAYER_INV_START_SLOTNO)) {
// Device slots
if(!mergeItemStack(slot_stack, PLAYER_INV_START_SLOTNO, PLAYER_INV_START_SLOTNO+36, false)) return ItemStack.EMPTY;
} else if((index >= PLAYER_INV_START_SLOTNO) && (index <= PLAYER_INV_START_SLOTNO+36)) {
// Player slot
if(!mergeItemStack(slot_stack, 0, BTileEntity.NUM_OF_SLOTS, false)) return ItemStack.EMPTY;
} else {
// invalid slot
return ItemStack.EMPTY;
}
if(slot_stack.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
if(slot_stack.getCount() == transferred.getCount()) return ItemStack.EMPTY;
slot.onTake(player, slot_stack);
return transferred;
}
// INetworkSynchronisableContainer ---------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public void onGuiAction(CompoundNBT nbt)
{ Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt); }
@OnlyIn(Dist.CLIENT)
public void onGuiAction(String key, int value)
{
CompoundNBT nbt = new CompoundNBT();
nbt.putInt(key, value);
Networking.PacketContainerSyncClientToServer.sendToServer(windowId, nbt);
}
@Override
public void onServerPacketReceived(int windowId, CompoundNBT nbt)
{}
@Override
public void onClientPacketReceived(int windowId, PlayerEntity player, CompoundNBT nbt)
{
if(!(inventory_ instanceof BTileEntity)) return;
BTileEntity te = (BTileEntity)inventory_;
if(nbt.contains("logic")) te.logic_ = nbt.getInt("logic");
if(nbt.contains("manual_trigger") && (nbt.getInt("manual_trigger")!=0)) { te.block_power_signal_=true; te.block_power_updated_=true; te.tick_timer_=1; }
te.markDirty();
}
}
//--------------------------------------------------------------------------------------------------------------------
// GUI
//--------------------------------------------------------------------------------------------------------------------
@OnlyIn(Dist.CLIENT)
public static class BGui extends ContainerScreen<BContainer>
{
protected final PlayerEntity player_;
public BGui(BContainer container, PlayerInventory player_inventory, ITextComponent title)
{ super(container, player_inventory, title); this.player_ = player_inventory.player; }
@Override
public void init()
{ super.init(); }
@Override
public void render(int mouseX, int mouseY, float partialTicks)
{
renderBackground();
super.render(mouseX, mouseY, partialTicks);
renderHoveredToolTip(mouseX, mouseY);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int mouseButton)
{
BContainer container = (BContainer)getContainer();
int mx = (int)(mouseX - getGuiLeft() + .5), my = (int)(mouseY - getGuiTop() + .5);
if((!isPointInRegion(126, 1, 49, 60, mouseX, mouseY))) {
return super.mouseClicked(mouseX, mouseY, mouseButton);
} else if(isPointInRegion(133, 49, 9, 9, mouseX, mouseY)) {
container.onGuiAction("manual_trigger", 1);
} else if(isPointInRegion(145, 49, 9, 9, mouseX, mouseY)) {
container.onGuiAction("logic", container.field(0) ^ BTileEntity.LOGIC_INVERTED);
} else if(isPointInRegion(159, 49, 7, 9, mouseX, mouseY)) {
container.onGuiAction("logic", container.field(0) ^ BTileEntity.LOGIC_CONTINUOUS);
}
return true;
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
{
GlStateManager.color4f(1f, 1f, 1f, 1f);
this.minecraft.getTextureManager().bindTexture(new ResourceLocation(ModEngineersDecor.MODID, "textures/gui/factory_placer_gui.png"));
final int x0=getGuiLeft(), y0=getGuiTop(), w=getXSize(), h=getYSize();
blit(x0, y0, 0, 0, w, h);
BContainer container = (BContainer)getContainer();
// active slot
{
int slot_index = container.field(2);
if((slot_index < 0) || (slot_index >= BTileEntity.NUM_OF_SLOTS)) slot_index = 0;
int x = (x0+10+((slot_index % 6) * 18));
int y = (y0+8+((slot_index / 6) * 17));
blit(x, y, 200, 8, 18, 18);
}
// redstone input
{
if(container.field(1) != 0) {
blit(x0+133, y0+49, 217, 49, 9, 9);
}
}
// trigger logic
{
int inverter_offset = ((container.field(0) & BTileEntity.LOGIC_INVERTED) != 0) ? 11 : 0;
blit(x0+145, y0+49, 177+inverter_offset, 49, 9, 9);
int pulse_mode_offset = ((container.field(0) & BTileEntity.LOGIC_CONTINUOUS ) != 0) ? 9 : 0;
blit(x0+159, y0+49, 199+pulse_mode_offset, 49, 9, 9);
}
}
}
}

View file

@ -0,0 +1,194 @@
/*
* @file BlockDecorTreeCutter.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Small Tree Cutter
*/
package wile.engineersdecor.blocks;
import net.minecraft.item.BlockItemUseContext;
import wile.engineersdecor.ModContent;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import wile.engineersdecor.detail.TreeCutting;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockDecorTreeCutter extends BlockDecorDirectedHorizontal
{
public static final BooleanProperty ACTIVE = BooleanProperty.create("active");
public BlockDecorTreeCutter(long config, Block.Properties builder, final AxisAlignedBB unrotatedAABB)
{ super(config, builder, unrotatedAABB); }
@Override
protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder)
{ super.fillStateContainer(builder); builder.add(ACTIVE); }
@Override
@Nullable
public BlockState getStateForPlacement(BlockItemUseContext context)
{ return super.getStateForPlacement(context).with(ACTIVE, false); }
@Override
public boolean hasTileEntity(BlockState state)
{ return true; }
@Override
@Nullable
public TileEntity createTileEntity(BlockState state, IBlockReader world)
{ return new BTileEntity(); }
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, World world, BlockPos pos, Random rnd)
{
if((state.getBlock()!=this) || (!state.get(ACTIVE))) return;
final double rv = rnd.nextDouble();
if(rv > 0.8) return;
final double x=0.5+pos.getX(), y=0.5+pos.getY(), z=0.5+pos.getZ();
final double xc=0.52, xr=rnd.nextDouble()*0.4-0.2, yr=(y-0.3+rnd.nextDouble()*0.2);
switch(state.get(HORIZONTAL_FACING)) {
case WEST: world.addParticle(ParticleTypes.SMOKE, x-xc, yr, z+xr, 0.0, 0.0, 0.0); break;
case EAST: world.addParticle(ParticleTypes.SMOKE, x+xc, yr, z+xr, 0.0, 0.0, 0.0); break;
case NORTH: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z-xc, 0.0, 0.0, 0.0); break;
default: world.addParticle(ParticleTypes.SMOKE, x+xr, yr, z+xc, 0.0, 0.0, 0.0); break;
}
}
//--------------------------------------------------------------------------------------------------------------------
// Tile entity
//--------------------------------------------------------------------------------------------------------------------
public static class BTileEntity extends TileEntity implements ITickableTileEntity, IEnergyStorage
{
public static final int IDLE_TICK_INTERVAL = 40;
public static final int TICK_INTERVAL = 5;
public static final int BOOST_FACTOR = 6;
public static final int DEFAULT_BOOST_ENERGY = 64;
public static final int DEFAULT_CUTTING_TIME_NEEDED = 20 * 60; // 60 secs, so that people don't come to the bright idea to carry one with them.
private static int boost_energy_consumption = DEFAULT_BOOST_ENERGY;
private static int cutting_time_needed = DEFAULT_CUTTING_TIME_NEEDED;
private int tick_timer_;
private int proc_time_elapsed_; // small, not saved in nbt.
private int boost_energy_; // small, not saved in nbt.
public static void on_config(int boost_energy_per_tick)
{
boost_energy_consumption = TICK_INTERVAL * MathHelper.clamp(boost_energy_per_tick, 16, 512);
ModEngineersDecor.logger().info("Config tree cutter: Boost energy consumption:" + boost_energy_consumption + "rf/t");
}
public BTileEntity()
{ super(ModContent.TET_SMALL_TREE_CUTTER); }
public BTileEntity(TileEntityType<?> te_type)
{ super(te_type); }
// IEnergyStorage ----------------------------------------------------------------------------
protected LazyOptional<IEnergyStorage> energy_handler_ = LazyOptional.of(() -> (IEnergyStorage)this);
@Override
public boolean canExtract()
{ return false; }
@Override
public boolean canReceive()
{ return true; }
@Override
public int getMaxEnergyStored()
{ return boost_energy_consumption; }
@Override
public int getEnergyStored()
{ return boost_energy_; }
@Override
public int extractEnergy(int maxExtract, boolean simulate)
{ return 0; }
@Override
public int receiveEnergy(int maxReceive, boolean simulate)
{ // only speedup support, no buffering, not in nbt -> no markdirty
if((boost_energy_ >= boost_energy_consumption) || (maxReceive < boost_energy_consumption)) return 0;
if(!simulate) boost_energy_ = boost_energy_consumption;
return boost_energy_consumption;
}
// Capability export ----------------------------------------------------------------------------
@Override
public <T> LazyOptional<T> getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable Direction facing)
{
if(!this.removed && (facing != null)) {
if(capability== CapabilityEnergy.ENERGY) {
return energy_handler_.cast();
}
}
return super.getCapability(capability, facing);
}
// ITickable ------------------------------------------------------------------------------------
@Override
public void tick()
{
if(--tick_timer_ > 0) return;
if(world.isRemote) {
if(!world.getBlockState(pos).get(ACTIVE)) {
tick_timer_ = TICK_INTERVAL;
} else {
tick_timer_ = 1;
world.playSound(pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_WOOD_HIT, SoundCategory.BLOCKS, 0.1f, 1.0f, false);
}
} else {
tick_timer_ = TICK_INTERVAL;
final BlockState device_state = world.getBlockState(pos);
final BlockPos tree_pos = pos.offset(device_state.get(HORIZONTAL_FACING));
final BlockState tree_state = world.getBlockState(tree_pos);
if(!TreeCutting.canChop(tree_state) || (world.isBlockPowered(pos))) {
if(device_state.get(ACTIVE)) world.setBlockState(pos, device_state.with(ACTIVE, false), 1|2);
proc_time_elapsed_ = 0;
tick_timer_ = IDLE_TICK_INTERVAL;
return;
}
proc_time_elapsed_ += TICK_INTERVAL;
if(boost_energy_ >= boost_energy_consumption) { boost_energy_ = 0; proc_time_elapsed_ += TICK_INTERVAL*BOOST_FACTOR; }
boolean active = true;
if(proc_time_elapsed_ >= cutting_time_needed) {
proc_time_elapsed_ = 0;
TreeCutting.chopTree(world, tree_state, tree_pos, 512, false);
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_WOOD_BREAK, SoundCategory.BLOCKS, 1.0f, 1.0f);
active = false;
}
if(device_state.get(ACTIVE) != active) {
world.setBlockState(pos, device_state.with(ACTIVE, active), 1|2);
}
}
}
}
}

View file

@ -109,6 +109,8 @@ public class ModConfig
public final ForgeConfigSpec.BooleanValue without_waste_incinerator;
public final ForgeConfigSpec.BooleanValue without_sign_plates;
public final ForgeConfigSpec.BooleanValue without_factory_dropper;
public final ForgeConfigSpec.BooleanValue without_factory_placer;
public final ForgeConfigSpec.BooleanValue without_factory_breaker;
public final ForgeConfigSpec.BooleanValue without_slabs;
public final ForgeConfigSpec.BooleanValue without_halfslabs;
public final ForgeConfigSpec.BooleanValue without_direct_slab_pickup;
@ -247,6 +249,14 @@ public class ModConfig
.translation(ModEngineersDecor.MODID + ".config.without_factory_dropper")
.comment("Disable the factory dropper.")
.define("without_factory_dropper", false);
without_factory_placer = builder
.translation(ModEngineersDecor.MODID + ".config.without_factory_placer")
.comment("Disable the factory placer.")
.define("without_factory_placer", false);
without_factory_breaker = builder
.translation(ModEngineersDecor.MODID + ".config.without_factory_breaker")
.comment("Disable the small block breaker.")
.define("without_factory_breaker", false);
without_slabs = builder
.translation(ModEngineersDecor.MODID + ".config.without_slabs")
.comment("Disable horizontal half-block slab.")
@ -419,6 +429,8 @@ public class ModConfig
if(block instanceof BlockDecorPassiveFluidAccumulator) return COMMON.without_passive_fluid_accumulator.get();
if(block instanceof BlockDecorWasteIncinerator) return COMMON.without_waste_incinerator.get();
if(block instanceof BlockDecorDropper) return COMMON.without_factory_dropper.get();
if(block instanceof BlockDecorPlacer) return COMMON.without_factory_placer.get();
if(block instanceof BlockDecorBreaker) return COMMON.without_factory_breaker.get();
if(block instanceof BlockDecorHalfSlab) return COMMON.without_halfslabs.get();
if(block instanceof BlockDecorLadder) return COMMON.without_ladders.get();
if(block instanceof BlockDecorWindow) return COMMON.without_windows.get();

View file

@ -0,0 +1,176 @@
/*
* @file TreeCutting.java
* @author Stefan Wilhelm (wile)
* @copyright (C) 2019 Stefan Wilhelm
* @license MIT (see https://opensource.org/licenses/MIT)
*
* Simple tree cutting algorithm.
*/
package wile.engineersdecor.detail;
import net.minecraft.block.*;
import net.minecraft.util.ResourceLocation;
import wile.engineersdecor.ModEngineersDecor;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import com.google.common.collect.ImmutableList;
import java.util.*;
public class TreeCutting
{
private static org.apache.logging.log4j.Logger LOGGER = ModEngineersDecor.logger();
public static boolean canChop(BlockState state)
{ return isLog(state); }
// -------------------------------------------------------------------------------------------------------------------
private static final List<Vec3i> hoffsets = ImmutableList.of(
new Vec3i( 1,0, 0), new Vec3i( 1,0, 1), new Vec3i( 0,0, 1),
new Vec3i(-1,0, 1), new Vec3i(-1,0, 0), new Vec3i(-1,0,-1),
new Vec3i( 0,0,-1), new Vec3i( 1,0,-1)
);
private static boolean isLog(BlockState state)
{ return (state.getBlock() instanceof LogBlock) || (state.getBlock().getTags().contains(new ResourceLocation("minecraft","logs"))); }
private static boolean isSameLog(BlockState a, BlockState b)
{ return (a.getBlock()==b.getBlock()); }
private static boolean isLeaves(BlockState state)
{
if(state.getBlock() instanceof LeavesBlock) return true;
if(state.getBlock().getTags().contains(new ResourceLocation("minecraft","leaves"))) return true;
return false;
}
private static List<BlockPos> findBlocksAround(final World world, final BlockPos centerPos, final BlockState leaf_type_state, final Set<BlockPos> checked, int recursion_left)
{
ArrayList<BlockPos> to_decay = new ArrayList<BlockPos>();
for(int y=-1; y<=1; ++y) {
final BlockPos layer = centerPos.add(0,y,0);
for(Vec3i v:hoffsets) {
BlockPos pos = layer.add(v);
if((!checked.contains(pos)) && (world.getBlockState(pos).getBlock()==leaf_type_state.getBlock())) {
checked.add(pos);
to_decay.add(pos);
if(recursion_left > 0) {
to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, recursion_left-1));
}
}
}
}
return to_decay;
}
private static void breakBlock(World world, BlockPos pos)
{
Block.spawnDrops(world.getBlockState(pos), world, pos);
if(world.setBlockState(pos, world.getFluidState(pos).getBlockState(), 1|2|8)) {} //state.getBlock().onPlayerDestroy(world, pos, state);
}
public static int chopTree(World world, BlockState broken_state, BlockPos startPos, int max_blocks_to_break, boolean without_target_block)
{
if(world.isRemote || !isLog(broken_state)) return 0;
final long ymin = startPos.getY();
final long max_leaf_distance = 6;
Set<BlockPos> checked = new HashSet<BlockPos>();
ArrayList<BlockPos> to_break = new ArrayList<BlockPos>();
ArrayList<BlockPos> to_decay = new ArrayList<BlockPos>();
checked.add(startPos);
// Initial simple layer-up search of same logs. This forms the base corpus, and only leaves and
// leaf-enclosed logs attached to this corpus may be broken/decayed.
{
LinkedList<BlockPos> queue = new LinkedList<BlockPos>();
LinkedList<BlockPos> upqueue = new LinkedList<BlockPos>();
queue.add(startPos);
int cutlevel = 0;
int steps_left = 64;
while(!queue.isEmpty() && (--steps_left >= 0)) {
final BlockPos pos = queue.removeFirst();
// Vertical search
final BlockPos uppos = pos.up();
final BlockState upstate = world.getBlockState(uppos);
if(!checked.contains(uppos)) {
checked.add(uppos);
if(isSameLog(upstate, broken_state)) {
// Up is log
upqueue.add(uppos);
to_break.add(uppos);
steps_left = 64;
} else {
boolean isleaf = isLeaves(upstate);
if(isleaf || world.isAirBlock(uppos) || (upstate.getBlock() instanceof VineBlock)) {
if(isleaf) 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);
if(checked.contains(p)) continue;
checked.add(p);
final BlockState st = world.getBlockState(p);
final Block bl = st.getBlock();
if(isSameLog(st, broken_state)) {
queue.add(p);
to_break.add(p);
} else if(isLeaves(st)) {
to_decay.add(p);
}
}
}
}
}
// Lateral search
for(Vec3i v:hoffsets) {
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 BlockState st = world.getBlockState(p);
final Block bl = st.getBlock();
if(isSameLog(st, broken_state)) {
queue.add(p);
to_break.add(p);
} else if(isLeaves(st)) {
to_decay.add(p);
}
}
if(queue.isEmpty() && (!upqueue.isEmpty())) {
queue = upqueue;
upqueue = new LinkedList<BlockPos>();
++cutlevel;
}
}
}
{
// Determine lose logs between the leafs
for(BlockPos pos:to_decay) {
int dist = 1;
to_break.addAll(findBlocksAround(world, pos, broken_state, checked, dist));
}
}
if(!to_decay.isEmpty()) {
final BlockState leaf_type_state = world.getBlockState(to_decay.get(0));
final ArrayList<BlockPos> leafs = to_decay;
to_decay = new ArrayList<BlockPos>();
for(BlockPos pos:leafs) {
int dist = 2;
to_decay.add(pos);
to_decay.addAll(findBlocksAround(world, pos, leaf_type_state, checked, dist));
}
}
if(without_target_block) {
checked.remove(startPos);
} else {
to_break.add(startPos);
}
for(BlockPos pos:to_break) breakBlock(world, pos);
for(BlockPos pos:to_decay) breakBlock(world, pos);
{
// And now the bill.
return MathHelper.clamp(((to_break.size()*6/5)+(to_decay.size()/10)-1), 1, 65535);
}
}
}

View file

@ -18,7 +18,7 @@ logoFile="logo.png"
[[dependencies.engineersdecor]]
modId="forge"
mandatory=true
versionRange="[28.0.81,)"
versionRange="[28.1.68,)"
ordering="NONE"
side="BOTH"

View file

@ -0,0 +1,9 @@
{
"forge_marker": 1,
"defaults": {
"model": "engineersdecor:block/device/factory_placer_model"
},
"variants": {
"facing": { "north":{"y":0}, "south":{"y":180}, "west":{"y":270}, "east":{"y":90}, "up": {"x":-90}, "down": {"x":90} }
}
}

View file

@ -0,0 +1,10 @@
{
"forge_marker": 1,
"defaults": {
"model": "engineersdecor:block/device/small_block_breaker_model"
},
"variants": {
"facing": { "north":{"y":0}, "south":{"y":180}, "west":{"y":270}, "east":{"y":90} },
"active": { "true":{ "model": "engineersdecor:block/device/small_block_breaker_model_active" }, "false":{}}
}
}

View file

@ -0,0 +1,12 @@
{
"forge_marker": 1,
"defaults": { "model": "engineersdecor:block/device/small_fluid_funnel_model_s0" },
"variants": {
"level": {
"0":{},
"1":{"model": "engineersdecor:block/device/small_fluid_funnel_model_s1"},
"2":{"model": "engineersdecor:block/device/small_fluid_funnel_model_s2"},
"3":{"model": "engineersdecor:block/device/small_fluid_funnel_model_s3"}
}
}
}

View file

@ -0,0 +1,8 @@
{
"forge_marker": 1,
"defaults": { "model": "engineersdecor:block/device/small_tree_cutter_model" },
"variants": {
"facing": { "north": {"y":0}, "south": {"y":180}, "west": {"y":-90}, "east": {"y":90} },
"active": { "true":{ "model": "engineersdecor:block/device/small_tree_cutter_model_active" }, "false":{}}
}
}

View file

@ -128,7 +128,9 @@
"block.engineersdecor.treated_wood_side_table": "Treated Wood Side Table",
"block.engineersdecor.treated_wood_side_table.help": "§6Needed after the work's done.",
"block.engineersdecor.iron_inset_light": "Inset Light",
"block.engineersdecor.iron_inset_light.help": "§6Small glowstone light source, sunk into the floor, ceiling or wall.§r\n Useful to light up places where electrical light installations are problematic. Light level like a torch.",
"block.engineersdecor.iron_inset_light.help": "§6Small glowstone light source, sunk into the floor, ceiling or wall.§r\n Useful to light up places where electrical light installations are problematic.",
"block.engineersdecor.iron_floor_edge_light": "Inset Floor Edge Light",
"block.engineersdecor.iron_floor_edge_light.help": "§6Small glowstone light source, placed at the edge of a floor block.§r\n Useful to light up places where electrical light installations are problematic.",
"block.engineersdecor.treated_wood_window": "Treated Wood Window",
"block.engineersdecor.treated_wood_window.help": "§6Wood framed triple glazed window. Well insulating.§r Does not connect to adjacent blocks like glass panes.",
"block.engineersdecor.treated_wood_windowsill": "Treated Wood Window Sill",
@ -153,12 +155,18 @@
"block.engineersdecor.straight_pipe_valve_redstone_analog.help": "§6Straight fluid pipe fragment.§r Conducts fluids only in one direction. Does not connect to the sides. Sneak to place in reverse direction. Blocks if not redstone powered, reduces the flow rate linear from power 1 to 14, opens to maximum possible valve flow rate for power 15.",
"block.engineersdecor.passive_fluid_accumulator": "Passive Fluid Accumulator",
"block.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.",
"block.engineersdecor.small_fluid_funnel": "Small Fluid Collection Funnel",
"block.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.",
"block.engineersdecor.factory_dropper": "Factory Dropper",
"block.engineersdecor.factory_dropper.help": "§6Dropper suitable for advanced factory automation.§r Has twelve round-robin selected slots. Drop force, angle, stack size, and cool-down delay adjustable in the GUI. Three stack compare slots with logical AND or OR can be used as internal trigger source. Internal trigger can be AND'ed or OR'ed with the external redstone signal trigger. Trigger simulation buttons for testing. Pre-opens shutter door when internal trigger conditions are met. Drops all matching stacks simultaneously. Click on all elements in the GUI to see how it works.",
"block.engineersdecor.factory_dropper.help": "§6Dropper suitable for advanced factory automation.§r Has twelve round-robin selected slots. Drop force, angle, stack size, and cool-down delay adjustable using sliders in the GUI. Three stack compare slots (below the inventory slots) with logical AND or OR can be used as internal trigger source. The internal trigger can be AND'ed or OR'ed with the external redstone signal trigger. Trigger simulation buttons for testing. Pre-opens shutter door when internal trigger conditions are met. Drops all matching stacks simultaneously. Simply click on all elements in the GUI to see how it works.",
"block.engineersdecor.factory_hopper": "Factory Hopper",
"block.engineersdecor.factory_hopper.help": "§6Hopper suitable for advanced factory automation.§r",
"block.engineersdecor.factory_hopper.help": "§6Hopper suitable for advanced factory automation.§r Can transfer half-stacks, max collection range 9x9.\n GUI Slider controls: Collection range (0 to 4), insertion delay (0.5s to 10s), insertion stack size (1 to 32).\\n GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).",
"block.engineersdecor.factory_placer": "Factory Block Placer",
"block.engineersdecor.factory_placer.help": "§6Allows placing blocks and planting crops or trees.§r\n GUI Redstone controls: Not inverted / inverted (default), pulse mode / continuous mode (default).\\n Also supports spike planing from underneath the soil. Spits out items that it cannot place or plant.",
"block.engineersdecor.small_block_breaker": "Small Block Breaker",
"block.engineersdecor.small_block_breaker.help": "§6Breaks blocks in front of it.§r\n Can be disabled by applying a redstone signal. The time needed to destroy a block depends on the hardness of that block. Provide RF/FE power to speed up the breaking process (massively).",
"block.engineersdecor.small_mineral_smelter": "Small Mineral Melting Furnace",
"block.engineersdecor.small_mineral_smelter.help": "§6High temperature, high insulation electrical stone melting furnace.§r\n Heats up mineral blocks to magma blocks, and finally to lava. Due to the miniturized device size the process is rather inefficient - much time and energy is needed to liquefy a stone block.",
"block.engineersdecor.small_mineral_smelter.help": "§6High temperature, high insulation electrical stone melting furnace.§r\n Heats up mineral blocks to magma blocks, and finally to lava. Click with a mineral block (stone, cobble, etc) or use item insertion to place a block in the melter. Use bucket or fluid extraction to retrieve the lava. When cooling down lava, obsidian is generated. Remove the RF power or apply a redstone signal to disable the furnace. For automation, use a redstone comparator to see in which melting phase the mineral is a moment.",
"block.engineersdecor.small_solar_panel": "Small Solar Panel",
"block.engineersdecor.small_solar_panel.help": "§6Produces a small amount of power when exposed to sunlight.§r\n Useful for charging LF capacitors in remote systems with low consumption. The internal charge pump circuit accumulates and frequently transfers RF. Production depends on day time and the weather.",
"block.engineersdecor.small_tree_cutter": "Small Tree Cutter",

View file

@ -128,6 +128,7 @@
"block.engineersdecor.treated_wood_side_table.help": "§6Needed after the work's done.",
"block.engineersdecor.iron_inset_light": "Встраиваемый осветитель",
"block.engineersdecor.iron_inset_light.help": "§6Маленький источник света, интегрируемый в стены, пол или потолок.§r\n Полезно для освещения мест, где проблематичны электрические осветительные установки. Уровень света - как факел.",
"block.engineersdecor.iron_floor_edge_light": "Inset Floor Edge Light",
"block.engineersdecor.treated_wood_window": "Обработанное деревянное окно",
"block.engineersdecor.treated_wood_window.help": "§6Деревянный каркас окна с тройным остеклением. Ну и шумоизоляция.",
"block.engineersdecor.treated_wood_windowsill": "Обработанный деревянный подоконник",
@ -150,9 +151,12 @@
"block.engineersdecor.straight_pipe_valve_redstone_analog.help": "§6Фрагмент прямой трубы.§r Проводит жидкости только в одном направлении. Не соединяется по бокам. SHIFT для размещения в обратном направлении. Не пропускает при отсутствии сигнала красного камня, уменьшает расход линейно с мощности 1 до 14, открывается максимально-возможно при уровне сигнала красного камня 15.",
"block.engineersdecor.passive_fluid_accumulator": "Пассивный жидкостный накопитель",
"block.engineersdecor.passive_fluid_accumulator.help": "§6Вакуумный всасывающий жидкостный коллектор.§r Имеет один выход, все остальные стороны входные. Сливает жидкости из соседних резервуаров при выкачивании жидкости из выходного порта.",
"block.engineersdecor.small_fluid_funnel": "Small Fluid Collection Funnel",
"block.engineersdecor.factory_dropper": "Фабричный выбрасыватель",
"block.engineersdecor.factory_dropper.help": "§6Выбрасыватель подходит для продвинутой автоматизации производства.§r Имеет 12 выборочных слотов. Сила броска, угол, размер стопки и задержка настраиваются в GUI. 3 слота сравнения стека с логическим И или ИЛИ могут использоваться в качестве внутреннего источника запуска. Внутренний триггер может быть И или ИЛИ с внешним триггерным сигналом красного камня. Триггерные кнопки симуляции для тестирования. Предварительно открывает дверцу затвора, когда выполняются условия внутреннего запуска. Сбрасывает все соответствующие стеки одновременно. Нажмите на все элементы в GUI, чтобы увидеть, как это работает.",
"block.engineersdecor.factory_hopper": "Factory Hopper",
"block.engineersdecor.factory_placer": "Factory Block Placer",
"block.engineersdecor.small_block_breaker": "Factory Block Breaker",
"block.engineersdecor.small_mineral_smelter": "Small Mineral Melting Furnace",
"block.engineersdecor.small_solar_panel": "Small Solar Panel",
"block.engineersdecor.small_tree_cutter": "Small Tree Cutter",

View file

@ -129,6 +129,7 @@
"block.engineersdecor.treated_wood_side_table.help": "§6干完活后需要喝杯茶。",
"block.engineersdecor.iron_inset_light": "嵌入灯",
"block.engineersdecor.iron_inset_light.help": "§6小型荧石光源能嵌入地板、天花板或墙里。§r\n 用于照亮电力光源难以安装的地方。 亮度与火把一样。",
"block.engineersdecor.iron_floor_edge_light": "Inset Floor Edge Light",
"block.engineersdecor.treated_wood_window": "防腐木窗",
"block.engineersdecor.treated_wood_window.help": "§6木框三层玻璃窗。绝缘良好。§r不像玻璃板一样连接到相邻方块。",
"block.engineersdecor.treated_wood_windowsill": "防腐木窗台",
@ -153,9 +154,12 @@
"block.engineersdecor.straight_pipe_valve_redstone_analog.help": "§6一段直输液管。§r单向传递流体。 侧面不会与管道连接。会减少流速。潜行能反方向放置。 没有红石信号时断流流速与红石信号强度从1到14线性增长 15时流速上限达到最大。",
"block.engineersdecor.passive_fluid_accumulator": "被动流体累积器。",
"block.engineersdecor.passive_fluid_accumulator.help": "§6基于真空吸力的流体收集器。§r有一个输出面其他面都是输入。 当从输出面被泵抽取时,从输入面邻接储罐抽取液体。",
"block.engineersdecor.small_fluid_funnel": "Small Fluid Collection Funnel",
"block.engineersdecor.factory_dropper": "工厂掉落器",
"block.engineersdecor.factory_dropper.help": "§6适用于高级工厂自动化的掉落器。§r有十二个轮询选择的储物格。 掉落的力度、角度、一叠数量和冷却延时可在GUI调节。三个 内部比较槽带有逻辑与或逻辑或功能,可用作内部触发源。内部触发 还能和外部红石信号触发再进行逻辑与或逻辑或。触发模拟按钮仅作测试用途。 当内部触发条件满足时,预先打开卷帘门。所有符合条件的物品 会同时掉落。点击GUI的各处来了解如何运作。",
"block.engineersdecor.factory_hopper": "Factory Hopper",
"block.engineersdecor.factory_placer": "Factory Block Placer",
"block.engineersdecor.small_block_breaker": "Factory Block Breaker",
"block.engineersdecor.small_mineral_smelter": "小型矿物熔炼炉",
"block.engineersdecor.small_mineral_smelter.help": "§6高温、高绝缘电熔石炉。§r\n 把矿物块加热成岩浆块,最后变成熔岩。由于 小型化的设备大小,该过程效率不高,需要大量时间和能源 来液化一块石头。",
"block.engineersdecor.small_solar_panel": "小型太阳能板",

View file

@ -0,0 +1,170 @@
{
"parent": "block/cube",
"textures": {
"top": "engineersdecor:block/device/factory_placer_top",
"bottom": "engineersdecor:block/device/factory_placer_bottom",
"side": "engineersdecor:block/device/factory_placer_side",
"particle": "engineersdecor:block/device/factory_placer_side"
},
"elements": [
{
"from": [0, 0, 3],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 16], "texture": "#bottom"},
"east": {"uv": [3, 0, 16, 16], "rotation": 180, "texture": "#side"},
"south": {"uv": [0, 0, 16, 16], "texture": "#top"},
"west": {"uv": [3, 0, 16, 16], "texture": "#side"},
"up": {"uv": [3, 0, 16, 16], "rotation": 90, "texture": "#side"},
"down": {"uv": [3, 0, 16, 16], "rotation": 270, "texture": "#side"}
}
},
{
"from": [15, 0, 0],
"to": [16, 16, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 7]},
"faces": {
"north": {"uv": [0, 0, 1, 16], "texture": "#bottom"},
"east": {"uv": [15, 0, 16, 16], "texture": "#side"},
"south": {"uv": [14, 0, 16, 16], "texture": "#top"},
"west": {"uv": [0, 0, 1, 16], "texture": "#side"},
"up": {"uv": [14, 0, 15, 1], "rotation": 90, "texture": "#side"},
"down": {"uv": [14, 15, 16, 16], "texture": "#side"}
}
},
{
"from": [15, 0, 2],
"to": [16, 16, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [0, 0, 1, 16], "texture": "#bottom"},
"east": {"uv": [2, 0, 3, 16], "rotation": 180, "texture": "#side"},
"south": {"uv": [15, 0, 16, 16], "texture": "#top"},
"west": {"uv": [2, 0, 4, 16], "texture": "#side"},
"up": {"uv": [15, 2, 16, 4], "rotation": 90, "texture": "#side"},
"down": {"uv": [15, 12, 16, 14], "rotation": 270, "texture": "#side"}
}
},
{
"from": [0, 0, 0],
"to": [1, 16, 1],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 7]},
"faces": {
"north": {"uv": [15, 0, 16, 16], "texture": "#bottom"},
"east": {"uv": [15, 0, 16, 16], "texture": "#side"},
"south": {"uv": [0, 0, 2, 16], "texture": "#top"},
"west": {"uv": [0, 0, 1, 16], "texture": "#side"},
"up": {"uv": [0, 0, 1, 1], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 15, 2, 16], "texture": "#side"}
}
},
{
"from": [0, 0, 2],
"to": [1, 16, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [15, 0, 16, 16], "texture": "#bottom"},
"east": {"uv": [2, 0, 3, 16], "texture": "#side"},
"south": {"uv": [0, 0, 1, 16], "texture": "#top"},
"west": {"uv": [2, 0, 3, 16], "texture": "#side"},
"up": {"uv": [0, 2, 1, 3], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 13, 1, 14], "rotation": 270, "texture": "#side"}
}
},
{
"from": [0, 15, 1],
"to": [1, 16, 2],
"faces": {
"north": {"uv": [15, 0, 16, 1], "texture": "#bottom"},
"east": {"uv": [14, 0, 15, 1], "texture": "#side"},
"south": {"uv": [0, 0, 1, 1], "texture": "#top"},
"west": {"uv": [1, 0, 2, 1], "texture": "#side"},
"up": {"uv": [1, 0, 2, 1], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 14, 1, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [15, 15, 1],
"to": [16, 16, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [23, 8, 8]},
"faces": {
"north": {"uv": [0, 0, 1, 1], "texture": "#bottom"},
"east": {"uv": [14, 0, 15, 1], "texture": "#side"},
"south": {"uv": [15, 0, 16, 1], "texture": "#top"},
"west": {"uv": [1, 0, 2, 1], "texture": "#side"},
"up": {"uv": [15, 1, 16, 2], "rotation": 90, "texture": "#side"},
"down": {"uv": [15, 14, 16, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [15, 0, 1],
"to": [16, 1, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [23, -7, 8]},
"faces": {
"north": {"uv": [0, 15, 1, 16], "texture": "#bottom"},
"east": {"uv": [14, 15, 15, 16], "texture": "#side"},
"south": {"uv": [15, 15, 16, 16], "texture": "#top"},
"west": {"uv": [1, 15, 2, 16], "texture": "#side"},
"up": {"uv": [15, 1, 16, 2], "rotation": 90, "texture": "#side"},
"down": {"uv": [15, 14, 16, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [0, 0, 1],
"to": [1, 1, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -7, 8]},
"faces": {
"north": {"uv": [15, 15, 16, 16], "texture": "#bottom"},
"east": {"uv": [14, 15, 15, 16], "texture": "#side"},
"south": {"uv": [0, 15, 1, 16], "texture": "#top"},
"west": {"uv": [1, 15, 2, 16], "texture": "#side"},
"up": {"uv": [0, 1, 1, 2], "rotation": 90, "texture": "#side"},
"down": {"uv": [0, 14, 1, 15], "texture": "#side"}
}
},
{
"from": [1, 15, 2],
"to": [15, 16, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [1, 0, 15, 1], "texture": "#bottom"},
"east": {"uv": [12, 0, 14, 1], "texture": "#side"},
"south": {"uv": [1, 0, 15, 1], "texture": "#top"},
"west": {"uv": [2, 0, 4, 1], "texture": "#side"},
"up": {"uv": [2, 1, 3, 15], "rotation": 90, "texture": "#side"},
"down": {"uv": [2, 1, 3, 15], "rotation": 270, "texture": "#side"}
}
},
{
"from": [1, 0, 2],
"to": [15, 1, 3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 11]},
"faces": {
"north": {"uv": [1, 15, 15, 16], "texture": "#bottom"},
"east": {"uv": [12, 15, 14, 16], "texture": "#side"},
"south": {"uv": [1, 15, 15, 16], "texture": "#top"},
"west": {"uv": [2, 15, 4, 16], "texture": "#side"},
"up": {"uv": [2, 1, 3, 15], "rotation": 180, "texture": "#side"},
"down": {"uv": [2, 1, 3, 15], "rotation": 270, "texture": "#side"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -0,0 +1,383 @@
{
"parent": "block/cube",
"textures": {
"t": "engineersdecor:block/device/small_block_breaker_top",
"b": "engineersdecor:block/device/small_block_breaker_bottom",
"l": "engineersdecor:block/device/small_block_breaker_left",
"particle": "engineersdecor:block/device/small_block_breaker_left",
"k": "engineersdecor:block/device/small_block_breaker_back",
"r": "engineersdecor:block/device/small_block_breaker_right",
"f": "engineersdecor:block/device/small_block_breaker_front",
"sh": "engineersdecor:block/device/small_block_breaker_shaft"
},
"elements": [
{
"from": [0, 0, 4],
"to": [1, 12, 16],
"faces": {
"north": {"uv": [15, 4, 16, 16], "texture": "#f"},
"east": {"uv": [4, 0, 16, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [0, 4, 1, 16], "texture": "#k"},
"west": {"uv": [4, 4, 16, 16], "texture": "#l"},
"up": {"uv": [0, 4, 1, 16], "texture": "#t"},
"down": {"uv": [0, 0, 1, 12], "texture": "#b"}
}
},
{
"from": [15, 0, 4],
"to": [16, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [23, 8, 8]},
"faces": {
"north": {"uv": [0, 4, 1, 16], "texture": "#f"},
"east": {"uv": [0, 4, 12, 16], "texture": "#r"},
"south": {"uv": [15, 4, 16, 16], "texture": "#k"},
"west": {"uv": [0, 0, 12, 12], "texture": "#l"},
"up": {"uv": [15, 4, 16, 16], "texture": "#t"},
"down": {"uv": [15, 0, 16, 12], "texture": "#b"}
}
},
{
"from": [7.5, 4, 6],
"to": [8.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15.5, 12, -5]},
"faces": {
"north": {"uv": [7, 4, 8, 12], "texture": "#f"},
"east": {"uv": [9, 4, 11, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [7.5, 4, 8.5, 12], "texture": "#t"},
"west": {"uv": [5, 4, 7, 12], "texture": "#f"},
"up": {"uv": [12, 7, 13, 9], "texture": "#t"},
"down": {"uv": [7.5, 9, 8.5, 11], "rotation": 270, "texture": "#l"}
}
},
{
"from": [8.5, 4, 6],
"to": [9, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [16.5, 12, -5]},
"faces": {
"north": {"uv": [7, 4, 7.5, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [8.5, 4, 9, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#l"},
"up": {"uv": [8.5, 7, 9, 8], "texture": "#t"},
"down": {"uv": [8.5, 9, 9, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [7, 4, 6],
"to": [7.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15, 12, -5]},
"faces": {
"north": {"uv": [8.5, 4, 9, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#l"},
"south": {"uv": [7, 4, 7.5, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#f"},
"up": {"uv": [7.5, 7, 8.5, 8], "texture": "#t"},
"down": {"uv": [7, 9, 7.5, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [15, 0, 0],
"to": [16, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [23, -2, 8]},
"faces": {
"north": {"uv": [0, 11, 1, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#r"},
"south": {"uv": [15, 11, 16, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [15, 0, 16, 4], "texture": "#t"},
"down": {"uv": [15, 12, 16, 16], "texture": "#b"}
}
},
{
"from": [0, 0, 0],
"to": [1, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -2, 8]},
"faces": {
"north": {"uv": [15, 11, 16, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#l"},
"south": {"uv": [0, 11, 1, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [0, 0, 1, 4], "texture": "#t"},
"down": {"uv": [0, 12, 1, 16], "texture": "#b"}
}
},
{
"from": [1, 0, 0],
"to": [15, 4, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 24]},
"faces": {
"north": {"uv": [1, 12, 15, 16], "texture": "#f"},
"east": {"uv": [0, 12, 16, 16], "texture": "#l"},
"south": {"uv": [1, 12, 15, 16], "texture": "#k"},
"west": {"uv": [0, 12, 16, 16], "texture": "#l"},
"up": {"uv": [1, 0, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 16], "texture": "#b"}
}
},
{
"from": [1, 4, 7],
"to": [15, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 12, 24]},
"faces": {
"north": {"uv": [1, 4, 15, 12], "texture": "#f"},
"east": {"uv": [0, 4, 9, 12], "texture": "#l"},
"south": {"uv": [1, 4, 15, 12], "texture": "#k"},
"west": {"uv": [7, 4, 16, 12], "texture": "#l"},
"up": {"uv": [1, 7, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 9], "rotation": 270, "texture": "#l"}
}
},
{
"from": [4, 9.5, 5],
"to": [5, 10.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, 2.5, 12.5]},
"faces": {
"north": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"east": {"uv": [9.5, 5.5, 11, 6.5], "texture": "#sh"},
"south": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"west": {"uv": [5, 5.5, 6.5, 6.5], "texture": "#sh"},
"up": {"uv": [4, 5, 5, 6.5], "texture": "#sh"},
"down": {"uv": [4, 9.5, 5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, 6.5],
"to": [6.5, 10, 7.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12.5, 1, 13.5]},
"faces": {
"north": {"uv": [12, 12, 16, 16], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [2.5, 6, 6.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [2, 6, 6, 7], "texture": "#sh"},
"down": {"uv": [2.5, 15.5, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 9, 5],
"to": [3.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, 2, 12.5]},
"faces": {
"north": {"uv": [12, 6, 13, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [2.5, 6, 3.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [2, 5, 3, 6.5], "texture": "#sh"},
"down": {"uv": [2.5, 9.5, 3.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2, 7.5, 5],
"to": [3, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 0.5, 12.5]},
"faces": {
"north": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [2, 5, 3, 6.5], "texture": "#sh"},
"down": {"uv": [2, 9.5, 3, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, 5],
"to": [3.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, -1, 12.5]},
"faces": {
"north": {"uv": [12, 9, 13, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [2.5, 9, 3.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [2.5, 5, 3.5, 6.5], "texture": "#sh"},
"down": {"uv": [2.5, 9.5, 3.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [4, 5.5, 5],
"to": [5, 6.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, -1.5, 12.5]},
"faces": {
"north": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"east": {"uv": [9.5, 9.5, 11, 10.5], "texture": "#sh"},
"south": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"west": {"uv": [5, 9.5, 6.5, 10.5], "texture": "#sh"},
"up": {"uv": [4, 5, 5, 6.5], "texture": "#sh"},
"down": {"uv": [4, 9.5, 5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 6, 5],
"to": [6.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, -1, 12.5]},
"faces": {
"north": {"uv": [9, 9, 10, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [5.5, 9, 6.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [5, 5, 6, 6.5], "texture": "#sh"},
"down": {"uv": [5.5, 9.5, 6.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 9, 5],
"to": [6.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, 2, 12.5]},
"faces": {
"north": {"uv": [9, 6, 10, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [5.5, 6, 6.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [5, 5, 6, 6.5], "texture": "#sh"},
"down": {"uv": [5.5, 9.5, 6.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [6, 7.5, 5],
"to": [7, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [14, 0.5, 12.5]},
"faces": {
"north": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [6, 5, 7, 6.5], "texture": "#sh"},
"down": {"uv": [6, 9.5, 7, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, 6.5],
"to": [13.5, 10, 7.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19.5, 1, 13.5]},
"faces": {
"north": {"uv": [10, 12, 14, 16], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [9.5, 6, 13.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [10, 7, 14, 8], "texture": "#sh"},
"down": {"uv": [9.5, 15.5, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 9, 5],
"to": [13.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, 2, 12.5]},
"faces": {
"north": {"uv": [2, 6, 3, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [12.5, 6, 13.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [12, 5, 13, 6.5], "texture": "#sh"},
"down": {"uv": [12.5, 9.5, 13.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 9.5, 5],
"to": [12, 10.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, 2.5, 12.5]},
"faces": {
"north": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"east": {"uv": [9.5, 5.5, 11, 6.5], "texture": "#sh"},
"south": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"west": {"uv": [5, 5.5, 6.5, 6.5], "texture": "#sh"},
"up": {"uv": [11, 5, 12, 6.5], "texture": "#sh"},
"down": {"uv": [11, 9.5, 12, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 9, 5],
"to": [10.5, 10, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, 2, 12.5]},
"faces": {
"north": {"uv": [5, 6, 6, 7], "texture": "#sh"},
"east": {"uv": [9.5, 6, 11, 7], "texture": "#sh"},
"south": {"uv": [9.5, 6, 10.5, 7], "texture": "#sh"},
"west": {"uv": [5, 6, 6.5, 7], "texture": "#sh"},
"up": {"uv": [9, 5, 10, 6.5], "texture": "#sh"},
"down": {"uv": [9.5, 9.5, 10.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9, 7.5, 5],
"to": [10, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17, 0.5, 12.5]},
"faces": {
"north": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [9, 5, 10, 6.5], "texture": "#sh"},
"down": {"uv": [9, 9.5, 10, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, 5],
"to": [10.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, -1, 12.5]},
"faces": {
"north": {"uv": [5, 9, 6, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [9.5, 9, 10.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [9, 5, 10, 6.5], "texture": "#sh"},
"down": {"uv": [9.5, 9.5, 10.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 5.5, 5],
"to": [12, 6.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, -1.5, 12.5]},
"faces": {
"north": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"east": {"uv": [9.5, 9.5, 11, 10.5], "texture": "#sh"},
"south": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"west": {"uv": [5, 9.5, 6.5, 10.5], "texture": "#sh"},
"up": {"uv": [11, 5, 12, 6.5], "texture": "#sh"},
"down": {"uv": [11, 9.5, 12, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 6, 5],
"to": [13.5, 7, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, -1, 12.5]},
"faces": {
"north": {"uv": [2, 9, 3, 10], "texture": "#sh"},
"east": {"uv": [9.5, 9, 11, 10], "texture": "#sh"},
"south": {"uv": [12.5, 9, 13.5, 10], "texture": "#sh"},
"west": {"uv": [5, 9, 6.5, 10], "texture": "#sh"},
"up": {"uv": [12, 5, 13, 6.5], "texture": "#sh"},
"down": {"uv": [12.5, 9.5, 13.5, 11], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [13, 7.5, 5],
"to": [14, 8.5, 6.5],
"rotation": {"angle": 0, "axis": "y", "origin": [21, 0.5, 12.5]},
"faces": {
"north": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"east": {"uv": [9.5, 7.5, 11, 8.5], "texture": "#sh"},
"south": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"west": {"uv": [5, 7.5, 6.5, 8.5], "texture": "#sh"},
"up": {"uv": [13, 5, 14, 6.5], "texture": "#sh"},
"down": {"uv": [13, 9.5, 14, 11], "rotation": 270, "texture": "#sh"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -0,0 +1,461 @@
{
"parent": "block/cube",
"textures": {
"t": "engineersdecor:block/device/small_block_breaker_top",
"b": "engineersdecor:block/device/small_block_breaker_bottom",
"l": "engineersdecor:block/device/small_block_breaker_left",
"particle": "engineersdecor:block/device/small_block_breaker_left",
"k": "engineersdecor:block/device/small_block_breaker_back",
"r": "engineersdecor:block/device/small_block_breaker_right",
"f": "engineersdecor:block/device/small_block_breaker_front",
"sh": "engineersdecor:block/device/small_block_breaker_shaft_active"
},
"elements": [
{
"from": [0, 0, 4],
"to": [1, 12, 16],
"faces": {
"north": {"uv": [15, 4, 16, 16], "texture": "#f"},
"east": {"uv": [4, 0, 16, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [0, 4, 1, 16], "texture": "#k"},
"west": {"uv": [4, 4, 16, 16], "texture": "#l"},
"up": {"uv": [0, 4, 1, 16], "texture": "#t"},
"down": {"uv": [0, 0, 1, 12], "texture": "#b"}
}
},
{
"from": [15, 0, 4],
"to": [16, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [23, 8, 8]},
"faces": {
"north": {"uv": [0, 4, 1, 16], "texture": "#f"},
"east": {"uv": [0, 4, 12, 16], "texture": "#r"},
"south": {"uv": [15, 4, 16, 16], "texture": "#k"},
"west": {"uv": [0, 0, 12, 12], "texture": "#l"},
"up": {"uv": [15, 4, 16, 16], "texture": "#t"},
"down": {"uv": [15, 0, 16, 12], "texture": "#b"}
}
},
{
"from": [7.5, 4, 6],
"to": [8.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15.5, 12, -5]},
"faces": {
"north": {"uv": [7.5, 4, 8.5, 12], "texture": "#f"},
"east": {"uv": [9, 4, 11, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [7.5, 4, 8.5, 12], "texture": "#t"},
"west": {"uv": [5, 4, 7, 12], "texture": "#f"},
"up": {"uv": [12, 7, 13, 9], "texture": "#t"},
"down": {"uv": [7.5, 9, 8.5, 11], "rotation": 270, "texture": "#l"}
}
},
{
"from": [8.5, 4, 6],
"to": [9, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [16.5, 12, -5]},
"faces": {
"north": {"uv": [7, 4, 7.5, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#f"},
"south": {"uv": [8.5, 4, 9, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#l"},
"up": {"uv": [8, 7, 9.5, 8], "texture": "#t"},
"down": {"uv": [8.5, 9, 9, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [7, 4, 6],
"to": [7.5, 12, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [15, 12, -5]},
"faces": {
"north": {"uv": [8.5, 4, 9, 12], "texture": "#f"},
"east": {"uv": [9, 4, 10, 12], "rotation": 180, "texture": "#l"},
"south": {"uv": [7, 4, 7.5, 12], "texture": "#t"},
"west": {"uv": [6, 4, 7, 12], "texture": "#f"},
"up": {"uv": [7, 7, 7.5, 8], "texture": "#t"},
"down": {"uv": [7, 9, 7.5, 10], "rotation": 270, "texture": "#l"}
}
},
{
"from": [15, 0, 0],
"to": [16, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [23, -2, 8]},
"faces": {
"north": {"uv": [0, 11, 1, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#r"},
"south": {"uv": [15, 11, 16, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [15, 0, 16, 4], "texture": "#t"},
"down": {"uv": [15, 12, 16, 16], "texture": "#b"}
}
},
{
"from": [0, 0, 0],
"to": [1, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, -2, 8]},
"faces": {
"north": {"uv": [15, 11, 16, 16], "texture": "#f"},
"east": {"uv": [12, 11, 16, 16], "texture": "#l"},
"south": {"uv": [0, 11, 1, 16], "texture": "#t"},
"west": {"uv": [0, 11, 4, 16], "texture": "#l"},
"up": {"uv": [0, 0, 1, 4], "texture": "#t"},
"down": {"uv": [0, 12, 1, 16], "texture": "#b"}
}
},
{
"from": [1, 0, 0],
"to": [15, 4, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 24]},
"faces": {
"north": {"uv": [1, 12, 15, 16], "texture": "#f"},
"east": {"uv": [0, 12, 16, 16], "texture": "#l"},
"south": {"uv": [1, 12, 15, 16], "texture": "#k"},
"west": {"uv": [0, 12, 16, 16], "texture": "#l"},
"up": {"uv": [1, 0, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 16], "texture": "#b"}
}
},
{
"from": [1, 4, 7],
"to": [15, 12, 16],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 12, 24]},
"faces": {
"north": {"uv": [1, 4, 15, 12], "texture": "#f"},
"east": {"uv": [0, 4, 9, 12], "texture": "#l"},
"south": {"uv": [1, 4, 15, 12], "texture": "#k"},
"west": {"uv": [7, 4, 16, 12], "texture": "#l"},
"up": {"uv": [1, 7, 15, 16], "texture": "#t"},
"down": {"uv": [1, 0, 15, 9], "rotation": 270, "texture": "#l"}
}
},
{
"from": [4, 9.5, -2],
"to": [5, 10.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, 2.5, 5.5]},
"faces": {
"north": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"east": {"uv": [16, 5.5, 16, 6.5], "texture": "#sh"},
"south": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"west": {"uv": [0, 5.5, 0, 6.5], "texture": "#sh"},
"up": {"uv": [4, 0, 5, 0], "texture": "#sh"},
"down": {"uv": [4, 16, 5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [3, 7, 0.5],
"to": [3.5, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [2.5, 1, 5]},
"faces": {
"north": {"uv": [12.5, 7, 13, 9], "texture": "#sh"},
"east": {"uv": [9, 7, 15.5, 9], "texture": "#sh"},
"south": {"uv": [3, 7, 3.5, 9], "texture": "#sh"},
"west": {"uv": [1, 0, 7.5, 2], "texture": "#sh"},
"up": {"uv": [3, 0.5, 9.5, 2], "rotation": 90, "texture": "#sh"},
"down": {"uv": [3, 9, 3.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, -0.5],
"to": [6.5, 10, 0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12.5, 1, 6.5]},
"faces": {
"north": {"uv": [11, 11, 15, 15], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [2.5, 6, 6.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [3, 2, 7, 2.5], "texture": "#sh"},
"down": {"uv": [2.5, 15.5, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 9, -2],
"to": [3.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, 2, 5.5]},
"faces": {
"north": {"uv": [12.5, 6, 13.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [2.5, 6, 3.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [3, 2, 4, 3], "texture": "#sh"},
"down": {"uv": [2.5, 16, 3.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2, 7.5, -2],
"to": [3, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 0.5, 5.5]},
"faces": {
"north": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [2, 0, 3, 0], "texture": "#sh"},
"down": {"uv": [2, 16, 3, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [2.5, 6, -2],
"to": [3.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [10.5, -1, 5.5]},
"faces": {
"north": {"uv": [12.5, 9, 13.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [2.5, 9, 3.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [2.5, 0, 3.5, 0], "texture": "#sh"},
"down": {"uv": [2.5, 16, 3.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [4, 5.5, -2],
"to": [5, 6.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [12, -1.5, 5.5]},
"faces": {
"north": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"east": {"uv": [16, 9.5, 16, 10.5], "texture": "#sh"},
"south": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"west": {"uv": [0, 9.5, 0, 10.5], "texture": "#sh"},
"up": {"uv": [4, 0, 5, 0], "texture": "#sh"},
"down": {"uv": [4, 16, 5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 6, -2],
"to": [6.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, -1, 5.5]},
"faces": {
"north": {"uv": [9.5, 9, 10.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [5.5, 9, 6.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [5.5, 0, 6.5, 0], "texture": "#sh"},
"down": {"uv": [5.5, 16, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 9, -2],
"to": [6.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [13.5, 2, 5.5]},
"faces": {
"north": {"uv": [9.5, 6, 10.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [5.5, 6, 6.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [5.5, 0, 6.5, 0], "texture": "#sh"},
"down": {"uv": [5.5, 16, 6.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [6, 7.5, -2],
"to": [7, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [14, 0.5, 5.5]},
"faces": {
"north": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [6, 0, 7, 0], "texture": "#sh"},
"down": {"uv": [6, 16, 7, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [5.5, 7, 0.5],
"to": [6, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [5.5, 1, 5]},
"faces": {
"north": {"uv": [10, 7, 10.5, 9], "texture": "#sh"},
"east": {"uv": [8, 7, 14.5, 9], "texture": "#sh"},
"south": {"uv": [5.5, 7, 6, 9], "texture": "#sh"},
"west": {"uv": [0.5, 7, 7, 9], "texture": "#sh"},
"up": {"uv": [5, 2, 11.5, 2.5], "texture": "#sh"},
"down": {"uv": [5.5, 9, 6, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [3.5, 6.5, 0.5],
"to": [5.5, 9.5, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [4.5, 1, 5]},
"faces": {
"north": {"uv": [10.5, 6.5, 12.5, 9.5], "texture": "#sh"},
"east": {"uv": [9, 6.5, 15.5, 9.5], "texture": "#sh"},
"south": {"uv": [3.5, 6.5, 5.5, 9.5], "texture": "#sh"},
"west": {"uv": [0.5, 6.5, 7, 9.5], "texture": "#sh"},
"up": {"uv": [4, 1, 11, 3.5], "rotation": 90, "texture": "#sh"},
"down": {"uv": [3.5, 9, 5.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 7, 0.5],
"to": [13, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [12.5, 1, 5]},
"faces": {
"north": {"uv": [3, 7, 3.5, 9], "texture": "#sh"},
"east": {"uv": [9, 7, 15.5, 9], "texture": "#sh"},
"south": {"uv": [12.5, 7, 13, 9], "texture": "#sh"},
"west": {"uv": [0.5, 7, 7, 9], "texture": "#sh"},
"up": {"uv": [3, 0, 11.5, 0.5], "rotation": 90, "texture": "#sh"},
"down": {"uv": [12.5, 9, 13, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [10.5, 6.5, 0.5],
"to": [12.5, 9.5, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [11.5, 1, 5]},
"faces": {
"north": {"uv": [3.5, 6.5, 5.5, 9.5], "texture": "#sh"},
"east": {"uv": [9, 6.5, 15.5, 9.5], "texture": "#sh"},
"south": {"uv": [10.5, 6.5, 12.5, 9.5], "texture": "#sh"},
"west": {"uv": [0.5, 6.5, 7, 9.5], "texture": "#sh"},
"up": {"uv": [6, 0, 12, 2.5], "rotation": 90, "texture": "#sh"},
"down": {"uv": [10.5, 9, 12.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [10, 7, 0.5],
"to": [10.5, 9, 7],
"rotation": {"angle": 0, "axis": "y", "origin": [9.5, 1, 5]},
"faces": {
"north": {"uv": [5.5, 7, 6, 9], "texture": "#sh"},
"east": {"uv": [9, 7, 15.5, 9], "texture": "#sh"},
"south": {"uv": [10, 7, 10.5, 9], "texture": "#sh"},
"west": {"uv": [0.5, 7, 7, 9], "texture": "#sh"},
"up": {"uv": [10, 0.5, 15.5, 1], "rotation": 90, "texture": "#sh"},
"down": {"uv": [10, 9, 10.5, 15.5], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, -0.5],
"to": [13.5, 10, 0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19.5, 1, 6.5]},
"faces": {
"north": {"uv": [10, 12, 14, 16], "texture": "#sh"},
"east": {"uv": [15.5, 6, 16, 10], "texture": "#sh"},
"south": {"uv": [9.5, 6, 13.5, 10], "texture": "#sh"},
"west": {"uv": [0, 6, 0.5, 10], "texture": "#sh"},
"up": {"uv": [9.5, 0, 13.5, 0.5], "texture": "#sh"},
"down": {"uv": [9.5, 15.5, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 9, -2],
"to": [13.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, 2, 5.5]},
"faces": {
"north": {"uv": [2.5, 6, 3.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [12.5, 6, 13.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [12.5, 0, 13.5, 0], "texture": "#sh"},
"down": {"uv": [12.5, 16, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 9.5, -2],
"to": [12, 10.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, 2.5, 5.5]},
"faces": {
"north": {"uv": [4, 5.5, 5, 6.5], "texture": "#sh"},
"east": {"uv": [16, 5.5, 16, 6.5], "texture": "#sh"},
"south": {"uv": [11, 5.5, 12, 6.5], "texture": "#sh"},
"west": {"uv": [0, 5.5, 0, 6.5], "texture": "#sh"},
"up": {"uv": [11, 0, 12, 0], "texture": "#sh"},
"down": {"uv": [11, 16, 12, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 9, -2],
"to": [10.5, 10, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, 2, 5.5]},
"faces": {
"north": {"uv": [5.5, 6, 6.5, 7], "texture": "#sh"},
"east": {"uv": [16, 6, 16, 7], "texture": "#sh"},
"south": {"uv": [9.5, 6, 10.5, 7], "texture": "#sh"},
"west": {"uv": [0, 6, 0, 7], "texture": "#sh"},
"up": {"uv": [9.5, 0, 10.5, 0], "texture": "#sh"},
"down": {"uv": [9.5, 16, 10.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9, 7.5, -2],
"to": [10, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17, 0.5, 5.5]},
"faces": {
"north": {"uv": [6, 7.5, 7, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [9, 7.5, 10, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [9, 0, 10, 0], "texture": "#sh"},
"down": {"uv": [9, 16, 10, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [9.5, 6, -2],
"to": [10.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [17.5, -1, 5.5]},
"faces": {
"north": {"uv": [5.5, 9, 6.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [9.5, 9, 10.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [9.5, 0, 10.5, 0], "texture": "#sh"},
"down": {"uv": [9.5, 16, 10.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [11, 5.5, -2],
"to": [12, 6.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [19, -1.5, 5.5]},
"faces": {
"north": {"uv": [4, 9.5, 5, 10.5], "texture": "#sh"},
"east": {"uv": [16, 9.5, 16, 10.5], "texture": "#sh"},
"south": {"uv": [11, 9.5, 12, 10.5], "texture": "#sh"},
"west": {"uv": [0, 9.5, 0, 10.5], "texture": "#sh"},
"up": {"uv": [11, 0, 12, 0], "texture": "#sh"},
"down": {"uv": [11, 16, 12, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [12.5, 6, -2],
"to": [13.5, 7, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [20.5, -1, 5.5]},
"faces": {
"north": {"uv": [2.5, 9, 3.5, 10], "texture": "#sh"},
"east": {"uv": [16, 9, 16, 10], "texture": "#sh"},
"south": {"uv": [12.5, 9, 13.5, 10], "texture": "#sh"},
"west": {"uv": [0, 9, 0, 10], "texture": "#sh"},
"up": {"uv": [12.5, 0, 13.5, 0], "texture": "#sh"},
"down": {"uv": [12.5, 16, 13.5, 16], "rotation": 270, "texture": "#sh"}
}
},
{
"from": [13, 7.5, -2],
"to": [14, 8.5, -0.5],
"rotation": {"angle": 0, "axis": "y", "origin": [21, 0.5, 5.5]},
"faces": {
"north": {"uv": [2, 7.5, 3, 8.5], "texture": "#sh"},
"east": {"uv": [16, 7.5, 16, 8.5], "texture": "#sh"},
"south": {"uv": [13, 7.5, 14, 8.5], "texture": "#sh"},
"west": {"uv": [0, 7.5, 0, 8.5], "texture": "#sh"},
"up": {"uv": [13, 0, 14, 0], "texture": "#sh"},
"down": {"uv": [13, 16, 14, 16], "rotation": 270, "texture": "#sh"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -0,0 +1,295 @@
{
"parent": "block/cube",
"textures": {
"top": "engineersdecor:block/device/small_fluid_funnel_top",
"bottom": "engineersdecor:block/device/small_fluid_funnel_bottom",
"side": "engineersdecor:block/device/small_fluid_funnel_side_s0",
"particle": "engineersdecor:block/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]
}
}
}

View file

@ -0,0 +1,4 @@
{
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
"textures": { "side": "engineersdecor:block/device/small_fluid_funnel_side_s1" }
}

View file

@ -0,0 +1,4 @@
{
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
"textures": { "side": "engineersdecor:block/device/small_fluid_funnel_side_s2" }
}

View file

@ -0,0 +1,4 @@
{
"parent": "engineersdecor:block/device/small_fluid_funnel_model_s0",
"textures": { "side": "engineersdecor:block/device/small_fluid_funnel_side_s3" }
}

View file

@ -0,0 +1,222 @@
{
"parent": "block/cube",
"textures": {
"2": "engineersdecor:block/device/tree_cutter_side",
"3": "engineersdecor:block/device/tree_cutter_top",
"particle": "engineersdecor:block/device/tree_cutter_side",
"b": "engineersdecor:block/device/tree_cutter_blade_off",
"top": "engineersdecor:block/device/tree_cutter_bottom"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 3, 16],
"faces": {
"north": {"uv": [0, 13, 16, 16], "texture": "#2"},
"east": {"uv": [0, 13, 16, 16], "texture": "#2"},
"south": {"uv": [0, 13, 16, 16], "texture": "#2"},
"west": {"uv": [0, 13, 16, 16], "texture": "#2"},
"up": {"uv": [0, 0, 16, 16], "texture": "#3"},
"down": {"uv": [0, 0, 16, 16], "texture": "#top"}
}
},
{
"from": [0, 3, 0],
"to": [3, 8, 16],
"faces": {
"north": {"uv": [13, 0, 16, 5], "texture": "#2"},
"east": {"uv": [0, 3, 16, 8], "texture": "#2"},
"south": {"uv": [0, 8, 3, 13], "texture": "#2"},
"west": {"uv": [0, 8, 16, 13], "texture": "#2"},
"up": {"uv": [0, 0, 3, 16], "texture": "#3"},
"down": {"uv": [0, 0, 3, 16], "texture": "#top"}
}
},
{
"from": [3, 3, 13],
"to": [16, 8, 16],
"faces": {
"north": {"uv": [0, 3, 13, 8], "texture": "#2"},
"east": {"uv": [0, 8, 3, 13], "texture": "#2"},
"south": {"uv": [3, 8, 16, 13], "texture": "#2"},
"west": {"uv": [13, 8, 16, 13], "texture": "#b"},
"up": {"uv": [3, 13, 16, 16], "texture": "#3"},
"down": {"uv": [3, 0, 16, 3], "texture": "#top"}
}
},
{
"from": [15, 3, 0],
"to": [16, 6, 13],
"faces": {
"north": {"uv": [0, 10, 1, 13], "texture": "#2"},
"east": {"uv": [3, 10, 16, 13], "texture": "#2"},
"south": {"uv": [15, 10, 16, 13], "texture": "#b"},
"west": {"uv": [0, 0, 13, 3], "texture": "#2"},
"up": {"uv": [15, 0, 16, 13], "texture": "#3"},
"down": {"uv": [15, 3, 16, 16], "texture": "#top"}
}
},
{
"from": [3, 7, 0],
"to": [5, 8, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 13, 5]},
"faces": {
"north": {"uv": [11, 8, 13, 9], "texture": "#2"},
"east": {"uv": [3, 8, 16, 9], "texture": "#2"},
"south": {"uv": [3, 8, 5, 9], "texture": "#b"},
"west": {"uv": [0, 8, 13, 9], "texture": "#b"},
"up": {"uv": [3, 0, 5, 13], "texture": "#3"},
"down": {"uv": [3, 3, 5, 16], "texture": "#b"}
}
},
{
"from": [5, 6, 12],
"to": [16, 8, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 13, 5]},
"faces": {
"north": {"uv": [0, 0, 11, 2], "texture": "#2"},
"east": {"uv": [3, 8, 4, 10], "texture": "#2"},
"south": {"uv": [5, 8, 16, 10], "texture": "#b"},
"west": {"uv": [12, 8, 13, 10], "texture": "#b"},
"up": {"uv": [5, 12, 16, 13], "texture": "#3"},
"down": {"uv": [5, 3, 16, 4], "texture": "#top"}
}
},
{
"from": [5, 4.5, 4],
"to": [13, 5, 12],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]},
"faces": {
"north": {"uv": [3, 11, 11, 11.5], "texture": "#b"},
"east": {"uv": [4, 11, 12, 11.5], "texture": "#b"},
"south": {"uv": [5, 11, 13, 11.5], "texture": "#b"},
"west": {"uv": [4, 11, 12, 11.5], "texture": "#b"},
"up": {"uv": [5, 4, 13, 12], "texture": "#b"},
"down": {"uv": [5, 4, 13, 12], "texture": "#b"}
}
},
{
"from": [8, 5, 7],
"to": [10, 5.5, 9],
"faces": {
"north": {"uv": [6, 10.5, 8, 11], "texture": "#2"},
"east": {"uv": [7, 10.5, 9, 11], "texture": "#b"},
"south": {"uv": [8, 10.5, 10, 11], "texture": "#b"},
"west": {"uv": [7, 10.5, 9, 11], "texture": "#b"},
"up": {"uv": [8, 7, 10, 9], "texture": "#b"},
"down": {"uv": [8, 7, 10, 9], "texture": "#b"}
}
},
{
"from": [8, 4, 7],
"to": [10, 4.5, 9],
"faces": {
"north": {"uv": [6, 11.5, 8, 12], "texture": "#b"},
"east": {"uv": [7, 11.5, 9, 12], "texture": "#b"},
"south": {"uv": [8, 11.5, 10, 12], "texture": "#b"},
"west": {"uv": [7, 11.5, 9, 12], "texture": "#b"},
"up": {"uv": [8, 7, 10, 9], "texture": "#b"},
"down": {"uv": [8, 7, 10, 9], "texture": "#b"}
}
},
{
"from": [3, 3.5, 7],
"to": [10, 4, 9],
"faces": {
"north": {"uv": [8, 6, 15, 6.5], "texture": "#2"},
"east": {"uv": [7, 12, 9, 12.5], "texture": "#2"},
"south": {"uv": [3, 12, 10, 12.5], "texture": "#2"},
"west": {"uv": [7, 12, 9, 12.5], "texture": "#2"},
"up": {"uv": [3, 7, 10, 9], "texture": "#2"},
"down": {"uv": [3, 7, 10, 9], "texture": "#2"}
}
},
{
"from": [3, 5.5, 7],
"to": [10, 6, 9],
"faces": {
"north": {"uv": [6, 10, 13, 10.5], "texture": "#2"},
"east": {"uv": [7, 10, 9, 10.5], "texture": "#2"},
"south": {"uv": [3, 10, 10, 10.5], "texture": "#2"},
"west": {"uv": [7, 10, 9, 10.5], "texture": "#2"},
"up": {"uv": [9, 0, 16, 2], "texture": "#2"},
"down": {"uv": [3, 7, 10, 9], "texture": "#2"}
}
},
{
"from": [6, 4.5, 3],
"to": [12, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]},
"faces": {
"north": {"uv": [4, 11, 10, 11.5], "texture": "#b"},
"east": {"uv": [12, 11, 13, 11.5], "texture": "#b"},
"south": {"uv": [6, 11, 12, 11.5], "texture": "#b"},
"west": {"uv": [3, 11, 4, 11.5], "texture": "#b"},
"up": {"uv": [6, 3, 12, 4], "texture": "#b"},
"down": {"uv": [6, 12, 12, 13], "texture": "#b"}
}
},
{
"from": [4, 4.5, 5],
"to": [5, 5, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]},
"faces": {
"north": {"uv": [11, 11, 12, 11.5], "texture": "#b"},
"east": {"uv": [5, 11, 11, 11.5], "texture": "#b"},
"south": {"uv": [4, 11, 5, 11.5], "texture": "#b"},
"west": {"uv": [5, 11, 11, 11.5], "texture": "#b"},
"up": {"uv": [4, 5, 5, 11], "texture": "#b"},
"down": {"uv": [4, 5, 5, 11], "texture": "#b"}
}
},
{
"from": [13, 4.5, 5],
"to": [14, 5, 11],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]},
"faces": {
"north": {"uv": [2, 11, 3, 11.5], "texture": "#b"},
"east": {"uv": [5, 11, 11, 11.5], "texture": "#b"},
"south": {"uv": [13, 11, 14, 11.5], "texture": "#b"},
"west": {"uv": [5, 11, 11, 11.5], "texture": "#b"},
"up": {"uv": [13, 5, 14, 11], "texture": "#b"},
"down": {"uv": [13, 5, 14, 11], "texture": "#b"}
}
},
{
"from": [6, 4.5, 12],
"to": [12, 5, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 8]},
"faces": {
"north": {"uv": [4, 11, 10, 11.5], "texture": "#b"},
"east": {"uv": [3, 11, 4, 11.5], "texture": "#b"},
"south": {"uv": [6, 11, 12, 11.5], "texture": "#b"},
"west": {"uv": [12, 11, 13, 11.5], "texture": "#b"},
"up": {"uv": [6, 12, 12, 13], "texture": "#b"},
"down": {"uv": [6, 3, 12, 4], "texture": "#b"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [0.75, 0.25, 0.5],
"scale": [0.35, 0.35, 0.35]
},
"firstperson_righthand": {
"rotation": [18, 22, 0],
"translation": [1.25, 0, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, -1.25, 0],
"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]
}
}
}

View file

@ -0,0 +1,221 @@
{
"parent": "block/cube",
"textures": {
"2": "engineersdecor:block/device/tree_cutter_side",
"3": "engineersdecor:block/device/tree_cutter_top",
"particle": "engineersdecor:block/device/tree_cutter_side",
"b": "engineersdecor:block/device/tree_cutter_blade",
"top": "engineersdecor:block/device/tree_cutter_bottom"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 3, 16],
"faces": {
"north": {"uv": [0, 13, 16, 16], "texture": "#2"},
"east": {"uv": [0, 13, 16, 16], "texture": "#2"},
"south": {"uv": [0, 13, 16, 16], "texture": "#2"},
"west": {"uv": [0, 13, 16, 16], "texture": "#2"},
"up": {"uv": [0, 0, 16, 16], "texture": "#3"},
"down": {"uv": [0, 0, 16, 16], "texture": "#top"}
}
},
{
"from": [0, 3, 0],
"to": [3, 8, 16],
"faces": {
"north": {"uv": [13, 0, 16, 5], "texture": "#2"},
"east": {"uv": [0, 3, 16, 8], "texture": "#2"},
"south": {"uv": [0, 8, 3, 13], "texture": "#2"},
"west": {"uv": [0, 8, 16, 13], "texture": "#2"},
"up": {"uv": [0, 0, 3, 16], "texture": "#3"},
"down": {"uv": [0, 0, 3, 16], "texture": "#top"}
}
},
{
"from": [3, 3, 13],
"to": [16, 8, 16],
"faces": {
"north": {"uv": [0, 3, 13, 8], "texture": "#2"},
"east": {"uv": [0, 8, 3, 13], "texture": "#2"},
"south": {"uv": [3, 8, 16, 13], "texture": "#2"},
"west": {"uv": [13, 8, 16, 13], "texture": "#b"},
"up": {"uv": [3, 13, 16, 16], "texture": "#3"},
"down": {"uv": [3, 0, 16, 3], "texture": "#top"}
}
},
{
"from": [15, 3, 0],
"to": [16, 6, 13],
"faces": {
"north": {"uv": [0, 10, 1, 13], "texture": "#2"},
"east": {"uv": [3, 10, 16, 13], "texture": "#2"},
"south": {"uv": [15, 10, 16, 13], "texture": "#b"},
"west": {"uv": [0, 0, 13, 3], "texture": "#2"},
"up": {"uv": [15, 0, 16, 13], "texture": "#3"},
"down": {"uv": [15, 3, 16, 16], "texture": "#top"}
}
},
{
"from": [3, 7, 0],
"to": [5, 8, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [10, 13, 5]},
"faces": {
"north": {"uv": [11, 8, 13, 9], "texture": "#2"},
"east": {"uv": [3, 8, 16, 9], "texture": "#2"},
"south": {"uv": [3, 8, 5, 9], "texture": "#b"},
"west": {"uv": [0, 8, 13, 9], "texture": "#b"},
"up": {"uv": [3, 0, 5, 13], "texture": "#3"},
"down": {"uv": [3, 3, 5, 16], "texture": "#b"}
}
},
{
"from": [5, 6, 12],
"to": [16, 8, 13],
"rotation": {"angle": 0, "axis": "y", "origin": [11, 13, 5]},
"faces": {
"north": {"uv": [0, 0, 11, 2], "texture": "#2"},
"east": {"uv": [3, 8, 4, 10], "texture": "#2"},
"south": {"uv": [5, 8, 16, 10], "texture": "#b"},
"west": {"uv": [12, 8, 13, 10], "texture": "#b"},
"up": {"uv": [5, 12, 16, 13], "texture": "#3"},
"down": {"uv": [5, 3, 16, 4], "texture": "#top"}
}
},
{
"from": [5, 4.5, -3],
"to": [13, 5, 5],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 1]},
"faces": {
"north": {"uv": [5, 13, 13, 13.5], "texture": "#b"},
"east": {"uv": [11, 11, 16, 11.5], "texture": "#b"},
"south": {"uv": [5, 11, 13, 11.5], "texture": "#b"},
"west": {"uv": [0, 11, 5, 11.5], "texture": "#b"},
"up": {"uv": [5, 4, 13, 12], "texture": "#b"},
"down": {"uv": [5, 4, 13, 12], "texture": "#b"}
}
},
{
"from": [8, 5, 0],
"to": [10, 5.5, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1]},
"faces": {
"north": {"uv": [6, 10.5, 8, 11], "texture": "#2"},
"east": {"uv": [14, 10.5, 16, 11], "texture": "#b"},
"south": {"uv": [8, 10.5, 10, 11], "texture": "#b"},
"west": {"uv": [0, 10.5, 2, 11], "texture": "#b"},
"up": {"uv": [8, 0, 10, 2], "texture": "#b"},
"down": {"uv": [8, 14, 10, 16], "texture": "#b"}
}
},
{
"from": [8, 4, 0],
"to": [10, 4.5, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1]},
"faces": {
"north": {"uv": [6, 11.5, 8, 12], "texture": "#b"},
"east": {"uv": [14, 11.5, 16, 12], "texture": "#b"},
"south": {"uv": [8, 11.5, 10, 12], "texture": "#b"},
"west": {"uv": [0, 11.5, 2, 12], "texture": "#b"},
"up": {"uv": [8, 0, 10, 2], "texture": "#b"},
"down": {"uv": [8, 14, 10, 16], "texture": "#b"}
}
},
{
"from": [3, 3.5, 0],
"to": [10, 4, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1]},
"faces": {
"north": {"uv": [8, 6, 15, 6.5], "texture": "#2"},
"east": {"uv": [7, 12, 9, 12.5], "texture": "#2"},
"south": {"uv": [3, 12, 10, 12.5], "texture": "#2"},
"west": {"uv": [7, 12, 9, 12.5], "texture": "#2"},
"up": {"uv": [3, 7, 10, 9], "texture": "#2"},
"down": {"uv": [3, 7, 10, 9], "texture": "#2"}
}
},
{
"from": [3, 5.5, 0],
"to": [10, 6, 2],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 1]},
"faces": {
"north": {"uv": [6, 10, 13, 10.5], "texture": "#2"},
"east": {"uv": [7, 10, 9, 10.5], "texture": "#2"},
"south": {"uv": [3, 10, 10, 10.5], "texture": "#2"},
"west": {"uv": [7, 10, 9, 10.5], "texture": "#2"},
"up": {"uv": [9, 0, 16, 2], "texture": "#2"},
"down": {"uv": [3, 7, 10, 9], "texture": "#2"}
}
},
{
"from": [6, 4.5, -4],
"to": [12, 5, -3],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 1]},
"faces": {
"north": {"uv": [7, 12, 13, 12.5], "texture": "#b"},
"east": {"uv": [16, 11, 16, 11.5], "texture": "#b"},
"south": {"uv": [6, 11, 12, 11.5], "texture": "#b"},
"west": {"uv": [0, 11, 0, 11.5], "texture": "#b"},
"up": {"uv": [6, 12, 12, 13], "texture": "#b"},
"down": {"uv": [6, 3, 12, 4], "texture": "#b"}
}
},
{
"from": [4, 4.5, -2],
"to": [5, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 1]},
"faces": {
"north": {"uv": [11, 12, 12, 12.5], "texture": "#b"},
"east": {"uv": [12, 11, 16, 11.5], "texture": "#b"},
"south": {"uv": [4, 11, 5, 11.5], "texture": "#b"},
"west": {"uv": [0, 11, 4, 11.5], "texture": "#b"},
"up": {"uv": [4, 4, 5, 12], "texture": "#b"},
"down": {"uv": [4, 5, 5, 11], "texture": "#b"}
}
},
{
"from": [13, 4.5, -2],
"to": [14, 5, 4],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 1]},
"faces": {
"north": {"uv": [2, 11, 3, 11.5], "texture": "#b"},
"east": {"uv": [6, 12, 10, 12.5], "texture": "#b"},
"south": {"uv": [13, 11, 14, 11.5], "texture": "#b"},
"west": {"uv": [0, 11, 4, 11.5], "texture": "#b"},
"up": {"uv": [13, 5, 14, 11], "texture": "#b"},
"down": {"uv": [13, 5, 14, 11], "texture": "#b"}
}
},
{
"from": [6, 4.5, 5],
"to": [12, 5, 6],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 7.5, 1]},
"faces": {
"north": {"uv": [4, 11, 10, 11.5], "texture": "#b"},
"east": {"uv": [10, 11, 11, 11.5], "texture": "#b"},
"south": {"uv": [6, 11, 12, 11.5], "texture": "#b"},
"west": {"uv": [5, 11, 6, 11.5], "texture": "#b"},
"up": {"uv": [6, 12, 12, 13], "texture": "#b"},
"down": {"uv": [6, 10, 12, 11], "texture": "#b"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [85, 3, -10],
"translation": [1.75, -0.75, -2.25],
"scale": [0.35, 0.35, 0.35]
},
"ground": {
"translation": [0, -0.75, 0],
"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]
}
}
}

View file

@ -0,0 +1 @@
{ "parent": "engineersdecor:block/device/factory_placer_model" }

View file

@ -0,0 +1 @@
{ "parent": "engineersdecor:block/device/small_block_breaker_model_active" }

View file

@ -0,0 +1 @@
{ "parent": "engineersdecor:block/device/small_fluid_funnel_model_s0" }

View file

@ -0,0 +1 @@
{ "parent": "engineersdecor:block/device/small_tree_cutter_model" }

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

View file

@ -0,0 +1 @@
{ "animation":{ "frames": [0,1], "frametime":2, "interpolate":false }}

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Some files were not shown because too many files have changed in this diff Show more