Mechanical Ploughs

- Added the Mechanical Plough
- Mounted Drills now briefly stall a contraption after breaking a block below falling blocks (sand, gravel, etc)
- Mechanical Saws can now harvest Bamboo, Chorus, Kelp, Sugar Cane, Cactus, Melons and Pumpkins
- Fixed harvester position inconsistency when moved vertically
- Fixed saws/drills not hurting entities in some cases
- Fixed saws/drill throwing back hurt entities in weird directions
This commit is contained in:
simibubi 2020-04-29 19:12:42 +02:00
parent 83cfc097ae
commit 45897e4c18
18 changed files with 550 additions and 95 deletions

View File

@ -14,6 +14,7 @@ import com.simibubi.create.modules.IModule;
import com.simibubi.create.modules.contraptions.CasingBlock; import com.simibubi.create.modules.contraptions.CasingBlock;
import com.simibubi.create.modules.contraptions.components.actors.DrillBlock; import com.simibubi.create.modules.contraptions.components.actors.DrillBlock;
import com.simibubi.create.modules.contraptions.components.actors.HarvesterBlock; import com.simibubi.create.modules.contraptions.components.actors.HarvesterBlock;
import com.simibubi.create.modules.contraptions.components.actors.PloughBlock;
import com.simibubi.create.modules.contraptions.components.actors.PortableStorageInterfaceBlock; import com.simibubi.create.modules.contraptions.components.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.modules.contraptions.components.clock.CuckooClockBlock; import com.simibubi.create.modules.contraptions.components.clock.CuckooClockBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.bearing.ClockworkBearingBlock; import com.simibubi.create.modules.contraptions.components.contraptions.bearing.ClockworkBearingBlock;
@ -162,9 +163,10 @@ public enum AllBlocks {
ROTATION_CHASSIS(new RadialChassisBlock()), ROTATION_CHASSIS(new RadialChassisBlock()),
DRILL(new DrillBlock()), DRILL(new DrillBlock()),
SAW(new SawBlock()), SAW(new SawBlock()),
HARVESTER(new HarvesterBlock()),
DEPLOYER(new DeployerBlock()), DEPLOYER(new DeployerBlock()),
PORTABLE_STORAGE_INTERFACE(new PortableStorageInterfaceBlock()), PORTABLE_STORAGE_INTERFACE(new PortableStorageInterfaceBlock()),
PLOUGH(new PloughBlock()),
HARVESTER(new HarvesterBlock()),
ANALOG_LEVER(new AnalogLeverBlock()), ANALOG_LEVER(new AnalogLeverBlock()),
ANDESITE_CASING(new CasingBlock("andesite_casing")), ANDESITE_CASING(new CasingBlock("andesite_casing")),

View File

@ -1,6 +1,7 @@
package com.simibubi.create.foundation.utility; package com.simibubi.create.foundation.utility;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -8,8 +9,16 @@ import java.util.Set;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import net.minecraft.block.BambooBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.CactusBlock;
import net.minecraft.block.ChorusFlowerBlock;
import net.minecraft.block.ChorusPlantBlock;
import net.minecraft.block.KelpBlock;
import net.minecraft.block.KelpTopBlock;
import net.minecraft.block.LeavesBlock; import net.minecraft.block.LeavesBlock;
import net.minecraft.block.SugarCaneBlock;
import net.minecraft.tags.BlockTags; import net.minecraft.tags.BlockTags;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -40,6 +49,41 @@ public class TreeCutter {
Set<BlockPos> visited = new HashSet<>(); Set<BlockPos> visited = new HashSet<>();
List<BlockPos> frontier = new LinkedList<>(); List<BlockPos> frontier = new LinkedList<>();
// Bamboo, Sugar Cane, Cactus
BlockState stateAbove = reader.getBlockState(pos.up());
if (isVerticalPlant(stateAbove)) {
logs.add(pos.up());
for (int i = 1; i < 256; i++) {
BlockPos current = pos.up(i);
if (!isVerticalPlant(reader.getBlockState(current)))
break;
logs.add(current);
}
Collections.reverse(logs);
return new Tree(logs, leaves);
}
// Chorus
if (isChorus(stateAbove)) {
frontier.add(pos.up());
while (!frontier.isEmpty()) {
BlockPos current = frontier.remove(0);
visited.add(current);
logs.add(current);
for (Direction direction : Iterate.directions) {
BlockPos offset = current.offset(direction);
if (visited.contains(offset))
continue;
if (!isChorus(reader.getBlockState(offset)))
continue;
frontier.add(offset);
}
}
Collections.reverse(logs);
return new Tree(logs, leaves);
}
// Regular Tree
if (!validateCut(reader, pos)) if (!validateCut(reader, pos))
return null; return null;
@ -94,6 +138,25 @@ public class TreeCutter {
return new Tree(logs, leaves); return new Tree(logs, leaves);
} }
public static boolean isChorus(BlockState stateAbove) {
return stateAbove.getBlock() instanceof ChorusPlantBlock || stateAbove.getBlock() instanceof ChorusFlowerBlock;
}
public static boolean isVerticalPlant(BlockState stateAbove) {
Block block = stateAbove.getBlock();
if (block instanceof BambooBlock)
return true;
if (block instanceof CactusBlock)
return true;
if (block instanceof SugarCaneBlock)
return true;
if (block instanceof KelpBlock)
return true;
if (block instanceof KelpTopBlock)
return true;
return false;
}
/** /**
* Checks whether a tree was fully cut by seeing whether the layer above the cut * Checks whether a tree was fully cut by seeing whether the layer above the cut
* is not supported by any more logs. * is not supported by any more logs.

View File

@ -0,0 +1,60 @@
package com.simibubi.create.modules.contraptions.components.actors;
import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.modules.contraptions.components.contraptions.IPortableBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.HorizontalBlock;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
public abstract class AttachedActorBlock extends HorizontalBlock implements IPortableBlock {
public AttachedActorBlock() {
super(Properties.from(Blocks.IRON_BLOCK));
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
Direction direction = state.get(HORIZONTAL_FACING);
return AllShapes.HARVESTER_BASE.get(direction);
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
builder.add(HORIZONTAL_FACING);
super.fillStateContainer(builder);
}
@Override
public boolean isValidPosition(BlockState state, IWorldReader worldIn, BlockPos pos) {
Direction direction = state.get(HORIZONTAL_FACING);
BlockPos offset = pos.offset(direction.getOpposite());
return Block.hasSolidSide(worldIn.getBlockState(offset), worldIn, offset, direction);
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
Direction facing;
if (context.getFace().getAxis().isVertical())
facing = context.getPlacementHorizontalFacing().getOpposite();
else {
BlockState blockState =
context.getWorld().getBlockState(context.getPos().offset(context.getFace().getOpposite()));
if (blockState.getBlock() instanceof AttachedActorBlock)
facing = blockState.get(HORIZONTAL_FACING);
else
facing = context.getFace();
}
return getDefaultState().with(HORIZONTAL_FACING, facing);
}
}

View File

@ -6,9 +6,11 @@ import com.simibubi.create.modules.contraptions.components.contraptions.Movement
import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext; import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.FallingBlock;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.item.minecart.AbstractMinecartEntity; import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.DamageSource; import net.minecraft.util.DamageSource;
@ -32,30 +34,10 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour {
World world = context.world; World world = context.world;
BlockState stateVisited = world.getBlockState(pos); BlockState stateVisited = world.getBlockState(pos);
if (!stateVisited.isNormalCube(world, pos))
damageEntities(context, pos, world);
if (world.isRemote) if (world.isRemote)
return; return;
if (stateVisited.getCollisionShape(world, pos).isEmpty()) {
DamageSource damageSource = getDamageSource();
if (damageSource == null)
return;
for (Entity entity : world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(pos))) {
if (entity instanceof ItemEntity)
return;
if (entity instanceof ContraptionEntity)
return;
if (entity instanceof AbstractMinecartEntity)
for (Entity passenger : entity.getRecursivePassengers())
if (passenger instanceof ContraptionEntity
&& ((ContraptionEntity) passenger).getContraption() == context.contraption)
return;
float damage = (float) MathHelper.clamp(Math.abs(context.relativeMotion.length() * 10) + 1, 5, 20);
entity.attackEntityFrom(damageSource, damage);
entity.setMotion(entity.getMotion().add(context.relativeMotion.scale(3)));
}
return;
}
if (!canBreak(world, pos, stateVisited)) if (!canBreak(world, pos, stateVisited))
return; return;
@ -63,10 +45,39 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour {
context.stall = true; context.stall = true;
} }
public void damageEntities(MovementContext context, BlockPos pos, World world) {
DamageSource damageSource = getDamageSource();
if (damageSource == null && !throwsEntities())
return;
Entities: for (Entity entity : world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(pos))) {
if (entity instanceof ItemEntity)
continue;
if (entity instanceof ContraptionEntity)
continue;
if (entity instanceof AbstractMinecartEntity)
for (Entity passenger : entity.getRecursivePassengers())
if (passenger instanceof ContraptionEntity
&& ((ContraptionEntity) passenger).getContraption() == context.contraption)
continue Entities;
float damage = (float) MathHelper.clamp(Math.abs(context.relativeMotion.length() * 10) + 1, 5, 20);
if (damageSource != null && !world.isRemote)
entity.attackEntityFrom(damageSource, damage);
if (throwsEntities() && (world.isRemote == (entity instanceof PlayerEntity))) {
entity.setMotion(entity.getMotion().add(context.motion.add(0, context.motion.length() / 4f, 0)));
entity.velocityChanged = true;
}
}
}
protected DamageSource getDamageSource() { protected DamageSource getDamageSource() {
return null; return null;
} }
protected boolean throwsEntities() {
return getDamageSource() != null;
}
@Override @Override
public void stopMoving(MovementContext context) { public void stopMoving(MovementContext context) {
CompoundNBT data = context.data; CompoundNBT data = context.data;
@ -89,6 +100,27 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour {
@Override @Override
public void tick(MovementContext context) { public void tick(MovementContext context) {
tickBreaker(context);
CompoundNBT data = context.data;
if (!data.contains("WaitingTicks"))
return;
int waitingTicks = data.getInt("WaitingTicks");
if (waitingTicks-- > 0) {
data.putInt("WaitingTicks", waitingTicks);
context.stall = true;
return;
}
BlockPos pos = NBTUtil.readBlockPos(data.getCompound("LastPos"));
data.remove("WaitingTicks");
data.remove("LastPos");
context.stall = false;
visitNewPosition(context, pos);
}
public void tickBreaker(MovementContext context) {
CompoundNBT data = context.data; CompoundNBT data = context.data;
if (context.world.isRemote) if (context.world.isRemote)
return; return;
@ -129,13 +161,13 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour {
if (destroyProgress >= 10) { if (destroyProgress >= 10) {
BlockHelper.destroyBlock(context.world, breakingPos, 1f, stack -> this.dropItem(context, stack)); BlockHelper.destroyBlock(context.world, breakingPos, 1f, stack -> this.dropItem(context, stack));
onBlockBroken(context, breakingPos); context.stall = false;
onBlockBroken(context, breakingPos, stateToBreak);
ticksUntilNextProgress = -1; ticksUntilNextProgress = -1;
world.sendBlockBreakProgress(id, breakingPos, -1); world.sendBlockBreakProgress(id, breakingPos, -1);
data.remove("Progress"); data.remove("Progress");
data.remove("TicksUntilNextProgress"); data.remove("TicksUntilNextProgress");
data.remove("BreakingPos"); data.remove("BreakingPos");
context.stall = false;
return; return;
} }
@ -150,7 +182,15 @@ public class BlockBreakingMovementBehaviour extends MovementBehaviour {
return BlockBreakingKineticTileEntity.isBreakable(state, blockHardness); return BlockBreakingKineticTileEntity.isBreakable(state, blockHardness);
} }
protected void onBlockBroken(MovementContext context, BlockPos pos) { protected void onBlockBroken(MovementContext context, BlockPos pos, BlockState brokenState) {
BlockState above = context.world.getBlockState(pos.up());
if (!(above.getBlock() instanceof FallingBlock))
return;
CompoundNBT data = context.data;
data.putInt("WaitingTicks", 10);
data.put("LastPos", NBTUtil.writeBlockPos(pos));
context.stall = true;
} }
} }

View File

@ -4,8 +4,11 @@ import com.simibubi.create.foundation.utility.SuperByteBuffer;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext; import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext;
import net.minecraft.block.BlockState;
import net.minecraft.util.DamageSource; import net.minecraft.util.DamageSource;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
@ -33,4 +36,9 @@ public class DrillMovementBehaviour extends BlockBreakingMovementBehaviour {
return DrillBlock.damageSourceDrill; return DrillBlock.damageSourceDrill;
} }
@Override
public boolean canBreak(World world, BlockPos breakingPos, BlockState state) {
return super.canBreak(world, breakingPos, state) && !state.getCollisionShape(world, breakingPos).isEmpty();
}
} }

View File

@ -1,34 +1,17 @@
package com.simibubi.create.modules.contraptions.components.actors; package com.simibubi.create.modules.contraptions.components.actors;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.modules.contraptions.components.contraptions.IPortableBlock; import com.simibubi.create.modules.contraptions.components.contraptions.IPortableBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour; import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.HorizontalBlock;
import net.minecraft.block.material.PushReaction;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
public class HarvesterBlock extends HorizontalBlock implements IPortableBlock { public class HarvesterBlock extends AttachedActorBlock implements IPortableBlock {
public static MovementBehaviour MOVEMENT = new HarvesterMovementBehaviour(); public static MovementBehaviour MOVEMENT = new HarvesterMovementBehaviour();
public HarvesterBlock() {
super(Properties.from(Blocks.IRON_BLOCK));
}
@Override @Override
public boolean hasTileEntity(BlockState state) { public boolean hasTileEntity(BlockState state) {
return true; return true;
@ -39,51 +22,11 @@ public class HarvesterBlock extends HorizontalBlock implements IPortableBlock {
return new HarvesterTileEntity(); return new HarvesterTileEntity();
} }
@Override
public PushReaction getPushReaction(BlockState state) {
return PushReaction.PUSH_ONLY;
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
Direction direction = state.get(HORIZONTAL_FACING);
return AllShapes.HARVESTER_BASE.get(direction);
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
builder.add(HORIZONTAL_FACING);
super.fillStateContainer(builder);
}
@Override @Override
public BlockRenderLayer getRenderLayer() { public BlockRenderLayer getRenderLayer() {
return BlockRenderLayer.CUTOUT; return BlockRenderLayer.CUTOUT;
} }
@Override
public boolean isValidPosition(BlockState state, IWorldReader worldIn, BlockPos pos) {
Direction direction = state.get(HORIZONTAL_FACING);
BlockPos offset = pos.offset(direction.getOpposite());
return Block.hasSolidSide(worldIn.getBlockState(offset), worldIn, offset, direction);
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
Direction facing;
if (context.getFace().getAxis().isVertical())
facing = context.getPlacementHorizontalFacing().getOpposite();
else {
BlockState blockState =
context.getWorld().getBlockState(context.getPos().offset(context.getFace().getOpposite()));
if (AllBlocks.HARVESTER.typeOf(blockState))
facing = blockState.get(HORIZONTAL_FACING);
else
facing = context.getFace();
}
return getDefaultState().with(HORIZONTAL_FACING, facing);
}
@Override @Override
public MovementBehaviour getMovementBehaviour() { public MovementBehaviour getMovementBehaviour() {
return MOVEMENT; return MOVEMENT;

View File

@ -42,7 +42,7 @@ public class HarvesterMovementBehaviour extends MovementBehaviour {
@Override @Override
public Vec3d getActiveAreaOffset(MovementContext context) { public Vec3d getActiveAreaOffset(MovementContext context) {
return new Vec3d(context.state.get(HORIZONTAL_FACING).getDirectionVec()).scale(.5); return new Vec3d(context.state.get(HORIZONTAL_FACING).getDirectionVec()).scale(.45);
} }
@Override @Override

View File

@ -0,0 +1,34 @@
package com.simibubi.create.modules.contraptions.components.actors;
import java.util.UUID;
import com.mojang.authlib.GameProfile;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.util.FakePlayer;
public class PloughBlock extends AttachedActorBlock {
public static MovementBehaviour MOVEMENT = new PloughMovementBehaviour();
@Override
public MovementBehaviour getMovementBehaviour() {
return MOVEMENT;
}
/**
* The OnHoeUse event takes a player, so we better not pass null
*/
static class PloughFakePlayer extends FakePlayer {
public static final GameProfile PLOUGH_PROFILE =
new GameProfile(UUID.fromString("9e2faded-eeee-4ec2-c314-dad129ae971d"), "Plough");
public PloughFakePlayer(ServerWorld world) {
super(world, PLOUGH_PROFILE);
}
}
}

