From 370e6162c3919a6af1116a248c08e042c41ff9d2 Mon Sep 17 00:00:00 2001 From: Zenon Seth Date: Wed, 29 Nov 2023 21:30:22 +0000 Subject: [PATCH] Add Reservoir access to Access Point; add related network storage funcs --- api/access_point.lua | 7 +- api/reservoir.lua | 14 ++- logic/access_point.lua | 90 +++++++++++++- logic/access_point_formspec.lua | 75 ++++++++++-- logic/groups.lua | 7 +- logic/network_logic.lua | 16 +++ logic/network_storage.lua | 136 ++++++++++++++++++++-- logic/reservoir.lua | 64 +++++++++- textures/logistica_reservoir_obsidian.png | Bin 2593 -> 2228 bytes textures/logistica_reservoir_silverin.png | Bin 2717 -> 2220 bytes 10 files changed, 380 insertions(+), 29 deletions(-) diff --git a/api/access_point.lua b/api/access_point.lua index e775618..4b2941f 100644 --- a/api/access_point.lua +++ b/api/access_point.lua @@ -43,6 +43,11 @@ local function on_access_point_rightclick(pos, node, clicker, itemstack, pointed logistica.access_point_on_rightclick(pos, node, clicker, itemstack, pointed_thing) end +local function after_dig_access_point(pos, oldnode, oldmeta, digger) + logistica.on_access_point_change(pos, oldnode, oldmeta) + logistica.access_point_on_dug(pos) +end + local function can_dig_access_point(pos, player) return true end @@ -83,7 +88,7 @@ function logistica.register_access_point(desc, name, tiles) can_dig = can_dig_access_point, connect_sides = {"top", "bottom", "left", "back", "right" }, after_place_node = after_place_access_point, - after_dig_node = logistica.on_access_point_change, + after_dig_node = after_dig_access_point, on_rightclick = on_access_point_rightclick, -- on_metadata_inventory_move = on_access_point_inv_move, -- on_metadata_inventory_put = on_access_point_inv_put, diff --git a/api/reservoir.lua b/api/reservoir.lua index d090518..cfb30fc 100644 --- a/api/reservoir.lua +++ b/api/reservoir.lua @@ -66,13 +66,14 @@ local function after_place_node(pos, placer, itemstack, pointed_thing) if not nodeDef or not nodeDef.logistica then return end local liquidLevel = stackMeta:get_int(META_LIQUID_LEVEL) - local liquidDesc = nodeDef.logistica.liquidDesc + local liquidDesc = logistica.reservoir_get_description_of_liquid(nodeDef.logistica.liquidName) local maxBuckets = nodeDef.logistica.maxBuckets nodeMeta:set_int(META_LIQUID_LEVEL, liquidLevel) node.param2 = logistica.reservoir_make_param2(liquidLevel, maxBuckets) minetest.swap_node(pos, node) nodeMeta:set_string("infotext", logistica.reservoir_get_description(liquidLevel, maxBuckets, liquidDesc)) + logistica.on_reservoir_change(pos) end @@ -84,7 +85,7 @@ local function preserve_metadata(pos, oldnode, oldmeta, drops) local meta = minetest.get_meta(pos) local drop = drops[1] local dropMeta = drop:get_meta() - local liquidDesc = nodeDef.logistica.liquidDesc + local liquidDesc = logistica.reservoir_get_description_of_liquid(nodeDef.logistica.liquidName) local maxBuckets = nodeDef.logistica.maxBuckets local liquidLevel = meta:get_int(META_LIQUID_LEVEL) @@ -118,9 +119,10 @@ local commonDef = { paramtype2 = "glasslikeliquidlevel", is_ground_content = false, sunlight_propagates = false, - groups = {cracky = 3, level = 1}, + groups = {cracky = 3, level = 1, [logistica.TIER_ALL] = 1}, preserve_metadata = preserve_metadata, after_place_node = after_place_node, + after_dig_node = logistica.on_reservoir_change, on_rightclick = on_rightclick, stack_max = 1, backface_culling = false, @@ -144,8 +146,8 @@ for _, variantName in ipairs(variants) do local nodeName = L("reservoir_"..variantName..EMPTY_SUFFIX) def.drops = nodeName def.logistica.liquidName = LIQUID_NONE - def.logistica.liquidDesc = LIQUID_NONE minetest.register_node(nodeName, def) + logistica.reservoirs[nodeName] = true end --[[ @@ -165,12 +167,12 @@ function logistica.register_reservoir(liquidName, liquidDesc, bucketItemName, li def.drops = nodeName def.special_tiles = {liquidTexture} def.logistica.liquidName = lname - def.logistica.liquidDesc = liquidDesc def.groups.not_in_creative_inventory = 1 def.light_source = optLight minetest.register_node(nodeName, def) + logistica.reservoirs[nodeName] = true - logistica.reservoir_register_names(lname, bucketItemName, optEmptyBucketName) + logistica.reservoir_register_names(lname, bucketItemName, optEmptyBucketName, liquidDesc, liquidTexture) end end diff --git a/logic/access_point.lua b/logic/access_point.lua index ec932d9..47158bd 100644 --- a/logic/access_point.lua +++ b/logic/access_point.lua @@ -1,10 +1,19 @@ +local S = logistica.TRANSLATOR + local META_CURR_PAGE = "ap_curr_p" local META_SORT_TYPE = "ap_sort" local META_FILTER_TYPE = "ap_fltr" local META_CURR_SEARCH = "ap_curr_s" local META_IGNORE_METADATA = "ap_usemd" +local META_CURR_LIQUID_INDEX = "ap_curr_l" + +local STR_NO_LIQUID = S("No Liquid Storage") +local STR_EMPTY_RESERVOIRS = S("Free Capacity:") local fakeInvMap = {} +local liquidsMap = {} + +local LIQUID_NONE = -1 local SORT_NAME = 1 local SORT_MOD = 2 @@ -126,6 +135,26 @@ local function build_stack_list(pos) } end +-- { description = "description here", capacity = "3/32", texture = "texture_name.png"} +local function make_display_info(name, currBuckets, maxBuckets) + if not currBuckets or not maxBuckets then return { description = name, capacity = "", texture = ""} end + + local desc, cap + if name == "" then + desc = STR_EMPTY_RESERVOIRS + cap = tostring(maxBuckets) + else + desc = logistica.reservoir_get_description_of_liquid(name) + cap = tostring(currBuckets).." / "..tostring(maxBuckets) + end + + local texture = logistica.reservoir_get_texture_of_liquid(name) + return { + description = desc, + capacity = cap, + texture = texture, + } +end -------------------------------- -- public functions @@ -133,6 +162,7 @@ end function logistica.access_point_on_player_close(playerName) fakeInvMap[playerName] = nil + liquidsMap[playerName] = nil end function logistica.update_fake_inv(pos, invName, listName, listSize, playerName) @@ -242,4 +272,62 @@ end function logistica.access_point_get_current_search_term(meta) return meta:get_string(META_CURR_SEARCH) -end \ No newline at end of file +end + +function logistica.access_point_change_liquid(meta, dir, playerName) + local liquidsInfo = liquidsMap[playerName] + if not liquidsInfo then return end + local currLiquidIndex = meta:get_int(META_CURR_LIQUID_INDEX) + if currLiquidIndex == LIQUID_NONE then return end + local numLiquids = #liquidsInfo + if numLiquids <= 0 then return end + local next = currLiquidIndex + dir + + if next > numLiquids then next = 1 end + if next < 1 then next = numLiquids end + + meta:set_int(META_CURR_LIQUID_INDEX, next) + return true +end + +function logistica.access_point_refresh_liquids(pos, playerName) + local liquidsInfo = logistica.get_available_liquids_in_network(pos) + liquidsMap[playerName] = liquidsInfo + + local meta = minetest.get_meta(pos) + if #liquidsInfo <= 0 then + meta:set_int(META_CURR_LIQUID_INDEX, LIQUID_NONE) + return + end + + local currLiquid = meta:get_int(META_CURR_LIQUID_INDEX) + local validSelected = currLiquid >= 1 and currLiquid <= #liquidsInfo + if not validSelected then + meta:set_int(META_CURR_LIQUID_INDEX, 1) + end +end + +-- returns a table: { description = "description here", capacity = "3/32", texture = "texture_name.png"} +function logistica.access_point_get_current_liquid_display_info(meta, playerName) + local liquidsInfo = liquidsMap[playerName] + if not liquidsInfo then return make_display_info(STR_NO_LIQUID, nil, nil) end + local currLiquid = meta:get_int(META_CURR_LIQUID_INDEX) + local liquidInfo = liquidsInfo[currLiquid] + + if currLiquid == LIQUID_NONE or not liquidInfo then return make_display_info(STR_NO_LIQUID, nil, nil) end + + return make_display_info(liquidInfo.name, liquidInfo.curr, liquidInfo.max) +end + +-- returns the current liquid name (which may be "" for the empty reservoirs) or nil if invalid/no reservoirs +function logistica.access_point_get_current_liquid_name(meta, playerName) + local liquidsInfo = liquidsMap[playerName] + if not liquidsInfo then return nil end + + local currLiquid = meta:get_int(META_CURR_LIQUID_INDEX) + if currLiquid == LIQUID_NONE then return "" end + + local liquidInfo = liquidsInfo[currLiquid] + if not liquidInfo then return nil + else return liquidInfo.name end +end diff --git a/logic/access_point_formspec.lua b/logic/access_point_formspec.lua index 5fc3968..6be2004 100644 --- a/logic/access_point_formspec.lua +++ b/logic/access_point_formspec.lua @@ -19,9 +19,12 @@ local SORT_COUNT_BTN = "sort_cnt" local SORT_WEAR_BTN = "sort_wer" local SEARCH_FIELD = "srch_fld" local USE_META_BTN = "tgl_meta" +local LIQUID_NEXT_BTN = "liq_nxt" +local LIQUID_PREV_BTN = "liq_prv" local INV_FAKE = "fake" local INV_INSERT = "isert" +local INV_LIQUID = "liqd" local FAKE_INV_W = 12 local FAKE_INV_H = 4 local FAKE_INV_SIZE = FAKE_INV_W * FAKE_INV_H @@ -49,15 +52,16 @@ local STR_LIGHT_DESC = S("Show Light sources only") local STR_SERCH_DESC = S("Search by text\nUse group:some_group to find items belongong to some_group") local STR_CLEAR_DESC = S("Clear search") - +local detachedInventories = {} local accessPointForms = {} -- creates the inv and returns the inv name local function get_or_create_detached_inventory(pos, playerName) - if accessPointForms[playerName] and accessPointForms[playerName].invName then - return accessPointForms[playerName].invName + local hash = minetest.hash_node_position(pos) + if detachedInventories[hash] then + return detachedInventories[hash] end - local invName = playerName.."_LAP_"..logistica.get_rand_string_for(pos) + local invName = "Logistica_AP_"..logistica.get_rand_string_for(pos) local inv = minetest.create_detached_inventory(invName, { allow_move = logistica.access_point_allow_move, allow_put = logistica.access_point_allow_put, @@ -68,6 +72,8 @@ local function get_or_create_detached_inventory(pos, playerName) }, playerName) inv:set_size(INV_FAKE, FAKE_INV_SIZE) inv:set_size(INV_INSERT, 1) + inv:set_size(INV_LIQUID, 1) + detachedInventories[hash] = invName return invName end @@ -144,6 +150,17 @@ local function get_search_and_page_section(searchTerm, pageInfo) return "image_button[13.9,6.5;0.8,0.8;logistica_icon_last.png;"..LAST_BTN..";;false;false;]" end +local function get_liquid_section(invName, meta, playerName) + local currInfo = logistica.access_point_get_current_liquid_display_info(meta, playerName) + + return + "list[detached:"..invName..";"..INV_LIQUID..";1.2,7.3;1,1;0]".. + "image[1.3,6;0.8,0.8;"..currInfo.texture.."]".. + "label[1.0,7.1;"..currInfo.description.." "..currInfo.capacity.."]".. + "image_button[0.7,6;0.6,0.8;logistica_icon_prev.png;"..LIQUID_PREV_BTN..";;false;false]".. + "image_button[2.1,6;0.6,0.8;logistica_icon_next.png;"..LIQUID_NEXT_BTN..";;false;false]" +end + local function get_access_point_formspec(pos, invName, optMeta, playerName) --local posForm = "nodemeta:"..pos.x..","..pos.y..","..pos.z local meta = optMeta or minetest.get_meta(pos) @@ -158,12 +175,13 @@ local function get_access_point_formspec(pos, invName, optMeta, playerName) "size[15.2,12.5]".. logistica.ui.background.. "list[detached:"..invName..";"..INV_FAKE..";0.2,0.2;"..FAKE_INV_W..","..FAKE_INV_H..";0]".. - "image[2.8,6.4;1,1;logistica_icon_input.png]".. - "list[detached:"..invName..";"..INV_INSERT..";3.8,6.4;1,1;0]".. + "image[3.2,6.5;0.8,0.8;logistica_icon_input.png]".. + "list[detached:"..invName..";"..INV_INSERT..";4.0,6.4;1,1;0]".. "list[current_player;main;5.2,7.5;8.0,4.0;0]".. "label[1.4,12.2;"..S("Crafting").."]".. "list[current_player;craft;0.2,8.4;3,3;]".. "list[current_player;craftpreview;3.9,8.4;1,1;]".. + get_liquid_section(invName, meta, playerName).. get_listrings(invName).. get_filter_section(usesMetaStr, filterHighImg).. get_tooltips().. @@ -180,6 +198,7 @@ local function show_access_point_formspec(pos, playerName, optMeta) invName = invName, } logistica.access_point_refresh_fake_inv(pos, invName, INV_FAKE, FAKE_INV_SIZE, playerName) + logistica.access_point_refresh_liquids(pos, playerName) minetest.show_formspec( playerName, FORMSPEC_NAME, @@ -207,7 +226,6 @@ function logistica.on_receive_access_point_formspec(player, formname, fields) if minetest.is_protected(pos, playerName) or not pos then return true end if fields.quit and not fields.key_enter_field then - logistica.access_point_on_player_leave(playerName) return true elseif fields[FRST_BTN] then if not logistica.access_point_change_page(pos, -2, playerName, FAKE_INV_SIZE) then return true end @@ -241,6 +259,10 @@ function logistica.on_receive_access_point_formspec(player, formname, fields) logistica.access_point_on_search_clear(pos) elseif fields[SEARCH_BTN] or fields.key_enter_field then logistica.access_point_on_search_change(pos, fields[SEARCH_FIELD]) + elseif fields[LIQUID_PREV_BTN] then + if not logistica.access_point_change_liquid(minetest.get_meta(pos),-1, playerName) then return true end + elseif fields[LIQUID_NEXT_BTN] then + if not logistica.access_point_change_liquid(minetest.get_meta(pos), 1, playerName) then return true end end show_access_point_formspec(pos, playerName) return true @@ -252,9 +274,18 @@ end function logistica.access_point_allow_put(inv, listname, index, stack, player) if listname == INV_FAKE then return 0 end + local pos = get_curr_pos(player) if not pos then return 0 end if not logistica.get_network_or_nil(pos) then return 0 end + + if listname == INV_LIQUID then + if logistica.reservoir_is_known_bucket(stack:get_name()) then + local currStack = inv:get_stack(listname, index) + if currStack:is_empty() then return 1 else return 0 end + else return 0 end + end + return stack:get_count() end @@ -298,6 +329,7 @@ end function logistica.access_point_allow_move(inv, from_list, from_index, to_list, to_index, count, player) if from_list == INV_FAKE or to_list == INV_FAKE then return 0 end + if to_list == INV_LIQUID then return 0 end return count end @@ -319,6 +351,13 @@ function logistica.access_point_on_put(inv, listname, index, stack, player) end inv:set_stack(listname, index, ItemStack("")) show_access_point_formspec(pos, player:get_player_name()) + elseif listname == INV_LIQUID then + local currLiquid = logistica.access_point_get_current_liquid_name(minetest.get_meta(pos), player:get_player_name()) + local newStack = logistica.use_bucket_for_liquid_in_network(pos, stack, currLiquid) + if newStack then + inv:set_stack(listname, index, newStack) + show_access_point_formspec(pos, player:get_player_name()) + end end end @@ -342,8 +381,28 @@ end function logistica.access_point_on_player_leave(playerName) local info = accessPointForms[playerName] if info and info.invName then - minetest.remove_detached_inventory(info.invName) + local onlyRef = true + for _, otherInfo in pairs(accessPointForms) do + if otherInfo.invName == info.invName then onlyRef = false end + end + if onlyRef then + minetest.remove_detached_inventory(info.invName) + end end accessPointForms[playerName] = nil logistica.access_point_on_player_close(playerName) end + +function logistica.access_point_on_dug(pos) + local removeForPlayers = {} + local i =0 + for playerName, info in pairs(accessPointForms) do + if info.position and vector.equals(pos, info.position) then + i = i + 1 + removeForPlayers[i] = playerName + end + end + for _, playerName in ipairs(removeForPlayers) do + logistica.access_point_on_player_leave(playerName) + end +end diff --git a/logic/groups.lua b/logic/groups.lua index ef3ff4e..6038fa3 100644 --- a/logic/groups.lua +++ b/logic/groups.lua @@ -9,6 +9,7 @@ logistica.item_storage = {} logistica.misc_machines = {} logistica.trashcans = {} logistica.vaccuum_suppliers = {} +logistica.reservoirs = {} logistica.TIER_ALL = "logistica_all_tiers" logistica.GROUP_ALL = "group:" .. logistica.TIER_ALL logistica.TIER_CONTROLLER = "logistica_controller" @@ -20,7 +21,7 @@ function logistica.is_machine(name) return logistica.is_requester(name) or logistica.is_supplier(name) or logistica.is_mass_storage(name) or logistica.is_item_storage(name) or logistica.is_controller(name) or logistica.is_injector(name) or logistica.is_crafting_supplier(name) or logistica.is_trashcan(name) or logistica.is_vaccuum_supplier(name) - or logistica.is_misc(name) + or logistica.is_misc(name) or logistica.is_reservoir(name) end function logistica.is_cable(name) @@ -66,3 +67,7 @@ end function logistica.is_vaccuum_supplier(name) return logistica.vaccuum_suppliers[name] ~= nil end + +function logistica.is_reservoir(name) + return logistica.reservoirs[name] ~= nil +end diff --git a/logic/network_logic.lua b/logic/network_logic.lua index bce5a8f..1c26fa4 100644 --- a/logic/network_logic.lua +++ b/logic/network_logic.lua @@ -23,6 +23,7 @@ local function has_machine(network, id) or network.injectors[id] or network.misc[id] or network.trashcans[id] + or network.reservoirs[id] then return true else @@ -204,6 +205,10 @@ local function recursive_scan_for_nodes_for_controller(network, positionHashes, network.trashcans[otherHash] = true valid = true end + if logistica.is_reservoir(otherName) then + network.reservoirs[otherHash] = true + valid = true + end if valid then newToScan = newToScan + 1 set_cache_network_id(minetest.get_meta(otherPos), network.controller) @@ -242,6 +247,7 @@ local function create_network(controllerPosition, oldNetworkName) network.storage_cache = {} network.supplier_cache = {} network.requester_cache = {} + network.reservoirs = {} local startPos = {} startPos[controllerHash] = true local status = recursive_scan_for_nodes_for_controller(network, startPos) @@ -369,6 +375,12 @@ local TRASHCAN_OPS = { update_cache_node_removed = function(_) end, } +local RESERVOIR_OPS = { + get_list = function(network) return network.reservoirs end, + update_cache_node_added = function(_) end, + update_cache_node_removed = function(_) end, +} + local function cable_can_extend_network_from(pos) local node = minetest.get_node_or_nil(pos) if not node then return false end @@ -494,3 +506,7 @@ end function logistica.on_trashcan_change(pos, oldNode, oldMeta) on_node_change(pos, oldNode, oldMeta, TRASHCAN_OPS) end + +function logistica.on_reservoir_change(pos, oldNode, oldMeta) + on_node_change(pos, oldNode, oldMeta, RESERVOIR_OPS) +end diff --git a/logic/network_storage.lua b/logic/network_storage.lua index b8e0449..3959ee6 100644 --- a/logic/network_storage.lua +++ b/logic/network_storage.lua @@ -3,11 +3,75 @@ local MASS_STORAGE_LIST_NAME = "storage" local ITEM_STORAGE_LIST_NAME = "main" local MAX_NETWORK_DEPTH_SEARCH = 16 -- somewhat arbitrary but prevents stackoverflows +local h2p = minetest.get_position_from_hash + local function get_meta(pos) logistica.load_position(pos) return minetest.get_meta(pos) end +local function fill_bucket_from_network(network, bucketItemStack, liquidName) + local lowestReservoirPos = nil + local lowestReservoirLvl = 999999 + for hash, _ in pairs(network.reservoirs or {}) do + local pos = h2p(hash) + logistica.load_position(pos) + if logistica.reservoir_get_liquid_name(pos) == liquidName then + local levels = logistica.reservoir_get_liquid_level(pos) + if levels and levels[1] < lowestReservoirLvl then + lowestReservoirPos = pos + lowestReservoirLvl = levels[1] + end + end + end + + if lowestReservoirPos then + return logistica.reservoir_use_item_on(lowestReservoirPos, bucketItemStack) + else + return nil + end +end + +local function empty_bucket_into_network(network, bucketItemStack) + local bucketName = bucketItemStack:get_name() + local liquidName = logistica.reservoir_get_liquid_name_for_bucket(bucketName) + + local highestReservoirPos = nil + local emptyReservoirPos = nil + local emptyResrvoirMinCap = 999999 + local highestReservoirLvl = 0 + for hash, _ in pairs(network.reservoirs or {}) do + local pos = h2p(hash) + logistica.load_position(pos) + local liquidInReservoir = logistica.reservoir_get_liquid_name(pos) + if liquidInReservoir == liquidName then + local levels = logistica.reservoir_get_liquid_level(pos) + if levels and levels[1] < levels[2] and levels[1] > highestReservoirLvl then + highestReservoirPos = pos + highestReservoirLvl = levels[1] + end + elseif liquidInReservoir == "" then + local levels = logistica.reservoir_get_liquid_level(pos) + if levels and levels[2] < emptyResrvoirMinCap then + emptyResrvoirMinCap = levels[2] + emptyReservoirPos = pos + end + end + end + + if highestReservoirPos then + return logistica.reservoir_use_item_on(highestReservoirPos, bucketItemStack) + elseif emptyReservoirPos then + return logistica.reservoir_use_item_on(emptyReservoirPos, bucketItemStack) + else + return nil + end +end + +-------------------------------- +-- public functions +-------------------------------- + -- tries to take a stack from the network locations -- calls the collectorFunc with the stack - collectorFunc needs to return how many were left-over
-- `collectorFunc = function(stackToInsert)`
@@ -40,7 +104,7 @@ function logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc local stackName = stackToTake:get_name() local validSupplers = network.supplier_cache[stackName] or {} for hash, _ in pairs(validSupplers) do - local pos = minetest.get_position_from_hash(hash) + local pos = h2p(hash) logistica.load_position(pos) local nodeName = minetest.get_node(pos).name if logistica.is_supplier(nodeName) or logistica.is_vaccuum_supplier(nodeName) then @@ -64,7 +128,7 @@ function logistica.take_stack_from_item_storage(stack, network, collectorFunc, i if useMetadata then eq = function(s1, s2) return s1:equals(s2) end end for storageHash, _ in pairs(network.item_storage) do - local storagePos = minetest.get_position_from_hash(storageHash) + local storagePos = h2p(storageHash) local storageInv = get_meta(storagePos):get_inventory() local storageList = storageInv:get_list(ITEM_STORAGE_LIST_NAME) or {} for i, storedStack in ipairs(storageList) do @@ -97,7 +161,7 @@ function logistica.take_stack_from_mass_storage(stackToTake, network, collectorF if stackToTake:get_count() == 0 then return end if massLocations == nil then return end for storageHash, _ in pairs(massLocations) do - local storagePos = minetest.get_position_from_hash(storageHash) + local storagePos = h2p(storageHash) local meta = get_meta(storagePos) local storageInv = meta:get_inventory() local storageList = storageInv:get_list(MASS_STORAGE_LIST_NAME) or {} @@ -155,7 +219,7 @@ function logistica.insert_item_in_network(itemstack, networkId, dryRun) -- check requesters first local listOfRequestersInNeedOfItem = network.requester_cache[itemstack:get_name()] or {} for hash, _ in pairs(listOfRequestersInNeedOfItem) do - local pos = minetest.get_position_from_hash(hash) + local pos = h2p(hash) logistica.load_position(pos) local leftover = logistica.insert_itemstack_for_requester(pos, workingStack, true) if leftover <= 0 then return 0 end -- we took all items @@ -173,7 +237,7 @@ function logistica.insert_item_in_network(itemstack, networkId, dryRun) addFunc = logistica.insert_item_into_mass_storage end for hash, _ in pairs(storages) do - local pos = minetest.get_position_from_hash(hash) + local pos = h2p(hash) local inv = get_meta(pos):get_inventory() local remainingStack = addFunc(pos, inv, workingStack, dryRun) if remainingStack:is_empty() then return 0 end -- we took all items @@ -183,7 +247,7 @@ function logistica.insert_item_in_network(itemstack, networkId, dryRun) -- try to add to passive suppliers that accept this local suppliers = network.suppliers for hash, _ in pairs(suppliers) do - local pos = minetest.get_position_from_hash(hash) + local pos = h2p(hash) logistica.load_position(pos) local leftover = logistica.put_item_in_supplier(pos, workingStack) if leftover:is_empty() then return 0 end @@ -193,11 +257,67 @@ function logistica.insert_item_in_network(itemstack, networkId, dryRun) -- [Keep this last] delete the item if any trashcan accepts it local trashcans = network.trashcans or {} for hash, _ in pairs(trashcans) do - local pos = minetest.get_position_from_hash(hash) + local pos = h2p(hash) logistica.load_position(pos) workingStack = logistica.trashcan_trash_item(pos, workingStack) if workingStack:is_empty() then return 0 end end return workingStack:get_count() -end \ No newline at end of file +end + +--[[ returns a natural-indexed list of tables - or empty table if there's no network or no liquids: + ``` + [1] = { + name = "liquid_name", -- name of the liquid or "" if for empty reservoirs + curr = 0 -- amount of liquid stored in network + max = 32 -- combined max capacity of the reservoirs occupied by liquid + }, + [2] = {...} + ``` +]] +function logistica.get_available_liquids_in_network(pos) + local network = logistica.get_network_or_nil(pos) + if not network then return {} end + local liquidInfo = {} + for hash, _ in pairs(network.reservoirs or {}) do + local resPos = h2p(hash) + local liquidName = logistica.reservoir_get_liquid_name(resPos) + local liquidLevels = logistica.reservoir_get_liquid_level(resPos) + if liquidName and liquidLevels then + local info = liquidInfo[liquidName] or {curr = 0, max = 0} + info.curr = info.curr + liquidLevels[1] + info.max = info.max + liquidLevels[2] + liquidInfo[liquidName] = info + end + end + return + logistica.table_to_list_indexed(liquidInfo, function(lName, lInfo) + return { + name = lName, + curr = lInfo.curr, + max = lInfo.max, + } + end) +end + +-- attempts to use, either fill or empty, the given bucket in/from liquid storage on +-- the network.
+-- `liquidName` is only used if the bucketItem is a type of empty bucket
+-- Otherwise a full bucket will attempt to fill any applicable reservoir on the network. +-- This function attempts to take from the lowest filled reservoir, and insert into the highest filled reservoir first.
+-- returns new itemstack to replace the old one, or `nil` if it wasn't changed +function logistica.use_bucket_for_liquid_in_network(pos, bucketItemStack, liquidName) + local network = logistica.get_network_or_nil(pos) + if not network then return nil end + + local bucketName = bucketItemStack:get_name() + local isEmptyBucket = logistica.reservoir_is_empty_bucket(bucketName) + local isFullBucket = logistica.reservoir_is_full_bucket(bucketName) + if isEmptyBucket then + if not liquidName then return nil end + return fill_bucket_from_network(network, bucketItemStack, liquidName) + elseif isFullBucket then + return empty_bucket_into_network(network, bucketItemStack) + end +end diff --git a/logic/reservoir.lua b/logic/reservoir.lua index 15e65c7..8ec404a 100644 --- a/logic/reservoir.lua +++ b/logic/reservoir.lua @@ -3,6 +3,9 @@ local S = logistica.TRANSLATOR local BUCKET_TO_NAME = {} local NAME_TO_BUCKET = {} local NAME_TO_EMPTY_BUCKET = {} +local NAME_TO_DESC = {} +local NAME_TO_TEXTURE = {} + local EMPTY_BUCKET = "bucket:bucket_empty" local EMPTY_SUFFIX = "_empty" @@ -54,6 +57,14 @@ end -- public functions -------------------------------- +function logistica.reservoir_get_description_of_liquid(liquidName) + return NAME_TO_DESC[liquidName] or LIQUID_NONE +end + +function logistica.reservoir_get_texture_of_liquid(liquidName) + return NAME_TO_TEXTURE[liquidName] or "" +end + function logistica.reservoir_make_param2(val, max) local ret = math.floor(63*(val/max)) if val > 0 and ret == 0 then @@ -66,12 +77,14 @@ function logistica.reservoir_get_description(currBuckets, maxBuckets, liquidName return strDescription.."\n"..getStrContains(currBuckets, maxBuckets, liquidName) end -function logistica.reservoir_register_names(liquidName, bucketName, emptyBucketName) +function logistica.reservoir_register_names(liquidName, bucketName, emptyBucketName, liquidDesc, liquidTexture) BUCKET_TO_NAME[bucketName] = liquidName NAME_TO_BUCKET[liquidName] = bucketName if emptyBucketName then NAME_TO_EMPTY_BUCKET[liquidName] = emptyBucketName end + NAME_TO_DESC[liquidName] = liquidDesc + NAME_TO_TEXTURE[liquidName] = liquidTexture end -- returns nil if item had no effect
@@ -86,7 +99,7 @@ function logistica.reservoir_use_item_on(pos, itemstack, optNode) local nodeLiquidLevel = meta:get_int(META_LIQUID_LEVEL) local liquidName = nodeDef.logistica.liquidName local maxBuckets = nodeDef.logistica.maxBuckets - local liquidDesc = nodeDef.logistica.liquidDesc + local liquidDesc = logistica.reservoir_get_description_of_liquid(liquidName) local emptyBucket = get_empty_bucket_needed_for(liquidName) local fullBucket = get_full_bucket_needed_for(liquidName) @@ -127,14 +140,57 @@ function logistica.reservoir_use_item_on(pos, itemstack, optNode) minetest.swap_node(pos, node) local newNodeName = get_liquid_reservoir_name_for(node.name, newLiquidName) - if not minetest.registered_nodes[newNodeName] then return nil end + local nodeDef = minetest.registered_nodes[newNodeName] + if not nodeDef or not nodeDef.logistica then return nil end if nodeLiquidLevel == 1 then -- first bucket we added, swap to that reservoir type logistica.swap_node(pos, newNodeName) end - local newLiquidDesc = minetest.registered_nodes[newNodeName].logistica.liquidDesc + local newLiquidDesc = logistica.reservoir_get_description_of_liquid(nodeDef.logistica.liquidName) meta:set_string("infotext", logistica.reservoir_get_description(nodeLiquidLevel, maxBuckets, newLiquidDesc)) meta:set_int(META_LIQUID_LEVEL, nodeLiquidLevel) return ItemStack(newEmptyBucket) end return nil end + +-- returns the liquid name for the reservoir; or "" if there's no liquid stored, or nil if its not a reservoir +function logistica.reservoir_get_liquid_name(pos) + local node = minetest.get_node(pos) + if not logistica.is_reservoir(node.name) then return nil end + local def = minetest.registered_nodes[node.name] + if not def or not def.logistica or not def.logistica.liquidName then return nil end + return def.logistica.liquidName +end + +-- return {currentLevel, maxLevel} measured in buckets; or nil if its not a reservoir +function logistica.reservoir_get_liquid_level(pos) + local node = minetest.get_node(pos) + if not logistica.is_reservoir(node.name) then return nil end + local def = minetest.registered_nodes[node.name] + if not def or not def.logistica or not def.logistica.maxBuckets then return nil end + local meta = minetest.get_meta(pos) + return { meta:get_int(META_LIQUID_LEVEL), def.logistica.maxBuckets } +end + +function logistica.reservoir_is_empty_bucket(bucketName) + if bucketName == EMPTY_BUCKET then return true end + for _, bucket in pairs(NAME_TO_EMPTY_BUCKET) do + if bucket == bucketName then return true end + end + return false +end + +function logistica.reservoir_is_full_bucket(bucketName) + if BUCKET_TO_NAME[bucketName] ~= nil then return true end + return false +end + +-- returns true if the itemname is a known empty or filled bucket that can be used in a reservoir +function logistica.reservoir_is_known_bucket(bucketName) + return logistica.reservoir_is_empty_bucket(bucketName) or logistica.reservoir_is_full_bucket(bucketName) +end + +-- return the liquid name for the given bucket name, or nil if there's none registered +function logistica.reservoir_get_liquid_name_for_bucket(bucketName) + return BUCKET_TO_NAME[bucketName] +end \ No newline at end of file diff --git a/textures/logistica_reservoir_obsidian.png b/textures/logistica_reservoir_obsidian.png index acf7520e1c10f929f1d7322b373828a6b21287d3..5c8f68877ce54f508df6b194042757d0092f80b1 100644 GIT binary patch delta 583 zcmZ1|vPDp}Gr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK9Zfq~U?qiQh|lcCw#1pP|Z~Cep~_X`d4>=Idm&ENt58UHiU= zL|!kQ#L_VH8#KHXpYgu) zd+@zw$0`;zzs(a=uI2VWdHTz78}r9=hBDt(6O0#g7VDWC|GLccde;=Y3CrC>wtaEl z-J!v`{j3hdgrY}Q4Jt*F9%seb{(HUN$TZ_}q1XhL#6x?2G#&g^V9Eb) ze?^nb#EWM*suHH!KR&3Lc;wBFf*rAY+2^Hq^k$t>W%tsY!t?I+uK6<4)7U>fUFP9r zmZ%u)wa8(s`pvkGbhe;i$5-x#evF&hgYs6}&TMmk@Lp}&pWT~Yd})!$h(0{G>tdCt z!RE@IjW@#t??|hpzi|KcFU0ai`~1xj*GtW{zsUMO`E&d2&tLEOUshamt90ho0HzHF MPgg&ebxsLQ095DsApigX delta 951 zcmV;o14#U|5up?%iBL{Q4GJ0x0000DNk~Le0000W0000W2nGNE0CReJ^Z)<_La`-t z0s=EKlbQk>0y8p`w*oC2GcqCyARr)gX=Y|RNo`?gWlUvmXLM*iA~Q0R{{prOHdHb* zIyE*rFgBCF10f1FR5CL?_6lRX5&3N}CyWO70Vz^+WnPGSAuCA_rET{eS6~Je_&HA=8<6mC0`{wqB05I(*y%xc) zK773V1OT92gmR|JC-w|H9&T~S_p66poL%GUd~Z7I6yRU<_^$V zAY=?kJvY0!HTY_q0v94i0xdKW<(Ke3HUQB6BtcF;ircbK3?Rt)DY($0@1f)g1`5v+ zrTIe@`g@92mw#sBV{~L??fQS|cVcFdzz_luFfA2E!^Z%q9!QGF1(`@{0A`9AQeh2d zngOn1=$&u|1R*f9 zc1*ENJ43ct72GC(Z~~f+S_v6MK+WFd;ubNqED($0*)l=q300cN$jZ!aVzGs@JLZ)z z+&hD`bR1y8{Z~s#L1&HztylT?lBY&rh~nbnX)FyF z1qdiv5r4i~tpkqCsH!xvCi0+tvR3!Pn5>w{bCT;K#De0Ux|glx9+i)zAKB;tsC6S`)ShnNW$!4b&1ZB%EDela z-bo@Qp8{eUHe|hK>7{gNwGz6J)X6LWgyf2~_J6I`-O72->!1uGaxKNtu0H3rI#5Rv zuneXh-K-USq!0zVycDu}v&c0EaMTe;J$VSN9P&M;{iOMC!}D~7Cp{RFZoCewpA{Xz-QORBK5nmn`2lzN=Zp66t$&{X Z2NZRAd*d(N9y0&{002ovPDHLkV1ffQnezYu diff --git a/textures/logistica_reservoir_silverin.png b/textures/logistica_reservoir_silverin.png index c6b5b37cf22dcfd8f99beac3f6106719b7f9102e..d78b406671755463bbc65f5a2c4ed97c4f41356a 100644 GIT binary patch delta 575 zcmbO$x<*j7Gr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK9Zfq~U?qiQh|lcCwq9A5-6{sro;{&SL44WXIU+|F*{bEZg~M`~C8NJ7adPd(HI! z-}8H#s^Qxr&3z36h4^Q)F@3pNzh!pap;a%f7_>Yu%v~XSev!~SMdfn}3`N!+s~J{R z&Uvvv>WpE-L)GlGZVQ7Az8sIPe)W}8Uvs-}-}x=SV`i0gBwzD56zqS_)pmE9KkFGD zM-idi%cjlxq7M4P$r?iUV&XsjyBp15SNAW{-G<+u7?}%s#_rGJmZ1+=Bx@g&ywBS z7;-oN+FjY_%rV*aX3lnXwz=MHlR7qY*_fR8l;S^!zgnR)>~bi#p%AN+`rXBhYuoZ! z4eYhIo!!5@S*LNPk;Se>ALA?TJ+XLeBX|F=D7(h;Yu(OUQp0y8p`w*oC2GcqCyARr)gX=Y|RNo`?gWlUvmXLM*iA~Q0R{{prOHdHb) zIyE>tGc}XH10f1FR5CI;H8?smHIwQCLJDJYVP?_6lRX5&3N}1e?3OiarzDTmdBBQ z4n_HQU$gto-){sk9Vh=<1c$nK_46-)s3&Q-M)~uP-?_WJ*?&Cj`PBZ1 zRB z5n@3vVPy9UP=K(R6Ki?;G?w_Q5Yb8Q%;uOo;DopZAZ8FM2pz9pB$UI#!}>ep8wErF zf(o~Q5c=~cA*iltjxNkg_5QGa-hY}K39BarnCB{^g?}dp66OqO8F&)z{aG+4%T}uJ zs$^3!Gn8ncs zjd+xHTuFU;_8LGG90Dk(P-u0bq+sn+4~RE6FE4NZ^TkRS)rv^IXzkg1gFh>^%ytiy zY;rXZ#IOOND$J#~dm*E%w;jFUV&rvkzd^FvOMjii763{e$UMmLvL&OngNb96p)#oW zq0AN|p&Edc5^2sEYUA$p@~nLP^cjlMIvCW*RRHtcE5R@k<*7(zmhw7L4$XZ}2vW-M zUESg(b8KrOa=;bxOdugGQf6lws`OeF5N17vD+%2469Ycgzg8q5W#T>70C+=l;dBLCd|HrC7j!w{JI5j^CeMUh(eVTc+dW%Xhomf{hBrsMRQ$9p;bOOO3}{y%Qt+z~>Wfg(-