Add Access Point with working sort, filter, take, insert

This commit is contained in:
Zenon Seth 2023-11-12 13:30:16 +00:00
parent 3b0ecb62e5
commit 92c27da19a
42 changed files with 710 additions and 73 deletions

View File

@ -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.

118
api/access_point.lua Normal file
View File

@ -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",
})

View File

@ -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")

View File

@ -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 "<ERROR>"
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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
local path = logistica.MODPATH .. "/entity"
dofile(path .. "/io_entity.lua")
dofile(path .. "/display_item.lua")
dofile(path.."/io_entity.lua")
dofile(path.."/display_item.lua")

View File

@ -1,4 +1,4 @@
local path = logistica.MODPATH .. "/item"
logistica.craftitem = {}
dofile(path .. "/storage_upgrade.lua")
dofile(path.."/storage_upgrade.lua")

208
logic/access_point.lua Normal file
View File

@ -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

View File

@ -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("<NONE>")
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

View File

@ -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")

View File

@ -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

View File

@ -18,15 +18,17 @@ end
-- calls the collectorFunc with the stack - collectorFunc needs to return how many were left-over<br>
-- `collectorFunc = function(stackToInsert)`<br>
-- 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<br>
-- `collectorFunc = function(stackToInsert)`<br>
-- 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("")

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -20,12 +20,14 @@ end
-- global namespaced functions
----------------------------------------------------------------
-- Loads and returns the given position, or nil if its outisde the current bounds
function logistica.load_position(pos)
if pos.x < -30912 or pos.y < -30912 or pos.z < -30912 or
pos.x > 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)

View File

@ -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)

View File

@ -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

View File

@ -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 = {}