Pulse Repeaters, Belt Funnels and working Fans

- Added the Pulse Repeater, a simple Pulse former circuit.
- Added a Belt Funnel, along with a generic belt attachment interface.
- Fans now push or pull entities based on their rotation speed.
- Added little symbols to extractor and funnel item models
This commit is contained in:
simibubi 2019-08-31 08:37:57 +02:00
parent 12baa08232
commit ebaf22ddcc
31 changed files with 1461 additions and 94 deletions

View file

@ -17,7 +17,6 @@ import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalP
import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalPistonHeadBlock;
import com.simibubi.create.modules.contraptions.receivers.constructs.PistonPoleBlock;
import com.simibubi.create.modules.contraptions.redstone.ContactBlock;
import com.simibubi.create.modules.contraptions.relays.BeltBlock;
import com.simibubi.create.modules.contraptions.relays.ClutchBlock;
import com.simibubi.create.modules.contraptions.relays.CogWheelBlock;
import com.simibubi.create.modules.contraptions.relays.EncasedBeltBlock;
@ -26,11 +25,14 @@ import com.simibubi.create.modules.contraptions.relays.GearboxBlock;
import com.simibubi.create.modules.contraptions.relays.GearshiftBlock;
import com.simibubi.create.modules.contraptions.relays.ShaftBlock;
import com.simibubi.create.modules.contraptions.relays.ShaftHalfBlock;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock;
import com.simibubi.create.modules.economy.ShopShelfBlock;
import com.simibubi.create.modules.gardens.CocoaLogBlock;
import com.simibubi.create.modules.logistics.block.BeltFunnelBlock;
import com.simibubi.create.modules.logistics.block.ExtractorBlock;
import com.simibubi.create.modules.logistics.block.FlexcrateBlock;
import com.simibubi.create.modules.logistics.block.LinkedExtractorBlock;
import com.simibubi.create.modules.logistics.block.PulseRepeaterBlock;
import com.simibubi.create.modules.logistics.block.RedstoneBridgeBlock;
import com.simibubi.create.modules.logistics.block.StockswitchBlock;
import com.simibubi.create.modules.schematics.block.CreativeCrateBlock;
@ -98,11 +100,13 @@ public enum AllBlocks {
CONTACT(new ContactBlock()),
// Logistics
PULSE_REPEATER(new PulseRepeaterBlock()),
REDSTONE_BRIDGE(new RedstoneBridgeBlock()),
STOCKSWITCH(new StockswitchBlock()),
FLEXCRATE(new FlexcrateBlock()),
EXTRACTOR(new ExtractorBlock()),
LINKED_EXTRACTOR(new LinkedExtractorBlock()),
BELT_FUNNEL(new BeltFunnelBlock()),
// Symmetry
SYMMETRY_PLANE(new PlaneSymmetryBlock()),

View file

@ -1,6 +1,6 @@
package com.simibubi.create;
import com.simibubi.create.modules.contraptions.relays.BeltItem;
import com.simibubi.create.modules.contraptions.relays.belt.BeltItem;
import com.simibubi.create.modules.curiosities.placementHandgun.BuilderGunItem;
import com.simibubi.create.modules.curiosities.placementHandgun.BuilderGunItemRenderer;
import com.simibubi.create.modules.curiosities.placementHandgun.BuilderGunModel;

View file

@ -14,17 +14,18 @@ import com.simibubi.create.modules.contraptions.receivers.EncasedFanTileEntityRe
import com.simibubi.create.modules.contraptions.receivers.TurntableTileEntity;
import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalPistonTileEntity;
import com.simibubi.create.modules.contraptions.receivers.constructs.MechanicalPistonTileEntityRenderer;
import com.simibubi.create.modules.contraptions.relays.ShaftTileEntity;
import com.simibubi.create.modules.contraptions.relays.ClutchTileEntity;
import com.simibubi.create.modules.contraptions.relays.EncasedShaftTileEntity;
import com.simibubi.create.modules.contraptions.relays.EncasedShaftTileEntityRenderer;
import com.simibubi.create.modules.contraptions.relays.BeltTileEntity;
import com.simibubi.create.modules.contraptions.relays.BeltTileEntityRenderer;
import com.simibubi.create.modules.contraptions.relays.ClutchTileEntity;
import com.simibubi.create.modules.contraptions.relays.GearboxTileEntity;
import com.simibubi.create.modules.contraptions.relays.GearboxTileEntityRenderer;
import com.simibubi.create.modules.contraptions.relays.GearshiftTileEntity;
import com.simibubi.create.modules.contraptions.relays.ShaftTileEntity;
import com.simibubi.create.modules.contraptions.relays.SplitShaftTileEntityRenderer;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntityRenderer;
import com.simibubi.create.modules.economy.ShopShelfTileEntity;
import com.simibubi.create.modules.logistics.block.BeltFunnelTileEntity;
import com.simibubi.create.modules.logistics.block.ExtractorTileEntity;
import com.simibubi.create.modules.logistics.block.FlexcrateTileEntity;
import com.simibubi.create.modules.logistics.block.LinkedExtractorTileEntity;
@ -73,8 +74,10 @@ public enum AllTileEntities {
// Logistics
REDSTONE_BRIDGE(RedstoneBridgeTileEntity::new, AllBlocks.REDSTONE_BRIDGE),
STOCKSWITCH(StockswitchTileEntity::new, AllBlocks.STOCKSWITCH),
FLEXCRATE(FlexcrateTileEntity::new, AllBlocks.FLEXCRATE), EXTRACTOR(ExtractorTileEntity::new, AllBlocks.EXTRACTOR),
FLEXCRATE(FlexcrateTileEntity::new, AllBlocks.FLEXCRATE),
EXTRACTOR(ExtractorTileEntity::new, AllBlocks.EXTRACTOR),
LINKED_EXTRACTOR(LinkedExtractorTileEntity::new, AllBlocks.LINKED_EXTRACTOR),
BELT_FUNNEL(BeltFunnelTileEntity::new, AllBlocks.BELT_FUNNEL),
// Economy
SHOP_SHELF(ShopShelfTileEntity::new, AllBlocks.SHOP_SHELF),

View file

@ -0,0 +1,19 @@
package com.simibubi.create.foundation.block;
import java.util.function.Consumer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
public interface IWithTileEntity<T extends TileEntity> {
default void withTileEntityDo(IWorld world, BlockPos pos, Consumer<T> action) {
@SuppressWarnings("unchecked")
T te = (T) world.getTileEntity(pos);
if (te == null)
return;
action.accept(te);
}
}

View file

@ -6,10 +6,10 @@ import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.base.IRotate;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.BeltTileEntity;
import com.simibubi.create.modules.contraptions.relays.EncasedBeltBlock;
import com.simibubi.create.modules.contraptions.relays.GearboxTileEntity;
import com.simibubi.create.modules.contraptions.relays.SplitShaftTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import net.minecraft.block.BlockState;
import net.minecraft.state.IProperty;
@ -66,6 +66,12 @@ public class RotationPropagator {
&& stateTo.get(EncasedBeltBlock.CONNECTED_FACE) == direction.getOpposite();
return connected ? 1 : 0;
}
// Attached Fans
if (AllBlocks.ENCASED_FAN.typeOf(stateFrom) && AllBlocks.ENCASED_FAN.typeOf(stateTo)) {
if (stateFrom.get(BlockStateProperties.AXIS) == stateTo.get(BlockStateProperties.AXIS))
return 1;
}
// Gear <-> Large Gear
if (isLargeToSmallGear(stateFrom, stateTo, diff))

View file

@ -34,13 +34,6 @@ public abstract class KineticTileEntity extends SyncedTileEntity {
public void onSpeedChanged() {
}
@Override
public void onLoad() {
if (!hasWorld())
return;
super.onLoad();
}
@Override
public void remove() {
if (world.isRemote) {

View file

@ -47,12 +47,6 @@ public class WaterWheelTileEntity extends KineticTileEntity {
flows.put(direction, speed);
}
@Override
public void onLoad() {
super.onLoad();
// updateSpeed();
}
public void updateSpeed() {
float speed = 0;
for (Integer i : flows.values())

View file

@ -1,29 +1,70 @@
package com.simibubi.create.modules.contraptions.receivers;
import com.simibubi.create.foundation.block.IWithTileEntity;
import com.simibubi.create.foundation.utility.ItemDescription;
import com.simibubi.create.modules.contraptions.relays.EncasedShaftBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
public class EncasedFanBlock extends EncasedShaftBlock {
public class EncasedFanBlock extends EncasedShaftBlock implements IWithTileEntity<EncasedFanTileEntity> {
@Override
public ItemDescription getDescription() {
return new ItemDescription(color)
.withSummary("Exchange rotational power for air flow and back.").createTabs();
return new ItemDescription(color).withSummary("Exchange rotational power for air flow and back.").createTabs();
}
@Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return new EncasedFanTileEntity();
}
@Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
Axis axisIn = state.get(AXIS);
notifyFanTile(worldIn, pos, Direction.getFacingFromAxisDirection(axisIn, AxisDirection.POSITIVE));
notifyFanTile(worldIn, pos, Direction.getFacingFromAxisDirection(axisIn, AxisDirection.NEGATIVE));
}
@Override
public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
boolean isMoving) {
Axis axisIn = state.get(AXIS);
notifyFanTile(worldIn, pos, Direction.getFacingFromAxisDirection(axisIn, AxisDirection.POSITIVE));
notifyFanTile(worldIn, pos, Direction.getFacingFromAxisDirection(axisIn, AxisDirection.NEGATIVE));
}
@Override
public BlockState updatePostPlacement(BlockState stateIn, Direction facing, BlockState facingState, IWorld worldIn,
BlockPos currentPos, BlockPos facingPos) {
if (facing.getAxis() == stateIn.get(AXIS))
notifyFanTile(worldIn, currentPos, facing);
return stateIn;
}
protected void notifyFanTile(IWorld world, BlockPos pos, Direction facing) {
withTileEntityDo(world, pos, te -> te.setNeighbour(facing, world.getBlockState(pos.offset(facing))));
}
@Override
public BlockRenderLayer getRenderLayer() {
return BlockRenderLayer.CUTOUT;
}
public static boolean canAirPassThrough(World world, BlockPos pos, Direction direction) {
if (!world.isBlockPresent(pos))
return true;
BlockState state = world.getBlockState(pos);
return !Block.hasSolidSide(state, world, pos, direction.getOpposite());
}
}

View file

@ -1,12 +1,400 @@
package com.simibubi.create.modules.contraptions.receivers;
import static net.minecraft.state.properties.BlockStateProperties.AXIS;
import static net.minecraft.util.Direction.AxisDirection.NEGATIVE;
import static net.minecraft.util.Direction.AxisDirection.POSITIVE;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
public class EncasedFanTileEntity extends KineticTileEntity {
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.particles.BlockParticleData;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.particles.RedstoneParticleData;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
public class EncasedFanTileEntity extends KineticTileEntity implements ITickableTileEntity {
public static final int PUSH_DISTANCE_MAX = 20;
public static final int PULL_DISTANCE_MAX = 5;
public static final int DISTANCE_ARGMAX = 6400;
public static final float PUSH_FORCE_MAX = 20;
public static final float PULL_FORCE_MAX = 10;
public static final int FORCE_ARGMAX = 6400;
public static final int BLOCK_CHECK_UPDATE_DELAY = 100;
public static final Map<Block, List<FanEffect>> effects = new HashMap<>();
private static DamageSource damageSourceFire = new DamageSource("create.fan_fire").setDifficultyScaled()
.setFireDamage();
private static DamageSource damageSourceLava = new DamageSource("create.fan_lava").setDifficultyScaled()
.setFireDamage();
protected BlockState frontBlock;
protected BlockState backBlock;
protected float pushDistance;
protected float pullDistance;
protected float pushForce;
protected float pullForce;
protected AxisAlignedBB frontBB;
protected AxisAlignedBB backBB;
protected int blockCheckCooldown;
protected boolean findLoadedItems;
public List<ProcessedItem> items;
public class ProcessedItem {
private UUID loadedUUID;
private ItemEntity entity;
private int processingTimeLeft;
public ProcessedItem(UUID uuid, int timeLeft) {
loadedUUID = uuid;
processingTimeLeft = timeLeft;
}
public ProcessedItem(ItemEntity item) {
entity = item;
}
}
protected static class FanEffect {
private IParticleData particle;
private float density;
private float chance;
private float spread;
private float speed;
private Random r;
public FanEffect(IParticleData particle, float density, float chance, float spread, float speed) {
r = new Random();
this.particle = particle;
this.density = density;
this.chance = chance;
this.spread = spread;
this.speed = speed;
}
public void render(Vec3i directionVec, boolean front, EncasedFanTileEntity te) {
render(directionVec, front ? .5f : -te.pullDistance, front ? te.pushDistance : -.5f, te);
}
private void render(Vec3i directionVec, float start, float end, EncasedFanTileEntity te) {
float x = directionVec.getX();
float y = directionVec.getY();
float z = directionVec.getZ();
float speed = this.speed * Math.abs(te.speed) / 512f;
for (float offset = start; offset < end; offset += density) {
if (r.nextFloat() > chance)
continue;
float xs = rollOffset() * spread;
float ys = rollOffset() * spread;
float zs = rollOffset() * spread;
float xs2 = rollOffset() * spread;
float ys2 = rollOffset() * spread;
float zs2 = rollOffset() * spread;
te.world.addParticle(particle, te.pos.getX() + .5f + x * offset + xs2,
te.pos.getY() + .5f + y * offset + ys2, te.pos.getZ() + .5f + z * offset + zs2, x * speed + xs,
y * speed + ys, z * speed + zs);
}
}
private float rollOffset() {
return (r.nextFloat() - .5f) * 2;
}
}
public EncasedFanTileEntity() {
super(AllTileEntities.ENCASED_FAN.type);
blockCheckCooldown = BLOCK_CHECK_UPDATE_DELAY;
frontBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
backBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
items = new ArrayList<>();
if (effects.isEmpty())
initEffects();
}
private static void initEffects() {
effects.clear();
List<FanEffect> standardFX = new ArrayList<>(2);
standardFX.add(new FanEffect(ParticleTypes.BUBBLE_POP, 1 / 4f, 1 / 8f, 1 / 8f, 1));
standardFX.add(new FanEffect(new RedstoneParticleData(1, 1, 1, 1), 1 / 2f, 1 / 32f, 0f, 512f));
effects.put(Blocks.AIR, standardFX);
List<FanEffect> waterFX = new ArrayList<>(2);
waterFX.add(new FanEffect(new BlockParticleData(ParticleTypes.BLOCK, Blocks.WATER.getDefaultState()), 1 / 4f,
1 / 2f, 1 / 4f, 1));
waterFX.add(new FanEffect(ParticleTypes.SPLASH, 1 / 4f, 1 / 2f, 0.5f, 1));
effects.put(Blocks.WATER, waterFX);
List<FanEffect> fireFX = new ArrayList<>(2);
fireFX.add(new FanEffect(ParticleTypes.LARGE_SMOKE, 1 / 4f, 1 / 8f, 0.125f, .5f));
fireFX.add(new FanEffect(ParticleTypes.FLAME, 1 / 4f, 1 / 8f, 1 / 32f, 1 / 256f));
effects.put(Blocks.FIRE, fireFX);
List<FanEffect> lavaFX = new ArrayList<>(3);
lavaFX.add(new FanEffect(new BlockParticleData(ParticleTypes.BLOCK, Blocks.LAVA.getDefaultState()), 1 / 4f,
1 / 2f, 1 / 4f, 1));
lavaFX.add(new FanEffect(ParticleTypes.LAVA, 1 / 2f, 1 / 32f, 0, .25f));
lavaFX.add(new FanEffect(ParticleTypes.FLAME, 1 / 4f, 1 / 32f, 1 / 32f, 1 / 256f));
effects.put(Blocks.LAVA, lavaFX);
}
@Override
public void readClientUpdate(CompoundNBT tag) {
super.readClientUpdate(tag);
pushDistance = tag.getFloat("PushDistance");
pullDistance = tag.getFloat("PullDistance");
pushForce = tag.getFloat("PushForce");
pullForce = tag.getFloat("PullForce");
updateBothNeighbours();
updateBBs();
}
@Override
public CompoundNBT writeToClient(CompoundNBT tag) {
super.writeToClient(tag);
tag.putFloat("PushDistance", pushDistance);
tag.putFloat("PullDistance", pullDistance);
tag.putFloat("PushForce", pushForce);
tag.putFloat("PullForce", pullForce);
return tag;
}
@Override
public void onLoad() {
blockCheckCooldown = 0;
}
@Override
public void read(CompoundNBT compound) {
super.read(compound);
ListNBT itemsNBT = compound.getList("Items", 10);
items.clear();
for (INBT iNBT : itemsNBT) {
CompoundNBT itemNBT = (CompoundNBT) iNBT;
items.add(new ProcessedItem(NBTUtil.readUniqueId(itemNBT.getCompound("UUID")), itemNBT.getInt("TimeLeft")));
}
findLoadedItems = true;
}
@Override
public CompoundNBT write(CompoundNBT compound) {
ListNBT itemsNBT = new ListNBT();
for (ProcessedItem item : items) {
CompoundNBT itemNBT = new CompoundNBT();
itemNBT.put("UUID", NBTUtil.writeUniqueId(item.entity.getUniqueID()));
itemNBT.putInt("TimeLeft", item.processingTimeLeft);
itemsNBT.add(itemNBT);
}
compound.put("Items", itemsNBT);
return super.write(compound);
}
protected void updateReachAndForce() {
if (getWorld() == null)
return;
if (world.isRemote)
return;
float speed = Math.abs(this.speed);
float distanceFactor = Math.min(speed / DISTANCE_ARGMAX, 1);
float forceFactor = Math.min(speed / FORCE_ARGMAX, 1);
pushDistance = MathHelper.lerp(distanceFactor, 3, PUSH_DISTANCE_MAX);
pullDistance = MathHelper.lerp(distanceFactor, 1.5f, PULL_DISTANCE_MAX);
pushForce = MathHelper.lerp(forceFactor, 1, PUSH_FORCE_MAX);
pullForce = MathHelper.lerp(forceFactor, 1, PULL_FORCE_MAX);
Direction direction = getAirFlow();
if (speed != 0) {
for (int distance = 1; distance <= pushDistance; distance++) {
if (!EncasedFanBlock.canAirPassThrough(world, getPos().offset(direction, distance), direction)) {
pushDistance = distance - 1;
break;
}
}
for (int distance = 1; distance <= pullDistance; distance++) {
if (!EncasedFanBlock.canAirPassThrough(world, getPos().offset(direction, -distance), direction)) {
pullDistance = distance - 1;
break;
}
}
updateBBs();
} else {
frontBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
backBB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
}
sendData();
}
protected void updateBBs() {
Direction flow = getAirFlow();
if (flow == null)
return;
Vec3i flowVec = flow.getDirectionVec();
float remainder = pushDistance - (int) pushDistance;
frontBB = new AxisAlignedBB(pos.offset(flow), pos.offset(flow, (int) pushDistance))
.expand(flowVec.getX() * remainder + 1, flowVec.getY() * remainder + 1, flowVec.getZ() * remainder + 1)
.grow(.25f);
remainder = pullDistance - (int) pullDistance;
backBB = new AxisAlignedBB(pos.offset(flow, -(int) pullDistance), pos.offset(flow, -1))
.expand(-flowVec.getX() * remainder + 1, -flowVec.getY() * remainder + 1,
-flowVec.getZ() * remainder + 1)
.grow(.25f);
}
public void updateBothNeighbours() {
Axis axis = getBlockState().get(AXIS);
Direction frontFacing = Direction.getFacingFromAxis(POSITIVE, axis);
Direction backFacing = Direction.getFacingFromAxis(NEGATIVE, axis);
BlockPos front = pos.offset(frontFacing);
BlockPos back = pos.offset(backFacing);
if (world.isBlockPresent(front))
setNeighbour(frontFacing, world.getBlockState(front));
if (world.isBlockPresent(back))
setNeighbour(backFacing, world.getBlockState(back));
}
public void setNeighbour(Direction direction, BlockState neighbourState) {
if (direction.getAxisDirection() == NEGATIVE)
backBlock = neighbourState;
else
frontBlock = neighbourState;
updateReachAndForce();
}
public Direction getAirFlow() {
if (speed == 0)
return null;
return Direction.getFacingFromAxisDirection(getBlockState().get(AXIS), speed > 0 ? POSITIVE : NEGATIVE);
}
@Override
public void onSpeedChanged() {
updateReachAndForce();
}
@Override
public void tick() {
if (speed == 0)
return;
List<Entity> frontEntities = world.getEntitiesWithinAABBExcludingEntity(null, frontBB);
for (Entity entity : frontEntities) {
moveEntity(entity, true);
if (!(entity instanceof ItemEntity)) {
if (frontBlock != null && frontBlock.getBlock() == Blocks.FIRE) {
entity.setFire(2);
entity.attackEntityFrom(damageSourceFire, 4);
}
if (frontBlock != null && frontBlock.getBlock() == Blocks.LAVA) {
entity.setFire(10);
entity.attackEntityFrom(damageSourceLava, 8);
}
}
}
for (Entity entity : world.getEntitiesWithinAABBExcludingEntity(null, backBB)) {
moveEntity(entity, false);
}
if (world.isRemote) {
makeParticles();
return;
}
if (blockCheckCooldown-- <= 0) {
blockCheckCooldown = BLOCK_CHECK_UPDATE_DELAY;
updateReachAndForce();
}
if (findLoadedItems) {
findLoadedItems = false;
for (ProcessedItem item : items) {
for (Entity entity : frontEntities) {
if (!(entity instanceof ItemEntity))
continue;
if (entity.getUniqueID().equals(item.loadedUUID))
item.entity = (ItemEntity) entity;
}
}
}
}
protected void moveEntity(Entity entity, boolean push) {
if ((entity instanceof ItemEntity) && AllBlocks.BELT.typeOf(world.getBlockState(entity.getPosition()))) {
return;
}
Vec3d center = VecHelper.getCenterOf(pos);
Vec3i flow = getAirFlow().getDirectionVec();
float modifier = entity.isSneaking() ? 4096f : 512f;
float s = (float) (speed * 1 / modifier
/ (entity.getPositionVec().distanceTo(center) / (push ? pushDistance : pullDistance)));
Vec3d motion = entity.getMotion();
double xIn = flow.getX() * s - motion.x;
double yIn = flow.getY() * s - motion.y;
double zIn = flow.getZ() * s - motion.z;
entity.setMotion(motion.add(new Vec3d(xIn, yIn, zIn).mul(flow.getX(), flow.getY(), flow.getZ()).scale(1 / 8f)));
entity.fallDistance = 0;
}
protected void makeParticles() {
Direction direction = getAirFlow();
Vec3i directionVec = direction.getDirectionVec();
boolean hasFx = false;
if (frontBlock != null) {
if (effects.containsKey(frontBlock.getBlock())) {
hasFx = true;
for (FanEffect fx : effects.get(frontBlock.getBlock()))
fx.render(directionVec, true, this);
}
}
if (backBlock != null && !hasFx) {
if (effects.containsKey(backBlock.getBlock())) {
hasFx = true;
for (FanEffect fx : effects.get(backBlock.getBlock()))
fx.render(directionVec, true, this);
}
}
if (!hasFx)
for (FanEffect fx : effects.get(Blocks.AIR))
fx.render(directionVec, true, this);
for (FanEffect fx : effects.get(Blocks.AIR))
fx.render(directionVec, false, this);
}
}

View file

@ -0,0 +1,144 @@
package com.simibubi.create.modules.contraptions.relays.belt;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import com.simibubi.create.AllBlocks;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
public enum AllBeltAttachments {
BELT_FUNNEL(AllBlocks.BELT_FUNNEL),
;
IBeltAttachment attachment;
private AllBeltAttachments(AllBlocks attachment) {
this.attachment = (IBeltAttachment) attachment.get();
}
public interface IBeltAttachment {
public Optional<BlockPos> getValidAttachmentFor(BeltTileEntity te);
public Optional<BlockPos> getValidBeltPositionFor(IWorld world, BlockPos pos, BlockState state);
public boolean handleEntity(BeltTileEntity te, Entity entity, BeltAttachmentState state);
default void onAttachmentPlaced(IWorld world, BlockPos pos, BlockState state) {
Optional<BlockPos> beltPos = getValidBeltPositionFor(world, pos, state);
if (!beltPos.isPresent())
return;
BeltTileEntity te = (BeltTileEntity) world.getTileEntity(beltPos.get());
if (te == null)
return;
te.attachmentTracker.addAttachment(world, pos);
te.sendData();
}
default void onAttachmentRemoved(IWorld world, BlockPos pos, BlockState state) {
Optional<BlockPos> beltPos = getValidBeltPositionFor(world, pos, state);
if (!beltPos.isPresent())
return;
BeltTileEntity te = (BeltTileEntity) world.getTileEntity(beltPos.get());
if (te == null)
return;
te.attachmentTracker.removeAttachment(pos);
te.sendData();
}
}
public static class BeltAttachmentState {
public IBeltAttachment attachment;
public BlockPos attachmentPos;
public int processingDuration;
public Entity processingEntity;
public BeltAttachmentState(IBeltAttachment attachment, BlockPos attachmentPos) {
this.attachment = attachment;
this.attachmentPos = attachmentPos;
}
}
public static class Tracker {
public List<BeltAttachmentState> attachments;
public Tracker() {
attachments = new ArrayList<>(0);
}
public void findAttachments(BeltTileEntity belt) {
for (AllBeltAttachments ba : AllBeltAttachments.values()) {
Optional<BlockPos> validAttachmentFor = ba.attachment.getValidAttachmentFor(belt);
if (validAttachmentFor.isPresent()) {
BlockPos pos = validAttachmentFor.get();
addAttachment(belt.getWorld(), pos);
}
}
}
public BeltAttachmentState addAttachment(IWorld world, BlockPos pos) {
BlockState state = world.getBlockState(pos);
removeAttachment(pos);
BeltAttachmentState newAttachmentState = new BeltAttachmentState((IBeltAttachment) state.getBlock(), pos);
attachments.add(newAttachmentState);
return newAttachmentState;
}
public void removeAttachment(BlockPos pos) {
BeltAttachmentState toRemove = null;
for (BeltAttachmentState atState : attachments)
if (atState.attachmentPos.equals(pos))
toRemove = atState;
if (toRemove != null)
attachments.remove(toRemove);
}
public void forEachAttachment(Consumer<BeltAttachmentState> consumer) {
attachments.forEach(consumer::accept);
}
public void readAndSearch(CompoundNBT nbt, BeltTileEntity belt) {
attachments.clear();
if (!nbt.contains("HasAttachments"))
return;
if (nbt.contains("AttachmentData")) {
ListNBT list = (ListNBT) nbt.get("AttachmentData");
for (INBT data : list) {
CompoundNBT stateNBT = (CompoundNBT) data;
BlockPos attachmentPos = NBTUtil.readBlockPos(stateNBT.getCompound("Position"));
BeltAttachmentState atState = addAttachment(belt.getWorld(), attachmentPos);
atState.processingDuration = stateNBT.getInt("Duration");
}
}
}
public void write(CompoundNBT nbt) {
if (!attachments.isEmpty()) {
nbt.putBoolean("HasAttachments", true);
ListNBT list = new ListNBT();
forEachAttachment(atState -> {
CompoundNBT stateNBT = new CompoundNBT();
stateNBT.put("Position", NBTUtil.writeBlockPos(atState.attachmentPos));
stateNBT.putInt("Duration", atState.processingDuration);
list.add(stateNBT);
});
nbt.put("AttachmentData", list);
}
}
}
}

View file

@ -1,14 +1,15 @@
package com.simibubi.create.modules.contraptions.relays;
package com.simibubi.create.modules.contraptions.relays.belt;
import java.util.LinkedList;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.foundation.block.IWithTileEntity;
import com.simibubi.create.foundation.block.IWithoutBlockItem;
import com.simibubi.create.foundation.utility.ItemDescription;
import com.simibubi.create.modules.contraptions.base.HorizontalKineticBlock;
import com.simibubi.create.modules.contraptions.relays.BeltTileEntity.TransportedEntityInfo;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity.TransportedEntityInfo;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
@ -36,7 +37,7 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem {
public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockItem, IWithTileEntity<BeltTileEntity> {
public static final IProperty<Slope> SLOPE = EnumProperty.create("slope", Slope.class);
public static final IProperty<Part> PART = EnumProperty.create("part", Part.class);
@ -103,6 +104,8 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
if (controller == null)
return;
if (controller.passengers == null)
return;
if (controller.passengers.containsKey(entityIn))
controller.passengers.get(entityIn).refresh(belt.getPos(), belt.getBlockState());
@ -132,6 +135,8 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
if (controller == null)
return;
if (controller.passengers == null)
return;
if (controller.passengers.containsKey(entityIn)) {
TransportedEntityInfo transportedEntityInfo = controller.passengers.get(entityIn);
@ -141,6 +146,13 @@ public class BeltBlock extends HorizontalKineticBlock implements IWithoutBlockIt
controller.passengers.put(entityIn, new TransportedEntityInfo(pos, state));
}
@Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
withTileEntityDo(worldIn, pos, te -> {
te.attachmentTracker.findAttachments(te);
});
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
builder.add(SLOPE, PART);

View file

@ -1,12 +1,12 @@
package com.simibubi.create.modules.contraptions.relays;
package com.simibubi.create.modules.contraptions.relays.belt;
import java.util.LinkedList;
import java.util.List;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.BeltBlock.Part;
import com.simibubi.create.modules.contraptions.relays.BeltBlock.Slope;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope;
import net.minecraft.block.BlockState;
import net.minecraft.item.Item;

View file

@ -1,4 +1,4 @@
package com.simibubi.create.modules.contraptions.relays;
package com.simibubi.create.modules.contraptions.relays.belt;
import java.util.LinkedList;
import java.util.List;

View file

@ -1,4 +1,4 @@
package com.simibubi.create.modules.contraptions.relays;
package com.simibubi.create.modules.contraptions.relays.belt;
import java.util.ArrayList;
import java.util.HashMap;
@ -8,8 +8,10 @@ import java.util.Map;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.BeltBlock.Part;
import com.simibubi.create.modules.contraptions.relays.BeltBlock.Slope;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.Tracker;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Part;
import com.simibubi.create.modules.contraptions.relays.belt.BeltBlock.Slope;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
@ -34,6 +36,8 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn
protected BlockPos controller;
public Map<Entity, TransportedEntityInfo> passengers;
public AllBeltAttachments.Tracker attachmentTracker;
private CompoundNBT trackerUpdateTag;
protected static class TransportedEntityInfo {
int ticksSinceLastCollision;
@ -59,18 +63,37 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn
public BeltTileEntity() {
super(AllTileEntities.BELT.type);
controller = BlockPos.ZERO;
passengers = new HashMap<>();
attachmentTracker = new Tracker();
}
protected boolean isLastBelt() {
if (speed == 0)
return false;
Direction direction = getBlockState().get(BlockStateProperties.HORIZONTAL_FACING);
if (getBlockState().get(BeltBlock.SLOPE) == Slope.VERTICAL)
return false;
Part part = getBlockState().get(BeltBlock.PART);
if (part == Part.MIDDLE)
return false;
boolean movingPositively = (speed > 0 == (direction.getAxisDirection().getOffset() == 1))
^ direction.getAxis() == Axis.X;
return part == Part.START ^ movingPositively;
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.put("Controller", NBTUtil.writeBlockPos(controller));
attachmentTracker.write(compound);
return super.write(compound);
}
@Override
public void read(CompoundNBT compound) {
controller = NBTUtil.readBlockPos(compound.getCompound("Controller"));
trackerUpdateTag = compound;
super.read(compound);
}
@ -94,8 +117,14 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn
@Override
public void tick() {
if (world != null && trackerUpdateTag != null) {
attachmentTracker.readAndSearch(trackerUpdateTag, this);
trackerUpdateTag = null;
}
if (!isController())
return;
if (passengers == null)
passengers = new HashMap<>();
passengers.forEach((entity, info) -> {
transportEntity(entity, info);
@ -110,7 +139,11 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn
}
info.tick();
});
toRemove.forEach(passengers::remove);
toRemove.forEach(e -> {
if (e instanceof ItemEntity)
((ItemEntity) e).setAgeToCreativeDespawnTime();
passengers.remove(e);
});
if (speed == 0)
return;
@ -133,7 +166,16 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn
return;
}
if (((KineticTileEntity) te).getSpeed() == 0)
if (entityIn instanceof ItemEntity) {
if (speed == 0) {
((ItemEntity) entityIn).setAgeToCreativeDespawnTime();
} else {
if (((ItemEntity) entityIn).getAge() > 0)
((ItemEntity) entityIn).setNoDespawn();
}
}
if (speed == 0)
return;
if (entityIn.posY - .25f < pos.getY())
@ -143,6 +185,13 @@ public class BeltTileEntity extends KineticTileEntity implements ITickableTileEn
((LivingEntity) entityIn).setIdleTime(101);
}
BeltTileEntity belt = (BeltTileEntity) te;
for (BeltAttachmentState state : belt.attachmentTracker.attachments) {
if (state.attachment.handleEntity(belt, entityIn, state))
return;
}
final Direction beltFacing = blockState.get(BlockStateProperties.HORIZONTAL_FACING);
final Slope slope = blockState.get(BeltBlock.SLOPE);
final Axis axis = beltFacing.getAxis();

View file

@ -1,4 +1,4 @@
package com.simibubi.create.modules.contraptions.relays;
package com.simibubi.create.modules.contraptions.relays.belt;
import java.nio.ByteBuffer;

View file

@ -0,0 +1,150 @@
package com.simibubi.create.modules.logistics.block;
import java.util.Optional;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.block.IWithTileEntity;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.BeltAttachmentState;
import com.simibubi.create.modules.contraptions.relays.belt.AllBeltAttachments.IBeltAttachment;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.HorizontalBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
public class BeltFunnelBlock extends HorizontalBlock implements IBeltAttachment, IWithTileEntity<BeltFunnelTileEntity> {
public static final VoxelShape SHAPE_NORTH = makeCuboidShape(4, 2, -1, 12, 10, 5),
SHAPE_SOUTH = makeCuboidShape(4, 2, 11, 12, 10, 17), SHAPE_WEST = makeCuboidShape(-1, 2, 4, 5, 10, 12),
SHAPE_EAST = makeCuboidShape(11, 2, 4, 17, 10, 12);
public BeltFunnelBlock() {
super(Properties.from(Blocks.ANDESITE));
}
@Override
public boolean hasTileEntity(BlockState state) {
return true;
}
@Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return new BeltFunnelTileEntity();
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
builder.add(HORIZONTAL_FACING);
super.fillStateContainer(builder);
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
BlockState state = getDefaultState();
if (context.getFace().getAxis().isHorizontal()) {
state = state.with(HORIZONTAL_FACING, context.getFace().getOpposite());
} else {
state = state.with(HORIZONTAL_FACING, context.getPlacementHorizontalFacing());
}
return state;
}
@Override
public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
Direction facing = state.get(HORIZONTAL_FACING);
if (facing == Direction.EAST)
return SHAPE_EAST;
if (facing == Direction.WEST)
return SHAPE_WEST;
if (facing == Direction.SOUTH)
return SHAPE_SOUTH;
if (facing == Direction.NORTH)
return SHAPE_NORTH;
return VoxelShapes.empty();
}
@Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
onAttachmentPlaced(worldIn, pos, state);
updateObservedInventory(state, worldIn, pos);
}
@Override
public void onNeighborChange(BlockState state, IWorldReader world, BlockPos pos, BlockPos neighbor) {
if (!neighbor.equals(pos.offset(state.get(HORIZONTAL_FACING))))
return;
updateObservedInventory(state, world, pos);
}
private void updateObservedInventory(BlockState state, IWorldReader world, BlockPos pos) {
IInventoryManipulator te = (IInventoryManipulator) world.getTileEntity(pos);
if (te == null)
return;
te.neighborChanged();
}
@Override
public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) {
onAttachmentRemoved(worldIn, pos, state);
if (state.hasTileEntity() && state.getBlock() != newState.getBlock()) {
worldIn.removeTileEntity(pos);
}
}
@Override
public Optional<BlockPos> getValidAttachmentFor(BeltTileEntity te) {
BlockPos validPos = te.getPos().up();
BlockState blockState = te.getWorld().getBlockState(validPos);
if (blockState.getBlock() != this
|| blockState.get(HORIZONTAL_FACING).getAxis() != te.getBlockState().get(HORIZONTAL_FACING).getAxis())
return Optional.empty();
return Optional.of(validPos);
}
@Override
public Optional<BlockPos> getValidBeltPositionFor(IWorld world, BlockPos pos, BlockState state) {
BlockPos validPos = pos.down();
BlockState blockState = world.getBlockState(validPos);
if (!AllBlocks.BELT.typeOf(blockState)
|| blockState.get(HORIZONTAL_FACING).getAxis() != state.get(HORIZONTAL_FACING).getAxis())
return Optional.empty();
return Optional.of(validPos);
}
@Override
public boolean handleEntity(BeltTileEntity te, Entity entity, BeltAttachmentState state) {
if (!(entity instanceof ItemEntity))
return false;
if (entity.getPositionVec().distanceTo(VecHelper.getCenterOf(te.getPos())) > .4f)
return false;
entity.setMotion(Vec3d.ZERO);
withTileEntityDo(te.getWorld(), state.attachmentPos, funnelTE -> {
funnelTE.tryToInsert((ItemEntity) entity);
});
return true;
}
}

View file

@ -0,0 +1,115 @@
package com.simibubi.create.modules.logistics.block;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.block.SyncedTileEntity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.ItemParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
public class BeltFunnelTileEntity extends SyncedTileEntity implements ITickableTileEntity, IInventoryManipulator {
private LazyOptional<IItemHandler> inventory;
protected boolean waitingForInventorySpace;
private boolean initialize;
public BeltFunnelTileEntity() {
super(AllTileEntities.BELT_FUNNEL.type);
inventory = LazyOptional.empty();
}
@Override
public void read(CompoundNBT compound) {
waitingForInventorySpace = compound.getBoolean("Waiting");
super.read(compound);
}
@Override
public void onLoad() {
initialize = true;
}
@Override
public void readClientUpdate(CompoundNBT tag) {
super.readClientUpdate(tag);
if (!waitingForInventorySpace)
neighborChanged();
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putBoolean("Waiting", waitingForInventorySpace);
return super.write(compound);
}
@Override
public BlockPos getInventoryPos() {
return pos.offset(getBlockState().get(BlockStateProperties.HORIZONTAL_FACING));
}
@Override
public LazyOptional<IItemHandler> getInventory() {
return inventory;
}
@Override
public void tick() {
if (initialize && hasWorld()) {
neighborChanged();
initialize = false;
}
}
@Override
public void setInventory(LazyOptional<IItemHandler> inventory) {
this.inventory = inventory;
}
@Override
public void neighborChanged() {
IInventoryManipulator.super.neighborChanged();
waitingForInventorySpace = false;
if (!world.isRemote)
sendData();
}
public void tryToInsert(ItemEntity entity) {
if (!inventory.isPresent())
return;
if (waitingForInventorySpace)
return;
ItemStack stack = entity.getItem().copy();
IItemHandler inv = inventory.orElse(null);
for (int slot = 0; slot < inv.getSlots(); slot++) {
stack = inv.insertItem(slot, stack, world.isRemote);
if (stack.isEmpty()) {
if (!world.isRemote)
entity.remove();
else {
Vec3i directionVec = getBlockState().get(BlockStateProperties.HORIZONTAL_FACING).getDirectionVec();
float xSpeed = directionVec.getX() * 1/8f;
float zSpeed = directionVec.getZ() * 1/8f;
world.addParticle(new ItemParticleData(ParticleTypes.ITEM, entity.getItem()), entity.posX, entity.posY, entity.posZ, xSpeed, 1/6f, zSpeed);
}
return;
}
}
waitingForInventorySpace = true;
sendData();
if (!stack.equals(entity.getItem(), false))
entity.setItem(stack);
}
}

View file

@ -14,10 +14,11 @@ public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor,
private State state;
private int cooldown;
private LazyOptional<IItemHandler> inventory;
private boolean initialize;
public ExtractorTileEntity() {
super(AllTileEntities.EXTRACTOR.type);
state = State.WAITING_FOR_ITEM;
state = State.WAITING_FOR_INVENTORY;
inventory = LazyOptional.empty();
}
@ -26,6 +27,20 @@ public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor,
return state;
}
@Override
public void onLoad() {
initialize = true;
}
@Override
public void tick() {
if (initialize && hasWorld()) {
neighborChanged();
initialize = false;
}
IExtractor.super.tick();
}
@Override
public void setState(State state) {
if (state == State.ON_COOLDOWN)

View file

@ -2,27 +2,22 @@ package com.simibubi.create.modules.logistics.block;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
// Its like delegation but better!
public interface IExtractor extends ITickableTileEntity {
public interface IExtractor extends ITickableTileEntity, IInventoryManipulator {
public static final int EXTRACTOR_COOLDOWN = 20;
public static final int EXTRACTION_COUNT = 16;
public enum State {
WAITING_FOR_ITEM, WAITING_FOR_SPACE, RUNNING, ON_COOLDOWN, LOCKED;
WAITING_FOR_INVENTORY, WAITING_FOR_ENTITY, RUNNING, ON_COOLDOWN, LOCKED;
}
public State getState();
@ -31,16 +26,6 @@ public interface IExtractor extends ITickableTileEntity {
public int tickCooldown();
public World getWorld();
public BlockPos getPos();
public BlockPos getInventoryPos();
public LazyOptional<IItemHandler> getInventory();
public void setInventory(LazyOptional<IItemHandler> inventory);
@Override
default void tick() {
State state = getState();
@ -62,18 +47,18 @@ public interface IExtractor extends ITickableTileEntity {
if (hasSpace && hasInventory)
toExtract = extract(true);
if (state == State.WAITING_FOR_SPACE) {
if (state == State.WAITING_FOR_ENTITY) {
if (hasSpace)
setState(State.RUNNING);
}
if (state == State.RUNNING) {
if (!hasSpace) {
setState(State.WAITING_FOR_SPACE);
setState(State.WAITING_FOR_ENTITY);
return;
}
if (!hasInventory || toExtract.isEmpty()) {
setState(State.WAITING_FOR_ITEM);
setState(State.WAITING_FOR_INVENTORY);
return;
}
@ -88,7 +73,7 @@ public interface IExtractor extends ITickableTileEntity {
public default void setLocked(boolean locked) {
setState(locked ? State.LOCKED : State.ON_COOLDOWN);
}
public default void neighborChanged() {
boolean hasSpace = hasSpaceForExtracting();
boolean hasInventory = getInventory().isPresent();
@ -96,8 +81,8 @@ public interface IExtractor extends ITickableTileEntity {
if (hasSpace && hasInventory)
toExtract = extract(true);
if (getState() == State.WAITING_FOR_ITEM) {
if (getState() == State.WAITING_FOR_INVENTORY) {
if (!hasInventory) {
if (findNewInventory()) {
setState(State.RUNNING);
@ -123,48 +108,27 @@ public interface IExtractor extends ITickableTileEntity {
compare.setCount(extracting.getCount());
if (!extracting.isEmpty() && !extracting.equals(compare, false))
continue;
if (extracting.isEmpty())
extracting = stack.copy();
else
extracting.grow(stack.getCount());
if (!simulate)
inv.extractItem(slot, stack.getCount(), false);
if (extracting.getCount() >= EXTRACTION_COUNT)
break;
}
if (!simulate) {
World world = getWorld();
Vec3d pos = VecHelper.getCenterOf(getPos()).add(0, -0.5f, 0);
ItemEntity entityIn = new ItemEntity(world, pos.x, pos.y, pos.z, extracting);
entityIn.setMotion(Vec3d.ZERO);
entityIn.setMotion(Vec3d.ZERO);
world.addEntity(entityIn);
}
return extracting;
}
default boolean findNewInventory() {
BlockPos invPos = getInventoryPos();
World world = getWorld();
if (!world.isBlockPresent(invPos))
return false;
BlockState invState = world.getBlockState(invPos);
if (!invState.hasTileEntity())
return false;
TileEntity invTE = world.getTileEntity(invPos);
LazyOptional<IItemHandler> inventory = invTE.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
setInventory(inventory);
if (inventory.isPresent()) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,51 @@
package com.simibubi.create.modules.logistics.block;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
public interface IInventoryManipulator {
public World getWorld();
public BlockPos getPos();
public BlockPos getInventoryPos();
public LazyOptional<IItemHandler> getInventory();
public void setInventory(LazyOptional<IItemHandler> inventory);
default boolean findNewInventory() {
BlockPos invPos = getInventoryPos();
World world = getWorld();
if (!world.isBlockPresent(invPos))
return false;
BlockState invState = world.getBlockState(invPos);
if (!invState.hasTileEntity())
return false;
TileEntity invTE = world.getTileEntity(invPos);
LazyOptional<IItemHandler> inventory = invTE.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
setInventory(inventory);
if (inventory.isPresent()) {
return true;
}
return false;
}
public default void neighborChanged() {
boolean hasInventory = getInventory().isPresent();
if (!hasInventory) {
findNewInventory();
}
}
}

View file

@ -22,7 +22,7 @@ public class LinkedExtractorTileEntity extends LinkedTileEntity
public LinkedExtractorTileEntity() {
super(AllTileEntities.LINKED_EXTRACTOR.type);
state = State.WAITING_FOR_ITEM;
state = State.WAITING_FOR_INVENTORY;
inventory = LazyOptional.empty();
}

View file

@ -0,0 +1,58 @@
package com.simibubi.create.modules.logistics.block;
import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.RedstoneDiodeBlock;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.TickPriority;
import net.minecraft.world.World;
public class PulseRepeaterBlock extends RedstoneDiodeBlock {
public static BooleanProperty PULSING = BooleanProperty.create("pulsing");
public PulseRepeaterBlock() {
super(Properties.from(Blocks.REPEATER));
setDefaultState(getDefaultState().with(PULSING, false).with(POWERED, false));
}
@Override
protected int getDelay(BlockState state) {
return 1;
}
@Override
public void tick(BlockState state, World worldIn, BlockPos pos, Random random) {
boolean powered = state.get(POWERED);
boolean pulsing = state.get(PULSING);
boolean shouldPower = shouldBePowered(worldIn, pos, state);
if (pulsing) {
worldIn.setBlockState(pos, state.with(POWERED, true).with(PULSING, false), 2);
} else if (powered && !shouldPower) {
worldIn.setBlockState(pos, state.with(POWERED, false).with(PULSING, false), 2);
} else if (!powered) {
worldIn.setBlockState(pos, state.with(POWERED, true).with(PULSING, true), 2);
worldIn.getPendingBlockTicks().scheduleTick(pos, this, this.getDelay(state), TickPriority.HIGH);
}
}
@Override
protected int getActiveSignal(IBlockReader worldIn, BlockPos pos, BlockState state) {
return state.get(PULSING) ? 15 : 0;
}
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
builder.add(HORIZONTAL_FACING, POWERED, PULSING);
super.fillStateContainer(builder);
}
}

View file

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

View file

@ -0,0 +1,23 @@
{
"variants": {
"powered=false,pulsing=false,facing=south": { "model": "create:block/pulse_repeater" },
"powered=false,pulsing=false,facing=north": { "model": "create:block/pulse_repeater", "y": 180 },
"powered=false,pulsing=false,facing=east": { "model": "create:block/pulse_repeater", "y": 270 },
"powered=false,pulsing=false,facing=west": { "model": "create:block/pulse_repeater", "y": 90 },
"powered=true,pulsing=false,facing=south": { "model": "create:block/pulse_repeater_powered" },
"powered=true,pulsing=false,facing=north": { "model": "create:block/pulse_repeater_powered", "y": 180 },
"powered=true,pulsing=false,facing=east": { "model": "create:block/pulse_repeater_powered", "y": 270 },
"powered=true,pulsing=false,facing=west": { "model": "create:block/pulse_repeater_powered", "y": 90 },
"powered=false,pulsing=true,facing=south": { "model": "create:block/pulse_repeater_pulsing" },
"powered=false,pulsing=true,facing=north": { "model": "create:block/pulse_repeater_pulsing", "y": 180 },
"powered=false,pulsing=true,facing=east": { "model": "create:block/pulse_repeater_pulsing", "y": 270 },
"powered=false,pulsing=true,facing=west": { "model": "create:block/pulse_repeater_pulsing", "y": 90 },
"powered=true,pulsing=true,facing=south": { "model": "create:block/pulse_repeater_pulsing" },
"powered=true,pulsing=true,facing=north": { "model": "create:block/pulse_repeater_pulsing", "y": 180 },
"powered=true,pulsing=true,facing=east": { "model": "create:block/pulse_repeater_pulsing", "y": 270 },
"powered=true,pulsing=true,facing=west": { "model": "create:block/pulse_repeater_pulsing", "y": 90 }
}
}

View file

@ -41,7 +41,9 @@
"block.create.stockswitch": "Stockpile Switch",
"block.create.flexcrate": "FlexCrate",
"block.create.extractor": "Extractor",
"block.create.belt_funnel": "Belt Funnel",
"block.create.linked_extractor": "Linked Extractor",
"block.create.pulse_repeater": "Pulse Repeater",
"block.create.andesite_bricks": "Andesite Bricks",
"block.create.diorite_bricks": "Diorite Bricks",
@ -74,6 +76,8 @@
"block.create.shop_shelf": "Shelf",
"death.attack.create.crush": "%1$s was crushed by a dangerous contraption",
"death.attack.create.fan_fire": "%1$s was burnt to death by hot air",
"death.attack.create.fan_lava": "%1$s tried to swim up a lava fountain",
"itemGroup.create": "Create"
}

View file

@ -0,0 +1,97 @@
{
"__comment": "Model generated using MrCrayfish's Model Creator (https://mrcrayfish.com/tools?id=mc)",
"parent": "block/block",
"textures": {
"belt_funnel": "create:block/belt_funnel",
"belt": "create:block/belt",
"particle": "create:block/belt_funnel"
},
"elements": [
{
"name": "Bottom",
"from": [ 3, 0, -1 ],
"to": [ 13, 1, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 },
"up": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] },
"down": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] }
}
},
{
"name": "Top",
"from": [ 3, 7, -1 ],
"to": [ 13, 8, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 },
"up": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] },
"down": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] }
}
},
{
"name": "Side",
"from": [ 3, 1, -1 ],
"to": [ 4, 7, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 9, 1, 10, 7 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 0, 1, 1, 7 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 }
}
},
{
"name": "Side",
"from": [ 12, 1, -1 ],
"to": [ 13, 7, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 0, 1, 1, 7 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 9, 1, 10, 7 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 }
}
},
{
"name": "Center",
"from": [ 4, 1, -1 ],
"to": [ 12, 7, 4 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 1, 8, 9, 14 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 1, 1, 9, 7 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 }
}
},
{
"name": "Top",
"from": [ 4, 6, 0 ],
"to": [ 12, 8, 4.8 ],
"rotation": { "origin": [ 8, 8, 0 ], "axis": "x", "angle": -22.5 },
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 1, 7, 9, 9 ], "rotation": 180 },
"east": { "texture": "#belt_funnel", "uv": [ 0, 9.6, 1.8, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 1, 7, 9, 9 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 2, 12.4 ], "rotation": 90 },
"up": { "texture": "#belt_funnel", "uv": [ 1, 8, 9, 12.4 ], "rotation": 180 },
"down": { "texture": "#belt_funnel", "uv": [ 1, 8, 9, 12.8 ] }
}
},
{
"name": "Ramp",
"from": [ 4, -0.9, 4 ],
"to": [ 12, 1.1, 11 ],
"rotation": { "origin": [ 8, 1, 5 ], "axis": "x", "angle": 45.0 },
"faces": {
"north": { "texture": "#belt", "uv": [ 0, 0, 8, 2 ] },
"east": { "texture": "#belt", "uv": [ 14, 3, 16, 10 ], "rotation": 90 },
"west": { "texture": "#belt", "uv": [ 0, 3, 2, 10 ], "rotation": 270 },
"up": { "texture": "#belt", "uv": [ 4, 1, 12, 8 ] },
"down": { "texture": "#belt", "uv": [ 0, 0, 8, 7 ] }
}
}
]
}

