Extractors and Linked Extractors

- Added extractors for dropping items from an inventory
- Further generalized Wireless Redstone actors
- Made the crushing wheel more dramatic
- Stationary Drills will drop items more carefully
This commit is contained in:
simibubi 2019-08-28 16:54:27 +02:00
parent 22fc9d1100
commit a0734dffaf
15 changed files with 754 additions and 220 deletions

View file

@ -22,9 +22,11 @@ import com.simibubi.create.modules.contraptions.relays.GearboxTileEntityRenderer
import com.simibubi.create.modules.contraptions.relays.GearshifterTileEntity;
import com.simibubi.create.modules.contraptions.relays.GearshifterTileEntityRenderer;
import com.simibubi.create.modules.economy.ShopShelfTileEntity;
import com.simibubi.create.modules.logistics.block.ExtractorTileEntity;
import com.simibubi.create.modules.logistics.block.FlexcrateTileEntity;
import com.simibubi.create.modules.logistics.block.LinkedExtractorTileEntity;
import com.simibubi.create.modules.logistics.block.RedstoneBridgeTileEntity;
import com.simibubi.create.modules.logistics.block.RedstoneBridgeTileEntityRenderer;
import com.simibubi.create.modules.logistics.block.LinkedTileEntityRenderer;
import com.simibubi.create.modules.logistics.block.StockswitchTileEntity;
import com.simibubi.create.modules.schematics.block.SchematicTableTileEntity;
import com.simibubi.create.modules.schematics.block.SchematicannonRenderer;
@ -66,6 +68,8 @@ public enum AllTileEntities {
REDSTONE_BRIDGE(RedstoneBridgeTileEntity::new, AllBlocks.REDSTONE_BRIDGE),
STOCKSWITCH(StockswitchTileEntity::new, AllBlocks.STOCKSWITCH),
FLEXCRATE(FlexcrateTileEntity::new, AllBlocks.FLEXCRATE),
EXTRACTOR(ExtractorTileEntity::new, AllBlocks.EXTRACTOR),
LINKED_EXTRACTOR(LinkedExtractorTileEntity::new, AllBlocks.LINKED_EXTRACTOR),
// Economy
SHOP_SHELF(ShopShelfTileEntity::new, AllBlocks.SHOP_SHELF),
@ -110,7 +114,8 @@ public enum AllTileEntities {
bind(DrillTileEntity.class, new KineticTileEntityRenderer());
bind(CrushingWheelTileEntity.class, new KineticTileEntityRenderer());
bind(WaterWheelTileEntity.class, new KineticTileEntityRenderer());
bind(RedstoneBridgeTileEntity.class, new RedstoneBridgeTileEntityRenderer());
bind(RedstoneBridgeTileEntity.class, new LinkedTileEntityRenderer());
bind(LinkedExtractorTileEntity.class, new LinkedTileEntityRenderer());
}
@OnlyIn(Dist.CLIENT)

View file

@ -1,5 +1,7 @@
package com.simibubi.create.foundation.utility;
import java.util.Random;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
@ -29,4 +31,9 @@ public class VecHelper {
return new Vec3d(pos).add(.5f, .5f, .5f);
}
public static Vec3d offsetRandomly(Vec3d vec, Random r, float radius) {
return new Vec3d(vec.x + (r.nextFloat() - .5f) * 2 * radius, vec.y + (r.nextFloat() - .5f) * 2 * radius,
vec.z + (r.nextFloat() - .5f) * 2 * radius);
}
}

View file