View File

@ -0,0 +1,88 @@
package com.simibubi.create.modules.contraptions.components.actors;
import static net.minecraft.block.HorizontalBlock.HORIZONTAL_FACING;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.components.actors.PloughBlock.PloughFakePlayer;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.item.Items;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceContext.BlockMode;
import net.minecraft.util.math.RayTraceContext.FluidMode;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
public class PloughMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override
public boolean isActive(MovementContext context) {
return !VecHelper.isVecPointingTowards(context.relativeMotion,
context.state.get(HORIZONTAL_FACING).getOpposite());
}
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
super.visitNewPosition(context, pos);
World world = context.world;
if (world.isRemote)
return;
BlockPos below = pos.down();
if (!world.isBlockPresent(below))
return;
Vec3d vec = VecHelper.getCenterOf(pos);
PloughFakePlayer player = getPlayer(context);
if (player == null)
return;
BlockRayTraceResult ray = world
.rayTraceBlocks(new RayTraceContext(vec, vec.add(0, -1, 0), BlockMode.OUTLINE, FluidMode.NONE, player));
if (ray == null || ray.getType() != Type.BLOCK)
return;
ItemUseContext ctx = new ItemUseContext(player, Hand.MAIN_HAND, ray);
new ItemStack(Items.DIAMOND_HOE).onItemUse(ctx);
}
@Override
public Vec3d getActiveAreaOffset(MovementContext context) {
return new Vec3d(context.state.get(HORIZONTAL_FACING).getDirectionVec()).scale(.45);
}
@Override
protected boolean throwsEntities() {
return true;
}
@Override
public boolean canBreak(World world, BlockPos breakingPos, BlockState state) {
return state.getCollisionShape(world, breakingPos).isEmpty();
}
@Override
public void stopMoving(MovementContext context) {
if (context.temporaryData instanceof PloughFakePlayer)
((PloughFakePlayer) context.temporaryData).remove();
}
private PloughFakePlayer getPlayer(MovementContext context) {
if (!(context.temporaryData instanceof PloughFakePlayer) && context.world instanceof ServerWorld) {
PloughFakePlayer player = new PloughFakePlayer((ServerWorld) context.world);
player.setHeldItem(Hand.MAIN_HAND, new ItemStack(Items.DIAMOND_HOE));
context.temporaryData = player;
}
return (PloughFakePlayer) context.temporaryData;
}
}