View file

@ -0,0 +1,101 @@
{
"__comment": "Model generated using MrCrayfish's Model Creator (https://mrcrayfish.com/tools?id=mc)",
"parent": "block/block",
"display": {
"gui": {
"rotation": [ 30, 45, 0 ],
"translation": [ 0, 0, 0],
"scale":[ 0.625, 0.625, 0.625 ]
}
},
"textures": {
"belt_funnel": "create:block/belt_funnel",
"symbols": "create:item/symbols"
},
"elements": [
{
"name": "Indicator",
"from": [ 8, 7, 13 ],
"to": [ 16, 15, 13 ],
"shade": false,
"rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": -45.0 },
"faces": {
"north": { "texture": "#symbols", "uv": [ 0, 8, 8, 16 ] },
"south": { "texture": "#symbols", "uv": [ 0, 8, 8, 16 ] }
}
},
{
"name": "Bottom",
"from": [ 3, 0, -1 ],
"to": [ 13, 1, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 },
"up": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] },
"down": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] }
}
},
{
"name": "Top",
"from": [ 3, 7, -1 ],
"to": [ 13, 8, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 0, 0, 10, 1 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 },
"up": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] },
"down": { "texture": "#belt_funnel", "uv": [ 0, 8, 10, 14 ] }
}
},
{
"name": "Side",
"from": [ 3, 1, -1 ],
"to": [ 4, 7, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 9, 1, 10, 7 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 0, 1, 1, 7 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 }
}
},
{
"name": "Side",
"from": [ 12, 1, -1 ],
"to": [ 13, 7, 5 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 0, 1, 1, 7 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 9, 1, 10, 7 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 }
}
},
{
"name": "Center",
"from": [ 4, 1, -1 ],
"to": [ 12, 7, 4 ],
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 1, 8, 9, 14 ] },
"east": { "texture": "#belt_funnel", "uv": [ 9, 8, 10, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 1, 1, 9, 7 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 1, 14 ], "rotation": 90 }
}
},
{
"name": "Top",
"from": [ 4, 6, 0 ],
"to": [ 12, 8, 4.8 ],
"rotation": { "origin": [ 8, 8, 0 ], "axis": "x", "angle": -22.5 },
"faces": {
"north": { "texture": "#belt_funnel", "uv": [ 1, 7, 9, 9 ], "rotation": 180 },
"east": { "texture": "#belt_funnel", "uv": [ 0, 9.6, 1.8, 14 ], "rotation": 90 },
"south": { "texture": "#belt_funnel", "uv": [ 1, 7, 9, 9 ] },
"west": { "texture": "#belt_funnel", "uv": [ 0, 8, 2, 12.4 ], "rotation": 90 },
"up": { "texture": "#belt_funnel", "uv": [ 1, 8, 9, 12.4 ], "rotation": 180 },
"down": { "texture": "#belt_funnel", "uv": [ 1, 8, 9, 12.8 ] }
}
}
]
}