@ -60,15 +60,15 @@ public class CrushingWheelControllerTileEntity extends SyncedTileEntity implemen
Inventory inventory = new Inventory();
NonNullList<ItemStack> stacks = NonNullList.withSize(10, ItemStack.EMPTY);
ItemStackHelper.loadAllItems(nbt, stacks);
for (int slot = 0; slot < stacks.size(); slot++)
inventory.setInventorySlotContents(slot, stacks.get(slot));
inventory.processingDuration = nbt.getInt("ProcessingTime");
inventory.appliedRecipe = nbt.getBoolean("AppliedRecipe");
inventory.processingDuration = nbt.getInt("ProcessingTime");
inventory.appliedRecipe = nbt.getBoolean("AppliedRecipe");
return inventory;
}
public ItemStackHandler getItems() {
return (ItemStackHandler) inv;
}
@ -97,14 +97,14 @@ public class CrushingWheelControllerTileEntity extends SyncedTileEntity implemen
float speed = crushingspeed / 2.5f;
if (!hasEntity()) {
float processingSpeed = speed / (!contents.appliedRecipe? contents.getStackInSlot(0).getCount() : 1);
float processingSpeed = speed / (!contents.appliedRecipe ? contents.getStackInSlot(0).getCount() : 1);
contents.processingDuration -= processingSpeed;
spawnParticles(contents.getStackInSlot(0));
if (world.isRemote)
return;
if (contents.processingDuration < 20 && !contents.appliedRecipe) {
applyRecipe();
contents.appliedRecipe = true;
@ -167,8 +167,9 @@ public class CrushingWheelControllerTileEntity extends SyncedTileEntity implemen
particleData = new ItemParticleData(ParticleTypes.ITEM, stack);
Random r = world.rand;
world.addParticle(particleData, pos.getX() + r.nextFloat(), pos.getY() + r.nextFloat(),
pos.getZ() + r.nextFloat(), 0, 0, 0);
for (int i = 0; i < 4; i++)
world.addParticle(particleData, pos.getX() + r.nextFloat(), pos.getY() + r.nextFloat(),
pos.getZ() + r.nextFloat(), 0, 0, 0);
}
private void applyRecipe() {
@ -178,16 +179,16 @@ public class CrushingWheelControllerTileEntity extends SyncedTileEntity implemen
if (recipe.isPresent()) {
int rolls = contents.getStackInSlot(0).getCount();
contents.clear();
for (int roll = 0; roll < rolls; roll++) {
List<ItemStack> rolledResults = recipe.get().rollResults();
for (int i = 0; i < rolledResults.size(); i++) {
ItemStack stack = rolledResults.get(i);
for (int slot = 0; slot < contents.getSizeInventory(); slot++) {
stack = contents.getItems().insertItem(slot, stack, false);
if (stack.isEmpty())
break;
}

View file

@ -3,20 +3,29 @@ package com.simibubi.create.modules.contraptions.receivers;
import java.util.concurrent.atomic.AtomicInteger;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import net.minecraft.block.AirBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.fluid.IFluidState;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
import net.minecraft.world.server.ServerWorld;
public class DrillTileEntity extends KineticTileEntity implements ITickableTileEntity {
private static final AtomicInteger NEXT_DRILL_ID = new AtomicInteger();
private int ticksUntilNextProgress;
private int destroyProgress;
private int drillId = -NEXT_DRILL_ID.incrementAndGet();
@ -34,21 +43,21 @@ public class DrillTileEntity extends KineticTileEntity implements ITickableTileE
public void destroyNextTick() {
ticksUntilNextProgress = 1;
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putInt("Progress", destroyProgress);
compound.putInt("NextTick", ticksUntilNextProgress);
return super.write(compound);
}
@Override
public void read(CompoundNBT compound) {
destroyProgress = compound.getInt("Progress");
ticksUntilNextProgress = compound.getInt("NextTick");
super.read(compound);
}
@Override
public void remove() {
if (!world.isRemote && destroyProgress != 0) {
@ -64,33 +73,52 @@ public class DrillTileEntity extends KineticTileEntity implements ITickableTileE
return;
if (speed == 0)
return;
if (ticksUntilNextProgress < 0)
return;
if (ticksUntilNextProgress-- > 0)
return;
BlockPos posToBreak = pos.offset(getBlockState().get(BlockStateProperties.FACING));
BlockState stateToBreak = world.getBlockState(posToBreak);
float blockHardness = stateToBreak.getBlockHardness(world, posToBreak);
if (stateToBreak.getMaterial().isLiquid() || stateToBreak.getBlock() instanceof AirBlock || blockHardness == -1) {
if (stateToBreak.getMaterial().isLiquid() || stateToBreak.getBlock() instanceof AirBlock
|| blockHardness == -1) {
destroyProgress = 0;
world.sendBlockBreakProgress(drillId, posToBreak, -1);
return;
}
float breakSpeed = Math.abs(speed / 100f);
destroyProgress += MathHelper.clamp((int) (breakSpeed / blockHardness), 1, 10 - destroyProgress);
if (destroyProgress >= 10) {
world.destroyBlock(posToBreak, true);
IFluidState ifluidstate = world.getFluidState(pos);
world.playEvent(2001, posToBreak, Block.getStateId(stateToBreak));
TileEntity tileentity = stateToBreak.hasTileEntity() ? world.getTileEntity(posToBreak) : null;
Vec3d vec = VecHelper.offsetRandomly(VecHelper.getCenterOf(posToBreak), world.rand, .125f);
Block.getDrops(stateToBreak, (ServerWorld) world, posToBreak, tileentity).forEach((stack) -> {
if (!stack.isEmpty() && world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
&& !world.restoringBlockSnapshots) {
ItemEntity itementity = new ItemEntity(world, vec.x, vec.y, vec.z, stack);
itementity.setDefaultPickupDelay();
itementity.setMotion(Vec3d.ZERO);
world.addEntity(itementity);
}
});
stateToBreak.spawnAdditionalDrops(world, posToBreak, ItemStack.EMPTY);
world.setBlockState(posToBreak, ifluidstate.getBlockState(), 3);
destroyProgress = 0;
ticksUntilNextProgress = -1;
world.sendBlockBreakProgress(drillId, posToBreak, -1);
return;
}
ticksUntilNextProgress = (int) (blockHardness / breakSpeed);
world.sendBlockBreakProgress(drillId, posToBreak, (int) destroyProgress);
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.modules.logistics;
import com.simibubi.create.modules.logistics.FrequencyHandler.Frequency;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
@ -9,6 +10,7 @@ public interface IHaveWireless {
public Frequency getFrequencyFirst();
public Frequency getFrequencyLast();
public void setFrequency(boolean first, ItemStack stack);
public World getWorld();
public BlockPos getPos();

View file

@ -4,16 +4,21 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.HorizontalBlock;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer.Builder;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
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.IWorldReader;
import net.minecraft.world.World;
public class ExtractorBlock extends HorizontalBlock {
@ -34,6 +39,22 @@ public class ExtractorBlock extends HorizontalBlock {
super.fillStateContainer(builder);
}
@Override
public boolean hasTileEntity(BlockState state) {
return true;
}
@Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return new ExtractorTileEntity();
}
@Override
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) {
return false;
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
BlockState state = getDefaultState();
@ -47,6 +68,31 @@ public class ExtractorBlock extends HorizontalBlock {
return state.with(POWERED, Boolean.valueOf(context.getWorld().isBlockPowered(context.getPos())));
}
@Override
public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) {
updateObservedInventory(state, worldIn, pos);
}
@Override
public void onNeighborChange(BlockState state, IWorldReader world, BlockPos pos, BlockPos neighbor) {
if (world.isRemote())
return;
if (!isObserving(state, pos, neighbor))
return;
updateObservedInventory(state, world, pos);
}
private void updateObservedInventory(BlockState state, IWorldReader world, BlockPos pos) {
IExtractor extractor = (IExtractor) world.getTileEntity(pos);
if (extractor == null)
return;
extractor.neighborChanged();
}
private boolean isObserving(BlockState state, BlockPos pos, BlockPos observing) {
return observing.equals(pos.offset(state.get(HORIZONTAL_FACING)));
}
@Override
public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
boolean isMoving) {
@ -56,6 +102,10 @@ public class ExtractorBlock extends HorizontalBlock {
boolean previouslyPowered = state.get(POWERED);
if (previouslyPowered != worldIn.isBlockPowered(pos)) {
worldIn.setBlockState(pos, state.cycle(POWERED), 2);
IExtractor extractor = (IExtractor) worldIn.getTileEntity(pos);
if (extractor == null)
return;
extractor.setLocked(!previouslyPowered);
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.modules.logistics.block;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.block.SyncedTileEntity;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
public class ExtractorTileEntity extends SyncedTileEntity implements IExtractor, ITickableTileEntity {
private State state;
private int cooldown;
private LazyOptional<IItemHandler> inventory;
public ExtractorTileEntity() {
super(AllTileEntities.EXTRACTOR.type);
state = State.WAITING_FOR_ITEM;
inventory = LazyOptional.empty();
}
@Override
public State getState() {
return state;
}
@Override
public void setState(State state) {
if (state == State.ON_COOLDOWN)
cooldown = EXTRACTOR_COOLDOWN;
this.state = state;
}
@Override
public int tickCooldown() {
return cooldown--;
}
@Override
public BlockPos getInventoryPos() {
return getPos().offset(getBlockState().get(BlockStateProperties.HORIZONTAL_FACING));
}
@Override
public LazyOptional<IItemHandler> getInventory() {
return inventory;
}
@Override
public void setInventory(LazyOptional<IItemHandler> inventory) {
this.inventory = inventory;
}
}

View file

@ -0,0 +1,127 @@
package com.simibubi.create.modules.logistics.block;
import org.apache.commons.lang3.tuple.Pair;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import com.simibubi.create.modules.logistics.IHaveWireless;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber(value = Dist.CLIENT)
public interface IBlockWithFrequency {
public Pair<Vec3d, Vec3d> getFrequencyItemPositions(BlockState state);
public Direction getFrequencyItemFacing(BlockState state);
public default float getItemHitboxScale() {
return 2 / 16f;
}
public default boolean handleActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player,
Hand handIn, BlockRayTraceResult hit) {
TileEntity te = worldIn.getTileEntity(pos);
if (te == null || !(te instanceof IHaveWireless))
return false;
IHaveWireless actor = (IHaveWireless) te;
Pair<Vec3d, Vec3d> positions = getFrequencyItemPositions(state);
ItemStack stack = player.getHeldItem(handIn);
Vec3d vec = new Vec3d(pos);
Vec3d first = positions.getLeft().add(vec);
Vec3d second = positions.getRight().add(vec);
float scale = getItemHitboxScale();
if (new AxisAlignedBB(first, first).grow(scale).contains(hit.getHitVec())) {
if (worldIn.isRemote)
return true;
actor.setFrequency(true, stack);
return true;
}
if (new AxisAlignedBB(second, second).grow(scale).contains(hit.getHitVec())) {
if (worldIn.isRemote)
return true;
actor.setFrequency(false, stack);
return true;
}
return false;
}
@SubscribeEvent
@OnlyIn(Dist.CLIENT)
public static void onDrawBlockHighlight(DrawBlockHighlightEvent event) {
if (event.getTarget() == null || !(event.getTarget() instanceof BlockRayTraceResult))
return;
BlockRayTraceResult result = (BlockRayTraceResult) event.getTarget();
ClientWorld world = Minecraft.getInstance().world;
BlockPos pos = result.getPos();
BlockState state = world.getBlockState(pos);
if (!(state.getBlock() instanceof IBlockWithFrequency))
return;
IBlockWithFrequency freqBlock = (IBlockWithFrequency) state.getBlock();
Pair<Vec3d, Vec3d> positions = freqBlock.getFrequencyItemPositions(state);
Vec3d vec = new Vec3d(pos);
Vec3d first = positions.getLeft().add(vec);
Vec3d second = positions.getRight().add(vec);
float scale = freqBlock.getItemHitboxScale();
AxisAlignedBB firstBB = new AxisAlignedBB(first, first).grow(scale);
AxisAlignedBB secondBB = new AxisAlignedBB(second, second).grow(scale);
TessellatorHelper.prepareForDrawing();
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA,
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE,
GlStateManager.DestFactor.ZERO);
GlStateManager.disableTexture();
GlStateManager.depthMask(false);
GlStateManager.matrixMode(5889);
if (firstBB.contains(result.getHitVec())) {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(firstBB.grow(1 / 128f), 1, 1, .5f, 1f);
} else {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(firstBB.grow(1 / 128f), .5f, .5f, .2f, 1f);
}
if (secondBB.contains(result.getHitVec())) {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(secondBB.grow(1 / 128f), 1, 1, .5f, 1f);
} else {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(secondBB.grow(1 / 128f), .5f, .5f, .2f, 1f);
}
GlStateManager.matrixMode(5888);
GlStateManager.depthMask(true);
GlStateManager.enableTexture();
GlStateManager.disableBlend();
GlStateManager.lineWidth(1);
TessellatorHelper.cleanUpAfterDrawing();
}
}

View file

@ -0,0 +1,170 @@
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 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;
}
public State getState();
public void setState(State state);
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();
if (state == State.LOCKED)
return;
if (state == State.ON_COOLDOWN) {
int cooldown = tickCooldown();
if (cooldown <= 0)
setState(State.RUNNING);
return;
}
boolean hasSpace = hasSpaceForExtracting();
boolean hasInventory = getInventory().isPresent();
ItemStack toExtract = ItemStack.EMPTY;
if (hasSpace && hasInventory)
toExtract = extract(true);
if (state == State.WAITING_FOR_SPACE) {
if (hasSpace)
setState(State.RUNNING);
}
if (state == State.RUNNING) {
if (!hasSpace) {
setState(State.WAITING_FOR_SPACE);
return;
}
if (!hasInventory || toExtract.isEmpty()) {
setState(State.WAITING_FOR_ITEM);
return;
}
extract(false);
setState(State.ON_COOLDOWN);
return;
}
}
public default void setLocked(boolean locked) {
setState(locked ? State.LOCKED : State.ON_COOLDOWN);
}
public default void neighborChanged() {
boolean hasSpace = hasSpaceForExtracting();
boolean hasInventory = getInventory().isPresent();
ItemStack toExtract = ItemStack.EMPTY;
if (hasSpace && hasInventory)
toExtract = extract(true);
if (getState() == State.WAITING_FOR_ITEM) {
if (!hasInventory) {
if (findNewInventory()) {
setState(State.RUNNING);
}
}
if (!toExtract.isEmpty())
setState(State.RUNNING);
return;
}
}
default boolean hasSpaceForExtracting() {
return getWorld().getEntitiesWithinAABBExcludingEntity(null, new AxisAlignedBB(getPos())).isEmpty();
}
default ItemStack extract(boolean simulate) {
IItemHandler inv = getInventory().orElse(null);
ItemStack extracting = ItemStack.EMPTY;
for (int slot = 0; slot < inv.getSlots(); slot++) {
ItemStack stack = inv.extractItem(slot, EXTRACTION_COUNT - extracting.getCount(), true);
ItemStack compare = stack.copy();
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);
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

@ -1,31 +1,108 @@
package com.simibubi.create.modules.logistics.block;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
public class LinkedExtractorBlock extends ExtractorBlock {
public class LinkedExtractorBlock extends ExtractorBlock implements IBlockWithFrequency {
private static final List<Pair<Vec3d, Vec3d>> itemPositions = new ArrayList<>(Direction.values().length);
public LinkedExtractorBlock() {
super();
cacheItemPositions();
}
@Override
public BlockRenderLayer getRenderLayer() {
return BlockRenderLayer.CUTOUT;
}
@Override
public boolean hasTileEntity(BlockState state) {
return true;
}
@Override
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return new LinkedExtractorTileEntity();
}
@Override
public BlockState getStateForPlacement(BlockItemUseContext context) {
return super.getStateForPlacement(context).with(POWERED, false);
}
@Override
public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos,
boolean isMoving) {
}
@Override
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) {
return super.onBlockActivated(state, worldIn, pos, player, handIn, hit)
|| handleActivated(state, worldIn, pos, player, handIn, hit);
}
private void cacheItemPositions() {
if (!itemPositions.isEmpty())
return;
Vec3d first = Vec3d.ZERO;
Vec3d second = Vec3d.ZERO;
Vec3d shift = VecHelper.getCenterOf(BlockPos.ZERO);
float zFightOffset = 1 / 128f;
for (int i = 0; i < 4; i++) {
Direction facing = Direction.byHorizontalIndex(i);
first = new Vec3d(11.5f / 16f + zFightOffset, 4f / 16f, 14f / 16f );
second = new Vec3d(11.5f / 16f + zFightOffset, 8f / 16f, 14f / 16f);
float angle = facing.getHorizontalAngle();
if (facing.getAxis() == Axis.X)
angle = -angle;
first = VecHelper.rotate(first.subtract(shift), angle, Axis.Y).add(shift);
second = VecHelper.rotate(second.subtract(shift), angle, Axis.Y).add(shift);
itemPositions.add(Pair.of(first, second));
}
}
@Override
public float getItemHitboxScale() {
return 3/32f;
}
@Override
public Pair<Vec3d, Vec3d> getFrequencyItemPositions(BlockState state) {
Direction facing = state.get(HORIZONTAL_FACING);
return itemPositions.get(facing.getHorizontalIndex());
}
@Override
public Direction getFrequencyItemFacing(BlockState state) {
return state.get(HORIZONTAL_FACING).rotateYCCW();
}
}

View file

@ -0,0 +1,78 @@
package com.simibubi.create.modules.logistics.block;
import static net.minecraft.state.properties.BlockStateProperties.POWERED;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.modules.logistics.IReceiveWireless;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
public class LinkedExtractorTileEntity extends LinkedTileEntity
implements IReceiveWireless, ITickableTileEntity, IExtractor {
public boolean receivedSignal;
private State state;
private int cooldown;
private LazyOptional<IItemHandler> inventory;
public LinkedExtractorTileEntity() {
super(AllTileEntities.LINKED_EXTRACTOR.type);
state = State.WAITING_FOR_ITEM;
inventory = LazyOptional.empty();
}
@Override
public void setSignal(boolean powered) {
receivedSignal = powered;
}
@Override
public void tick() {
IExtractor.super.tick();
if (world.isRemote)
return;
if (receivedSignal != getBlockState().get(POWERED)) {
setLocked(receivedSignal);
world.setBlockState(pos, getBlockState().cycle(POWERED));
return;
}
}
@Override
public State getState() {
return state;
}
@Override
public void setState(State state) {
if (state == State.ON_COOLDOWN)
cooldown = EXTRACTOR_COOLDOWN;
this.state = state;
}
@Override
public int tickCooldown() {
return cooldown--;
}
@Override
public BlockPos getInventoryPos() {
return getPos().offset(getBlockState().get(BlockStateProperties.HORIZONTAL_FACING));
}
@Override
public LazyOptional<IItemHandler> getInventory() {
return inventory;
}
@Override
public void setInventory(LazyOptional<IItemHandler> inventory) {
this.inventory = inventory;
}
}

View file

@ -0,0 +1,86 @@
package com.simibubi.create.modules.logistics.block;
import com.simibubi.create.foundation.block.SyncedTileEntity;
import com.simibubi.create.modules.logistics.FrequencyHandler;
import com.simibubi.create.modules.logistics.FrequencyHandler.Frequency;
import com.simibubi.create.modules.logistics.IHaveWireless;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntityType;
public abstract class LinkedTileEntity extends SyncedTileEntity implements IHaveWireless {
public Frequency frequencyFirst;
public Frequency frequencyLast;
public LinkedTileEntity(TileEntityType<?> tileEntityTypeIn) {
super(tileEntityTypeIn);
frequencyFirst = new Frequency(ItemStack.EMPTY);
frequencyLast = new Frequency(ItemStack.EMPTY);
}
@Override
public void onLoad() {
super.onLoad();
if (world.isRemote)
return;
FrequencyHandler.addToNetwork(this);
}
@Override
public void remove() {
super.remove();
if (world.isRemote)
return;
FrequencyHandler.removeFromNetwork(this);
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.put("FrequencyFirst", frequencyFirst.getStack().write(new CompoundNBT()));
compound.put("FrequencyLast", frequencyLast.getStack().write(new CompoundNBT()));
return super.write(compound);
}
@Override
public void read(CompoundNBT compound) {
frequencyFirst = new Frequency(ItemStack.read(compound.getCompound("FrequencyFirst")));
frequencyLast = new Frequency(ItemStack.read(compound.getCompound("FrequencyLast")));
super.read(compound);
}
@Override
public void setFrequency(boolean first, ItemStack stack) {
stack = stack.copy();
stack.setCount(1);
ItemStack toCompare = first ? frequencyFirst.getStack() : frequencyLast.getStack();
boolean changed = !ItemStack.areItemsEqual(stack, toCompare)
|| !ItemStack.areItemStackTagsEqual(stack, toCompare);
if (changed)
FrequencyHandler.removeFromNetwork(this);
if (first)
frequencyFirst = new Frequency(stack);
else
frequencyLast = new Frequency(stack);
if (!changed)
return;
sendData();
FrequencyHandler.addToNetwork(this);
}
@Override
public Frequency getFrequencyFirst() {
return frequencyFirst;
}
@Override
public Frequency getFrequencyLast() {
return frequencyLast;
}
}

View file

@ -5,72 +5,75 @@ import org.apache.commons.lang3.tuple.Pair;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType;
import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
@SuppressWarnings("deprecation")
public class RedstoneBridgeTileEntityRenderer extends TileEntityRenderer<RedstoneBridgeTileEntity> {
public class LinkedTileEntityRenderer extends TileEntityRenderer<LinkedTileEntity> {
@Override
public void render(RedstoneBridgeTileEntity tileEntityIn, double x, double y, double z, float partialTicks,
public void render(LinkedTileEntity tileEntityIn, double x, double y, double z, float partialTicks,
int destroyStage) {
super.render(tileEntityIn, x, y, z, partialTicks, destroyStage);
Direction facing = tileEntityIn.getBlockState().get(BlockStateProperties.FACING);
BlockState state = tileEntityIn.getBlockState();
IBlockWithFrequency block = (IBlockWithFrequency) state.getBlock();
Direction facing = block.getFrequencyItemFacing(state);
float scale = block.getItemHitboxScale();
TessellatorHelper.prepareForDrawing();
Pair<Vec3d, Vec3d> itemPositions = RedstoneBridgeBlock.getFrequencyItemPositions(tileEntityIn.getBlockState());
Pair<Vec3d, Vec3d> itemPositions = block.getFrequencyItemPositions(state);
Vec3d first = itemPositions.getLeft();
Vec3d second = itemPositions.getRight();
BlockPos pos = tileEntityIn.getPos();
GlStateManager.translated(pos.getX(), pos.getY(), pos.getZ());
renderFrequencyItem(tileEntityIn.frequencyFirst.getStack(), first, facing);
renderFrequencyItem(tileEntityIn.frequencyLast.getStack(), second, facing);
renderFrequencyItem(tileEntityIn.frequencyFirst.getStack(), first, facing, scale - 2/16f);
renderFrequencyItem(tileEntityIn.frequencyLast.getStack(), second, facing, scale - 2/16f);
TessellatorHelper.cleanUpAfterDrawing();
}
private void renderFrequencyItem(ItemStack stack, Vec3d position, Direction facing) {
private void renderFrequencyItem(ItemStack stack, Vec3d position, Direction facing, float scaleDiff) {
ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
boolean vertical = facing.getAxis().isVertical();
IBakedModel modelWithOverrides = itemRenderer.getModelWithOverrides(stack);
boolean blockItem = modelWithOverrides.isGui3d();
float offX = 0;
float offY = vertical && !blockItem ? 0 : 0;
float offZ = !blockItem ? 1/4f : 0;
float offZ = !blockItem ? 1 / 4f + 2 * scaleDiff : 0;
if (vertical)
offZ = -offZ;
float rotX = vertical ? 90 : 0;
float rotY = vertical ? 0 : facing.getHorizontalAngle() + (blockItem ? 180 : 0);
float rotZ = vertical && facing == Direction.DOWN ? 180 : 0;
if (facing.getAxis() == Axis.X) {
// offZ = -offZ;
rotY = -rotY;
}
float scale = !blockItem ? .25f : .5f;
scale *= 1 + 8 * scaleDiff;
GlStateManager.pushMatrix();
GlStateManager.translated(position.x, position.y, position.z);
GlStateManager.scaled(scale, scale, scale);
GlStateManager.rotatef(rotZ, 0, 0, 1);
GlStateManager.rotatef(rotY, 0, 1, 0);
GlStateManager.rotatef(rotX, 1, 0, 0);
GlStateManager.scaled(scale, scale, scale);
GlStateManager.translatef(offX, offY, offZ);
itemRenderer.renderItem(stack, TransformType.FIXED);
GlStateManager.popMatrix();

View file

@ -5,23 +5,17 @@ import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.block.ProperDirectionalBlock;
import com.simibubi.create.foundation.utility.ITooltip;
import com.simibubi.create.foundation.utility.ItemDescription;
import com.simibubi.create.foundation.utility.ItemDescription.Palette;
import com.simibubi.create.foundation.utility.TessellatorHelper;
import com.simibubi.create.foundation.utility.TooltipHolder;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
@ -33,7 +27,6 @@ import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
@ -46,12 +39,8 @@ import net.minecraft.world.IWorldReader;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber(value = Dist.CLIENT)
public class RedstoneBridgeBlock extends ProperDirectionalBlock implements ITooltip {
public class RedstoneBridgeBlock extends ProperDirectionalBlock implements ITooltip, IBlockWithFrequency {
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
public static final BooleanProperty RECEIVER = BooleanProperty.create("receiver");
@ -151,15 +140,12 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements ITool
@Override
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) {
Direction facing = state.get(FACING);
Pair<Vec3d, Vec3d> positions = itemPositions.get(facing.getIndex());
ItemStack stack = player.getHeldItem(handIn);
RedstoneBridgeTileEntity te = (RedstoneBridgeTileEntity) worldIn.getTileEntity(pos);
if (te == null)
return false;
if (player.isSneaking()) {
RedstoneBridgeTileEntity te = (RedstoneBridgeTileEntity) worldIn.getTileEntity(pos);
if (te == null)
return false;
if (!worldIn.isRemote) {
Boolean wasReceiver = state.get(RECEIVER);
boolean blockPowered = worldIn.isBlockPowered(pos);
@ -172,86 +158,7 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements ITool
return true;
}
Vec3d vec = new Vec3d(pos);
Vec3d first = positions.getLeft().add(vec);
Vec3d second = positions.getRight().add(vec);
if (new AxisAlignedBB(first, first).grow(2 / 16f).contains(hit.getHitVec())) {
if (worldIn.isRemote)
return true;
te.setFrequency(true, stack);
return true;
}
if (new AxisAlignedBB(second, second).grow(2 / 16f).contains(hit.getHitVec())) {
if (worldIn.isRemote)
return true;
te.setFrequency(false, stack);
return true;
}
return false;
}
@SubscribeEvent
@OnlyIn(Dist.CLIENT)
public static void onDrawBlockHighlight(DrawBlockHighlightEvent event) {
if (event.getTarget() == null || !(event.getTarget() instanceof BlockRayTraceResult))
return;
BlockRayTraceResult result = (BlockRayTraceResult) event.getTarget();
ClientWorld world = Minecraft.getInstance().world;
BlockPos pos = result.getPos();
BlockState state = world.getBlockState(pos);
if (!AllBlocks.REDSTONE_BRIDGE.typeOf(state))
return;
Direction facing = state.get(FACING);
Pair<Vec3d, Vec3d> positions = itemPositions.get(facing.getIndex());
RedstoneBridgeTileEntity te = (RedstoneBridgeTileEntity) world.getTileEntity(pos);
if (te == null)
return;
Vec3d vec = new Vec3d(pos);
Vec3d first = positions.getLeft().add(vec);
Vec3d second = positions.getRight().add(vec);
AxisAlignedBB firstBB = new AxisAlignedBB(first, first).grow(2 / 16f);
AxisAlignedBB secondBB = new AxisAlignedBB(second, second).grow(2 / 16f);
TessellatorHelper.prepareForDrawing();
GlStateManager.enableBlend();
GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA,
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE,
GlStateManager.DestFactor.ZERO);
GlStateManager.disableTexture();
GlStateManager.depthMask(false);
GlStateManager.matrixMode(5889);
if (firstBB.contains(result.getHitVec())) {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(firstBB.grow(1 / 128f), 1, 1, .5f, 1f);
} else {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(firstBB.grow(1 / 128f), .5f, .5f, .2f, 1f);
}
if (secondBB.contains(result.getHitVec())) {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(secondBB.grow(1 / 128f), 1, 1, .5f, 1f);
} else {
GlStateManager.lineWidth(2);
WorldRenderer.drawSelectionBoundingBox(secondBB.grow(1 / 128f), .5f, .5f, .2f, 1f);
}
GlStateManager.matrixMode(5888);
GlStateManager.depthMask(true);
GlStateManager.enableTexture();
GlStateManager.disableBlend();
GlStateManager.lineWidth(1);
TessellatorHelper.cleanUpAfterDrawing();
return handleActivated(state, worldIn, pos, player, handIn, hit);
}
@Override
@ -334,10 +241,15 @@ public class RedstoneBridgeBlock extends ProperDirectionalBlock implements ITool
}
public static Pair<Vec3d, Vec3d> getFrequencyItemPositions(BlockState state) {
public Pair<Vec3d, Vec3d> getFrequencyItemPositions(BlockState state) {
Direction facing = state.get(FACING);
return itemPositions.get(facing.getIndex());
}
@Override
public Direction getFrequencyItemFacing(BlockState state) {
return state.get(FACING);
}
@Override
@OnlyIn(value = Dist.CLIENT)

View file

@ -3,127 +3,59 @@ package com.simibubi.create.modules.logistics.block;
import static net.minecraft.state.properties.BlockStateProperties.POWERED;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.foundation.block.SyncedTileEntity;
import com.simibubi.create.modules.logistics.FrequencyHandler;
import com.simibubi.create.modules.logistics.FrequencyHandler.Frequency;
import com.simibubi.create.modules.logistics.IReceiveWireless;
import com.simibubi.create.modules.logistics.ITransmitWireless;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
public class RedstoneBridgeTileEntity extends SyncedTileEntity
public class RedstoneBridgeTileEntity extends LinkedTileEntity
implements ITickableTileEntity, IReceiveWireless, ITransmitWireless {
public Frequency frequencyFirst;
public Frequency frequencyLast;
public boolean receivedSignal;
public boolean transmittedSignal;
public RedstoneBridgeTileEntity() {
super(AllTileEntities.REDSTONE_BRIDGE.type);
frequencyFirst = new Frequency(ItemStack.EMPTY);
frequencyLast = new Frequency(ItemStack.EMPTY);
}
@Override
public void onLoad() {
super.onLoad();
if (world.isRemote)
return;
FrequencyHandler.addToNetwork(this);
}
@Override
public void remove() {
super.remove();
if (world.isRemote)
return;
FrequencyHandler.removeFromNetwork(this);
}
public void setFrequency(boolean first, ItemStack stack) {
stack = stack.copy();
stack.setCount(1);
ItemStack toCompare = first ? frequencyFirst.getStack() : frequencyLast.getStack();
boolean changed = !ItemStack.areItemsEqual(stack, toCompare)
|| !ItemStack.areItemStackTagsEqual(stack, toCompare);
if (changed)
FrequencyHandler.removeFromNetwork(this);
if (first)
frequencyFirst = new Frequency(stack);
else
frequencyLast = new Frequency(stack);
if (!changed)
return;
sendData();
FrequencyHandler.addToNetwork(this);
}
@Override
public Frequency getFrequencyFirst() {
return frequencyFirst;
}
@Override
public Frequency getFrequencyLast() {
return frequencyLast;
}
@Override
public boolean getSignal() {
return transmittedSignal;
}
public void transmit(boolean signal) {
transmittedSignal = signal;
notifySignalChange();
}
@Override
public void setSignal(boolean powered) {
receivedSignal = powered;
}
protected boolean isTransmitter() {
return !getBlockState().get(RedstoneBridgeBlock.RECEIVER);
}
protected boolean isBlockPowered() {
return getBlockState().get(POWERED);
public void transmit(boolean signal) {
transmittedSignal = signal;
notifySignalChange();
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.put("FrequencyFirst", frequencyFirst.getStack().write(new CompoundNBT()));
compound.put("FrequencyLast", frequencyLast.getStack().write(new CompoundNBT()));
compound.putBoolean("Transmit", transmittedSignal);
return super.write(compound);
}
@Override
public void read(CompoundNBT compound) {
frequencyFirst = new Frequency(ItemStack.read(compound.getCompound("FrequencyFirst")));
frequencyLast = new Frequency(ItemStack.read(compound.getCompound("FrequencyLast")));
transmittedSignal = compound.getBoolean("Transmit");
super.read(compound);
}
@Override
public void tick() {
if (isTransmitter())
if (!getBlockState().get(RedstoneBridgeBlock.RECEIVER))
return;
if (world.isRemote)
return;
if (receivedSignal != isBlockPowered()) {
if (receivedSignal != getBlockState().get(POWERED)) {
world.setBlockState(pos, getBlockState().cycle(POWERED));
Direction attachedFace = getBlockState().get(BlockStateProperties.FACING).getOpposite();
BlockPos attachedPos = pos.offset(attachedFace);