1.14: Small Tree Cutter and Block Placer/Planter ported. Factory Hopper recipe added.

This commit is contained in:
stfwi 2019-10-29 18:08:50 +01:00
parent 297b840b65
commit 5d04509eca
36 changed files with 1915 additions and 13 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-b2

View file

@ -10,6 +10,8 @@ Mod sources for Minecraft version 1.12.2.
----
## Version history
~ v1.0.15-b2 [A]
- v1.0.15-b1 [A] Added Floor Edge Light.
[A] Added Factory Block Placer and Planter.

View file

@ -8,15 +8,10 @@
*/
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;

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

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

View file

@ -1,6 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/engineers-decor/",
"1.14.4": {
"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 +29,6 @@
},
"promos": {
"1.14.4-recommended": "",
"1.14.4-latest": "1.0.15-b1"
"1.14.4-latest": "1.0.15-b2"
}
}

View file

@ -11,6 +11,10 @@ Mod sources for Minecraft version 1.14.4.
## Version history
- 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,12 @@ 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 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 +398,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),
@ -471,10 +483,12 @@ public class ModContent
TREATED_WOOD_CRAFTING_TABLE,
SMALL_LAB_FURNACE,
FACTORY_DROPPER,
FACTORY_PLACER,
FACTORY_HOPPER,
SMALL_ELECTRICAL_FURNACE,
SMALL_WASTE_INCINERATOR,
SMALL_MINERAL_SMELTER,
SMALL_TREE_CUTTER,
SMALL_SOLAR_PANEL,
CLINKER_BRICK_BLOCK,
CLINKER_BRICK_SLAB,
@ -567,6 +581,11 @@ 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_FACTORY_HOPPER = TileEntityType.Builder
.create(BlockDecorHopper.BTileEntity::new, FACTORY_HOPPER)
.build(null)
@ -597,18 +616,25 @@ 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_PLACER,
TET_FACTORY_HOPPER,
TET_SMALL_ELECTRICAL_FURNACE,
TET_WASTE_INCINERATOR,
TET_STRAIGHT_PIPE_VALVE,
TET_PASSIVE_FLUID_ACCUMULATOR,
TET_MINERAL_SMELTER,
TET_SMALL_SOLAR_PANEL
TET_SMALL_SOLAR_PANEL,
TET_SMALL_TREE_CUTTER
};
//--------------------------------------------------------------------------------------------------------------------
@ -634,6 +660,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 +671,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 +687,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 +770,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

@ -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,7 @@ 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_slabs;
public final ForgeConfigSpec.BooleanValue without_halfslabs;
public final ForgeConfigSpec.BooleanValue without_direct_slab_pickup;
@ -247,6 +248,10 @@ 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_slabs = builder
.translation(ModEngineersDecor.MODID + ".config.without_slabs")
.comment("Disable horizontal half-block slab.")
@ -419,6 +424,7 @@ 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 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,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

@ -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,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_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: 637 B

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,26 @@
{
"type": "minecraft:block",
"pools": [
{
"name": "small_tree_cutter_dlt",
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"functions": [
{
"function": "minecraft:copy_name",
"source": "block_entity"
},
{
"function": "minecraft:copy_nbt",
"source": "block_entity",
"ops": []
}
],
"name": "engineersdecor:small_tree_cutter"
}
]
}
]
}

View file

@ -0,0 +1,24 @@
{
"conditions": [
{
"type": "engineersdecor:optional",
"result": "engineersdecor:factory_hopper",
"missing": ["immersiveengineering:material"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"WWW",
"WHW",
"WPW"
],
"key": {
"H": { "item": "minecraft:hopper" },
"P": { "item": "minecraft:iron_ingot" },
"W": { "tag": "minecraft:planks" }
},
"result": {
"item": "engineersdecor:factory_hopper",
"count": 1
}
}

View file

@ -0,0 +1,24 @@
{
"conditions": [
{
"type": "engineersdecor:optional",
"result": "engineersdecor:factory_placer",
"missing": ["immersiveengineering:material"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"WWW",
"WDP",
"WWW"
],
"key": {
"D": { "item": "minecraft:dispenser" },
"P": { "item": "minecraft:iron_ingot" },
"W": { "tag": "minecraft:planks" }
},
"result": {
"item": "engineersdecor:factory_placer",
"count": 1
}
}

View file

@ -0,0 +1,33 @@
{
"conditions": [
{
"type": "engineersdecor:optional",
"result": "engineersdecor:small_solar_panel",
"missing": ["immersiveengineering:metal_device1"]
}
],
"type": "minecraft:crafting_shaped",
"pattern": [
"PPP",
"PAO",
"PRP"
],
"key": {
"O": {
"item": "minecraft:observer"
},
"P": {
"item": "minecraft:iron_ingot"
},
"A": {
"item": "minecraft:iron_axe"
},
"R": {
"item": "minecraft:redstone_block"
}
},
"result": {
"item": "engineersdecor:small_tree_cutter",
"count": 1
}
}

View file

@ -4,7 +4,7 @@
"1.12.2-recommended": "1.0.14",
"1.12.2-latest": "1.0.15-b1",
"1.14.4-recommended": "",
"1.14.4-latest": "1.0.15-b1"
"1.14.4-latest": "1.0.15-b2"
},
"1.12.2": {
"1.0.15-b1": "[A] Added Floor Edge Light.\n[A] Added Factory Block Placer and Planter.",
@ -69,6 +69,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."
},
"1.14.4": {
"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.",

View file

@ -67,6 +67,11 @@ looking manufacturing contraptions. Current feature set:
allowed to be picked up and on the ground. This prevents that the hopper snatches
blocks that you break when building before you can pick them up yourself.
- *Factory Block Placer*: Places blocks or plants crops/saplings in front of it.
Supports spike planting, means it can plant e.g. from underneath the soil block.
Automatically spits out items that it cannot plant or place. Can be redstone
controlled similar to the Factory Hopper (invertible, pulse/continuous mode).
- *Small Waste Incinerator*: Buffered and delayed item disposal device. 16 fifo
slots are filled when new items are pushed in from any side. A GUI allows to
take out accidentally trashed items or put in items to get rid of. When the fifo