From 92c27da19a08782e43f6ee62a932803a76597bec Mon Sep 17 00:00:00 2001 From: Zenon Seth Date: Sun, 12 Nov 2023 13:30:16 +0000 Subject: [PATCH] Add Access Point with working sort, filter, take, insert --- ROADMAP.md | 6 +- api/access_point.lua | 118 ++++++++++ api/api.lua | 1 + api/controller.lua | 6 +- api/injector.lua | 1 + api/item_storage.lua | 1 + api/mass_storage.lua | 11 +- api/requester.lua | 1 + api/supplier.lua | 1 + entity/entity.lua | 4 +- item/item.lua | 2 +- logic/access_point.lua | 208 +++++++++++++++++ logic/access_point_formspec.lua | 239 ++++++++++++++++++++ logic/logic.lua | 24 +- logic/network_logic.lua | 68 +++--- logic/network_storage.lua | 22 +- textures/logistica_access_point_back.png | Bin 0 -> 2324 bytes textures/logistica_access_point_bottom.png | Bin 0 -> 2324 bytes textures/logistica_access_point_front.png | Bin 0 -> 2250 bytes textures/logistica_access_point_side.png | Bin 0 -> 2314 bytes textures/logistica_access_point_top.png | Bin 0 -> 2324 bytes textures/logistica_blank.png | Bin 0 -> 1858 bytes textures/logistica_icon_all.png | Bin 0 -> 3359 bytes textures/logistica_icon_cancel.png | Bin 0 -> 2382 bytes textures/logistica_icon_craftitem.png | Bin 0 -> 3473 bytes textures/logistica_icon_first.png | Bin 0 -> 2479 bytes textures/logistica_icon_highlight.png | Bin 0 -> 2674 bytes textures/logistica_icon_last.png | Bin 0 -> 2450 bytes textures/logistica_icon_next.png | Bin 0 -> 2367 bytes textures/logistica_icon_node.png | Bin 0 -> 3445 bytes textures/logistica_icon_prev.png | Bin 0 -> 2381 bytes textures/logistica_icon_search.png | Bin 0 -> 3710 bytes textures/logistica_icon_sort_count_99_1.png | Bin 0 -> 3369 bytes textures/logistica_icon_sort_mod_az.png | Bin 0 -> 3339 bytes textures/logistica_icon_sort_name_az.png | Bin 0 -> 3622 bytes textures/logistica_icon_sort_wear_0_100.png | Bin 0 -> 3571 bytes textures/logistica_icon_tool.png | Bin 0 -> 3852 bytes textures/logistica_icon_torch.png | Bin 0 -> 2974 bytes util/common.lua | 4 +- util/inv_list_filtering.lua | 63 ++++++ util/inv_list_sorting.lua | 2 +- util/util.lua | 1 + 42 files changed, 710 insertions(+), 73 deletions(-) create mode 100644 api/access_point.lua create mode 100644 logic/access_point.lua create mode 100644 logic/access_point_formspec.lua create mode 100644 textures/logistica_access_point_back.png create mode 100644 textures/logistica_access_point_bottom.png create mode 100644 textures/logistica_access_point_front.png create mode 100644 textures/logistica_access_point_side.png create mode 100644 textures/logistica_access_point_top.png create mode 100644 textures/logistica_blank.png create mode 100644 textures/logistica_icon_all.png create mode 100644 textures/logistica_icon_cancel.png create mode 100644 textures/logistica_icon_craftitem.png create mode 100644 textures/logistica_icon_first.png create mode 100644 textures/logistica_icon_highlight.png create mode 100644 textures/logistica_icon_last.png create mode 100644 textures/logistica_icon_next.png create mode 100644 textures/logistica_icon_node.png create mode 100644 textures/logistica_icon_prev.png create mode 100644 textures/logistica_icon_search.png create mode 100644 textures/logistica_icon_sort_count_99_1.png create mode 100644 textures/logistica_icon_sort_mod_az.png create mode 100644 textures/logistica_icon_sort_name_az.png create mode 100644 textures/logistica_icon_sort_wear_0_100.png create mode 100644 textures/logistica_icon_tool.png create mode 100644 textures/logistica_icon_torch.png create mode 100644 util/inv_list_filtering.lua diff --git a/ROADMAP.md b/ROADMAP.md index 782be47..7db34ab 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,7 +3,6 @@ Missing Features: - Crafting recipes High Priority: -- Storage Upgrades - Wirelss Network Extender - Mesecons compat @@ -17,10 +16,11 @@ Medium Priority Low Priority: - Liquid Storage - Improve node sounds +- Direct pipeworks compatibility +- Direct tubelib compatibility Maybe not needed, unless specific common usecase found to support: - Demander modes: AND/OR - Mode AND: supply target with "item 1 AND item 2..." - Mode OR: supply target with "item 1" OR (if not available) "item 2" -- Direct pipeworks compatibility -- Direct tubelib compatibility +- API improvements: The API isn't much of an API in most cases, it barely allows customization. May be worth generifiying some of it sometime. \ No newline at end of file diff --git a/api/access_point.lua b/api/access_point.lua new file mode 100644 index 0000000..b845921 --- /dev/null +++ b/api/access_point.lua @@ -0,0 +1,118 @@ + +local function after_place_access_point(pos, placer, itemstack, numSlots, numUpgradeSlots) + local meta = minetest.get_meta(pos) + if placer and placer:is_player() then + meta:set_string("owner", placer:get_player_name()) + end + logistica.access_point_after_place(pos, meta) + logistica.on_access_point_change(pos) +end + +local function allow_access_point_inv_put(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + return logistica.access_point_allow_put(pos, listname, index, stack, player) +end + +local function allow_access_point_inv_take(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + return logistica.access_point_allow_take(pos, listname, index, stack, player) +end + +local function allow_access_point_inv_move(pos, from_list, from_index, to_list, to_index, count, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + return logistica.access_point_allow_move(pos, from_list, from_index, to_list, to_index, count, player) +end + +local function on_access_point_inv_move(pos, from_list, from_index, to_list, to_index, count, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + logistica.access_point_on_inv_move(pos, from_list, from_index, to_list, to_index, count, player) +end + +local function on_access_point_inv_put(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + logistica.access_point_on_put(pos, listname, index, stack, player) +end + +local function on_access_point_inv_take(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then return 0 end + logistica.access_point_on_take(pos, listname, index, stack, player) +end + +local function on_access_point_rightclick(pos, node, clicker, itemstack, pointed_thing) + if minetest.is_protected(pos, clicker:get_player_name()) then return 0 end + logistica.access_point_on_rightclick(pos, node, clicker, itemstack, pointed_thing) +end + +---------------------------------------------------------------- +-- registration calls +---------------------------------------------------------------- + +minetest.register_on_leaveplayer(function(objRef, timed_out) + if objRef:is_player() then + logistica.access_point_on_player_close(objRef:get_player_name()) + end +end) + +minetest.register_on_player_receive_fields(logistica.on_receive_access_point_formspec) + +---------------------------------------------------------------- +-- public api +---------------------------------------------------------------- + +-- `simpleName` is used for the description and for the name (can contain spaces) +function logistica.register_access_point(desc, name, tiles) + local lname = string.lower(name:gsub(" ", "_")) + local access_point_name = "logistica:access_point_"..lname + logistica.misc_machines[access_point_name] = true + local grps = {oddly_breakable_by_hand = 3, cracky = 3 } + grps[logistica.TIER_ALL] = 1 + local def = { + description = desc, + drawtype = "normal", + tiles = tiles, + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + groups = grps, + drop = access_point_name, + sounds = logistica.node_sound_metallic(), + connect_sides = {"top", "bottom", "left", "back", "right" }, + after_place_node = after_place_access_point, + after_destruct = logistica.on_access_point_change, + on_rightclick = on_access_point_rightclick, + on_metadata_inventory_move = on_access_point_inv_move, + on_metadata_inventory_put = on_access_point_inv_put, + on_metadata_inventory_take = on_access_point_inv_take, + allow_metadata_inventory_put = allow_access_point_inv_put, + allow_metadata_inventory_take = allow_access_point_inv_take, + allow_metadata_inventory_move = allow_access_point_inv_move, + logistica = {} + } + + minetest.register_node(access_point_name, def) + + local def_disabled = table.copy(def) + local tiles_disabled = {} + for k, v in pairs(def.tiles) do tiles_disabled[k] = v.."^logistica_disabled.png" end + + def_disabled.tiles = tiles_disabled + def_disabled.groups = { oddly_breakable_by_hand = 3, cracky = 3, choppy = 3, not_in_creative_inventory = 1 } + def_disabled.on_construct = nil + def_disabled.after_destruct = nil + def_disabled.on_punch = nil + def_disabled.on_rightclick = nil + def_disabled.on_timer = nil + def_disabled.logistica = nil + + minetest.register_node(access_point_name.."_disabled", def_disabled) + +end + +logistica.register_access_point("Access Point", "base", { + "logistica_access_point_top.png", + "logistica_access_point_bottom.png", + "logistica_access_point_side.png^[transformFX", + "logistica_access_point_side.png", + "logistica_access_point_back.png", + "logistica_access_point_front.png", +}) diff --git a/api/api.lua b/api/api.lua index 9f3b84e..667c6d5 100644 --- a/api/api.lua +++ b/api/api.lua @@ -7,3 +7,4 @@ dofile(path.."/supplier.lua") dofile(path.."/requester.lua") dofile(path.."/injector.lua") dofile(path.."/item_storage.lua") +dofile(path.."/access_point.lua") diff --git a/api/controller.lua b/api/controller.lua index bc61303..8edbea3 100644 --- a/api/controller.lua +++ b/api/controller.lua @@ -1,11 +1,12 @@ local SET_BUTTON = "logsetbtn" local NAME_FIELD = "namef" local FORMSPEC_NAME = "logconren" +local MAX_NETWORK_NAME_LENGTH = 20 local controllerForms = {} local function get_controller_formspec(pos) local name = logistica.get_network_name_or_nil(pos) or "" - return "formspec_version[6]" .. + return "formspec_version[4]" .. "size[10.5,2]" .. logistica.ui.background.. "field[2.5,0.6;3,0.8;"..NAME_FIELD..";Network Name;"..name.."]" .. @@ -28,6 +29,9 @@ local function on_controller_receive_fields(player, formname, fields) controllerForms[playerName] = nil elseif (fields[SET_BUTTON] or fields.key_enter_field) and fields[NAME_FIELD] then local newNetworkName = fields[NAME_FIELD] + if #newNetworkName > MAX_NETWORK_NAME_LENGTH then + newNetworkName = string.sub(newNetworkName, 1, MAX_NETWORK_NAME_LENGTH) + end logistica.rename_network(minetest.hash_node_position(pos), newNetworkName) local meta = minetest.get_meta(pos) meta:set_string("infotext", "Controller of Network: "..newNetworkName) diff --git a/api/injector.lua b/api/injector.lua index cd32a51..7b50090 100644 --- a/api/injector.lua +++ b/api/injector.lua @@ -61,6 +61,7 @@ end local function on_injector_rightclick(pos, node, clicker, itemstack, pointed_thing) if not clicker or not clicker:is_player() then return end + if minetest.is_protected(pos, clicker:get_player_name()) then return end show_injector_formspec(clicker:get_player_name(), pos) end diff --git a/api/item_storage.lua b/api/item_storage.lua index 72f8af7..274844a 100644 --- a/api/item_storage.lua +++ b/api/item_storage.lua @@ -55,6 +55,7 @@ end local function on_item_storage_rightclick(pos, _, clicker, _, _) if not clicker or not clicker:is_player() then return end + if minetest.is_protected(pos, clicker:get_player_name()) then return 0 end show_item_storage_formspec(clicker:get_player_name(), pos) end diff --git a/api/mass_storage.lua b/api/mass_storage.lua index 62f9da9..5b35d5c 100644 --- a/api/mass_storage.lua +++ b/api/mass_storage.lua @@ -272,12 +272,11 @@ end local function on_mass_storage_inv_move(pos, from_list, from_index, to_list, to_index, count, player) - if minetest.is_protected(pos, player) then return 0 end + if minetest.is_protected(pos, player:get_player_name()) then return 0 end return 0 end local function on_mass_storage_inv_put(pos, listname, index, stack, player) - if minetest.is_protected(pos, player) then return 0 end if listname == "main" then local remaining = logistica.try_to_add_item_to_storage(pos, stack) local taken = stack:get_count() - remaining @@ -298,7 +297,6 @@ local function on_mass_storage_inv_put(pos, listname, index, stack, player) end local function on_mass_storage_inv_take(pos, listname, index, stack, player) - if minetest.is_protected(pos, player) then return 0 end if listname == "upgrade" then logistica.on_mass_storage_upgrade_change(pos, stack:get_name(), false) end @@ -306,13 +304,14 @@ end local function on_mass_storage_punch(pos, node, puncher, pointed_thing) if not puncher and not puncher:is_player() then return end - if minetest.is_protected(pos, puncher) then return end + if minetest.is_protected(pos, puncher:get_player_name()) then return end logistica.try_to_add_player_wield_item_to_mass_storage(pos, puncher) end local function on_mass_storage_right_click(pos, node, clicker, itemstack, pointed_thing) - local name = clicker:get_player_name() - show_mass_storage_formspec(pos, name) + if not clicker or not clicker:is_player() then return end + if minetest.is_protected(pos, clicker:get_player_name()) then return end + show_mass_storage_formspec(pos, clicker:get_player_name()) end local function on_mass_storage_rotate(pos, node, player, mode, newParam2) diff --git a/api/requester.lua b/api/requester.lua index 4a3273e..d3fa5be 100644 --- a/api/requester.lua +++ b/api/requester.lua @@ -59,6 +59,7 @@ end local function on_requester_rightclick(pos, node, clicker, itemstack, pointed_thing) if not clicker or not clicker:is_player() then return end + if minetest.is_protected(pos, clicker:get_player_name()) then return end show_requester_formspec(clicker:get_player_name(), pos) end diff --git a/api/supplier.lua b/api/supplier.lua index ff9295a..da58c41 100644 --- a/api/supplier.lua +++ b/api/supplier.lua @@ -42,6 +42,7 @@ end local function on_supplier_rightclick(pos, node, clicker, itemstack, pointed_thing) if not clicker or not clicker:is_player() then return end + if minetest.is_protected(pos, clicker:get_player_name()) then return end show_supplier_formspec(clicker:get_player_name(), pos) end diff --git a/entity/entity.lua b/entity/entity.lua index 5c24e4f..323c0df 100644 --- a/entity/entity.lua +++ b/entity/entity.lua @@ -1,4 +1,4 @@ local path = logistica.MODPATH .. "/entity" -dofile(path .. "/io_entity.lua") -dofile(path .. "/display_item.lua") \ No newline at end of file +dofile(path.."/io_entity.lua") +dofile(path.."/display_item.lua") \ No newline at end of file diff --git a/item/item.lua b/item/item.lua index 72242cb..c187057 100644 --- a/item/item.lua +++ b/item/item.lua @@ -1,4 +1,4 @@ local path = logistica.MODPATH .. "/item" logistica.craftitem = {} -dofile(path .. "/storage_upgrade.lua") +dofile(path.."/storage_upgrade.lua") diff --git a/logic/access_point.lua b/logic/access_point.lua new file mode 100644 index 0000000..f49c2da --- /dev/null +++ b/logic/access_point.lua @@ -0,0 +1,208 @@ +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 fakeInvMap = {} + +local SORT_NAME = 1 +local SORT_MOD = 2 +local SORT_COUNT = 3 +local SORT_WEAR = 4 +local sortMethodMap = { + [SORT_NAME] = function(list) return logistica.sort_list_by(list, LOG_SORT_NAME_AZ) end, + [SORT_MOD] = function(list) return logistica.sort_list_by(list, LOG_SORT_MOD_AZ) end, + [SORT_COUNT] = function(list) return logistica.sort_list_by(list, LOG_SORT_STACK_SIZE_REV) end, + [SORT_WEAR] = function(list) return logistica.sort_list_by(list, LOG_SORT_DURABILITY_FWD) end, +} + +local FILTER_ALL = 1 +local FILTER_NODES = 2 +local FILTER_ITEMS = 3 +local FILTER_TOOLS = 4 +local FILTER_LIGHTS = 5 + +local filterMethodMap = { + [FILTER_ALL] = function(list) return list end, + [FILTER_NODES] = function(list) return logistica.filter_list_by(list, LOG_FILTER_NODE) end, + [FILTER_ITEMS] = function(list) return logistica.filter_list_by(list, LOG_FILTER_CRAFTITEM) end, + [FILTER_TOOLS] = function(list) return logistica.filter_list_by(list, LOG_FILTER_TOOL) end, + [FILTER_LIGHTS] = function(list) return logistica.filter_list_by(list, LOG_FILTER_LIGHT) end, +} + +local h2p = minetest.get_position_from_hash +local pth = minetest.hash_node_position +local get_meta = minetest.get_meta + +local function get_curr_sort_method_int(meta) + local curr = meta:get_int(META_SORT_TYPE) + if curr <= 0 or curr >= 5 then return 1 else return curr end +end + +local function set_curr_sort_method_int(meta, methodIndex) + meta:set_int(META_SORT_TYPE, methodIndex) +end + +local function get_curr_filter_method_int(meta) + local curr = meta:get_int(META_FILTER_TYPE) + if curr <= 0 or curr >= 6 then return 1 else return curr end +end + +local function set_curr_filter_method_int(meta, methodIndex) + meta:set_int(META_FILTER_TYPE, methodIndex) +end + +local function add_list_to_itemmap(itemMap, list, useMetadata) + for _, stack in ipairs(list) do + if not stack:is_empty() then + local stackName = stack:get_name() + if useMetadata then stackName = stack:to_string() end + if itemMap[stackName] == nil then itemMap[stackName] = 0 end + itemMap[stackName] = itemMap[stackName] + stack:get_count() + end + end +end + +local function build_stack_list(pos) + local network = logistica.get_network_or_nil(pos) + if not network then return {stackList = {}, stackListSize = 0} end + local useMetadata = logistica.access_point_is_set_to_use_metadata(pos) + local meta = get_meta(pos) + + local itemMap = {} + -- we scan all supply chests and mass storage and item storage + for hash, _ in pairs(network.suppliers) do + local mPos = logistica.load_position(h2p(hash)) + local list = get_meta(mPos):get_inventory():get_list("main") or {} + add_list_to_itemmap(itemMap, list, useMetadata) + end + for hash, _ in pairs(network.mass_storage) do + local mPos = logistica.load_position(h2p(hash)) + local list = get_meta(mPos):get_inventory():get_list("storage") or {} + add_list_to_itemmap(itemMap, list, useMetadata) + end + for hash, _ in pairs(network.item_storage) do + local mPos = logistica.load_position(h2p(hash)) + local list = get_meta(mPos):get_inventory():get_list("main") or {} + add_list_to_itemmap(itemMap, list, useMetadata) + end + local itemList = {} + local listSize = 0 + for item, count in pairs(itemMap) do + local stack = ItemStack(item) + stack:set_count(count) + listSize = listSize + 1 + itemList[listSize] = stack + end + local filtered = filterMethodMap[get_curr_filter_method_int(meta)](itemList) + local sorted = sortMethodMap[get_curr_sort_method_int(meta)](filtered) + return { + stackList = sorted, + stackListSize = listSize, + } +end + + +-------------------------------- +-- public functions +-------------------------------- + +function logistica.access_point_on_player_close(playerName) + fakeInvMap[playerName] = nil +end + +function logistica.update_fake_inv(pos, listName, listSize, playerName) + local meta = get_meta(pos) + local inv = meta:get_inventory() + local pageInfo = logistica.access_point_get_current_page_info(pos, playerName, listSize, meta) + if pageInfo.max == 0 then inv:set_list(listName, {}) ; return end + local startingPos = (pageInfo.curr - 1) * listSize + 1 + local fullList = fakeInvMap[playerName].stackList + if not fullList then return end + local fakeInvList = {} + for i = startingPos, startingPos + listSize do + fakeInvList[i - startingPos + 1] = fullList[i] or ItemStack("") + end + inv:set_list(listName, fakeInvList) +end + +-- returns a table representing pages: {curr = #, max = #} +function logistica.access_point_get_current_page_info(pos, playerName, listSize, optMeta) + if not fakeInvMap[playerName] then return {curr = 0, max = 0} end + local meta = optMeta or get_meta(pos) + local storedPage = meta:get_int(META_CURR_PAGE) + if storedPage <= 0 then storedPage = 1 end + local fakeInv = fakeInvMap[playerName] + local maxPages = math.ceil(fakeInv.stackListSize / listSize) + if storedPage > maxPages then + storedPage = maxPages + meta:set_int(META_CURR_PAGE, storedPage) + end + return {curr = storedPage, max = maxPages} +end + +function logistica.access_point_is_set_to_use_metadata(pos) + local meta = get_meta(pos) + return meta:get_int(META_IGNORE_METADATA) == 0 +end + +-- toggles whether to use metadata and returns the new state +function logistica.access_point_toggle_use_metadata(pos) + local meta = get_meta(pos) + local curr = meta:get_int(META_IGNORE_METADATA) + local new = (curr + 1) % 2 + meta:set_int(META_IGNORE_METADATA, new) + return new == 0 +end + +-- returns true if page was changed, false if it wasn't +function logistica.access_point_change_page(pos, op, playerName, listSize) + local meta = get_meta(pos) + local currInfo = logistica.access_point_get_current_page_info(pos, playerName, listSize, meta) + if currInfo.max == 0 then return false end + local newPage = currInfo.curr + if op == 1 then newPage = newPage + 1 + elseif op > 1 then newPage = currInfo.max + elseif op == -1 then newPage = newPage - 1 + elseif op < -1 then newPage = 1 end + newPage = logistica.clamp(newPage, 1, currInfo.max) + if currInfo.curr == newPage then return false end + meta:set_int(META_CURR_PAGE, newPage) + return true +end + +function logistica.access_point_get_filter_highlight_images(meta, highlightImg, blankImg) + local method = get_curr_filter_method_int(meta) + return { + all = (method == FILTER_ALL and highlightImg) or blankImg, + node = (method == FILTER_NODES and highlightImg) or blankImg, + craftitem = (method == FILTER_ITEMS and highlightImg) or blankImg, + tools = (method == FILTER_TOOLS and highlightImg) or blankImg, + lights = (method == FILTER_LIGHTS and highlightImg) or blankImg, + } +end + +function logistica.access_point_get_sort_highlight_images(meta, highlightImg, blankImg) + local method = get_curr_sort_method_int(meta) + return { + name = (method == SORT_NAME and highlightImg) or blankImg, + mod = (method == SORT_MOD and highlightImg) or blankImg, + count = (method == SORT_COUNT and highlightImg) or blankImg, + wear = (method == SORT_WEAR and highlightImg) or blankImg, + } +end + +function logistica.access_point_refresh_fake_inv(pos, listName, listSize, playerName) + local listInfo = build_stack_list(pos) + fakeInvMap[playerName] = listInfo + logistica.update_fake_inv(pos, listName, listSize, playerName) +end + +function logistica.access_point_set_filter_method(pos, playerName, method) + set_curr_filter_method_int(get_meta(pos), method) +end + +function logistica.access_point_set_sort_method(pos, playerName, method) + set_curr_sort_method_int(get_meta(pos), method) +end diff --git a/logic/access_point_formspec.lua b/logic/access_point_formspec.lua new file mode 100644 index 0000000..fa58e6a --- /dev/null +++ b/logic/access_point_formspec.lua @@ -0,0 +1,239 @@ +local S = logistica.TRANSLATOR + +local FORMSPEC_NAME = "accesspoint_formspec" +-- local LOCK_BTN = "on_off" +local NEXT_BTN = "next" +local PREV_BTN = "prev" +local FRST_BTN = "frst" +local LAST_BTN = "last" +local SEARCH_BTN = "search" +local CLEAR_BTN = "clear" +local FILTER_ALL_BTN = "filter_all" +local FILTER_NODES_BTN = "filter_blk" +local FILTER_CRFTITM_BTN = "filter_cft" +local FILTER_TOOLS_BTN = "filter_tol" +local FILTER_LIGHTS_BTN = "filter_lig" +local SORT_NAME_BTN = "sort_name" +local SORT_MOD_BTN = "sort_mod" +local SORT_COUNT_BTN = "sort_cnt" +local SORT_WEAR_BTN = "sort_wer" +local SEARCH_FIELD = "srch_fld" +local USE_META_BTN = "tgl_meta" + +local INV_FAKE = "fake" +local INV_CRAFT = "craft" +local INV_CRAFT_OUTPUT = "output" +local INV_INSERT = "isert" +local FAKE_INV_W = 12 +local FAKE_INV_H = 4 +local FAKE_INV_SIZE = FAKE_INV_W * FAKE_INV_H + +local IMG_HIGHLGIHT = "logistica_icon_highlight.png" +local IMG_BLANK = "logistica_blank.png" +local IMG_SORT_NAME = "logistica_icon_sort_name_az.png" +local IMG_SORT_MOD = "logistica_icon_sort_mod_az.png" +local IMG_SORT_COUNT = "logistica_icon_sort_count_99_1.png" +local IMG_SORT_WEAR = "logistica_icon_sort_wear_0_100.png" +local IMG_FILT_ALL = "logistica_icon_all.png" +local IMG_FILT_NODE = "logistica_icon_node.png" +local IMG_FILT_ITEM = "logistica_icon_craftitem.png" +local IMG_FILT_TOOL = "logistica_icon_tool.png" +local IMG_FILT_LIGHT = "logistica_icon_torch.png" + +local ACCESS_POINT_TIMER = 1 + +local accessPointForms = {} + + + +---------------------------------------------------------------- +-- formspec +---------------------------------------------------------------- + +local function get_access_point_formspec(pos, optMeta, playerName) + local posForm = "nodemeta:"..pos.x..","..pos.y..","..pos.z + local meta = optMeta or minetest.get_meta(pos) + local currentNetwork = logistica.get_network_name_or_nil(pos) or S("") + local filterHighImg = logistica.access_point_get_filter_highlight_images(meta, IMG_HIGHLGIHT, IMG_BLANK) + local sortHighImg = logistica.access_point_get_sort_highlight_images(meta, IMG_HIGHLGIHT, IMG_BLANK) + local pageInfo = logistica.access_point_get_current_page_info(pos, playerName, FAKE_INV_SIZE, meta) + local usesMetadata = logistica.access_point_is_set_to_use_metadata(pos) + local usesMetaStr = usesMetadata and S("Metadata: ON") or S("Metadata: OFF") + return "formspec_version[4]".. + "size[15.2,12.5]".. + logistica.ui.background.. + "list["..posForm..";"..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["..posForm..";"..INV_INSERT..";3.8,6.4;1,1;0]".. + "list[current_player;main;5.2,7.5;8.0,4.0;0]".. + "listring[]".. + "label[1.4,12.2;"..S("Crafting").."]".. + "list["..posForm..";"..INV_CRAFT..";0.2,8.4;3,3;0]".. + "list["..posForm..";"..INV_CRAFT_OUTPUT..";3.9,8.4;1,1;0]".. + "button[1.4,5.2;2.6,0.6;"..USE_META_BTN..";"..usesMetaStr.."]".. + "label[4.3,5.5;"..S("Filter").."]".. + "image[5.1,5;1,1;"..filterHighImg.all.."]".. + "image[6.0,5;1,1;"..filterHighImg.node.."]".. + "image[6.9,5;1,1;"..filterHighImg.craftitem.."]".. + "image[7.8,5;1,1;"..filterHighImg.tools.."]".. + "image[8.7,5;1,1;"..filterHighImg.lights.."]".. + "image_button[5.2,5.1;0.8,0.8;"..IMG_FILT_ALL..";"..FILTER_ALL_BTN..";;false;false;]".. + "image_button[6.1,5.1;0.8,0.8;"..IMG_FILT_NODE..";"..FILTER_NODES_BTN..";;false;false;]".. + "image_button[7.0,5.1;0.8,0.8;"..IMG_FILT_ITEM..";"..FILTER_CRFTITM_BTN..";;false;false;]".. + "image_button[7.9,5.1;0.8,0.8;"..IMG_FILT_TOOL..";"..FILTER_TOOLS_BTN..";;false;false;]".. + "image_button[8.8,5.1;0.8,0.8;"..IMG_FILT_LIGHT..";"..FILTER_LIGHTS_BTN..";;false;false;]".. + "label[10.5,5.5;"..S("Sort").."]".. + "image[11.0,5;1,1;"..sortHighImg.name.."]".. + "image[11.9,5;1,1;"..sortHighImg.mod.."]".. + "image[12.8,5;1,1;"..sortHighImg.count.."]".. + "image[13.7,5;1,1;"..sortHighImg.wear.."]".. + "image_button[11.1,5.1;0.8,0.8;"..IMG_SORT_NAME..";"..SORT_NAME_BTN..";;false;false;]".. + "image_button[12.0,5.1;0.8,0.8;"..IMG_SORT_MOD..";"..SORT_MOD_BTN..";;false;false;]".. + "image_button[12.9,5.1;0.8,0.8;"..IMG_SORT_COUNT..";"..SORT_COUNT_BTN..";;false;false;]".. + "image_button[13.8,5.1;0.8,0.8;"..IMG_SORT_WEAR..";"..SORT_WEAR_BTN..";;false;false;]".. + "label[5.3,6.3;"..S("Network: ")..currentNetwork.."]".. + "field[5.2,6.5;2.8,0.8;"..SEARCH_FIELD..";;]".. + "image_button[8.1,6.5;0.8,0.8;logistica_icon_search.png;"..SEARCH_BTN..";;false;false;]".. + "image_button[9.2,6.5;0.8,0.8;logistica_icon_cancel.png;"..CLEAR_BTN..";;false;false;]".. + "label[12.0,6.3;"..S("Page")..": "..pageInfo.curr.." / "..pageInfo.max.."]".. + "image_button[10.6,6.5;0.8,0.8;logistica_icon_first.png;"..FRST_BTN..";;false;false;]".. + "image_button[11.7,6.5;0.8,0.8;logistica_icon_prev.png;"..PREV_BTN..";;false;false;]".. + "image_button[12.8,6.5;0.8,0.8;logistica_icon_next.png;"..NEXT_BTN..";;false;false;]".. + "image_button[13.9,6.5;0.8,0.8;logistica_icon_last.png;"..LAST_BTN..";;false;false;]" + -- TODO tooltips +end + +local function show_access_point_formspec(pos, playerName, optMeta) + local meta = optMeta or minetest.get_meta(pos) + accessPointForms[playerName] = { position = pos } + logistica.access_point_refresh_fake_inv(pos, INV_FAKE, FAKE_INV_SIZE, playerName) + minetest.show_formspec( + playerName, + FORMSPEC_NAME, + get_access_point_formspec(pos, meta, playerName) + ) +end + +---------------------------------------------------------------- +-- callbacks +---------------------------------------------------------------- + +function logistica.on_receive_access_point_formspec(player, formname, fields) + if formname ~= FORMSPEC_NAME then return end + local playerName = player:get_player_name() + if not accessPointForms[playerName] then return true end + local pos = accessPointForms[playerName].position + if minetest.is_protected(pos, playerName) or not pos then return true end + + if fields.quit and not fields.key_enter_field then + accessPointForms[playerName] = nil + logistica.access_point_on_player_close(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 + elseif fields[PREV_BTN] then + if not logistica.access_point_change_page(pos, -1, playerName, FAKE_INV_SIZE) then return true end + elseif fields[NEXT_BTN] then + if not logistica.access_point_change_page(pos, 1, playerName, FAKE_INV_SIZE) then return true end + elseif fields[LAST_BTN] then + if not logistica.access_point_change_page(pos, 2, playerName, FAKE_INV_SIZE) then return true end + elseif fields[USE_META_BTN] then + logistica.access_point_toggle_use_metadata(pos) + elseif fields[FILTER_ALL_BTN] then + logistica.access_point_set_filter_method(pos, playerName, 1) + elseif fields[FILTER_NODES_BTN] then + logistica.access_point_set_filter_method(pos, playerName, 2) + elseif fields[FILTER_CRFTITM_BTN] then + logistica.access_point_set_filter_method(pos, playerName, 3) + elseif fields[FILTER_TOOLS_BTN] then + logistica.access_point_set_filter_method(pos, playerName, 4) + elseif fields[FILTER_LIGHTS_BTN] then + logistica.access_point_set_filter_method(pos, playerName, 5) + elseif fields[SORT_NAME_BTN] then + logistica.access_point_set_sort_method(pos, playerName, 1) + elseif fields[SORT_MOD_BTN] then + logistica.access_point_set_sort_method(pos, playerName, 2) + elseif fields[SORT_COUNT_BTN] then + logistica.access_point_set_sort_method(pos, playerName, 3) + elseif fields[SORT_WEAR_BTN] then + logistica.access_point_set_sort_method(pos, playerName, 4) + end + show_access_point_formspec(pos, playerName) + return true +end + +function logistica.access_point_after_place(pos, meta) + local inv = meta:get_inventory() + inv:set_size(INV_FAKE, FAKE_INV_SIZE) + inv:set_size(INV_CRAFT, 9) + inv:set_width(INV_CRAFT, 3) + inv:set_size(INV_CRAFT_OUTPUT, 1) + inv:set_size(INV_INSERT, 1) +end + +function logistica.access_point_allow_put(pos, listname, index, stack, player) + if listname == INV_FAKE or listname == INV_CRAFT_OUTPUT then return 0 end + return stack:get_count() +end + +function logistica.access_point_allow_take(pos, listname, index, _stack, player) + local stack = ItemStack(_stack) + if listname == INV_FAKE then + local network = logistica.get_network_or_nil(pos) + if not network then + show_access_point_formspec(pos, player:get_player_name()) + return 0 + end + -- either way, only allow taking up to stack max + stack:set_count(math.min(stack:get_count(), stack:get_stack_max())) + if stack:get_stack_max() > 1 then + local taken = nil + local acceptTaken = function(st) taken = st; return 0 end + logistica.take_stack_from_network(stack, network, acceptTaken) + if not taken or taken:is_empty() then return 0 end + return math.min(taken:get_count(), stack:get_stack_max()) + else -- individual items are trickier + -- we want to take the actual item, so place it in the slot before its taken + local useMetadata = logistica.access_point_is_set_to_use_metadata(pos) + local taken = nil + local acceptTaken = function(st) taken = st; return 0 end + logistica.take_stack_from_network(stack, network, acceptTaken, false, useMetadata) + if not taken or taken:is_empty() then return 0 end + local inv = minetest.get_meta(pos):get_inventory() + inv:set_stack(listname, index, taken) + return taken:get_count() + end + end + return stack:get_count() +end + +function logistica.access_point_allow_move(pos, from_list, from_index, to_list, to_index, count, player) + if from_list == INV_FAKE or to_list == INV_FAKE or to_list == INV_CRAFT_OUTPUT then return 0 end + return count +end + +function logistica.access_point_on_inv_move(pos, from_list, from_index, to_list, to_index, count, player) + +end + +function logistica.access_point_on_put(pos, listname, index, stack, player) + local networkId = logistica.get_network_id_or_nil(pos) + if not networkId then show_access_point_formspec(pos, player:get_player_name()) ; return end + if listname == INV_INSERT then + local leftover = logistica.insert_item_in_network(stack, networkId) + stack:set_count(leftover) + minetest.get_meta(pos):get_inventory():set_stack(listname, index, stack) + show_access_point_formspec(pos, player:get_player_name()) + end +end + +function logistica.access_point_on_take(pos, listname, index, stack, player) + if listname == INV_FAKE then + -- refresh the page in case we had to swap out a fake item or a stack is gone + logistica.access_point_refresh_fake_inv(pos, listname, FAKE_INV_SIZE, player:get_player_name()) + end +end + +function logistica.access_point_on_rightclick(pos, node, clicker, itemstack, pointed_thing) + show_access_point_formspec(pos, clicker:get_player_name()) +end \ No newline at end of file diff --git a/logic/logic.lua b/logic/logic.lua index b081cd4..da926b4 100644 --- a/logic/logic.lua +++ b/logic/logic.lua @@ -1,13 +1,15 @@ local path = logistica.MODPATH .. "/logic" -- once again, order is important -dofile(path .. "/processing_queue.lua") -dofile(path .. "/groups.lua") -dofile(path .. "/network_cache.lua") -dofile(path .. "/network_logic.lua") -dofile(path .. "/network_storage.lua") -dofile(path .. "/controller.lua") -dofile(path .. "/mass_storage.lua") -dofile(path .. "/supplier.lua") -dofile(path .. "/requester.lua") -dofile(path .. "/injector.lua") -dofile(path .. "/item_storage.lua") +dofile(path.."/processing_queue.lua") +dofile(path.."/groups.lua") +dofile(path.."/network_cache.lua") +dofile(path.."/network_logic.lua") +dofile(path.."/network_storage.lua") +dofile(path.."/controller.lua") +dofile(path.."/mass_storage.lua") +dofile(path.."/supplier.lua") +dofile(path.."/requester.lua") +dofile(path.."/injector.lua") +dofile(path.."/item_storage.lua") +dofile(path.."/access_point.lua") +dofile(path.."/access_point_formspec.lua") diff --git a/logic/network_logic.lua b/logic/network_logic.lua index 2ef8fee..fbbc659 100644 --- a/logic/network_logic.lua +++ b/logic/network_logic.lua @@ -1,5 +1,5 @@ local networks = {} -local HARD_NETWORK_NODE_LIMIT = 1000 -- A network cannot consist of more than this many nodes +local HARD_NETWORK_NODE_LIMIT = 4000 -- A network cannot consist of more than this many nodes local STATUS_OK = 0 local CREATE_NETWORK_STATUS_FAIL_OTHER_NETWORK = -1 local CREATE_NETWORK_STATUS_TOO_MANY_NODES = -2 @@ -10,12 +10,8 @@ local p2h = minetest.hash_node_position local h2p = minetest.get_position_from_hash local adjecent = { - vector.new( 1, 0, 0), - vector.new( 0, 1, 0), - vector.new( 0, 0, 1), - vector.new(-1, 0, 0), - vector.new( 0, -1, 0), - vector.new( 0, 0, -1), + vector.new( 1, 0, 0), vector.new( 0, 1, 0), vector.new( 0, 0, 1), + vector.new(-1, 0, 0), vector.new( 0, -1, 0), vector.new( 0, 0, -1), } local function has_machine(network, id) @@ -25,6 +21,7 @@ local function has_machine(network, id) or network.mass_storage[id] or network.item_storage[id] or network.injectors[id] + or network.misc[id] then return true else @@ -192,10 +189,10 @@ local function create_network(controllerPosition, oldNetworkName) network.suppliers = {} network.mass_storage = {} network.item_storage = {} + network.misc = {} network.storage_cache = {} network.supplier_cache = {} network.requester_cache = {} - network.misc = {} local startPos = {} startPos[controllerHash] = true local status = recursive_scan_for_nodes_for_controller(network, startPos) @@ -280,6 +277,15 @@ local function remove_from_network(pos, ops) ops.get_list(network)[hash] = nil end +local function on_node_change(pos, oldNode, ops) + local placed = (oldNode == nil) -- if oldNode is nil, we placed a new one + if placed == true then + try_to_add_to_network(pos, ops) + else + remove_from_network(pos, ops) + end +end + local MASS_STORAGE_OPS = { get_list = function(network) return network.mass_storage end, update_cache_node_added = function(pos) logistica.update_cache_at_pos(pos, LOG_CACHE_MASS_STORAGE) end, @@ -304,13 +310,18 @@ local INJECTOR_OPS = { update_cache_node_removed = function(_) end, } - local ITEM_STORAGE_OPS = { get_list = function(network) return network.item_storage end, update_cache_node_added = function(_) end, update_cache_node_removed = function(_) end, } +local ACCESS_POINT_OPS = { + get_list = function(network) return network.misc 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 @@ -379,46 +390,25 @@ function logistica.on_controller_change(pos, oldNode) end function logistica.on_mass_storage_change(pos, oldNode) - local placed = (oldNode == nil) -- if oldNode is nil, we placed a new one - if placed == true then - try_to_add_to_network(pos, MASS_STORAGE_OPS) - else - remove_from_network(pos, MASS_STORAGE_OPS) - end + on_node_change(pos, oldNode, MASS_STORAGE_OPS) end function logistica.on_requester_change(pos, oldNode) - local placed = (oldNode == nil) -- if oldNode is nil, we placed a new one - if placed == true then - try_to_add_to_network(pos, REQUESTER_OPS) - else - remove_from_network(pos, REQUESTER_OPS) - end + on_node_change(pos, oldNode, REQUESTER_OPS) end function logistica.on_supplier_change(pos, oldNode) - local placed = (oldNode == nil) -- if oldNode is nil, we placed a new one - if placed == true then - try_to_add_to_network(pos, SUPPLIER_OPS) - else - remove_from_network(pos, SUPPLIER_OPS) - end + on_node_change(pos, oldNode, SUPPLIER_OPS) end function logistica.on_injector_change(pos, oldNode) - local placed = (oldNode == nil) -- if oldNode is nil, we placed a new one - if placed == true then - try_to_add_to_network(pos, INJECTOR_OPS) - else - remove_from_network(pos, INJECTOR_OPS) - end + on_node_change(pos, oldNode, INJECTOR_OPS) end function logistica.on_item_storage_change(pos, oldNode) - local placed = (oldNode == nil) -- if oldNode is nil, we placed a new one - if placed == true then - try_to_add_to_network(pos, ITEM_STORAGE_OPS) - else - remove_from_network(pos, ITEM_STORAGE_OPS) - end + on_node_change(pos, oldNode, ITEM_STORAGE_OPS) +end + +function logistica.on_access_point_change(pos, oldNode) + on_node_change(pos, oldNode, ACCESS_POINT_OPS) end diff --git a/logic/network_storage.lua b/logic/network_storage.lua index 3e0c3e0..68b61c8 100644 --- a/logic/network_storage.lua +++ b/logic/network_storage.lua @@ -18,15 +18,17 @@ end -- calls the collectorFunc with the stack - collectorFunc needs to return how many were left-over
-- `collectorFunc = function(stackToInsert)`
-- note that it may be called multiple times as the itemstack is gathered from mass storage -function logistica.take_stack_from_network(stackToTake, network, collectorFunc, isAutomatedRequest) +-- `isAutomatedRequest` is optional, assumed to be false if not set +-- `useMetaData` is optional, assume false if not set - only applies to items with stack_max = 1 +function logistica.take_stack_from_network(stackToTake, network, collectorFunc, isAutomatedRequest, useMetadata) if not network then return false end -- first check suppliers - if logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc, isAutomatedRequest) then + if logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc, isAutomatedRequest, useMetadata) then return end -- then check storages if stackToTake:get_stack_max() <= 1 then - logistica.take_stack_from_item_storage(stackToTake, network, collectorFunc, isAutomatedRequest) + logistica.take_stack_from_item_storage(stackToTake, network, collectorFunc, isAutomatedRequest, useMetadata) else logistica.take_stack_from_mass_storage(stackToTake, network, collectorFunc, isAutomatedRequest) end @@ -35,7 +37,9 @@ end -- tries to take the given stack from the passive suppliers on the network -- calls the collectorFunc with the stack when necessary -- note that it may be called multiple times as the itemstack is gathered from mass storage -function logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc, isAutomatedRequest) +function logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc, isAutomatedRequest, useMetadata) + local eq = function(s1, s2) return s1:get_name() == s2:get_name() end + if stackToTake:get_stack_max() == 1 and useMetadata then eq = function(s1, s2) return s1:equals(s2) end end local requestedAmount = stackToTake:get_count() local remaining = requestedAmount local stackName = stackToTake:get_name() @@ -46,7 +50,7 @@ function logistica.take_stack_from_suppliers(stackToTake, network, collectorFunc local supplierInv = get_meta(supplierPos):get_inventory() local supplyList = supplierInv:get_list(SUPPLIER_LIST_NAME) for i, supplyStack in ipairs(supplyList) do - if supplyStack:get_name() == stackName then + if eq(supplyStack, stackToTake) then table.insert(modifiedPos, supplierPos) local supplyCount = supplyStack:get_count() if supplyCount >= remaining then -- enough to fulfil requested @@ -82,15 +86,17 @@ end -- calls the collectorFunc with the stack - collectorFunc needs to return how many were left-over
-- `collectorFunc = function(stackToInsert)`
-- returns true if item successfully found and given to collector, false otherwise -function logistica.take_stack_from_item_storage(stack, network, collectorFunc, isAutomatedRequest) - local stackName = stack:get_name() +function logistica.take_stack_from_item_storage(stack, network, collectorFunc, isAutomatedRequest, useMetadata) + local eq = function(s1, s2) return s1:get_name() == s2:get_name() end + 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 storageInv = get_meta(storagePos):get_inventory() if logistica.is_machine_on(storagePos) then local storageList = storageInv:get_list(ITEM_STORAGE_LIST_NAME) or {} for i, storedStack in ipairs(storageList) do - if storedStack:get_name() == stackName then + if (not storedStack:is_empty()) and eq(storedStack, stack) then local leftover = collectorFunc(storedStack) if leftover == 0 then -- stack max is 1, so just take the whole itemstack out storageList[i] = ItemStack("") diff --git a/textures/logistica_access_point_back.png b/textures/logistica_access_point_back.png new file mode 100644 index 0000000000000000000000000000000000000000..2207c34bf4e4e83d3d700792f0d79aec8c6b4c58 GIT binary patch literal 2324 zcmaJ@dr%YS7T;jKq0@)Wb-Idb`mfqAOS*=q!?&bHpwPg*<@pOLlQ>R z>)4mJRYajt>!ahNR&DJq*eW9SqH?vg()ya)DmbGf725)rVpNp&+XQ)3+dnqR`F`j8 z&iT%3^LBpTyvT^>A|MEg)MTj(z;_h)golC8^_4am1dY{@dIM|F=3*w=F2v2Wkq~I7X!b!V-$gdDr~6&F=+E)740J66k&=`grr5la+leH6{s@@ zje(>jqX==R3C83ioh(T^ zV2%?v(r#9PAOMF4$<3xAZl~Kt1)0qzl%NPZ;b0k5EEJ<7^$9OI9F2n+OczKT${|2q zG)kQ%Eq|fTA&;p$Z8S?WHrhE9)F>B2@MDG`-A^|0;?5^2Yqfu`ZnuxR2E%5Qfy50K zVN^_y958yHlRygyhIYG5L`E6NUVJcdTwbtz(o0YVH3^c+fNCfZvFuM^(O-Znnxb7g z(1Zjq3fgd)!!&l>N-QAFtZj&E_&k?zSpN)gkx_206<%?W;4YG3F2gXUKxRqM5pIf} z2o2Q%3r44<%^)~wkZ>5%s8snb+CowQV+!WYg*6!}nK)G@6G((151?omuq7=%HO>Mp ziAW+9h{Xc2L@yFy(w8vl?BXD4P!Wuc8E5goQRVs`mYYo&OR^LZltOc$q}Y%Y5-E~v zevHB$Rt4e}m)%L(sHJcjdVm>iLtJ)1|7DZy9$Qa{sf$mTxqPC#rC9 z@5aSj{IeP_9P25k6SarWmaUmExiRL!SZY&Ufjax}fwv>iGu!;no!R>1kq)LYu|K_6WV+2(xbCr$O<@k5HKyG|rZL9KCbxNPVd-dE``oC<#o42IZ7xpo~7n^1} zKEC!*?DINz{0w=ryrKG{y)DL}(^bCTz39sc24jskDRJtmyFH>)vw3gCmFREc_xZaP7wXRh z9^N?j@99TAZoz!d6lQHe)-JfPRlhDL_pRq^Dii6z@ATqtPPUfpJNo1Jx|gf_E{$=& znm^NUsbeSc?79{{z>Wpq&O%p=$v$oUpZG*+EO?Fm)0tJoK+w2} z+{1%5Z{>pu-O#9&`e~Jl4LPp!`B52Nzg#}I;pc?Yo8l&~nr;Z!-TZlFvM%QRmrvcB zz2U`%U%x(a!oU5ihuyt<&qY`IE7~s?H!pn(+Hq+*xp@!HuO^NhKfc`OyVc*udvLd} zp83@Bk8l{icB^9G(CvuNGG)`G9Wm*RD;UoUHPSk5RrmNNi*8R{+^x){$^7rKlNXit zUF?7F%`Nslh-6a4E@c=mN>HT~9-g^2U7@&mkY3{7*&3btzO*~ z=r5SIcUR)Usl4bTwNI7oij9%J^yt*gEQBc9=P~!?11GV`g(0`cP&wL^Mz@4bh| zqK&fuf1k+9l1MclPFPcoUccb}AW)H>6jy2dc zBxy8qkpE2Kz!W0wQ{8Eek72I&%9({PY;Vu1QbKCz+U}`Qb0iyY|L|UOeB<(am{ym3 zs^ty*%li8IhK4E4pWQx+^MCE?8vkaK<#XS%Wmg}H?$3-SBTxOw I+>%v)0AZY7egFUf literal 0 HcmV?d00001 diff --git a/textures/logistica_access_point_bottom.png b/textures/logistica_access_point_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..558205878892a43d43aaf4a02f8d62b09299ae21 GIT binary patch literal 2324 zcmaJ@dsGu=7N20%EkUZ1p!p&s_3!4ZV=~N)y4NVA4S`^5_G_6SMQW17Cr@~F>D+&zeiZXM$1BW_BkjggZUIlh!+FDxnmT8Ln>^U0x@cJu!?pQaGEepC_*x#V7c33#R}Eg zL&iW-Aa<5@VJPbLdWBx8kapWpae8_>Dw3cQi2!H_7{8OneF7(w$Weqj)C6O8lP;E| zoiN9Vn`jTKKoEe#L*y3oFt^L&rb5gXGfGf|gK)A8Di(^-(fWjs9Erxs45bSs4&@M_ zE*hiGlGZ;^=a9$LU3QwK89VJ74r+{xVfZmaknSg&_;A;gl(pJFS9dtZT!Ue=%0c3W ziZCXoM-CW+-$kH>1Vek=W+JN`WG^w4I4&=kj`R_fQB8uRGN2j?L@fIqSo{Z|il%6{ z9yB2Vj6ybC<}i%|w-F0T3u_;V)F1Gc2quf|!R zB@s!a03Zfq!Wh$|#LB9o(IJjp?jS}m3X z7-+YDlyg0?Y^?Lp^uchbr!d^il|+HK+_c4GCM==*f@_3PhPJX^+)XHLpq&(m(rP6^ z@%*rkn-dt!rbrm>qhSlLod9f4%p^Mc7LTHmqmRWMzVxWw0qnwOU?+j)h7NBxAPnzE z!U>k58*D$Fdgj*<#QQr5>i<~XA!tUGr|R4+^@1ranNrosw@o{Z`Tt&O*O|=X$tqmj zw{gjqz?`NF$9gO1WbNUz+iKXpeL1^;Pfp>^pRM{e-BF?<(p}qOz2{2gGMPS4UiRmpVlS9;0UC)-N*9sPb{{mV7|m&SQs z)y+0u>fA{@yY57PWPBiZu#5NZv9*gov|k-)JI1rsJ}Wpnu5(M<;Ewt4{%75dN#5j1 zJrxn!32hb8(RsVKZ3?Czf0|fyyeq0axMSge=b$Ub<({_vPkf>*9=yi>>dGl*AZWs5 z?%_e3xAH-SZfMj>!;C7$hCKIqT})Q@PnXYa_%Z49ri7`hW*Q^)H-B82s*ijB-eeM=g2GZt*Pa+rHOo{Qt+bXEC13y`9O2C zN?IwW)&_P=Nm?XVLK;3l;O*?~>N3xTLH;vEgVTtNPxPlXeulZ;CubJDu)QOvS_!G4YrCh%%$01s{oQ*liA~GzVOo9a zsn$2~FB%#e8XKpze0uvR&i}c)d*WNo*3bN}zkbE}d2H{~)unT%o0Bh(=l;xSvI^86 I&nsQ^8#nY{2LJ#7 literal 0 HcmV?d00001 diff --git a/textures/logistica_access_point_front.png b/textures/logistica_access_point_front.png new file mode 100644 index 0000000000000000000000000000000000000000..29fd810e7a23a4b80ba763f1ef8ab55bdf005bc9 GIT binary patch literal 2250 zcmaJ@c~BE)6kkx0qKH)#Jf^d*3_>N@Bovfw0)+%6Kokf{P+D9zn`Cv9joFQa*y?Dl z_kbve2wGHVJv$w$1rLO=TI*4urs%Y4p&q4b>oFEnFYLDoa;Wu>P4d3qd%yR-cWu_B z%$V-$Gu8)!AYWyIA`N^G=O1rR@VTMT5D7tplvKKg)2Na$f-wqlEt5kEZALS|AxIiy zGvmYpl7n+dJ!O(1caJn6Fr}3t84)Ux$}A@hRD#_?s_iq<3Ht&fN{hrq`$%mVATW{~ z4%>{hiN$O(#Knt&-~2I(z%CVTfeg{8QeZh_A>jyNgiwS;`@m9*R)?i2rga+wNro6W z&WxdGZf>qHH(bbA^r$#0Dhd@zP>DnUGz4s(iNkFI6FZToaC0a~matG}j$%wO&xz+S zR!)W>0EfHDwL}lM*=nI(%vu5^Y0^lVI2IKP#b{rB(nj@0V`9701rmqy2v8UGQ|Bn% z|ETlGE9zzg!!fLZG4}-3&qWXXiXlk%>rHI9`E|-F)tl9g#(vjex%fpOaot7e7t;#| zYU)sP|L$d|y({{iI;%~;f+2}xk& zvf(p_DUGxJ(~HTFDH61go6OfjJ$1lb z(Ww|M2rk@3*b6D;@)QfBqiBG!Y189iWxPC692FTUkO0yI0v*OB1yPF zEEb3*=^_ypJ{1#3%yE&r6v5bNaSnfjD&P08R7+qS#nGfoa?e35&ABDFNK<_CV>E8k z%MhDDOX_ed%^}e=qo-JoBJgY@P8cW?$x2avY-p;R%O$-alcN1RNkNb*6_x-P7)xFs z=W23cf9J00gJD-sVK~8;M246xjMhq!TGxHSHQXr6=(t?mLdNPrJIRn(osI&<%Y#$+ zIf21!iiGYy>aoZ*kbv#AnMC{E;zd+a^p&{Xm+rGWfSvmc>?E+<(4OrEgr5CKn!r-D zfbCaPT74FRJeEfb}EIl}8_$-N`FKIRYHrk|C7jC|{`|FDvhWNDoYCmuSjhvM9l>BD<3eR@>?T0~27yc8l zH&i~dq|~v$nw|eB`eD-L`{%MWIiNs>jMP^V0vo9|u-t&rUz>e0IIL z=G{Yk&S7~&XD4h#)@A%$kzSCLy!xHe!eGYvoXP(2M9aM0hcEdZoLSO&VSsgRN|@$C z$98g5L1U+PKv`nfEsym_*L}3haOFwMQ4f9TD8bDq_<^3X5A*N#j!ez1pvByh_;a;M!nFW5(a4#oRc^f;emB^hdr#aq> z3sw156;(NIRt-)Jy*hPIBG8LeV?hXd+1m=J|ursD(v%gF0-R?PW>9s=Udw<8+Wdo z>#sRt*wI-3&1C;SOR~&Krq0SGw!BrIGq-@I?z+3Pap%fao(1E8u&-@ce*V>vqTs|& ze{Vl8Uv_Y^-=J+-wJGg4txxvn&Ky_%M8D}ykaFWd=v_9c?aQ;ppWQgqA%Q+Fe>@P% z{xW))UDtK$uhN?x9i^t)_a$ApG@np8yeCw!%@2G&i}R0PiJO}~X>0CyaQLg*OMlam cdyDLYJ};~FI2w?Z#s39S#?MfE6*q7Be+J(>v;Y7A literal 0 HcmV?d00001 diff --git a/textures/logistica_access_point_side.png b/textures/logistica_access_point_side.png new file mode 100644 index 0000000000000000000000000000000000000000..a4d67fedf1e2bfac395f990986ca1fb6910d445d GIT binary patch literal 2314 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 literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_all.png b/textures/logistica_icon_all.png new file mode 100644 index 0000000000000000000000000000000000000000..a34bf143c46a526f7d3c57b9324f176d84d93731 GIT binary patch literal 3359 zcmaJ^dpwi-AAgpVd#));Yoc!Wt$>h3{kxLh5d(5(%?HMDTmg>~Wky|0;a;V&L z6q%Hw#1TcQP92I`01zBRJSr_5gpeU1 zgUKPHhAv(~A(?a{$`41jB=elWFs5q^AEd-=^r6Ls(`@M|2YV%g2oDpmL5PYJu~{4e zUPMGq@#5iU*)bZ0oKk_pi70=v7t)E#2az}noP{OIUI|Iy(?jvzqz#{pVTp(egCHIr zjgE?nvWQw^!R0g17+YIgv?Uge#hSw!=7MMrL=~BH1cow-X$}%7pz)bJh{@$3Wt`Ly zt`H)kP%w`CL{6v8aPx$G))X_Hh6Y(68{|L&G{yph{#qXtF~3B^5qv5aTsX80fz>T% zsYA@re^Hkq->CD#xDZzm#^ucfHOs{e{EZ=8?(dt3sJ!nflga=!QoFBlPJ zjYWUuCg8i!Ods%5(UG}yIJh-agkK;xCnqmHH1 zL!l@tA9Q5EQ4vv&p`lE;bJ0jI*^nUN!L+onnr#sk2EuILEhGBtEIvm?Kz|eWbkbif z4%lw`3|>g^W<$@cHdvThjvxo#h$PNeU3ezABF*WndlpA=E6ogeDrcUGTkm*<@dNcXMe1odR@LFSm4q8RKD>Xu z??lZtYIBzT#VuokkAXBZPDyW(t^q}8Xi8X3s7t%Ye!P%Fp(JMv7gzkAq^#8QM@&^C z+G?f8d$2Np|D0Zy#@iKpB1UyfS37B?rFWh=DcJe9{acUj*KOPUL()YnjhDxbzqY(? zgZOcAuunhr-J-&vEj}&M$-ee;CKpQD@X;z;TvJf-es{Bd5kxF^KXfLno-7@!!dKn${jJPsvakEj!A7V&j+_(9#D#u=K0;SM(>exZ(=;e)Q9WB z?=VH4Yk&X%l(l3Z1dy4%2!7{{xRD%vMA?H`Et~ZHmNYw$8JaJ%ND0}uiZ#DFxsx~QWR)(ug?n!Xm!5HIM|FCQ={}eW}stKV(DhqD)C(` zt-P{duZ@+Qvf`PqC0!qs zqIV7UB1XmnU&Znc@DlWTu{@V;jCcUlITlbK1z4kXw3cd=>_1`fmutFMoho|t8~KswXuEBHyu8hV zq~s;1#cnrJ$`j7_)z3DaUpPd ze-#inG%ofBdKO<${kR0_K5^#(;-T$2s;QcKu2kGR`Mj@T&#xI7$6X4xL_A%ufk2Gp zA1&UMR9kHjlDPlPt*qwl13l+z+J-j007O#O@?>h}q@{k6MtyBVUm(E-7yf5^ZFK^) zxjLt+PxSDe)|xkWBUO@;7d<_fTpAt@KLa&pnWZ(83YS3+>=dg8GheZDIjs?tJ-_+z7}cfFHZI_JJXAH?wUbq;Ta!$_jr*)_W9g=ZEq& z?4h;4m4y4I9*=j9*8ktNL;k?&#QK2&J)ChVkoqQ`P{vI?V|TG)KV027&l6iW#H4KB zB`#(twBAwD(8Ec}({40P?ztzq(`|RPvT5V5n)Su=2@>ZYPTCgJ2Q!*|xt8cYFNV#v+m?sz+Kcu+1^zCR@ zdGDKDaR;qQ6a~p!xgGQ0CyJr!q369)px86WyI}~Ca>0JZz8<+kEa+e_4e8skVqvgK z@EzqyK~(F5(d@~{R%PY0z&(I?O-m`G(5$~9Vq%ce9D5dM2k`Tnv@P8NGHd4#O6-(| z0e|-Mm#5F{At_$e{u6XrmeB7AChwT3RN5OP;~e~E$1U*Bw%8aquQq< P`(NYcypdGCJ~;0GF(_Gn literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_cancel.png b/textures/logistica_icon_cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..71186cd962fbbcc35d4eec5c8f9f4a60d18d4b1b GIT binary patch literal 2382 zcmaJ@4Nw!;7TyGq7|N8Qh}NHpiv?7YC6UCEm++TR2oOynYM?4?k|kM5c4Kw}A=6ew z{KX2CLJ$;iMmrT-$6B>2S}c_+Eh?|@>a&W^Rtqz2-xMAXvDISl2INO=cV_SI+3%e1 zoO{kaC)uf4k>t;PnF~RXzbaXo4xVw$&6x()CVb;<2=aLi*Xn4UI!$UItpe0Y>M?=S zY6Ca~$zq*0)R2eKJUwQ@2|4_0a|_JFjdFO6SRJXhDKImh?6PASt`%B?E6)&Zgkxj4 zGN%*}STP#qIjt6gk~-zEhgS;Tna>E!^Qh2ya#*L<@D!vSETxq)U zwJ~EL$zd~1+oT9mP*5N!SSBFtCPWw=9gRea5Rr%vH272@L8DGSL4`0BqZ~?%GT3n& zjgthA;Y9VMgO8}%Nt&e0q-{K?DK5t0XAD8QpKszsZO>CytN&izYMrtTicZW2 zi5n}zl$f44ptOZH3`xf*(qT7XiTNOVA)drBd68;xCuY$pagbCBR6`C+qMiaX{{mEy z7Sf&p4k0Fog&rFwb5fNRHDPOTBW)h%n)sfE5vHdBCNhhIISQ)?9PC9j!KItP<3W+dUBH@b!qaHxfq`(%> zEmWd3&=N(8mhpu`zEGr%jFO6k(x~NG9#W4YXd5F+qkp5yoO_{qbfs&#IrXF(GW+#mf17`GW7VKdZrN~^m0JUTBCJ>bz zj?c}-LFEd08m38jpfMu_lBpI2W(=@BH;u^TT|9|OhCCDZ=%yzP4q!L>4GbhO*^u$k z288k9h!J2S+QI1iv~3+9f>@hzQ2b|#4ng4+j*4qbm9Njd6uV4uafkkZF0FNgS)(@y zXDCqN;CtD1dzZI#pBr$Iq3Tm#+M-YMn*+V|KVfT%l)|j`ObCXA|eS(eT02-poD$L z^3wf9n>PMG_{d_#jB0n+M~A2lzs1~7y*1FWR;PD6!$KDpKNyH?dzH0qPL8%0{eAYq z?A6-#;Xm$OJHF&hQ-`$hh1JRL!li4v_i4*g({?OySA>$okIC#S7dvwfpS}6w$(7YZ z-+DRT&_w9I?K^-4m0cL(1n*55`H8jrT`?U9^;DGGu5`KC4X`dyw zAC}xI^7^$g=xoFOhMp?ozAAazp1Q(@{+?fcfuh9V8aNrdKYK%8+&B0B7x9AA+jg_z z>Rdc$>DGC~!mv~I-mEexrnQTNdJe@tN-oX|>d0nUzDOnABmM2szJ3?B6s8ApLV8sZ zoOUR*qjFb5jc{ekwsP;B&t-=>CEkMv7ja6Vz$=x<6LM<8_z5*W7ot{v#g5OZkMctU zi#lcPGmBegcwlRIkw$lGCW{rqnby15eLZ#4gSXx^oCx6h=6}B>-O5@kZhQB@Z$yXe z*qi>x(u#)E;X(iSQJ|TZ&|Y)c5%+OPidUbu_{NMQ1J=7oeU87P``Hze>0cX`HK_l3 z8uahbc_T;l3j)|s2T#*l`{k~tJAq!{-F)zOOKU{JS|>mB*nSBR&zQ zu1Yld)xF(<;_5K!t-16%&Kbehb;!20$oA{s1XS#~tUGxz|NGaizD?r9Xy0a{wE6bz zjpxO^E~8$UB6tXt8AZKQFOE_RGdZe!esh>?uTAZw19gQ+oR%nSzoGHh){Tal

_kEt{`#jJ0xy)y_`T4HYRGY5` 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 L?n^!C%1r-14E=#g literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_first.png b/textures/logistica_icon_first.png new file mode 100644 index 0000000000000000000000000000000000000000..67b671ab9cd80be3f6d33694e5b8fb49e4202307 GIT binary patch literal 2479 zcmaJ@dsGu=7Ej1ZT^=fmyxg*Jpg{}CBmn}%gja$RAvBOELG40FhGd$@gv^8lXpzO1 z@+h^G0=2LzuI{mdg5_}pL0F5rwu)G+tJGyV*dtqxR-y&0P~1sC9@X}bNpip6y}$eV z?wu5i61F)zcsURV1ZP1kKMDViv_32C@y`=*<`99fVk<0>Vp3r|TaIXHG6j+j(ac&s zjuQx+2(w-$S3?*%9a6$NF7WVdH$a9JTp&4ENEhmPkP42?F+k#+1c^LHEe}%w5#bIT zGaDz+LYRzf)@pPp+sp-Qylnip^%w-mHWf_G1*Ad|nTHr4axg8JMhC(j$Q*+rgPp|R zI&X|iTtJ0kdNv4}OeUHskcJqPAR{a+45TwbCX~*XBgO>)94F6{E948@dZR&OV^+vPNCRmh9fpDo8UtLa51HY`Xmsd&x$we) zRs>h4FH^_hjNeeVA}^@xRS1TlDn!2!)G`+f@C$}`xnFK#mg!%nEEN8}x>ma^4HS#X z!V5Rwgk>>3cYsQ=^$?f@p@`8Shhnnu>iO6TXRQlc1e+m^ln>)YMe%NM0annjz?9zr zd58uvi1AAZaRG+S##%YHKr2&1$*=-bEpRQK$3r^huK{ah8l&|p?9jn@F3d$P=_1Aj zLYUwZHz_ZL7W%-pMJGfQcyNI>!bM2H}~YE#6=Mj^vwzoTls_hgPj&c`${~d86QkKjkVmuq=Im^mzEJ+n#J>|IN%F$&n3NNao~1yXZS+G zHygOH+HhfEIYK&oBO374S8@Dr(F6kVuQ1;H7n)8WY$`XFU-RcDx?YM1Q-1GwL@+?vAF!x@z0z7{gI=?e+P3~dO?<7+zixQ z_paTr#%0oXZ{|<#^_zL?s;UQ09z);yIeaqij^)Z8X?nHUkFv4giG_Y4lvwDQCb=hj z>Txu6r{wa~FW+DL;`R0uSJ>H3J7YfvN|SHYO7_Ra7kO8gQ;?}=Nb1$z{a?e(1#(1bPp%9 zhNg0x9}e}^?2A=2z)s%2SKszN!yE~8q{KBobre)_-@BVQV#-ON-}le8^l6fE9z8gF zQBC)9RrcI+aqeFgS)>jyD9Hl^f`rW}jU5`Ixsl=1TsNC%4t^M+;B!d-Iu=iuWnLh2F&`(pZ2uXM@OO z8NQysrgxtoDcWVVx6?`iCzM4FvJ5x5wz(V%dMr%N{6uv7lAcML-7)in*l&+hM9Ej- zg%eIqKi?bpdQ|&ouPnX}T=O*){ue?@VzrAV2 zrxRu2=mRKkc5F*W!`@ntv@d=N(XW&K@<>}Y9e2%=GT7dGyBQ}79%lFFPWPkQ>Z@w)gbR^hoHJVF2HjjzQZ>Vi7on8y0^8zm(}({{#2*JHcnttVY$ zoQ|VOuIdkO25&BDyHqMTy{ZWL$RobQ7^$w1ly*<=yOGF9BX160;SKjTl?~EPpFC<# zRL^KH<{e54irhlb9w81ARpO`q;}Nr4G#(c`MeTWQr6^Dy>oG%I@AK&3bm#pqt^czG MF$w%r(P;($2SQP$2mk;8 literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_highlight.png b/textures/logistica_icon_highlight.png new file mode 100644 index 0000000000000000000000000000000000000000..a5b01ca95025e4a9c820b797a3a893c0e789b1bb GIT binary patch literal 2674 zcmbVOd0Z1`8lG^AQpJO#Dvm=-K}jaZf@Dm%f?N`$1SM(}LNbJbBoi_dNbumOU9I}_ zSgVLw3oEF2RYj?y0`~4MPNc9L=yePY_V2^rxB4kI$V+yn;_50knIU}`8&v&4W#7*5G`gq9{q4Q$qw zrI31Bh#&xm2gMcgA#JT*r?Mz3PD+?5EunEIr zdgFjfFluo$9;ZmXPL9vY0@d@h6wX{1ERHbXDrp!2ib{cQ2oe9k{0^M|2cU>lkva)D zgt!plSZvId!=ltOC7wtq=(HiNxBD@;M)`Zd9GOaQJ_>U+1jvQ^R!jO86CyLX=sRxa ze-Ik#17?X%Oe#QdJd5C4C`u%X(~+r!3Sd-xL?|3JOXSbt`};GwtN{<8X&A62QjK9U z8nC!*E|1CKFge@=4j1G3V?6Exi>QSNu8l%Q%l?FFKKHOdA;)NfR^gV_z&!|2(F3c2 zrHU{geoQ6PD20fDslZcZdKHZX5*l;YNS!f>kdqopfST`&iWn5KtllUI&|z)_AVRSi zizFzs`8%E^c;;}=ma7NDmIH;!9!J$7QPOTVpZ^Rf7_=&Jo+J6)!B6n*^T_G>4)sM?+# zmrr*UNTT*_n}5fexydgyq*l3Hc1h9`<7qnD8nQ6V&s6keMd;iY$Ir^n7YCl4XQEy& zko#)(cJ6WYkmx<93w#8px3sHox@aVlja#2o9O~FG((&hi=Nzp^{Xd)Ck5`tiu^|jH*=4>%t zvDPn)^OaudD#a(PuQNHg7Dc~$z*u`~&Eh?2H(s=!VkiqIFsrS*id$cmM67LGe|K!Q z*VrdHR^pMZIZjTq%Xe(<;n&#Xi)tP?X7!XLw#-0tt)tH=uX5@#T){hRgf?%Yr|*2O_Nd939zi~E^u+JirGg}X;Vd0DSlO>eWY!x;Ke z>CW_}>z8oa+>SGrZ?bmlROZ>im5@paB}4>yPI*4{(8?)?L?t_CIxzY_Mf<{yo!H)m zc88hy+P(|-FSb!+{^Va^8}=utbXTn%JjUgbwabF4SLMAvc(D7Ea7=0w4Bz*^Zl7Yi z!Pb4ms}9^YZ%O~E(chRxxjA=vzzx}Ry{=Be=Z(_aPt9|__q{N`^xBx<7+!Mp;{A(% zRX3T|()Lp!pV*~yCPX(wclWuQirPvF$x;lu?K~Imt8`=JO<8tzjGlQr6wuO-)LZ?% zJ3}jhx9wF|`&MspF5M}kB_H^IHXU;(YYxg@PJguP zSnSQ8N`f3Ak&vHvW$c=ifT>4(m22!FY5V66B~g=Bmdy)ElLQ$?GLE`Rr?6N5vUWqD z&w|34E8@IuN|Krq_C0oRc)69KXsUL}1nKwPW3^Lfj_gXYgU{dNwWq@%GxvM@ycYF0 zUbsK!NtaG8@I7Ep7N!e}x?vl5cSZKou7b0Y4$qFLwLJI~tFp^CQE3wml$Q7glC^4A zud*G>n%Iu*jMj#W-kWZmo6fTe7I}}i`d8-Y2Y%>e&7JZJmhsHWmIf2QZA~fGEnC!I zl~laRy>&#A3)B((Si>zm`Q_x!Zy(0^*Q-wl_*^LA>}Vb?K~ literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_last.png b/textures/logistica_icon_last.png new file mode 100644 index 0000000000000000000000000000000000000000..d369a9ba86885e3f9562ee203f18f74faee5a6d2 GIT binary patch literal 2450 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 literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_node.png b/textures/logistica_icon_node.png new file mode 100644 index 0000000000000000000000000000000000000000..cc2e7506ecfff40961dbf2f78d3d57840244c02e GIT binary patch literal 3445 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 Tn_Q{l-xAfu)A@qq)}8+Y0-SGy literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_prev.png b/textures/logistica_icon_prev.png new file mode 100644 index 0000000000000000000000000000000000000000..7f9bd8f109ab3978d15c21d4b6c513c25aa12432 GIT binary patch literal 2381 zcmaJ@3se(l7M?)yflWmo#j;&Hj2@&yGD(oIWD^vUD6dE)2mxEokW9jqFcUKadDvQ| zo~}}@q7@6G1(ns(x@T#%zM`O_Rx1z1#Z|k&T9mHq9zzweR&@Uf$fMexGn2`k@80je z{(F

IL(;z9GI41aXrR73ttPm%S&91E00TvJMFHnMY)38LcW+qNfV@n1Ra0`St<} zz#&LF$8N#&`8WgT;wFNWA$O0~BQRl*Az9HXfyyGs^N2*J71uZyWaypw`d9-pC(c)D zmjHqSoWWpwftjQwb{XQ~m4NT;YZQS!Donl%(W=z2oU-C@G(VazK;nF1snuYVq$}nP z83Rd%!om;~ z3A3D7E>+0L5Cq`x5V=7=%xx*Onmx=0J&K$00-R)MRLB>iqxEq+F%k_)50wiP4rLLb zE*PWE5XRq7XOZXBEqN3}(Rq|*IH)l$hT-Q7LAhUSV#h2mQdX&cU%jAUOd2$!ECPia zYQmV9o;jd192Oi+$7!n2s>hW@pnB6ig=6a?Q4@CDtW^-8s5Iz?3=z-%6`1`Spqw&O zRt-3WxC{|`Y}m?4k_s>ro<$g#ykV}9_o+B(`Zd5tW-erp!XlCYxrj!%v?G`di4mcr z++@EH8t#L{6P=1OfZ(D$gdcuoTQYCg|T8WPsAVe0GcKNwuI54 zz!;z<5{ROBLLpBm$`H(!h+-u|L5_#iqX@>vfHBzbsIuoCmKyXDhG5LNM;e@icr!C7 z4T@%hJ^T_gMw(=ZooB#}SfQCg;s}!M8fA4XCG-?YOHp=a%)}6pM|#F2MaQ_2f(TVA zNg_eB=A%4o@MU8?dqxk2JqJpH>DhkB5Q~*E6zXw;n)q9XeTZ{FtUg!p>X3hbh$KXrQF{?_=#is-&__v(a2zZ^P+o!u69G_#L>nxl^- zYrFOaPtz1mkC478Jy!OQf{s^7ji&Td_ve4VvT>4c=QZd4MpQgA=fs+k2|)=bC8ttLCg@7cQ}>DYuDjl{-}p&=t4Y0s_$+n((eAQx zX;fs}%i)(|I+NPdy0|`pldp0QaRl$$HvakGBcHa3X_?-}RcB7@2@v<+-t_ZJGdFX> zRJR+Vx_0I)Z~1o*`c<~ijC)k;?sKy?dbzIcU3TqZXjw+0Ijb0NsN8-~TT9-%mz9F- zsnWityVM;c82|c;f%fjc_E&;M4xdv_RCj6j#0${+fn9pBZO)^XS#a}vt1ji@A=+`y zTYJtp?tgZZ#0UQPhF9Q%&0+1%%7drGO5tj|uIXmAYkjH6{qT-!QBE!SwYtJkb+=P~ z#1%?dw@(PF4!;!_uP{02sU$o^%9@(-p%R4u+q+{Ezx*%og1G{PA z9b+Ruaewe>a^8iN5$7Kte&h0iH6g8;ft_9vo9Y6U3q4oe4gY_h?5YqHx9?*A1tlpL KDE^k9EB-%)0(zSO literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_search.png b/textures/logistica_icon_search.png new file mode 100644 index 0000000000000000000000000000000000000000..a716fb894ce37dc5a538f2eeeec04bcb10316d08 GIT binary patch literal 3710 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 literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_sort_count_99_1.png b/textures/logistica_icon_sort_count_99_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2f6d58544ddf4d36db8644d49fdf0141ef1eb2ac GIT binary patch literal 3369 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 b>y*zwoKilpwd%;if3a@PUewc$VafjmZ+u3P literal 0 HcmV?d00001 diff --git a/textures/logistica_icon_torch.png b/textures/logistica_icon_torch.png new file mode 100644 index 0000000000000000000000000000000000000000..74e3f0217fa5427f72a6497e1d29262708affb0a GIT binary patch literal 2974 zcmb7G30P9w7T##in(tX=WdmVaW_kc;0@I|z0X65;QiLNM1r9POlBIUNrI~hZ&`c}4 zr4^Q0*=%Y(o4aYZ>6KPyUah>AmP2#zfMt2>-tT+g#{u^K|Mjo6*V=1Ac7WePBmIf` z001K|20ak_bXPsP+St1iNqhi+)&e9rTpG^wrLa+f9gBm;!gg|j2*UxO&XtQ;>;zbf zkA=C2kVbfMs*Zq1I5a|-Ba_G!dBQw|p%BAC3cp~sB7yD7AxuC2zWI`nm{9jGXwCRs2IjO+Bw=032yp$s+bc;38XLhY>cgF1fEnX zqCik`aHH)tai5DY zBBn16B*7^n7z%_Xs7%a;=Ou4nsIn-muo!eP39}{VSk~j4xANg%BZvorU~WEBq@)BRG?x zZ`>^YAv9bEiaI(b%E5xOR||fHygWSv#AqDC$1q9Y!g+YF`JPTBS0^W1vfYpetY{R> z7Kuxtv!ob{OeEXel1R2BaxjTZA-hnBE)i-`H4$qY4ok}V2de7cV5vr<&ic_KyHY$`*Ayv=tkej>K)fbAwVyo&wBZ$N(N5+OZ>ce7Z7(yjzoHUsw zhCR4gR5XG|TpWT`E(IT;Y7#uwm_$3Lkrv537*qRU8li7b@g*uM^j+LThyG@8V0J@$ z*g(Q28#FxHu*L9jgoW5d6l0@r*B?_C0f5Uuu;PDLbO2`N%kmp-=!?c)m}~FZkQKWl z-1lreFCdmp8so_#z1kRET;yDLv!+*pS}~7aPFiDPUN`Q&7Jt)`K)UzQec47gB-@H6 zUMgum(J84L+;-!1=gJ_jiY<#DYuavboaa%;UBS8@H0W#5U)|&pNq6io>dBlJ^6vP5 zSQm=jPKEYMK1Hx+3ze;V%%%j%ENrOO)YApG1P{gugM#um_w22@^NWH0!+VPA2FPiK z&wKdrj!cbK{=WyNrzQ58RakqDDJZEeZQ_*k@v@ExV1GoOZV{ZziX<&aSKgtPil4+H+?K! zxc1_@c9Uc)lOBZz)1XOVWaLw}ZPTD@tq#1T_L+XtU}@MjXDCh6`#kq9sXoCB`wi0* zF&0YzFc_nHa9~TxcmPHvdeJ?C=Op;w$6g zqgKml+11gPB9_@7ez$yk%A8kmWx8BnWjxSq&%l)$uGJ_^&vDm%uSL};pmSb13>x_V zF_5OTyhwE2((a#IpX07&*5@EvBIqv9O)BSC7n|uFJNt1jls>sN6&G;pU(Sbqa<_Kv zKs2n|7APVdYj<3K&NP29kdWFW&XteSiF;nXLZOYH&`-5%^=gfHS+q-U+5x3y?%-~R z2ex;4##NV0V;h7!%U5o8TkqCUH|V-op@G+{P50@lsBd)EMjz7`q0=jSP*6i1J*OOaR(%vxyrdgzg zb<=v=E>+&V2sT9O9Ceui=1oekh_7%3uNr6ndTML5$4g$%toT(e{!f>!jr@2`EB*YW z0Aa}!2l=LH4yi^)l^~-l;cXW~J~hm6YmO_}qUEP!ax+SrQGBrDZfTa@>O#x$H8zAg z&{bjqCSU1z=k?y)v(6-|4Q6plRo%YumN>$28HI-G&z*7t|rSNtL+L z#^$77Mghgx-&>}al~>~o!3O2Ps$3_-^z11|UR(z;U9Dg&<2v^q(9W=#*xeEusdIl} z8@GFz!&ICIm_y9KMG!gW=7LBVjQMcp;GBB;udBhHZ70t^WtBL*A%>9i_h=czH*j%xc0!4vpMS?Z*)qhweHO<+kURyV+~LA=D+7B zMrm6Utkz|gO-;NN-L>psrA@bYmHzCU&C9Izwd8eGm=>SXIuR+DQ&e@xI`;XaHKp+p zj$qezoGi-Jc?RpLpYNlxjE1rMGRx*1&-D*WO6}elubq_@a%)x(z2R!AB<yP0cUIwq7S|cZbhuWz&nyt<0=>J7d vd9ymZvF`1)3xeo9OS$#Fh4(nm3O;HrSuU*--(SO1{jK(z?? 30927 or pos.y > 30927 or pos.z > 30927 then return end - if minetest.get_node_or_nil(pos) then return end + if minetest.get_node_or_nil(pos) then return pos end local vm = minetest.get_voxel_manip() vm:read_from_map(pos, pos) + return pos end function logistica.swap_node(pos, newName) diff --git a/util/inv_list_filtering.lua b/util/inv_list_filtering.lua new file mode 100644 index 0000000..da63f2d --- /dev/null +++ b/util/inv_list_filtering.lua @@ -0,0 +1,63 @@ + +local lights_map = {} + +-- all these get initialized after loadtime + +LOG_FILTER_NODE = function(_) return true end +LOG_FILTER_CRAFTITEM = function(_) return true end +LOG_FILTER_TOOL = function(_) return true end +LOG_FILTER_LIGHT = function(_) return true end + +local function load_lights() + for name, def in pairs(minetest.registered_items) do + if def.light_source and def.light_source > 0 then + lights_map[name] = true + end + end +end + +local function create_filter(lookupTable) + return function(name) + return lookupTable[name] ~= nil + end +end + +local function load_filters() + LOG_FILTER_NODE = create_filter(minetest.registered_nodes) + LOG_FILTER_CRAFTITEM = create_filter(minetest.registered_craftitems) + LOG_FILTER_TOOL = create_filter(minetest.registered_tools) + LOG_FILTER_LIGHT = create_filter(lights_map) +end + +local function do_filter(stackList, filterMethod) + local res = {} + local idx = 1 + for _, stack in ipairs(stackList) do + if filterMethod(stack:get_name()) then + res[idx] = stack + idx = idx + 1 + end + end + return res +end + +-------------------------------- +-- public funcs +-------------------------------- + +-- returns a new list that cotains only matching items +-- or return just the same list immediately for convenience if `filterMethod` is nil +-- filterMethod should be one of LOG_FILTER_NODE, LOG_FILTER_CRAFTITEM, LOG_FILTER_TOOL, LOG_FILTER_LIGHT +function logistica.filter_list_by(stackList, filterMethod) + if not filterMethod then return stackList end + return do_filter(stackList, filterMethod) +end + +-------------------------------- +-- registration +-------------------------------- + +minetest.register_on_mods_loaded(function() + load_lights() + load_filters() +end) diff --git a/util/inv_list_sorting.lua b/util/inv_list_sorting.lua index 2abf826..bc63f79 100644 --- a/util/inv_list_sorting.lua +++ b/util/inv_list_sorting.lua @@ -5,7 +5,7 @@ local LANG_EN = "en" -------------------------------- local function get_description(st) return minetest.get_translated_string(LANG_EN, st:get_short_description()) end -local function get_name(st) d.log(st:get_name()) ; return st:get_name() end +local function get_name(st) return st:get_name() end local function get_stack_size(st) return st:get_count() end local function get_wear(st) return st:get_wear() end diff --git a/util/util.lua b/util/util.lua index 7649328..7388c62 100644 --- a/util/util.lua +++ b/util/util.lua @@ -7,6 +7,7 @@ dofile(path.."/ui_logic.lua") dofile(path.."/ui.lua") dofile(path.."/sound.lua") dofile(path.."/inv_list_sorting.lua") +dofile(path.."/inv_list_filtering.lua") -- bad debug d = {}