local META_IMG_PIC = "logimgpick" local META_RES_VAL = "logresval" local META_UPGRADE_ADD = "logstorupgr" local VALID_RESERVE_VALUES = {} for i = 0,5120,128 do VALID_RESERVE_VALUES[i/128 + 1] = i end local BASE_TRANSFER_RATE = 10 local function mass_storage_room_for_item(pos, meta, stack) local stackName = stack:get_name() local maxNum = logistica.get_mass_storage_max_size(pos) local filterList = logistica.get_list(meta:get_inventory(), "filter") local storageList = logistica.get_list(meta:get_inventory(), "storage") local roomForItems = 0 for i, storageStack in ipairs(filterList) do if storageStack:get_name() == stackName then roomForItems = roomForItems + maxNum - storageList[i]:get_count() end end return roomForItems end local function show_deposited_item_popup(player, numDeposited, name) logistica.show_popup(player:get_player_name(), "Stored "..numDeposited.." "..name, 1.5) end -- returns an ItemStack of how many items were taken local function take_item_from_supplier_for_mass_storage(pos, stack) logistica.load_position(pos) local node = minetest.get_node(pos) local removed = ItemStack("") local network = logistica.get_network_or_nil(pos) local collectFunc = function(st) removed:add_item(st); return 0 end if logistica.GROUPS.crafting_suppliers.is(node.name) then -- no-op: it doesn't really make sense for mass storages too pull from crafting suppliers -- logistica.take_item_from_crafting_supplier(pos, stack, network, collectFunc, false, false, 1) else logistica.take_item_from_supplier(pos, stack, network, collectFunc, false, false) end return removed end -------------------------------- -- public functions -------------------------------- function logistica.get_mass_storage_max_size(pos) local node = minetest.get_node(pos) if not node then return 0 end local def = minetest.registered_nodes[node.name] if def and def.logistica and def.logistica.maxItems then local meta = minetest.get_meta(pos) local storageUpgrade = meta:get_int(META_UPGRADE_ADD) return def.logistica.maxItems + storageUpgrade end return 0 end function logistica.get_mass_storage_num_slots(pos) local node = minetest.get_node(pos) if not node then return 0 end local def = minetest.registered_nodes[node.name] if def and def.logistica and def.logistica.numSlots then -- TODO: account for upgrades return def.logistica.numSlots end return 0 end -- returns the transfer rate of given mass storage node function logistica.get_supplier_transfer_rate(meta) -- TODO: account for speed upgrade return BASE_TRANSFER_RATE end -- Returns a stack of how many items remain function logistica.insert_item_into_mass_storage(pos, inv, inputStack, dryRun) local maxItems = logistica.get_mass_storage_max_size(pos) local numSlots = #(logistica.get_list(inv, "filter")) local inputStackName = inputStack:get_name() local indices = {} for i = 1, numSlots do local v = inv:get_stack("filter", i) if v:get_name() == inputStackName then table.insert(indices, i) end end local remainingStack = ItemStack(inputStack) for _, index in ipairs(indices) do local storageStack = inv:get_stack("storage", index) local canInsert = logistica.clamp(maxItems - storageStack:get_count(), 0, remainingStack:get_count()) if canInsert > 0 then local toInsert = ItemStack(inputStackName) toInsert:set_count(storageStack:get_count() + canInsert) if not dryRun then inv:set_stack("storage", index, toInsert) end if canInsert >= remainingStack:get_count() then remainingStack:set_count(0) return remainingStack -- nothing more to check, return early else remainingStack:set_count(remainingStack:get_count() - canInsert) end end end return remainingStack end function logistica.pull_items_from_network_into_mass_storage(pos) local network = logistica.get_network_or_nil(pos) if not network then return false end -- don't run timer when no nework - connecting to network will start it local meta = minetest.get_meta(pos) local stackPos = logistica.get_next_filled_item_slot(meta, "filter") if stackPos <= 0 then return true end local filterStack = meta:get_inventory():get_stack("filter", stackPos) local spaceForItems = mass_storage_room_for_item(pos, meta, filterStack) if spaceForItems == 0 then return true end spaceForItems = math.min(spaceForItems, logistica.get_supplier_transfer_rate(meta)) local requestStack = ItemStack(filterStack) requestStack:set_count(spaceForItems) local numTaken = 0 for hash, _ in pairs(network.supplier_cache[requestStack:get_name()] or {}) do local taken = take_item_from_supplier_for_mass_storage(minetest.get_position_from_hash(hash), requestStack) numTaken = numTaken + taken:get_count() logistica.insert_item_into_mass_storage(pos, meta:get_inventory(), taken) if numTaken >= spaceForItems then return true end -- everything isnerted, return requestStack:set_count(spaceForItems - numTaken) end return true end function logistica.start_mass_storage_timer(pos) logistica.start_node_timer(pos, 1) end function logistica.on_mass_storage_timer(pos, _) return logistica.pull_items_from_network_into_mass_storage(pos) end function logistica.try_to_add_player_wield_item_to_mass_storage(pos, player) if not pos or not player or not player:is_player() then return end local wieldStack = player:get_wielded_item() if wieldStack:get_count() == 0 or wieldStack:get_stack_max() <= 1 then return end local numDesposited = 0 local inv = minetest.get_meta(pos):get_inventory() local newStack = logistica.insert_item_into_mass_storage(pos, inv, wieldStack) if newStack:get_count() ~= wieldStack:get_count() then player:set_wielded_item(newStack) numDesposited = wieldStack:get_count() - newStack:get_count() end if newStack:get_count() > 0 or not player:get_player_control().sneak then show_deposited_item_popup(player, numDesposited, wieldStack:get_short_description()) return end -- else, storage potentially has more space, and player was holding sneak -- try to deposit as many items ouf of their inventory as possible local pInv = player:get_inventory() local pListName = player:get_wield_list() local pList = logistica.get_list(pInv, pListName) for i, pInvStack in ipairs(pList) do if pInvStack:get_name() == wieldStack:get_name() then newStack = logistica.insert_item_into_mass_storage(pos, inv, pInvStack) numDesposited = numDesposited + pInvStack:get_count() - newStack:get_count() pInv:set_stack(pListName, i, newStack) if newStack:get_count() > 0 then show_deposited_item_popup(player, numDesposited, wieldStack:get_short_description()) return -- failed to deposit some end end end show_deposited_item_popup(player, numDesposited, wieldStack:get_short_description()) end -- returns a table of {0,128,256,512...} up to the max this box supports function logistica.get_mass_storage_valid_reserve_list(pos) local max = logistica.get_mass_storage_max_size(pos) local vals = {} for _, v in ipairs(VALID_RESERVE_VALUES) do if v <= max then table.insert(vals, v) end end return vals end function logistica.set_mass_storage_reserve(meta, i, value) meta:set_int(META_RES_VAL..tostring(i), value) end function logistica.on_mass_storage_reserve_changed(pos, i, value) local meta = minetest.get_meta(pos) local intVal = tonumber(value) if type(intVal) ~= "number" then return end local invalid = true for _, v in ipairs(VALID_RESERVE_VALUES) do if v == intVal then invalid = false end end if invalid then return end meta:set_int(META_RES_VAL..tostring(i), intVal) end function logistica.on_mass_storage_image_select_change(pos, i) local meta = minetest.get_meta(pos) local prev = meta:get_int(META_IMG_PIC) if prev == i then meta:set_int(META_IMG_PIC, 0) else meta:set_int(META_IMG_PIC, i) end end function logistica.set_mass_storage_image_slot(meta, index) return meta:set_int(META_IMG_PIC, index) end -- returns an index of which slot is picked to be the front image, or 0 if there isn't one function logistica.get_mass_storage_image_slot(meta) return meta:get_int(META_IMG_PIC) end -- returns the picked reserve for the given index function logistica.get_mass_storage_reserve(meta, index) return meta:get_int(META_RES_VAL..tostring(index)) end -- `newParam2` is optional, will override the lookup of node.param2 for rotation function logistica.update_mass_storage_front_image(origPos, newParam2) local pos = vector.new(origPos) logistica.remove_item_on_block_front(pos) local meta = minetest.get_meta(pos) local slot = logistica.get_mass_storage_image_slot(meta) if slot > 0 then local inv = meta:get_inventory() local item = logistica.get_list(inv, "filter")[slot] or ItemStack("") logistica.display_item_on_block_front(pos, item:get_name(), newParam2) end end function logistica.get_mass_storage_imgname_or_first_item(meta) local inv = meta:get_inventory() if inv:is_empty("filter") then return "\n(Empty)" end local index = meta:get_int(META_IMG_PIC) local itemStack = inv:get_stack("filter", index) if not itemStack:is_empty() then return "\n(Has: "..itemStack:get_description()..")" end for _, v in ipairs(logistica.get_list(inv, "filter")) do if not v:is_empty() then return "\n(Has: "..v:get_description()..")" end end return "\n(Empty)" end function logistica.is_valid_storage_upgrade(stackName) return logistica.craftitem.storage_upgrade[stackName] ~= nil end function logistica.update_mass_storage_cap(pos, optMeta) local meta = optMeta or minetest.get_meta(pos) local storageUpgrade = 0 local list = logistica.get_list(meta:get_inventory(), "upgrade") for _, item in ipairs(list) do local upgradeDef = logistica.craftitem.storage_upgrade[item:get_name()] if upgradeDef and upgradeDef.storage_upgrade then storageUpgrade = storageUpgrade + upgradeDef.storage_upgrade end end meta:set_int(META_UPGRADE_ADD, storageUpgrade) end function logistica.on_mass_storage_upgrade_change(pos, upgradeName, wasAdded) local upgradeDef = logistica.craftitem.storage_upgrade[upgradeName] if not upgradeDef or not upgradeDef.storage_upgrade then return true end local meta = minetest.get_meta(pos) local storageUpgrade = meta:get_int(META_UPGRADE_ADD) if wasAdded then storageUpgrade = storageUpgrade + upgradeDef.storage_upgrade else storageUpgrade = storageUpgrade - upgradeDef.storage_upgrade end meta:set_int(META_UPGRADE_ADD, storageUpgrade) end function logistica.can_remove_mass_storage_upgrade(pos, upgradeName) local upgradeDef = logistica.craftitem.storage_upgrade[upgradeName] if not upgradeDef or not upgradeDef.storage_upgrade then return true end local inv = minetest.get_meta(pos):get_inventory() local maxStored = 0 for _, st in ipairs(logistica.get_list(inv, "storage")) do if st:get_count() > maxStored then maxStored = st:get_count() end end local currMax = logistica.get_mass_storage_max_size(pos) return (currMax - upgradeDef.storage_upgrade) >= maxStored end