diff --git a/client/src/components/EdgeOptions.tsx b/client/src/components/EdgeOptions.tsx index 8766dd3..d712250 100644 --- a/client/src/components/EdgeOptions.tsx +++ b/client/src/components/EdgeOptions.tsx @@ -110,6 +110,7 @@ export function EdgeOptions({ sendMessage }: EdgeOptionsProps) { className="mcui-button p-2 w-full h-10" > + } diff --git a/client/src/components/TempEdgeOptions.tsx b/client/src/components/TempEdgeOptions.tsx index 0000710..291d253 100644 --- a/client/src/components/TempEdgeOptions.tsx +++ b/client/src/components/TempEdgeOptions.tsx @@ -4,6 +4,8 @@ import { SendMessage } from "react-use-websocket"; import { Edge } from "reactflow"; import { v4 as uuidv4 } from "uuid"; import { FilterSyntax } from "./FilterSyntax"; +import { useFactoryStore } from "../stores/factory"; +import { PipeMode } from "@server/types/core-types"; export interface TempEdgeOptionsProps { sendMessage: SendMessage, @@ -15,6 +17,11 @@ export interface TempEdgeOptionsProps { export function TempEdgeOptions({ tempEdge, setTempEdge, sendMessage, onCancel }: TempEdgeOptionsProps) { const [ nickname, setNickname ] = useState(""); const [ filter, setFilter ] = useState(""); + const [ mode, setMode ] = useState(""); + + const groups = useFactoryStore(state => state.factory.groups); + + const isFluid = tempEdge && groups[tempEdge.source].fluid; function onCommit() { if (!(tempEdge && tempEdge.source && tempEdge.target)) return; @@ -32,6 +39,7 @@ export function TempEdgeOptions({ tempEdge, setTempEdge, sendMessage, onCancel } if (nickname !== "") pipeAddReq.pipe.nickname = nickname; if (filter !== "") pipeAddReq.pipe.filter = filter; + if (mode !== "") pipeAddReq.pipe.mode = mode as PipeMode; setTempEdge(null); sendMessage(JSON.stringify(pipeAddReq)); @@ -56,7 +64,7 @@ export function TempEdgeOptions({ tempEdge, setTempEdge, sendMessage, onCancel } /> -
+
+ { !isFluid && +
+ + +
+ } +
); -} \ No newline at end of file +} diff --git a/computercraft/sigils/pipe.lua b/computercraft/sigils/pipe.lua index dea46f8..e72e97f 100644 --- a/computercraft/sigils/pipe.lua +++ b/computercraft/sigils/pipe.lua @@ -4,11 +4,15 @@ ---A data structure for a Pipe matching `/server/src/types/core-types.ts#Pipe` ---@class Pipe -local PipeModeNatural = require('sigils.pipeModes.natural') local PipeModeFluid = require('sigils.pipeModes.fluid') local Filter = require('sigils.filter') local LOGGER = require('sigils.logging').LOGGER +local ITEM_PIPE_MODES = { + natural = require('sigils.pipeModes.natural'), + spread = require('sigils.pipeModes.spread'), +} + local function processFluidPipe (pipe, groupMap, missingPeriphs, filter) local ok, transferOrders = pcall( function () @@ -47,11 +51,8 @@ local function processPipe (pipe, groupMap, missingPeriphs) local ok, transferOrders = pcall( function () - if pipe.mode == "natural" then -- you can add more item pipe modes with more if/else statements - return PipeModeNatural.getTransferOrders(groupMap[pipe.from], groupMap[pipe.to], missingPeriphs, filter) - else -- natural is the default - return PipeModeNatural.getTransferOrders(groupMap[pipe.from], groupMap[pipe.to], missingPeriphs, filter) - end + local pipeMode = ITEM_PIPE_MODES[pipe.mode] or ITEM_PIPE_MODES["natural"] + return pipeMode.getTransferOrders(groupMap[pipe.from], groupMap[pipe.to], missingPeriphs, filter) end ) diff --git a/computercraft/sigils/pipeModes/spread.lua b/computercraft/sigils/pipeModes/spread.lua new file mode 100644 index 0000000..ac9734f --- /dev/null +++ b/computercraft/sigils/pipeModes/spread.lua @@ -0,0 +1,81 @@ +local ItemDetailAndLimitCache = require('sigils.ItemDetailAndLimitCache') + +---Get the amount of items from the origin item stack that can be moved to the +---destination item stack. +---@param originStack table ItemDetail of the origin item stack +---@param destStack table ItemDetail of the destination item stack +---@param destItemLimit table Item limit of the destination item stack +---@return number Number Number of items that can be moved. 0 if destination full or unstackable. +local function getAmountStackable(originStack, destStack, destItemLimit) + local isCompatibleItem = ( + destStack.name == originStack.name and + destStack.nbt == nil and + destStack.durability == nil + ) + + if isCompatibleItem then + return math.min(destStack.maxCount - destStack.count, originStack.count, destItemLimit) + else + return 0 + end +end + +---Get the transfer orders needed to spread items from the origin inventory throughout +---the destination inventory. +--- +---This emulates the spreading behavior of Minecraft. For instance, if you drag +---a stack of items in your cursor into several slots on a chest, the stack is +---divided evenly, and each portion is added to the destination slots. +---The remainder remains in your cursor, in addition to any items that couldn't +---be fully transferred. +--- +---This function uses slots in the origin inventory as the "cursor," which is +---why some remainder items stay in the origin inventory after a single pass. +--- +---@param origin Group Origin group to transfer from +---@param destination Group Destination group to transfer to +---@param missingPeriphs table Set of missing peripherals by ID +---@param filter function Filter function that accepts the result of inventory.getItemDetail() +---@return TransferOrder[] transferOrders List of transfer orders +local function getTransferOrders (origin, destination, missingPeriphs, filter) + local orders = {} + + local inventoryInfo = ItemDetailAndLimitCache.new(missingPeriphs) + inventoryInfo:Fulfill({origin, destination}) + + for _, originSlot in pairs(inventoryInfo:GetSlotsWithMatchingItems(origin, filter)) do + local originStack = inventoryInfo:GetItemDetail(originSlot) + if originStack then + local toTransfer = originStack.count + local actualTransferred = 0 + + local numDestinationSlots = math.min(#destination.slots, #destination.slots) + local transferAmount = math.floor(toTransfer / numDestinationSlots) -- we'll try to transfer this many items to each destination slot + if transferAmount == 0 then + transferAmount = 1 + end + + for writeHead=1, numDestinationSlots do + local destSlot = destination.slots[writeHead] + local destStack = inventoryInfo:GetItemDetail(destSlot) + local destItemLimit = inventoryInfo:GetItemLimit(destSlot) + + if destStack == nil or getAmountStackable(originStack, destStack, destItemLimit) > 0 then + local transferLimit = transferAmount + if destStack ~= nil then + transferLimit = math.min(getAmountStackable(originStack, destStack, destItemLimit), transferAmount) + end + actualTransferred = actualTransferred + transferLimit + + table.insert(orders, {from=originSlot, to=destSlot, limit=transferLimit}) + end + end + end + end + + return orders +end + +return { + getTransferOrders = getTransferOrders, +} \ No newline at end of file