Portable Funnels & Extractors

- Fixed custom sword not having a sweeping effect
- Fixed invalid kinetic tileentity state when dimension is being unloaded
- Mechanical harvesters now interact with kelp blocks
- Extractors and Funnels are now portable and have special movement traits #117
- Fixed false positives on having reached the block movement limit for contraptions
- Fixed self-modification within a belt's update cycle, addresses #116
- Fixed belt processing not re-activating segement attachments, addresses #132
- Fixed reversing components (gearbox, gearshift) breaking when a new source overpowers the network
- Reduced attack damage of the Deforester
- Fixed redstone links not being powered by wires consistently #106
- New entries for attribute filters: Washable, Smeltable, Smokable and Smeltable in Blast furnace
- Fixed redstone links behaving inconsistently when unloaded and reloaded
- Bumped oldest supported forge version
This commit is contained in:
simibubi 2020-03-28 17:48:55 +01:00
parent b885a60003
commit e891aeb8c1
31 changed files with 345 additions and 78 deletions

View file

@ -4,7 +4,6 @@ import static com.simibubi.create.foundation.item.AllToolTypes.AXE;
import static com.simibubi.create.foundation.item.AllToolTypes.HOE;
import static com.simibubi.create.foundation.item.AllToolTypes.PICKAXE;
import static com.simibubi.create.foundation.item.AllToolTypes.SHOVEL;
import static com.simibubi.create.foundation.item.AllToolTypes.SWORD;
import java.util.function.Function;
@ -21,6 +20,7 @@ import com.simibubi.create.modules.curiosities.RefinedRadianceItem;
import com.simibubi.create.modules.curiosities.ShadowSteelItem;
import com.simibubi.create.modules.curiosities.deforester.DeforesterItem;
import com.simibubi.create.modules.curiosities.symmetry.SymmetryWandItem;
import com.simibubi.create.modules.curiosities.tools.AllToolTiers;
import com.simibubi.create.modules.curiosities.tools.BlazingToolItem;
import com.simibubi.create.modules.curiosities.tools.RoseQuartzToolItem;
import com.simibubi.create.modules.curiosities.tools.SandPaperItem;
@ -39,6 +39,7 @@ import net.minecraft.item.Item;
import net.minecraft.item.Item.Properties;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Rarity;
import net.minecraft.item.SwordItem;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.RegistryEvent;
@ -113,16 +114,16 @@ public enum AllItems {
BLAZING_PICKAXE(p -> new BlazingToolItem(1, -2.8F, p, PICKAXE)),
BLAZING_SHOVEL(p -> new BlazingToolItem(1.5F, -3.0F, p, SHOVEL)),
BLAZING_AXE(p -> new BlazingToolItem(5.0F, -3.0F, p, AXE)),
BLAZING_SWORD(p -> new BlazingToolItem(3, -2.4F, p, SWORD)),
BLAZING_SWORD(p -> new SwordItem(AllToolTiers.BLAZING, 3, -2.4F, p)),
ROSE_QUARTZ_PICKAXE(p -> new RoseQuartzToolItem(1, -2.8F, p, PICKAXE)),
ROSE_QUARTZ_SHOVEL(p -> new RoseQuartzToolItem(1.5F, -3.0F, p, SHOVEL)),
ROSE_QUARTZ_AXE(p -> new RoseQuartzToolItem(5.0F, -3.0F, p, AXE)),
ROSE_QUARTZ_SWORD(p -> new RoseQuartzToolItem(3, -2.4F, p, SWORD)),
ROSE_QUARTZ_SWORD(p -> new SwordItem(AllToolTiers.ROSE_QUARTZ, 3, -2.4F, p)),
SHADOW_STEEL_PICKAXE(p -> new ShadowSteelToolItem(2.5F, -2.0F, p, PICKAXE)),
SHADOW_STEEL_MATTOCK(p -> new ShadowSteelToolItem(2.5F, -1.5F, p, SHOVEL, AXE, HOE)),
SHADOW_STEEL_SWORD(p -> new ShadowSteelToolItem(3, -2.0F, p, SWORD)),
SHADOW_STEEL_SWORD(p -> new SwordItem(AllToolTiers.SHADOW_STEEL, 3, -2.0F, p)),
;

View file

@ -145,7 +145,7 @@ public class FilteringBehaviour extends TileEntityBehaviour {
}
public boolean test(ItemStack stack) {
return filter.isEmpty() || FilterItem.test(stack, filter);
return filter.isEmpty() || FilterItem.test(tileEntity.getWorld(), stack, filter);
}
@Override

View file

@ -61,7 +61,7 @@ public class KineticNetwork {
if (te.isSource())
sources.put(te, te.getAddedStressCapacity());
members.put(te, te.getStressApplied());
te.updateStressFromNetwork(currentCapacity, currentStress);
te.updateFromNetwork(currentCapacity, currentStress, getSize());
te.networkDirty = true;
}
@ -81,7 +81,7 @@ public class KineticNetwork {
if (te.isSource())
sources.remove(te);
members.remove(te);
te.updateStressFromNetwork(0, 0);
te.updateFromNetwork(0, 0, 0);
if (members.isEmpty()) {
TorquePropagator.networks.get(te.getWorld()).remove(this.id);
@ -93,7 +93,7 @@ public class KineticNetwork {
public void sync() {
for (KineticTileEntity te : members.keySet())
te.updateStressFromNetwork(currentCapacity, currentStress);
te.updateFromNetwork(currentCapacity, currentStress, getSize());
}
public void updateCapacity() {

View file

@ -123,7 +123,8 @@ public class RotationPropagator {
if (isLargeGearToSpeedController(stateTo, stateFrom, diff))
return SpeedControllerTileEntity.getConveyedSpeed(to, from, false);
return from.getTheoreticalSpeed() * getRotationSpeedModifier(from, to);
float rotationSpeedModifier = getRotationSpeedModifier(from, to);
return from.getTheoreticalSpeed() * rotationSpeedModifier;
}
private static boolean isLargeToLargeGear(BlockState from, BlockState to, BlockPos diff) {
@ -233,8 +234,8 @@ public class RotationPropagator {
// Neighbour faster, overpower the incoming tree
if (Math.abs(oppositeSpeed) > Math.abs(speedOfCurrent)) {
float prevSpeed = currentTE.getSpeed();
currentTE.setSpeed(oppositeSpeed);
currentTE.setSource(neighbourTE.getPos());
currentTE.setSpeed(getConveyedSpeed(neighbourTE, currentTE));
currentTE.onSpeedChanged(prevSpeed);
currentTE.sendData();
@ -256,8 +257,8 @@ public class RotationPropagator {
currentTE.removeSource();
float prevSpeed = neighbourTE.getSpeed();
neighbourTE.setSpeed(newSpeed);
neighbourTE.setSource(currentTE.getPos());
neighbourTE.setSpeed(getConveyedSpeed(currentTE, neighbourTE));
neighbourTE.onSpeedChanged(prevSpeed);
neighbourTE.sendData();
propagateNewSource(neighbourTE);

View file

@ -120,10 +120,11 @@ public abstract class KineticTileEntity extends SmartTileEntity
}
}
public void updateStressFromNetwork(float maxStress, float currentStress) {
public void updateFromNetwork(float maxStress, float currentStress, int networkSize) {
networkDirty = false;
this.capacity = maxStress;
this.stress = currentStress;
this.networkSize = networkSize;
boolean overStressed = maxStress < currentStress && StressImpact.isEnabled();
if (overStressed != this.overStressed) {
@ -182,7 +183,7 @@ public abstract class KineticTileEntity extends SmartTileEntity
networkTag.putLong("Id", this.network);
networkTag.putFloat("Stress", stress);
networkTag.putFloat("Capacity", capacity);
networkTag.putInt("Size", getOrCreateNetwork().getSize());
networkTag.putInt("Size", networkSize);
float stressApplied = getStressApplied();
float addedStressCapacity = getAddedStressCapacity();

View file

@ -13,6 +13,7 @@ import com.simibubi.create.modules.contraptions.components.contraptions.Movement
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.CropsBlock;
import net.minecraft.block.KelpBlock;
import net.minecraft.block.SugarCaneBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.state.IProperty;
@ -111,6 +112,8 @@ public class HarvesterMovementBehaviour extends MovementBehaviour {
return false;
}
if (state.getBlock() instanceof KelpBlock)
return true;
if (state.getBlock() instanceof IPlantable)
return true;
}
@ -124,7 +127,9 @@ public class HarvesterMovementBehaviour extends MovementBehaviour {
return crop.withAge(0);
}
if (state.getBlock() == Blocks.SUGAR_CANE) {
if (state.getFluidState().isEmpty())
return Blocks.AIR.getDefaultState();
return state.getFluidState().getBlockState();
}
if (state.getCollisionShape(world, pos).isEmpty()) {
for (IProperty<?> property : state.getProperties()) {
@ -136,7 +141,9 @@ public class HarvesterMovementBehaviour extends MovementBehaviour {
}
}
if (state.getFluidState().isEmpty())
return Blocks.AIR.getDefaultState();
return state.getFluidState().getBlockState();
}
}

View file

@ -4,6 +4,10 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.modules.contraptions.components.actors.HarvesterBlock;
import com.simibubi.create.modules.contraptions.components.actors.PortableStorageInterfaceBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.chassis.AbstractChassisBlock;
import com.simibubi.create.modules.logistics.block.belts.AttachedLogisticalBlock;
import com.simibubi.create.modules.logistics.block.belts.FunnelBlock;
import com.simibubi.create.modules.logistics.block.extractor.ExtractorBlock;
import com.simibubi.create.modules.logistics.block.transposer.TransposerBlock;
import net.minecraft.block.AbstractPressurePlateBlock;
import net.minecraft.block.AbstractRailBlock;
@ -11,6 +15,7 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.DoorBlock;
import net.minecraft.block.FenceGateBlock;
import net.minecraft.block.FlowerPotBlock;
import net.minecraft.block.HorizontalFaceBlock;
import net.minecraft.block.LadderBlock;
@ -32,6 +37,8 @@ public class BlockMovementTraits {
BlockState state = world.getBlockState(pos);
if (isBrittle(state))
return true;
if (state.getBlock() instanceof FenceGateBlock)
return true;
if (state.getMaterial().isReplaceable())
return false;
if (state.getCollisionShape(world, pos).isEmpty())
@ -41,14 +48,19 @@ public class BlockMovementTraits {
public static boolean movementAllowed(World world, BlockPos pos) {
BlockState blockState = world.getBlockState(pos);
if (blockState.getBlock() instanceof AbstractChassisBlock)
Block block = blockState.getBlock();
if (block instanceof AbstractChassisBlock)
return true;
if (blockState.getBlockHardness(world, pos) == -1)
return false;
if (blockState.getBlock() == Blocks.OBSIDIAN)
if (block == Blocks.OBSIDIAN)
return false;
if (AllBlocks.BELT.typeOf(blockState))
return true;
if (block instanceof ExtractorBlock)
return true;
if (block instanceof FunnelBlock)
return true;
return blockState.getPushReaction() != PushReaction.BLOCK;
}
@ -62,6 +74,10 @@ public class BlockMovementTraits {
return true;
if (block instanceof LadderBlock)
return true;
if (block instanceof ExtractorBlock)
return true;
if (block instanceof FunnelBlock)
return true;
if (block instanceof TorchBlock)
return true;
if (block instanceof FlowerPotBlock)
@ -94,6 +110,8 @@ public class BlockMovementTraits {
return direction == Direction.DOWN;
if (block instanceof DoorBlock)
return direction == Direction.DOWN;
if (block instanceof AttachedLogisticalBlock && !(block instanceof TransposerBlock))
return direction == AttachedLogisticalBlock.getBlockFacing(state);
if (block instanceof FlowerPotBlock)
return direction == Direction.DOWN;
if (block instanceof RedstoneDiodeBlock)

View file

@ -114,9 +114,7 @@ public abstract class Contraption {
frontier.add(pos);
if (!addToInitialFrontier(world, pos, forcedDirection, frontier))
return false;
Integer blockLimit = AllConfigs.SERVER.kinetics.maxBlocksMoved.get();
for (int limit = blockLimit; limit > 0; limit--) {
for (int limit = 100000; limit > 0; limit--) {
if (frontier.isEmpty())
return true;
if (!moveBlock(world, frontier.remove(0), forcedDirection, frontier, visited))
@ -183,6 +181,8 @@ public abstract class Contraption {
}
add(pos, capture(world, pos));
if (blocks.size() > AllConfigs.SERVER.kinetics.maxBlocksMoved.get())
return false;
return true;
}

View file

@ -126,7 +126,7 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
@Override
public float getInterpolatedAngle(float partialTicks) {
if (movedContraption == null || movedContraption.isStalled())
if (movedContraption == null || movedContraption.isStalled() || !running)
partialTicks = 0;
return MathHelper.lerp(partialTicks, angle, angle + getAngularSpeed());
}

View file

@ -104,8 +104,9 @@ public class DeployerMovementBehaviour extends MovementBehaviour {
if (player == null)
return;
if (player.getHeldItemMainhand().isEmpty()) {
ItemStack filter = getFilter(context);
ItemStack held = ItemHelper.extract(context.contraption.inventory,
stack -> FilterItem.test(stack, getFilter(context)), 1, false);
stack -> FilterItem.test(context.world, stack, filter), 1, false);
player.setHeldItem(Hand.MAIN_HAND, held);
}
}
@ -123,7 +124,8 @@ public class DeployerMovementBehaviour extends MovementBehaviour {
if (itemstack.isEmpty())
continue;
if (list == inv.mainInventory && i == inv.currentItem && FilterItem.test(itemstack, filter))
if (list == inv.mainInventory && i == inv.currentItem
&& FilterItem.test(context.world, itemstack, filter))
continue;
dropItem(context, itemstack);

View file

@ -37,12 +37,14 @@ public class BeltInventory {
final BeltTileEntity belt;
final List<TransportedItemStack> items;
final List<TransportedItemStack> toInsert;
boolean beltMovementPositive;
final float SEGMENT_WINDOW = .75f;
public BeltInventory(BeltTileEntity te) {
this.belt = te;
items = new LinkedList<>();
toInsert = new LinkedList<>();
}
public void tick() {
@ -55,6 +57,14 @@ public class BeltInventory {
belt.sendData();
}
// Add items from previous cycle
if (!toInsert.isEmpty()) {
toInsert.forEach(this::insert);
toInsert.clear();
belt.markDirty();
belt.sendData();
}
// Assuming the first entry is furthest on the belt
TransportedItemStack stackInFront = null;
TransportedItemStack current = null;
@ -325,18 +335,29 @@ public class BeltInventory {
else if (!beltMovementPositive)
segmentPos += 1f;
for (TransportedItemStack stack : items) {
float currentPos = stack.beltPosition;
if (stack.insertedAt == segment && stack.insertedFrom == side
&& (beltMovementPositive ? currentPos <= segmentPos + 1 : currentPos >= segmentPos - 1))
for (TransportedItemStack stack : items)
if (isBlocking(segment, side, segmentPos, stack))
return false;
for (TransportedItemStack stack : toInsert)
if (isBlocking(segment, side, segmentPos, stack))
return false;
}
return true;
}
protected void insert(TransportedItemStack newStack) {
private boolean isBlocking(int segment, Direction side, float segmentPos, TransportedItemStack stack) {
float currentPos = stack.beltPosition;
if (stack.insertedAt == segment && stack.insertedFrom == side
&& (beltMovementPositive ? currentPos <= segmentPos + 1 : currentPos >= segmentPos - 1))
return true;
return false;
}
public void addItem(TransportedItemStack newStack) {
toInsert.add(newStack);
}
private void insert(TransportedItemStack newStack) {
if (items.isEmpty())
items.add(newStack);
else {

View file

@ -347,7 +347,7 @@ public class BeltTileEntity extends KineticTileEntity {
transportedStack.insertedAt = index;
transportedStack.insertedFrom = side;
transportedStack.prevBeltPosition = transportedStack.beltPosition;
nextInventory.insert(transportedStack);
nextInventory.addItem(transportedStack);
nextBeltController.markDirty();
nextBeltController.sendData();

View file

@ -34,7 +34,7 @@ public class ItemHandlerBeltSegment implements IItemHandler {
newStack.insertedAt = offset;
newStack.beltPosition = offset + .5f + (beltInventory.beltMovementPositive ? -1 : 1) / 16f;
newStack.prevBeltPosition = newStack.beltPosition;
this.beltInventory.insert(newStack);
this.beltInventory.addItem(newStack);
this.beltInventory.belt.markDirty();
this.beltInventory.belt.sendData();
}

View file

@ -18,8 +18,8 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
}
@Override
public void updateStressFromNetwork(float maxStress, float currentStress) {
super.updateStressFromNetwork(maxStress, currentStress);
public void updateFromNetwork(float maxStress, float currentStress, int networkSize) {
super.updateFromNetwork(maxStress, currentStress, networkSize);
if (!StressImpact.isEnabled())
dialTarget = 0;
@ -51,7 +51,8 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
markDirty();
return;
}
updateStressFromNetwork(capacity, stress);
updateFromNetwork(capacity, stress, getOrCreateNetwork().getSize());
}
@Override
@ -67,7 +68,8 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
tooltip.add(spacing + TextFormatting.GRAY + Lang.translate("gui.stress_gauge.title"));
if (getTheoreticalSpeed() == 0)
tooltip.add(TextFormatting.DARK_GRAY + ItemDescription.makeProgressBar(3, -1) + Lang.translate("gui.stress_gauge.no_rotation"));
tooltip.add(TextFormatting.DARK_GRAY + ItemDescription.makeProgressBar(3, -1)
+ Lang.translate("gui.stress_gauge.no_rotation"));
else {
tooltip.add(spacing + StressImpact.getFormattedStressText(stressFraction));
@ -76,10 +78,13 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
double remainingCapacity = capacity - getNetworkStress();
double remainingCapacityAtBase = remainingCapacity / Math.abs(getTheoreticalSpeed());
String capacityString = spacing + StressImpact.of(stressFraction).getRelativeColor() + "%s" + Lang.translate("generic.unit.stress") + " " + TextFormatting.DARK_GRAY + "%s";
String capacityString = spacing + StressImpact.of(stressFraction).getRelativeColor() + "%s"
+ Lang.translate("generic.unit.stress") + " " + TextFormatting.DARK_GRAY + "%s";
tooltip.add(String.format(capacityString, IHaveGoggleInformation.format(remainingCapacityAtBase), Lang.translate("gui.goggles.base_value")));
tooltip.add(String.format(capacityString, IHaveGoggleInformation.format(remainingCapacity), Lang.translate("gui.goggles.at_current_speed")));
tooltip.add(String.format(capacityString, IHaveGoggleInformation.format(remainingCapacityAtBase),
Lang.translate("gui.goggles.base_value")));
tooltip.add(String.format(capacityString, IHaveGoggleInformation.format(remainingCapacity),
Lang.translate("gui.goggles.at_current_speed")));
}

View file

@ -32,7 +32,7 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
public class DeforesterItem extends AxeItem implements IHaveCustomItemModel {
public DeforesterItem(Properties builder) {
super(AllToolTiers.RADIANT, 10.0F, -3.1F, builder);
super(AllToolTiers.RADIANT, 5.0F, -3.1F, builder);
}
// Moved away from Item#onBlockDestroyed as it does not get called in Creative

View file

@ -67,12 +67,16 @@ public class BlazingToolItem extends AbstractToolItem {
public void modifyDrops(Collection<ItemStack> drops, IWorld world, BlockPos pos, ItemStack tool, BlockState state) {
super.modifyDrops(drops, world, pos, tool, state);
World worldIn = world.getWorld();
helperFurnace.setWorld(worldIn);
RecipeManager recipeManager = worldIn.getRecipeManager();
int enchantmentLevel = EnchantmentHelper.getEnchantmentLevel(Enchantments.FORTUNE, tool);
if (state == null)
enchantmentLevel = 0;
List<ItemStack> smeltedStacks = smeltDrops(drops, worldIn, enchantmentLevel);
drops.addAll(smeltedStacks);
}
public static List<ItemStack> smeltDrops(Collection<ItemStack> drops, World worldIn, int enchantmentLevel) {
helperFurnace.setWorld(worldIn);
RecipeManager recipeManager = worldIn.getRecipeManager();
List<ItemStack> smeltedStacks = new ArrayList<>();
Iterator<ItemStack> dropper = drops.iterator();
while (dropper.hasNext()) {
@ -87,13 +91,12 @@ public class BlazingToolItem extends AbstractToolItem {
float modifier = 1;
if (stack.getItem() instanceof BlockItem && !(out.getItem() instanceof BlockItem))
modifier += world.getRandom().nextFloat() * enchantmentLevel;
modifier += worldIn.getRandom().nextFloat() * enchantmentLevel;
out.setCount((int) (out.getCount() * modifier + .4f));
smeltedStacks.addAll(ItemHelper.multipliedOutput(stack, out));
}
drops.addAll(smeltedStacks);
return smeltedStacks;
}
@Override

View file

@ -114,6 +114,11 @@ public class SandPaperItem extends Item implements IHaveCustomItemModel {
return enchantment == Enchantments.FORTUNE || super.canApplyAtEnchantingTable(stack, enchantment);
}
@Override
public int getItemEnchantability(ItemStack stack) {
return 1;
}
@Override
public ItemStack onItemUseFinish(ItemStack stack, World worldIn, LivingEntity entityLiving) {
if (!(entityLiving instanceof PlayerEntity))

View file

@ -16,8 +16,10 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.IItemTier;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.TieredItem;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.EntityDamageSource;
import net.minecraft.util.math.BlockPos;
@ -98,8 +100,11 @@ public class ToolEvents {
ItemStack heldItemMainhand = player.getHeldItemMainhand();
String marker = "create_roseQuartzRange";
CompoundNBT persistentData = player.getPersistentData();
Item item = heldItemMainhand.getItem();
boolean holdingRoseQuartz =
item instanceof TieredItem && ((TieredItem) item).getTier() == AllToolTiers.ROSE_QUARTZ;
if (!(heldItemMainhand.getItem() instanceof RoseQuartzToolItem)) {
if (!holdingRoseQuartz) {
if (persistentData.contains(marker)) {
player.getAttributes().removeAttributeModifiers(RoseQuartzToolItem.rangeModifier);
persistentData.remove(marker);
@ -127,18 +132,20 @@ public class ToolEvents {
PlayerEntity player = (PlayerEntity) trueSource;
ItemStack heldItemMainhand = player.getHeldItemMainhand();
Item item = heldItemMainhand.getItem();
IItemTier tier = item instanceof TieredItem ? ((TieredItem) item).getTier() : null;
if (item instanceof ShadowSteelToolItem)
if (tier == AllToolTiers.SHADOW_STEEL)
event.setCanceled(true);
if (item instanceof BlazingToolItem) {
BlazingToolItem blazingToolItem = (BlazingToolItem) item;
if (tier == AllToolTiers.BLAZING) {
List<ItemStack> drops = event.getDrops().stream().map(entity -> {
ItemStack stack = entity.getItem();
entity.remove();
return stack;
}).collect(Collectors.toList());
blazingToolItem.modifyDrops(drops, world, player.getPosition(), heldItemMainhand, null);
drops = BlazingToolItem.smeltDrops(drops, world, 0);
event.getDrops().clear();
drops.stream().map(stack -> {
ItemEntity entity = new ItemEntity(world, target.posX, target.posY, target.posZ, stack);
@ -156,7 +163,10 @@ public class ToolEvents {
if (attackingPlayer == null)
return;
ItemStack heldItemMainhand = attackingPlayer.getHeldItemMainhand();
if (heldItemMainhand.getItem() instanceof ShadowSteelToolItem) {
Item item = heldItemMainhand.getItem();
IItemTier tier = item instanceof TieredItem ? ((TieredItem) item).getTier() : null;
if (tier == AllToolTiers.SHADOW_STEEL) {
int level = EnchantmentHelper.getEnchantmentLevel(Enchantments.LOOTING, heldItemMainhand);
float modifier = 1 + event.getEntity().world.getRandom().nextFloat() * level;
event.setDroppedExperience((int) (event.getDroppedExperience() * modifier + .4f));

View file

@ -58,7 +58,7 @@ public class InWorldProcessing {
public static Type byBlock(IBlockReader reader, BlockPos pos) {
BlockState blockState = reader.getBlockState(pos);
IFluidState fluidState = reader.getFluidState(pos);
if (fluidState.getFluid() == Fluids.WATER)
if (fluidState.getFluid() == Fluids.WATER || fluidState.getFluid() == Fluids.FLOWING_WATER)
return Type.SPLASHING;
if (blockState.getBlock() == Blocks.FIRE
|| (blockState.getBlock() == Blocks.CAMPFIRE && blockState.get(CampfireBlock.LIT)))
@ -103,16 +103,19 @@ public class InWorldProcessing {
return recipe.isPresent();
}
if (type == Type.SPLASHING) {
if (type == Type.SPLASHING)
return isWashable(stack, world);
return false;
}
public static boolean isWashable(ItemStack stack, World world) {
splashingInv.setInventorySlotContents(0, stack);
Optional<SplashingRecipe> recipe =
world.getRecipeManager().getRecipe(AllRecipes.SPLASHING.getType(), splashingInv, world);
return recipe.isPresent();
}
return false;
}
public static void applyProcessing(ItemEntity entity, Type type) {
if (decrementProcessingTime(entity, type) != 0)
return;
@ -152,6 +155,8 @@ public class InWorldProcessing {
List<TransportedItemStack> transportedStacks = new ArrayList<>();
for (ItemStack additional : stacks) {
TransportedItemStack newTransported = transported.getSimilar();
newTransported.beltPosition -= Math.signum(belt.getDirectionAwareBeltMovementSpeed()) * 1/32f;
newTransported.prevBeltPosition = newTransported.beltPosition;
newTransported.stack = additional.copy();
transportedStacks.add(newTransported);
}
@ -279,7 +284,7 @@ public class InWorldProcessing {
public static void spawnParticlesForProcessing(World world, Vec3d vec, Type type) {
if (!world.isRemote)
return;
if (world.rand.nextInt(4) != 0)
if (world.rand.nextInt(8) != 0)
return;
switch (type) {

View file

@ -93,6 +93,10 @@ public class RedstoneLinkNetworkHandler {
iterator.remove();
continue;
}
if (actor.getWorld().getTileEntity(other.tileEntity.getPos()) != other.tileEntity) {
iterator.remove();
continue;
}
if (!withinRange(actor, other))
continue;
if (other.isTransmitting()) {

View file

@ -2,6 +2,7 @@ package com.simibubi.create.modules.logistics.block;
import com.simibubi.create.foundation.block.ProperDirectionalBlock;
import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
@ -61,6 +62,12 @@ public class RedstoneLinkBlock extends ProperDirectionalBlock {
return;
boolean shouldPower = worldIn.getWorld().isBlockPowered(pos);
for (Direction direction : Iterate.directions) {
BlockPos blockpos = pos.offset(direction);
shouldPower |= worldIn.getRedstonePower(blockpos, Direction.UP) > 0;
}
boolean previouslyPowered = state.get(POWERED);
if (previouslyPowered != shouldPower) {
@ -134,7 +141,7 @@ public class RedstoneLinkBlock extends ProperDirectionalBlock {
@Override
public boolean canConnectRedstone(BlockState state, IBlockReader world, BlockPos pos, Direction side) {
return state.get(FACING) == Direction.UP && side != null;
return side != null;
}
@Override

View file

@ -9,6 +9,8 @@ import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour;
import com.simibubi.create.foundation.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.block.IWithTileEntity;
import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.modules.contraptions.components.contraptions.IPortableBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour;
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;
@ -34,9 +36,10 @@ import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
public class FunnelBlock extends AttachedLogisticalBlock implements IBeltAttachment, IWithTileEntity<FunnelTileEntity> {
public class FunnelBlock extends AttachedLogisticalBlock implements IBeltAttachment, IWithTileEntity<FunnelTileEntity>, IPortableBlock {
public static final BooleanProperty BELT = BooleanProperty.create("belt");
public static final MovementBehaviour MOVEMENT = new FunnelMovementBehaviour();
@Override
protected void fillStateContainer(Builder<Block, BlockState> builder) {
@ -211,4 +214,9 @@ public class FunnelBlock extends AttachedLogisticalBlock implements IBeltAttachm
}
}
@Override
public MovementBehaviour getMovementBehaviour() {
return MOVEMENT;
}
}

View file

@ -0,0 +1,48 @@
package com.simibubi.create.modules.logistics.block.belts;
import java.util.List;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext;
import com.simibubi.create.modules.logistics.item.filter.FilterItem;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.items.ItemHandlerHelper;
public class FunnelMovementBehaviour extends MovementBehaviour {
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
super.visitNewPosition(context, pos);
World world = context.world;
List<ItemEntity> items = world.getEntitiesWithinAABB(ItemEntity.class, new AxisAlignedBB(pos));
ItemStack filter = getFilter(context);
for (ItemEntity item : items) {
ItemStack toInsert = item.getItem();
if (!filter.isEmpty() && !FilterItem.test(context.world, toInsert, filter))
continue;
ItemStack remainder = ItemHandlerHelper.insertItemStacked(context.contraption.inventory, toInsert, false);
if (remainder.getCount() == toInsert.getCount())
continue;
if (remainder.isEmpty()) {
item.setItem(ItemStack.EMPTY);
item.remove();
continue;
}
item.setItem(remainder);
}
}
private ItemStack getFilter(MovementContext context) {
return ItemStack.read(context.tileData.getCompound("Filter"));
}
}

View file

@ -4,6 +4,8 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.foundation.utility.AllShapes;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.components.contraptions.IPortableBlock;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour;
import com.simibubi.create.modules.logistics.block.belts.AttachedLogisticalBlock;
import com.simibubi.create.modules.logistics.block.belts.BeltAttachableLogisticalBlock;
@ -22,9 +24,10 @@ import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
public class ExtractorBlock extends BeltAttachableLogisticalBlock {
public class ExtractorBlock extends BeltAttachableLogisticalBlock implements IPortableBlock {
public static BooleanProperty POWERED = BlockStateProperties.POWERED;
private static final MovementBehaviour MOVEMENT = new ExtractorMovementBehaviour();
public ExtractorBlock() {
super();
@ -119,4 +122,9 @@ public class ExtractorBlock extends BeltAttachableLogisticalBlock {
}
}
@Override
public MovementBehaviour getMovementBehaviour() {
return MOVEMENT;
}
}

View file

@ -0,0 +1,66 @@
package com.simibubi.create.modules.logistics.block.extractor;
import com.simibubi.create.foundation.item.ItemHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour;
import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext;
import com.simibubi.create.modules.logistics.block.belts.AttachedLogisticalBlock;
import com.simibubi.create.modules.logistics.item.filter.FilterItem;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.World;
public class ExtractorMovementBehaviour extends MovementBehaviour {
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
super.visitNewPosition(context, pos);
World world = context.world;
VoxelShape collisionShape = world.getBlockState(pos).getCollisionShape(world, pos);
if (!collisionShape.isEmpty())
return;
if (!world.getEntitiesWithinAABB(ItemEntity.class, new AxisAlignedBB(pos)).isEmpty())
return;
ItemStack filter = getFilter(context);
int amount = getFilterAmount(context);
ItemStack dropped = ItemHelper.extract(context.contraption.inventory,
stack -> FilterItem.test(context.world, stack, filter), amount == 0 ? -1 : amount, false);
if (dropped.isEmpty())
return;
if (world.isRemote)
return;
Vec3d entityPos = VecHelper.getCenterOf(pos).add(0, -0.5f, 0);
Entity entityIn = null;
Direction facing = AttachedLogisticalBlock.getBlockFacing(context.state);
if (facing == Direction.DOWN)
entityPos = entityPos.add(0, .5, 0);
entityIn = new ItemEntity(world, entityPos.x, entityPos.y, entityPos.z, dropped);
entityIn.setMotion(Vec3d.ZERO);
((ItemEntity) entityIn).setPickupDelay(5);
world.playSound(null, pos, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 1/16f, .1f);
world.addEntity(entityIn);
}
private ItemStack getFilter(MovementContext context) {
return ItemStack.read(context.tileData.getCompound("Filter"));
}
private int getFilterAmount(MovementContext context) {
return context.tileData.getInt("FilterAmount");
}
}

View file

@ -109,7 +109,7 @@ public class AttributeFilterScreen extends AbstractFilterScreen<AttributeFilterC
attributeSelector.titled(stack.getDisplayName().getFormattedText() + "...");
attributesOfItem.clear();
for (ItemAttribute itemAttribute : ItemAttribute.types)
attributesOfItem.addAll(itemAttribute.listAttributesOf(stack));
attributesOfItem.addAll(itemAttribute.listAttributesOf(stack, this.minecraft.world));
List<String> options = attributesOfItem.stream().map(ItemAttribute::format).collect(Collectors.toList());
attributeSelector.forOptions(options);
attributeSelector.active = true;

View file

@ -152,11 +152,11 @@ public class FilterItem extends Item implements INamedContainerProvider {
return newInv;
}
public static boolean test(ItemStack stack, ItemStack filter) {
return test(stack, filter, false);
public static boolean test(World world, ItemStack stack, ItemStack filter) {
return test(world, stack, filter, false);
}
private static boolean test(ItemStack stack, ItemStack filter, boolean matchNBT) {
private static boolean test(World world, ItemStack stack, ItemStack filter, boolean matchNBT) {
if (filter.isEmpty())
return true;
@ -172,7 +172,7 @@ public class FilterItem extends Item implements INamedContainerProvider {
ItemStack stackInSlot = filterItems.getStackInSlot(slot);
if (stackInSlot.isEmpty())
continue;
boolean matches = test(stack, stackInSlot, respectNBT);
boolean matches = test(world, stack, stackInSlot, respectNBT);
if (matches)
return !blacklist;
}
@ -184,7 +184,7 @@ public class FilterItem extends Item implements INamedContainerProvider {
ListNBT attributes = filter.getOrCreateTag().getList("MatchedAttributes", NBT.TAG_COMPOUND);
for (INBT inbt : attributes) {
ItemAttribute attribute = ItemAttribute.fromNBT((CompoundNBT) inbt);
boolean matches = attribute.appliesTo(stack);
boolean matches = attribute.appliesTo(stack, world);
if (matches) {
switch (whitelistMode) {

View file

@ -5,6 +5,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@ -12,20 +13,27 @@ import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicates;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.modules.logistics.InWorldProcessing;
import net.minecraft.client.resources.I18n;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.item.crafting.IRecipeType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.AbstractFurnaceTileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.items.ItemStackHandler;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public interface ItemAttribute {
@ -41,7 +49,15 @@ public interface ItemAttribute {
return attributeType;
}
public boolean appliesTo(ItemStack stack);
default boolean appliesTo(ItemStack stack, World world) {
return appliesTo(stack);
}
boolean appliesTo(ItemStack stack);
default List<ItemAttribute> listAttributesOf(ItemStack stack, World world) {
return listAttributesOf(stack);
}
public List<ItemAttribute> listAttributesOf(ItemStack stack);
@ -93,28 +109,55 @@ public interface ItemAttribute {
BADLY_DAMAGED(s -> s.isDamaged() && s.getDamage() / s.getMaxDamage() > 3 / 4f),
NOT_STACKABLE(Predicates.not(ItemStack::isStackable)),
EQUIPABLE(s -> s.getEquipmentSlot() != null),
FURNACE_FUEL(AbstractFurnaceTileEntity::isFuel);
FURNACE_FUEL(AbstractFurnaceTileEntity::isFuel),
WASHABLE(InWorldProcessing::isWashable),
SMELTABLE((s, w) -> testRecipe(s, w, IRecipeType.SMELTING)),
SMOKABLE((s, w) -> testRecipe(s, w, IRecipeType.SMOKING)),
BLASTABLE((s, w) -> testRecipe(s, w, IRecipeType.BLASTING));
private static final RecipeWrapper RECIPE_WRAPPER = new RecipeWrapper(new ItemStackHandler(1));
private Predicate<ItemStack> test;
private BiPredicate<ItemStack, World> testWithWorld;
private StandardTraits(Predicate<ItemStack> test) {
this.test = test;
}
private static boolean testRecipe(ItemStack s, World w, IRecipeType<? extends IRecipe<IInventory>> smelting) {
RECIPE_WRAPPER.setInventorySlotContents(0, s.copy());
return w.getRecipeManager().getRecipe(smelting, RECIPE_WRAPPER, w).isPresent();
}
private StandardTraits(BiPredicate<ItemStack, World> test) {
this.testWithWorld = test;
}
@Override
public boolean appliesTo(ItemStack stack, World world) {
if (testWithWorld != null)
return testWithWorld.test(stack, world);
return appliesTo(stack);
}
@Override
public boolean appliesTo(ItemStack stack) {
return test.test(stack);
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack) {
public List<ItemAttribute> listAttributesOf(ItemStack stack, World world) {
List<ItemAttribute> attributes = new ArrayList<>();
for (StandardTraits trait : values())
if (trait.test.test(stack))
if (trait.appliesTo(stack, world))
attributes.add(trait);
return attributes;
}
@Override
public List<ItemAttribute> listAttributesOf(ItemStack stack) {
return null;
}
@Override
public String getTranslationKey() {
return Lang.asId(name());

View file

@ -14,7 +14,7 @@ Technology that empowers the player.'''
[[dependencies.create]]
modId="forge"
mandatory=true
versionRange="[28.1.20,)"
versionRange="[28.1.56,)"
ordering="NONE"
side="BOTH"

View file

@ -545,6 +545,10 @@
"create.item_attributes.placeable": "is placeable",
"create.item_attributes.consumable": "can be eaten",
"create.item_attributes.smeltable": "can be Smelted",
"create.item_attributes.washable": "can be Washed",
"create.item_attributes.smokable": "can be Smoked",
"create.item_attributes.blastable": "is smeltable in Blast Furnace",
"create.item_attributes.enchanted": "is enchanted",
"create.item_attributes.damaged": "is damaged",
"create.item_attributes.badly_damaged": "is heavily damaged",