View file

@ -1,10 +1,129 @@
{
"parent": "create:block/extractor_wireless_powered",
"parent": "block/block",
"display": {
"gui": {
"rotation": [ 30, 45, 0 ],
"translation": [ 0, 0, 0],
"scale":[ 0.625, 0.625, 0.625 ]
}
}
},
"textures": {
"redstone_antenna": "create:block/redstone_antenna",
"extractor": "create:block/extractor",
"symbols": "create:item/symbols"
},
"elements": [
{
"name": "Indicator",
"from": [ 8, 7, 13 ],
"to": [ 16, 15, 13 ],
"shade": false,
"rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": -45.0 },
"faces": {
"north": { "texture": "#symbols", "uv": [ 0, 0, 8, 8 ] },
"south": { "texture": "#symbols", "uv": [ 0, 0, 8, 8 ] }
}
},
{
"name": "Bottom",
"from": [ 4, 2, -1 ],
"to": [ 12, 3, 5 ],
"faces": {
"east": { "texture": "#extractor", "uv": [ 0, 0, 6, 1 ], "rotation": 180 },
"south": { "texture": "#extractor", "uv": [ 6, 7, 14, 8 ] },
"west": { "texture": "#extractor", "uv": [ 0, 0, 6, 1 ] },
"up": { "texture": "#extractor", "uv": [ 9, 0, 15, 8 ], "rotation": 90 },
"down": { "texture": "#extractor", "uv": [ 0, 0, 6, 8 ], "rotation": 270 }
}
},
{
"name": "Top",
"from": [ 4, 9, -1 ],
"to": [ 12, 10, 5 ],
"faces": {
"east": { "texture": "#extractor", "uv": [ 0, 0, 6, 1 ], "rotation": 180 },
"south": { "texture": "#extractor", "uv": [ 6, 0, 14, 1 ] },
"west": { "texture": "#extractor", "uv": [ 0, 0, 6, 1 ] },
"up": { "texture": "#extractor", "uv": [ 0, 0, 6, 8 ], "rotation": 90 },
"down": { "texture": "#extractor", "uv": [ 9, 0, 15, 8 ], "rotation": 270 }
}
},
{
"name": "Side",
"from": [ 4, 3, -1 ],
"to": [ 5, 9, 5 ],
"faces": {
"east": { "texture": "#extractor", "uv": [ 9, 1, 15, 7 ], "rotation": 180 },
"south": { "texture": "#extractor", "uv": [ 6, 1, 7, 7 ] },
"west": { "texture": "#extractor", "uv": [ 0, 1, 6, 7 ] }
}
},
{
"name": "Side",
"from": [ 11, 3, -1 ],
"to": [ 12, 9, 5 ],
"faces": {
"east": { "texture": "#extractor", "uv": [ 0, 1, 6, 7 ], "rotation": 180 },
"south": { "texture": "#extractor", "uv": [ 13, 1, 14, 7 ] },
"west": { "texture": "#extractor", "uv": [ 9, 1, 15, 7 ] }
}
},
{
"name": "Center",
"from": [ 5, 3, 3 ],
"to": [ 11, 9, 4 ],
"faces": {
"south": { "texture": "#extractor", "uv": [ 7, 1, 13, 7 ] }
}
},
{
"name": "FilterSpot",
"from": [ 5, 10, -0.6 ],
"to": [ 11, 12, 4 ],
"rotation": { "origin": [ 8, 11, -1 ], "axis": "x", "angle": 22.5 },
"faces": {
"north": { "texture": "#extractor", "uv": [ 13, 1, 15, 7 ], "rotation": 90 },
"east": { "texture": "#extractor", "uv": [ 0.1, 0, 4.7, 2 ], "rotation": 180 },
"south": { "texture": "#extractor", "uv": [ 4, 1, 5, 7 ], "rotation": 270 },
"west": { "texture": "#extractor", "uv": [ 0.1, 0, 4.7, 2 ] },
"up": { "texture": "#extractor", "uv": [ 0, 9, 5, 15 ], "rotation": 90 }
}
},
{
"name": "AntennaX",
"from": [ 11, 7, 2 ],
"to": [ 14, 17, 3 ],
"faces": {
"north": { "texture": "#redstone_antenna", "uv": [ 0, 0, 3, 10 ] },
"south": { "texture": "#redstone_antenna", "uv": [ 0, 0, 3, 10 ] },
"down": { "texture": "#redstone_antenna", "uv": [ 0, 9, 3, 10 ] }
}
},
{
"name": "AntennaZ",
"from": [ 12, 7, 1 ],
"to": [ 13, 17, 4 ],
"faces": {
"east": { "texture": "#redstone_antenna", "uv": [ 0, 0, 3, 10 ] },
"west": { "texture": "#redstone_antenna", "uv": [ 0, 0, 3, 10 ] }
}
},
{
"name": "AntennaTop",
"from": [ 12, 15, 2 ],
"to": [ 13, 16, 3 ],
"faces": {
"up": { "texture": "#redstone_antenna", "uv": [ 1, 1, 2, 2 ] }
}
},
{
"name": "AntennaDish",
"from": [ 10, 13, 0 ],
"to": [ 15, 13, 5 ],
"faces": {
"up": { "texture": "#redstone_antenna", "uv": [ 4, 0, 9, 5 ] },
"down": { "texture": "#redstone_antenna", "uv": [ 4, 0, 9, 5 ] }
}
}
]
}

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B