View File

@ -6,6 +6,7 @@ import com.simibubi.create.foundation.utility.TreeCutter.Tree;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext; import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext;
import com.simibubi.create.modules.contraptions.components.saw.SawBlock; import com.simibubi.create.modules.contraptions.components.saw.SawBlock;
import com.simibubi.create.modules.contraptions.components.saw.SawTileEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.item.ItemEntity;
@ -31,12 +32,13 @@ public class SawMovementBehaviour extends BlockBreakingMovementBehaviour {
@Override @Override
public boolean canBreak(World world, BlockPos breakingPos, BlockState state) { public boolean canBreak(World world, BlockPos breakingPos, BlockState state) {
return super.canBreak(world, breakingPos, state) return super.canBreak(world, breakingPos, state) && SawTileEntity.isSawable(state);
&& (state.isIn(BlockTags.LOGS) || state.isIn(BlockTags.LEAVES));
} }
@Override @Override
protected void onBlockBroken(MovementContext context, BlockPos pos) { protected void onBlockBroken(MovementContext context, BlockPos pos, BlockState brokenState) {
if (brokenState.isIn(BlockTags.LEAVES))
return;
Tree tree = TreeCutter.cutTree(context.world, pos); Tree tree = TreeCutter.cutTree(context.world, pos);
if (tree != null) { if (tree != null) {
for (BlockPos log : tree.logs) for (BlockPos log : tree.logs)

View File

@ -1,6 +1,7 @@
package com.simibubi.create.modules.contraptions.components.contraptions; package com.simibubi.create.modules.contraptions.components.contraptions;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.components.actors.AttachedActorBlock;
import com.simibubi.create.modules.contraptions.components.actors.HarvesterBlock; import com.simibubi.create.modules.contraptions.components.actors.HarvesterBlock;
import com.simibubi.create.modules.contraptions.components.actors.PortableStorageInterfaceBlock; import com.simibubi.create.modules.contraptions.components.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.bearing.ClockworkBearingBlock; import com.simibubi.create.modules.contraptions.components.contraptions.bearing.ClockworkBearingBlock;
@ -175,7 +176,7 @@ public class BlockMovementTraits {
return direction == (state.get(BlockStateProperties.HANGING) ? Direction.UP : Direction.DOWN); return direction == (state.get(BlockStateProperties.HANGING) ? Direction.UP : Direction.DOWN);
if (block instanceof AbstractRailBlock) if (block instanceof AbstractRailBlock)
return direction == Direction.DOWN; return direction == Direction.DOWN;
if (block instanceof HarvesterBlock) if (block instanceof AttachedActorBlock)
return direction == state.get(HarvesterBlock.HORIZONTAL_FACING).getOpposite(); return direction == state.get(HarvesterBlock.HORIZONTAL_FACING).getOpposite();
return false; return false;
} }
@ -191,7 +192,7 @@ public class BlockMovementTraits {
return state.get(BlockStateProperties.FACING) == facing; return state.get(BlockStateProperties.FACING) == facing;
if (AllBlocks.PORTABLE_STORAGE_INTERFACE.typeOf(state)) if (AllBlocks.PORTABLE_STORAGE_INTERFACE.typeOf(state))
return state.get(PortableStorageInterfaceBlock.FACING) == facing; return state.get(PortableStorageInterfaceBlock.FACING) == facing;
if (AllBlocks.HARVESTER.typeOf(state)) if (state.getBlock() instanceof AttachedActorBlock)
return state.get(BlockStateProperties.HORIZONTAL_FACING) == facing; return state.get(BlockStateProperties.HORIZONTAL_FACING) == facing;
if (AllBlocks.ROPE_PULLEY.typeOf(state)) if (AllBlocks.ROPE_PULLEY.typeOf(state))
return facing == Direction.DOWN; return facing == Direction.DOWN;

View File

@ -24,7 +24,15 @@ import com.simibubi.create.modules.contraptions.components.actors.BlockBreakingK
import com.simibubi.create.modules.contraptions.processing.ProcessingInventory; import com.simibubi.create.modules.contraptions.processing.ProcessingInventory;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity; import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import net.minecraft.block.BambooBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.CactusBlock;
import net.minecraft.block.ChorusPlantBlock;
import net.minecraft.block.KelpBlock;
import net.minecraft.block.KelpTopBlock;
import net.minecraft.block.StemGrownBlock;
import net.minecraft.block.SugarCaneBlock;
import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -379,7 +387,29 @@ public class SawTileEntity extends BlockBreakingKineticTileEntity {
@Override @Override
public boolean canBreak(BlockState stateToBreak, float blockHardness) { public boolean canBreak(BlockState stateToBreak, float blockHardness) {
return super.canBreak(stateToBreak, blockHardness) && stateToBreak.isIn(BlockTags.LOGS); boolean sawable = isSawable(stateToBreak);
return super.canBreak(stateToBreak, blockHardness) && sawable;
}
public static boolean isSawable(BlockState stateToBreak) {
if (stateToBreak.isIn(BlockTags.LOGS) || stateToBreak.isIn(BlockTags.LEAVES))
return true;
Block block = stateToBreak.getBlock();
if (block instanceof BambooBlock)
return true;
if (block instanceof StemGrownBlock)
return true;
if (block instanceof CactusBlock)
return true;
if (block instanceof SugarCaneBlock)
return true;
if (block instanceof KelpBlock)
return true;
if (block instanceof KelpTopBlock)
return true;
if (block instanceof ChorusPlantBlock)
return true;
return false;
} }
} }

View File

@ -0,0 +1,14 @@
{
"forge_marker": 1,
"defaults": {
"model": "create:block/plough"
},
"variants": {
"facing": {
"north": { "y": 180 },
"south": {},
"east": { "y": 270 },
"west": { "y": 90 }
}
}
}

View File

@ -99,6 +99,7 @@
"block.create.drill": "Mechanical Drill", "block.create.drill": "Mechanical Drill",
"block.create.portable_storage_interface": "Portable Storage Interface", "block.create.portable_storage_interface": "Portable Storage Interface",
"block.create.harvester": "Mechanical Harvester", "block.create.harvester": "Mechanical Harvester",
"block.create.plough": "Mechanical Plough",
"block.create.saw": "Mechanical Saw", "block.create.saw": "Mechanical Saw",
"block.create.water_wheel": "Water Wheel", "block.create.water_wheel": "Water Wheel",
"block.create.mechanical_press": "Mechanical Press", "block.create.mechanical_press": "Mechanical Press",
@ -986,17 +987,22 @@
"block.create.rotation_chassis.tooltip.action1": "Makes the clicked face _Sticky_. When Chassis move, all designated blocks attached to the sticky side are moved with it.", "block.create.rotation_chassis.tooltip.action1": "Makes the clicked face _Sticky_. When Chassis move, all designated blocks attached to the sticky side are moved with it.",
"block.create.drill.tooltip": "MECHANICAL DRILL", "block.create.drill.tooltip": "MECHANICAL DRILL",
"block.create.drill.tooltip.summary": "A mechanical device suitable for _breaking_ _blocks_. It is movable with _Mechanical_ _Pistons_ or _Bearings_.", "block.create.drill.tooltip.summary": "A mechanical device suitable for _breaking_ _blocks_. It is movable with _Mechanical_ _Pistons_, _Bearings_ or other controllers.",
"block.create.drill.tooltip.condition1": "When Rotated", "block.create.drill.tooltip.condition1": "When Rotated",
"block.create.drill.tooltip.behaviour1": "Acts as a _stationary_ Block Breaker. Also _hurts_ _entities_ in its effective area.", "block.create.drill.tooltip.behaviour1": "Acts as a _stationary_ Block Breaker. Also _hurts_ _entities_ in its effective area.",
"block.create.drill.tooltip.condition2": "While Moving", "block.create.drill.tooltip.condition2": "While Moving",
"block.create.drill.tooltip.behaviour2": "Breaks Blocks with which the drill collides.", "block.create.drill.tooltip.behaviour2": "Breaks Blocks with which the drill collides.",
"block.create.harvester.tooltip": "MECHANICAL HARVESTER", "block.create.harvester.tooltip": "MECHANICAL HARVESTER",
"block.create.harvester.tooltip.summary": "A mechanical plant cutter suitable for medium scale crop automation. It is movable with _Mechanical_ _Pistons_ or _Bearings_.", "block.create.harvester.tooltip.summary": "A mechanical plant cutter suitable for medium scale crop automation. It is movable with _Mechanical_ _Pistons_, _Bearings_ or other controllers.",
"block.create.harvester.tooltip.condition1": "While Moving", "block.create.harvester.tooltip.condition1": "While Moving",
"block.create.harvester.tooltip.behaviour1": "_Harvests_ all _mature_ _crops_ which which the blade collides and reset them to their initial growth state.", "block.create.harvester.tooltip.behaviour1": "_Harvests_ all _mature_ _crops_ which which the blade collides and reset them to their initial growth state.",
"block.create.plough.tooltip": "MECHANICAL PLOUGH",
"block.create.plough.tooltip.summary": "A mechanical plough has a variety of uses. It is movable with _Mechanical_ _Pistons_, _Bearings_ or other controllers.",
"block.create.plough.tooltip.condition1": "While Moving",
"block.create.plough.tooltip.behaviour1": "_Breaks_ _blocks_ which _cannot_ _be_ _collided_ with, such as torches, tracks or snow layers. _Applies_ its _motion_ to _entities_ without hurting them. _Tills_ _soil_ _blocks_ as though a Hoe would be used on them.",
"block.create.saw.tooltip": "MECHANICAL SAW", "block.create.saw.tooltip": "MECHANICAL SAW",
"block.create.saw.tooltip.summary": "Suitable for _cutting_ _trees_ effectively and for _cutting_ _blocks_ into their carpentered counterparts. It is movable using _Mechanical_ _Pistons_ or _Bearings_.", "block.create.saw.tooltip.summary": "Suitable for _cutting_ _trees_ effectively and for _cutting_ _blocks_ into their carpentered counterparts. It is movable using _Mechanical_ _Pistons_ or _Bearings_.",
"block.create.saw.tooltip.condition1": "When facing up", "block.create.saw.tooltip.condition1": "When facing up",
@ -1025,7 +1031,7 @@
"block.create.redstone_bridge.tooltip.action2": "Toggles between _Receiver_ and _Transmitter_ Mode.", "block.create.redstone_bridge.tooltip.action2": "Toggles between _Receiver_ and _Transmitter_ Mode.",
"block.create.contact.tooltip": "REDSTONE CONTACT", "block.create.contact.tooltip": "REDSTONE CONTACT",
"block.create.contact.tooltip.summary": "Only emits redstone power in pairs. It is movable with _Mechanical_ _Pistons_ or _Bearings_.", "block.create.contact.tooltip.summary": "Only emits redstone power in pairs. It is movable with _Mechanical_ _Pistons_, _Bearings_ or other controllers.",
"block.create.contact.tooltip.condition1": "When facing other Contact", "block.create.contact.tooltip.condition1": "When facing other Contact",
"block.create.contact.tooltip.behaviour1": "Provides a _Redstone_ _Signal_.", "block.create.contact.tooltip.behaviour1": "Provides a _Redstone_ _Signal_.",
"block.create.contact.tooltip.condition2": "While Moving", "block.create.contact.tooltip.condition2": "While Moving",

View File

@ -0,0 +1,113 @@
{
"credit": "Made with Blockbench",
"textures": {
"1": "block/anvil",
"particle": "create:block/andesite_casing_short",
"andesite_casing_short": "create:block/andesite_casing_short"
},
"elements": [
{
"name": "Core",
"from": [0, 2, 0],
"to": [16, 14, 2.9],
"faces": {
"north": {"uv": [0, 4, 16, 16], "texture": "#andesite_casing_short"},
"east": {"uv": [2, 0, 14, 3], "rotation": 270, "texture": "#andesite_casing_short"},
"south": {"uv": [0, 4, 16, 16], "texture": "#andesite_casing_short"},
"west": {"uv": [2, 0, 14, 3], "rotation": 90, "texture": "#andesite_casing_short"},
"up": {"uv": [0, 0, 16, 2.9], "rotation": 180, "texture": "#andesite_casing_short"},
"down": {"uv": [0, 0, 16, 2.9], "texture": "#andesite_casing_short"}
}
},
{
"name": "Attachment",
"from": [0.1, 4, 6],
"to": [2, 7, 15],
"rotation": {"angle": 22.5, "axis": "x", "origin": [0, 10, 3]},
"faces": {
"north": {"uv": [0, 0, 1.9, 3], "texture": "#andesite_casing_short"},
"east": {"uv": [0, 0, 9, 3], "texture": "#andesite_casing_short"},
"south": {"uv": [0.1, 8, 2, 11], "texture": "#andesite_casing_short"},
"west": {"uv": [0, 0, 9, 3], "texture": "#andesite_casing_short"},
"up": {"uv": [0, 4, 1.9, 13], "texture": "#andesite_casing_short"},
"down": {"uv": [0, 4, 1.9, 13], "texture": "#andesite_casing_short"}
}
},
{
"name": "Attachment",
"from": [2.1, 5, 5],
"to": [14, 6, 17],
"rotation": {"angle": 22.5, "axis": "x", "origin": [0, 10, 3]},
"faces": {
"north": {"uv": [0, 0, 11.9, 1], "texture": "#andesite_casing_short"},
"east": {"uv": [0, 0, 12, 1], "texture": "#1"},
"south": {"uv": [2, 15, 13.9, 16], "texture": "#1"},
"west": {"uv": [0, 0, 12, 1], "texture": "#1"},
"up": {"uv": [2, 2, 13.9, 14], "texture": "#1"},
"down": {"uv": [2, 4, 13.9, 16], "texture": "#1"}
}
},
{
"name": "Attachment",
"from": [14, 4, 6],
"to": [15.9, 7, 15],
"rotation": {"angle": 22.5, "axis": "x", "origin": [0, 10, 3]},
"faces": {
"north": {"uv": [0, 0, 1.9, 3], "texture": "#andesite_casing_short"},
"east": {"uv": [0, 0, 9, 3], "texture": "#andesite_casing_short"},
"south": {"uv": [14, 8, 15.9, 11], "texture": "#andesite_casing_short"},
"west": {"uv": [0, 0, 9, 3], "texture": "#andesite_casing_short"},
"up": {"uv": [14, 4, 15.9, 13], "texture": "#andesite_casing_short"},
"down": {"uv": [14, 4, 15.9, 13], "texture": "#andesite_casing_short"}
}
},
{
"name": "Attachment",
"from": [0, 4, 2.9],
"to": [16, 7, 6],
"rotation": {"angle": 0, "axis": "x", "origin": [0, 10, 2]},
"faces": {
"north": {"uv": [0, 0, 16, 3], "texture": "#andesite_casing_short"},
"east": {"uv": [0, 0, 3.1, 3], "texture": "#andesite_casing_short"},
"south": {"uv": [0, 0, 16, 3], "texture": "#andesite_casing_short"},
"west": {"uv": [0, 0, 3.1, 3], "texture": "#andesite_casing_short"},
"up": {"uv": [0, 0, 16, 3], "rotation": 180, "texture": "#andesite_casing_short"},
"down": {"uv": [0, 0, 16, 3], "texture": "#andesite_casing_short"}
}
}
],
"display": {
"thirdperson_righthand": {
"rotation": [75, -135, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"thirdperson_lefthand": {
"rotation": [75, -135, 0],
"translation": [0, 2.5, 0],
"scale": [0.375, 0.375, 0.375]
},
"firstperson_righthand": {
"rotation": [0, -135, 0],
"scale": [0.4, 0.4, 0.4]
},
"firstperson_lefthand": {
"rotation": [0, 225, 0],
"scale": [0.4, 0.4, 0.4]
},
"ground": {
"translation": [0, 3, 0],
"scale": [0.25, 0.25, 0.25]
},
"gui": {
"rotation": [30, 45, 0],
"translation": [1, 0, 0],
"scale": [0.625, 0.625, 0.625]
},
"fixed": {
"rotation": [0, 180, 0],
"translation": [0, 0, -4],
"scale": [0.5, 0.5, 0.5]
}
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "create:block/plough"
}

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "create:plough"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@ -0,0 +1,29 @@
{
"type": "crafting_shaped",
"pattern": [
"III",
"AAA",
" C "
],
"key": {
"C": {
"item": "create:andesite_casing"
},
"I": {
"tag": "forge:plates/iron"
},
"A": {
"item": "create:andesite_alloy"
}
},
"result": {
"item": "create:plough",
"count": 1
},
"conditions": [
{
"type": "create:module",
"module": "contraptions"
}
]
}