Skip to content

Commit

Permalink
Merge pull request #37 from fechan/spread
Browse files Browse the repository at this point in the history
Add spread mode
  • Loading branch information
fechan authored Nov 11, 2024
2 parents d35bbfc + c7e8175 commit e81496e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 8 deletions.
1 change: 1 addition & 0 deletions client/src/components/EdgeOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export function EdgeOptions({ sendMessage }: EdgeOptionsProps) {
className="mcui-button p-2 w-full h-10"
>
<option value="natural">Natural (default)</option>
<option value="spread">Spread</option>
</select>
</div>
}
Expand Down
26 changes: 24 additions & 2 deletions client/src/components/TempEdgeOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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));
Expand All @@ -56,7 +64,7 @@ export function TempEdgeOptions({ tempEdge, setTempEdge, sendMessage, onCancel }
/>
</div>

<div className="flex flex-col mb-5">
<div className="flex flex-col mb-3">
<label htmlFor="pipeFilter" className="mb-1">Item filter</label>
<input
type="text"
Expand All @@ -69,6 +77,20 @@ export function TempEdgeOptions({ tempEdge, setTempEdge, sendMessage, onCancel }
<FilterSyntax />
</div>

{ !isFluid &&
<div className="mb-5">
<label htmlFor="mode" className="block mb-1">Mode</label>
<select
value={ mode }
onChange={ e => setMode(e.target.value) }
className="mcui-button p-2 w-full h-10"
>
<option value="natural">Natural (default)</option>
<option value="spread">Spread</option>
</select>
</div>
}

<div className="text-right box-border">
<button
className="mcui-button bg-red-700 w-32 h-10 me-3"
Expand All @@ -86,4 +108,4 @@ export function TempEdgeOptions({ tempEdge, setTempEdge, sendMessage, onCancel }
</div>
</div>
);
}
}
13 changes: 7 additions & 6 deletions computercraft/sigils/pipe.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()
Expand Down Expand Up @@ -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
)

Expand Down
81 changes: 81 additions & 0 deletions computercraft/sigils/pipeModes/spread.lua
Original file line number Diff line number Diff line change
@@ -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,
}

0 comments on commit e81496e

Please sign in to comment.