retrieval module

This commit is contained in:
Ellpeck 2020-04-17 20:03:54 +02:00
parent 4368f2945b
commit c4329167c4
15 changed files with 274 additions and 27 deletions

View file

@ -1,5 +1,6 @@
package de.ellpeck.prettypipes;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.pipe.modules.LowPriorityModuleItem;
import de.ellpeck.prettypipes.pipe.modules.SpeedModuleItem;
@ -15,6 +16,9 @@ import de.ellpeck.prettypipes.pipe.modules.containers.*;
import de.ellpeck.prettypipes.pipe.modules.insertion.FilterModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.insertion.FilterModuleGui;
import de.ellpeck.prettypipes.pipe.modules.insertion.FilterModuleItem;
import de.ellpeck.prettypipes.pipe.modules.retrieval.RetrievalModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.retrieval.RetrievalModuleGui;
import de.ellpeck.prettypipes.pipe.modules.retrieval.RetrievalModuleItem;
import net.minecraft.block.Block;
import net.minecraft.client.gui.ScreenManager;
import net.minecraft.client.renderer.RenderType;
@ -68,6 +72,7 @@ public final class Registry {
public static ContainerType<MainPipeContainer> pipeContainer;
public static ContainerType<ExtractionModuleContainer> extractionModuleContainer;
public static ContainerType<FilterModuleContainer> filterModuleContainer;
public static ContainerType<RetrievalModuleContainer> retrievalModuleContainer;
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
@ -86,6 +91,7 @@ public final class Registry {
registry.registerAll(createTieredModule("filter_module", FilterModuleItem::new));
registry.registerAll(createTieredModule("speed_module", SpeedModuleItem::new));
registry.registerAll(createTieredModule("low_priority_module", LowPriorityModuleItem::new));
registry.registerAll(createTieredModule("retrieval_module", RetrievalModuleItem::new));
ForgeRegistries.BLOCKS.getValues().stream()
.filter(b -> b.getRegistryName().getNamespace().equals(PrettyPipes.ID))
@ -102,12 +108,23 @@ public final class Registry {
@SubscribeEvent
public static void registerContainer(RegistryEvent.Register<ContainerType<?>> event) {
event.getRegistry().registerAll(
// this needs to be registered manually since it doesn't send the module slot
pipeContainer = (ContainerType<MainPipeContainer>) IForgeContainerType.create((windowId, inv, data) -> new MainPipeContainer(pipeContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("pipe"),
extractionModuleContainer = (ContainerType<ExtractionModuleContainer>) IForgeContainerType.create((windowId, inv, data) -> new ExtractionModuleContainer(extractionModuleContainer, windowId, inv.player, data.readBlockPos(), data.readInt())).setRegistryName("extraction_module"),
filterModuleContainer = (ContainerType<FilterModuleContainer>) IForgeContainerType.create((windowId, inv, data) -> new FilterModuleContainer(filterModuleContainer, windowId, inv.player, data.readBlockPos(), data.readInt())).setRegistryName("filter_module")
extractionModuleContainer = createPipeContainer("extraction_module"),
filterModuleContainer = createPipeContainer("filter_module"),
retrievalModuleContainer = createPipeContainer("retrieval_module")
);
}
private static <T extends AbstractPipeContainer<?>> ContainerType<T> createPipeContainer(String name) {
return (ContainerType<T>) IForgeContainerType.create((windowId, inv, data) -> {
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, inv.player.world, data.readBlockPos());
int moduleIndex = data.readInt();
ItemStack moduleStack = tile.modules.getStackInSlot(moduleIndex);
return ((IModule) moduleStack.getItem()).getContainer(moduleStack, tile, windowId, inv, inv.player, moduleIndex);
}).setRegistryName(name);
}
public static void setup(FMLCommonSetupEvent event) {
CapabilityManager.INSTANCE.register(PipeNetwork.class, new Capability.IStorage<PipeNetwork>() {
@Nullable
@ -139,6 +156,7 @@ public final class Registry {
ScreenManager.registerFactory(pipeContainer, MainPipeGui::new);
ScreenManager.registerFactory(extractionModuleContainer, ExtractionModuleGui::new);
ScreenManager.registerFactory(filterModuleContainer, FilterModuleGui::new);
ScreenManager.registerFactory(retrievalModuleContainer, RetrievalModuleGui::new);
}
}
}

View file

@ -4,33 +4,22 @@ import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.packets.PacketButton;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.client.resources.I18n;
import net.minecraft.inventory.ItemStackHelper;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.fml.client.gui.GuiUtils;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
@ -38,8 +27,10 @@ public class ItemFilter extends ItemStackHandler {
private final ItemStack stack;
private final PipeTileEntity pipe;
public boolean isWhitelist;
public boolean canPopulateFromInventories;
private boolean isWhitelist;
public boolean canModifyWhitelist = true;
public ItemFilter(int size, ItemStack stack, PipeTileEntity pipe) {
super(size);
@ -59,12 +50,14 @@ public class ItemFilter extends ItemStackHandler {
@OnlyIn(Dist.CLIENT)
public List<Widget> getButtons(Screen gui, int x, int y) {
List<Widget> buttons = new ArrayList<>();
Supplier<String> whitelistText = () -> I18n.format("info." + PrettyPipes.ID + "." + (this.isWhitelist ? "whitelist" : "blacklist"));
buttons.add(new Button(x, y, 70, 20, whitelistText.get(), button -> {
PacketHandler.sendToServer(new PacketButton(this.pipe.getPos(), PacketButton.ButtonResult.FILTER_CHANGE, 0));
this.onButtonPacket(0);
button.setMessage(whitelistText.get());
}));
if (this.canModifyWhitelist) {
Supplier<String> whitelistText = () -> I18n.format("info." + PrettyPipes.ID + "." + (this.isWhitelist ? "whitelist" : "blacklist"));
buttons.add(new Button(x, y, 70, 20, whitelistText.get(), button -> {
PacketHandler.sendToServer(new PacketButton(this.pipe.getPos(), PacketButton.ButtonResult.FILTER_CHANGE, 0));
this.onButtonPacket(0);
button.setMessage(whitelistText.get());
}));
}
if (this.canPopulateFromInventories) {
buttons.add(new Button(x + 72, y, 70, 20, I18n.format("info." + PrettyPipes.ID + ".populate"), button -> {
PacketHandler.sendToServer(new PacketButton(this.pipe.getPos(), PacketButton.ButtonResult.FILTER_CHANGE, 1));
@ -80,9 +73,9 @@ public class ItemFilter extends ItemStackHandler {
}
public void onButtonPacket(int id) {
if (id == 0) {
if (id == 0 && this.canModifyWhitelist) {
this.isWhitelist = !this.isWhitelist;
} else if (id == 1) {
} else if (id == 1 && this.canPopulateFromInventories) {
// populate filter from inventories
for (Direction direction : Direction.values()) {
IItemHandler handler = this.pipe.getItemHandler(direction);

View file

@ -0,0 +1,31 @@
package de.ellpeck.prettypipes.network;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class NetworkLocation {
public final BlockPos pipePos;
public final ListMultimap<Direction, Pair<Integer, ItemStack>> items;
public NetworkLocation(BlockPos pipePos, ListMultimap<Direction, Pair<Integer, ItemStack>> items) {
this.pipePos = pipePos;
this.items = items;
}
public Pair<Direction, Integer> getStackLocation(ItemStack stack) {
for (Map.Entry<Direction, Pair<Integer, ItemStack>> entry : this.items.entries()) {
if (entry.getValue().getRight().isItemEqual(stack))
return Pair.of(entry.getKey(), entry.getValue().getLeft());
}
return null;
}
}

View file

@ -1,6 +1,7 @@
package de.ellpeck.prettypipes.network;
import com.google.common.collect.Lists;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Streams;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
@ -21,6 +22,8 @@ import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.IItemHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.jgrapht.GraphPath;
import org.jgrapht.ListenableGraph;
import org.jgrapht.alg.interfaces.ShortestPathAlgorithm;
@ -36,7 +39,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphListener<BlockPos, NetworkEdge> {
@ -125,7 +127,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
this.startProfile("find_destination");
for (BlockPos pipePos : this.getOrderedDestinations(startPipePos)) {
PipeTileEntity pipe = this.getPipe(pipePos);
BlockPos dest = pipe.getAvailableDestination(stack);
BlockPos dest = pipe.getAvailableDestination(stack, false);
if (dest != null) {
this.endProfile();
return this.routeItemToLocation(startPipePos, startInventory, pipe.getPos(), dest, itemSupplier);
@ -165,6 +167,34 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return tile;
}
public List<NetworkLocation> getOrderedNetworkItems(BlockPos node) {
if (!this.isNode(node))
return Collections.emptyList();
this.startProfile("get_network_items");
List<NetworkLocation> info = new ArrayList<>();
for (BlockPos dest : this.getOrderedDestinations(node)) {
PipeTileEntity pipe = this.getPipe(dest);
for (Direction dir : Direction.values()) {
IItemHandler handler = pipe.getItemHandler(dir);
if (handler == null)
continue;
ListMultimap<Direction, Pair<Integer, ItemStack>> items = null;
for (int i = 0; i < handler.getSlots(); i++) {
ItemStack found = handler.extractItem(i, Integer.MAX_VALUE, true);
if (found.isEmpty())
continue;
if (items == null)
items = ArrayListMultimap.create();
items.put(dir, Pair.of(i, found));
}
if (items != null)
info.add(new NetworkLocation(dest, items));
}
}
this.endProfile();
return info;
}
private void refreshNode(BlockPos pos, BlockState state) {
this.startProfile("refresh_node");
this.graph.removeAllEdges(new ArrayList<>(this.graph.edgesOf(pos)));
@ -248,6 +278,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
// sort destinations first by their priority (eg trash pipes should be last)
// and then by their distance from the specified node
ret = Streams.stream(new BreadthFirstIterator<>(this.graph, node))
.filter(p -> !p.equals(node))
.sorted(Comparator.<BlockPos>comparingInt(p -> this.getPipe(p).getPriority()).reversed().thenComparing(paths::getWeight))
.collect(Collectors.toList());
this.nodeToConnectedNodes.put(node, ret);

View file

@ -115,8 +115,8 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
return this.getBlockState().get(PipeBlock.DIRECTIONS.get(dir)).isConnected();
}
public BlockPos getAvailableDestination(ItemStack stack) {
if (this.streamModules().anyMatch(m -> !m.getRight().canAcceptItem(m.getLeft(), this, stack)))
public BlockPos getAvailableDestination(ItemStack stack, boolean internal) {
if (!internal && this.streamModules().anyMatch(m -> !m.getRight().canAcceptItem(m.getLeft(), this, stack)))
return null;
for (Direction dir : Direction.values()) {
IItemHandler handler = this.getItemHandler(dir);

View file

@ -0,0 +1,39 @@
package de.ellpeck.prettypipes.pipe.modules.retrieval;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.misc.ItemFilter.IFilteredContainer;
import de.ellpeck.prettypipes.pipe.modules.containers.AbstractPipeContainer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.util.math.BlockPos;
import javax.annotation.Nullable;
public class RetrievalModuleContainer extends AbstractPipeContainer<RetrievalModuleItem> implements IFilteredContainer {
public ItemFilter filter;
public RetrievalModuleContainer(@Nullable ContainerType<?> type, int id, PlayerEntity player, BlockPos pos, int moduleIndex) {
super(type, id, player, pos, moduleIndex);
}
@Override
protected void addSlots() {
this.filter = new ItemFilter(this.module.filterSlots, this.moduleStack, this.tile);
this.filter.canModifyWhitelist = false;
for (Slot slot : this.filter.getSlots((176 - this.module.filterSlots * 18) / 2 + 1, 17 + 32))
this.addSlot(slot);
}
@Override
public void onContainerClosed(PlayerEntity playerIn) {
super.onContainerClosed(playerIn);
this.filter.save();
}
@Override
public ItemFilter getFilter() {
return this.filter;
}
}

View file

@ -0,0 +1,20 @@
package de.ellpeck.prettypipes.pipe.modules.retrieval;
import de.ellpeck.prettypipes.pipe.modules.containers.AbstractPipeGui;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleContainer;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.text.ITextComponent;
public class RetrievalModuleGui extends AbstractPipeGui<RetrievalModuleContainer> {
public RetrievalModuleGui(RetrievalModuleContainer screenContainer, PlayerInventory inv, ITextComponent titleIn) {
super(screenContainer, inv, titleIn);
}
@Override
protected void init() {
super.init();
for (Widget widget : this.container.filter.getButtons(this, this.guiLeft + 7, this.guiTop + 17 + 32 + 20))
this.addButton(widget);
}
}

View file

@ -0,0 +1,93 @@
package de.ellpeck.prettypipes.pipe.modules.retrieval;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.items.ModuleTier;
import de.ellpeck.prettypipes.misc.ItemFilter;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.PipeItem;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.pipe.modules.containers.AbstractPipeContainer;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.items.IItemHandler;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
public class RetrievalModuleItem extends ModuleItem {
private final int maxExtraction;
private final int speed;
public final int filterSlots;
public RetrievalModuleItem(String name, ModuleTier tier) {
super(name);
this.maxExtraction = tier.forTier(1, 8, 16);
this.speed = tier.forTier(40, 20, 10);
this.filterSlots = tier.forTier(3, 6, 9);
}
@Override
public void tick(ItemStack module, PipeTileEntity tile) {
if (tile.getWorld().getGameTime() % this.speed != 0)
return;
PipeNetwork network = PipeNetwork.get(tile.getWorld());
List<NetworkLocation> locations = null;
ItemFilter filter = new ItemFilter(this.filterSlots, module, tile);
filter.isWhitelist = true;
// loop through filter to see which items to pull
for (int f = 0; f < filter.getSlots(); f++) {
ItemStack filtered = filter.getStackInSlot(f);
if (filtered.isEmpty())
continue;
ItemStack copy = filtered.copy();
copy.setCount(this.maxExtraction);
BlockPos dest = tile.getAvailableDestination(copy, true);
if (dest == null)
continue;
// loop through locations to find a location that has the item
if (locations == null)
locations = network.getOrderedNetworkItems(tile.getPos());
for (NetworkLocation location : locations) {
Pair<Direction, Integer> item = location.getStackLocation(filtered);
if (item == null)
continue;
// get that location's pipe and inventory
PipeTileEntity pipe = network.getPipe(location.pipePos);
IItemHandler handler = pipe.getItemHandler(item.getKey());
// try to extract from that location's inventory and send the item
ItemStack stack = handler.extractItem(item.getValue(), this.maxExtraction, true);
if (network.routeItemToLocation(location.pipePos, location.pipePos.offset(item.getKey()), tile.getPos(), dest, speed -> new PipeItem(stack, speed))) {
handler.extractItem(item.getValue(), stack.getCount(), false);
return;
}
}
}
}
@Override
public boolean canAcceptItem(ItemStack module, PipeTileEntity tile, ItemStack stack) {
return false;
}
@Override
public boolean isCompatible(ItemStack module, PipeTileEntity tile, IModule other) {
return !(other instanceof RetrievalModuleItem);
}
@Override
public boolean hasContainer(ItemStack module, PipeTileEntity tile) {
return true;
}
@Override
public AbstractPipeContainer<?> getContainer(ItemStack module, PipeTileEntity tile, int windowId, PlayerInventory inv, PlayerEntity player, int moduleIndex) {
return new RetrievalModuleContainer(Registry.retrievalModuleContainer, windowId, player, tile.getPos(), moduleIndex);
}
}

View file

@ -12,10 +12,14 @@
"item.prettypipes.low_low_priority_module": "Low Priority Module",
"item.prettypipes.medium_low_priority_module": "Lower Priority Module",
"item.prettypipes.high_low_priority_module": "Lowest Priority Module",
"item.prettypipes.low_retrieval_module": "Low Retrieval Module",
"item.prettypipes.medium_retrieval_module": "Medium Retrieval Module",
"item.prettypipes.high_retrieval_module": "High Retrieval Module",
"info.prettypipes.extraction_module": "Pulls items from adjacent inventories\nFilters and pull rates vary by tier",
"info.prettypipes.filter_module": "Restricts flow from pipes into adjacent inventories\nFilter amount varies by tier",
"info.prettypipes.speed_module": "Increases speed of items exiting adjacent inventories\nSpeed varies by tier",
"info.prettypipes.low_priority_module": "Decreases the reception priority of adjacent inventories\nLower priority means items will prefer other inventories",
"info.prettypipes.retrieval_module": "Pulls items from other inventories in the network\nFilters and pull rates vary by tier",
"block.prettypipes.pipe": "Pipe",
"itemGroup.prettypipes": "Pretty Pipes",
"container.prettypipes.pipe": "Pipe",

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/high_retrieval_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/low_retrieval_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/medium_retrieval_module"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B