Demander improvements; storage funcs; new sounds; added ROADMAP

This commit is contained in:
Zenon Seth 2023-11-06 17:54:05 +00:00
parent cab538cfbf
commit 61004f5bb0
26 changed files with 222 additions and 109 deletions

View File

@ -5,30 +5,38 @@ Logistica is a item transport, distribution and storage mod.
The core principle behind this mod is that item transportation is demand-driven: no item is moved unless it is requested somewhere.
# Nodes
# Machines
All nodes require to be connected to an active network to perform their tasks. Nodes without a network, or nodes with conflicting network connections will not do anything.
All machines require to be connected to an active network to perform their tasks. Nodes without a network, or nodes with conflicting network connections will not do anything.
## Controller
A Controller is a node required to create an active network. Exactly one controller can be conneted to a network, and removing it will disconnect all other devices connected to the network.
A Controller is the node required to create an active network. Exactly one controller can be conneted to a network, and removing it will disconnect all other devices connected to the network. Controllers allow you to rename the network.
## Cables
Cables are simple nodes that connect a controller to other nodes, allowing the establishing of a network. Placing a cable that bridges two different active networks will burn that cable. Burned cables can simply be picked up, and return normal cables. TODO Cables come in TODO variants, all being identical in function, but different variants do not connect to each other, allowing parallel networks.
Cables are used to connect a controller to other machines, allowing the establishing of a network. Placing a cable that bridges two different active networks will burn that cable. Burned cables can simply be picked up, and return normal cables. TODO Cables come in TODO variants, all being identical in function, but different variants do not connect to each other, allowing parallel networks.
## Demander
A Demander is a node that targets any other node with an inventory, and can be configured to request items from its network to keep the target's inventory full of a specific number of specific items.
A Demander is a machine that inserts items into almost any other node that has an inventory. The Demander takes items from the network and tries to keep its target's inventory full of the specific items and the exact count it's configured with. For example, a demander might be configured to target a Furnace's "fuel" inventory and always try to keep 2 coal in it.
There's two variation of the Demander:
- Basic Demander: Moves 1 item at a time to fulfil the requested items.
- Stack Demander: Moves up to 1 stack at a time to fulfil the requested items.
Demanders check network storage first to fulfil their demand, and if not, they will check any suppliers.
## Supplier
A Supplier is a node that takes items from another node's inventory, configuratble to only inject certain items, and tries to fulfil any supply for that item in its network. Suppliers have a tick rate and each tick they pick one inventory slot of the target's invenotry and check if there's demand anywhere on the network for that type of item. On the following tick, the supplier will move onto the next slot of the target's inventory and check that.
A Supplier acts as a source for specific items that must be configured. It takes items from other nodes' inventories (e.g. chests, furnaces) and makes them available to any demand in the network. Suppliers won't actively push items into the network, but Demanders and Storage nodes can both take items from Suppliers when needed.
There are two variants:
## Storage Injectors
Storage Injectors take items from other nodes' inventories and make them available only to Mass Storage on the network. Just as with Suppliers, they won't push items into the network, but the difference is that Storage Injectors don't need to be configured for any items and instead will act as a source of items for any Mass Storage boxes that is pulling in items. Storage Injectors do not interact with Demanders
### Item Supplier
This supplier takes only 1 item from each inventory slot it checks and fulfils one single demand with it.
## Mass Storage
Mass Storage box provide mass-storage for items, and are the first place a Demander will look for to fulfil any demand. Mass Storage needs to be configured with the exact items to store, and can also be upgraded to store more items. Mass Storage can also be set to actively pull items in from Suppliers and Storage Injectors.
### Stack Supplier
This supplier will take the whole stack, or as many items as are needed out of a stack, and try to fulfil any demand on its network with those items.
## Item Storage
The Item Storage box provides a large number of storage slots, spread over 2 pages -- but it can only store tools (specifically, items that have a max stack size of 1). Item storage is also accessed by Demanders to provide items.
## Storage
# Tools
Storage nodes provide mass-storage for items. They also act as suppliers for any demand. Storage nodes are more efficient at fulfilling demand over suppliers because they do not need to check one inventory slot at a time, but instead consider their entire inventory.
## Network Info Tool
Whe used on a Logistica node it will show which network, if any, the node is part of.

View File

@ -11,6 +11,7 @@ High Priority:
- Crafting Supplier
- Ability to name storages
- Machine Upgrades
- Mesecons compat
Medium Priority
- Wirelss Network Extender
@ -19,6 +20,9 @@ Medium Priority
- Rework all UI icons to be.. 48x48? 64x64? something else?
- Direct pipeworks compatibility
- Direct tubelib compatibility
- 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"
Low Priority:
- Improve node sounds

View File

@ -32,7 +32,7 @@ function logistica.register_cable(tier, size)
oddly_breakable_by_hand = 2,
[cable_group] = 1,
},
sounds = default.node_sound_metal_defaults(),
sounds = logistica.node_sound_metallic(),
drop = cable_name,
paramtype = "light",
sunlight_propagates = true,

View File

@ -4,7 +4,7 @@ local FORMSPEC_NAME = "logconren"
local controllerForms = {}
local function get_controller_formspec(pos)
local name = logistica.get_network_name_or_nil(pos) or "<NO NETWORK>"
local name = logistica.get_network_name_or_nil(pos) or "<ERROR>"
return "formspec_version[6]" ..
"size[10.5,2]" ..
logistica.ui.background..
@ -22,7 +22,6 @@ end
local function on_controller_receive_fields(player, formname, fields)
if formname ~= FORMSPEC_NAME then return end
local playerName = player:get_player_name()
minetest.chat_send_all("rec: "..logistica.ttos(fields))
if fields.quit and not fields.key_enter_field then
controllerForms[playerName] = nil
elseif (fields[SET_BUTTON] or fields.key_enter_field) and fields[NAME_FIELD] then
@ -114,7 +113,7 @@ logistica.register_controller("Simple Controller", {
groups = {
oddly_breakable_by_hand = 1, cracky = 2,
},
sounds = default.node_sound_metal_defaults(),
sounds = logistica.node_sound_metallic(),
paramtype = "light",
sunlight_propagates = false,
drawtype = "normal",

View File

@ -17,8 +17,8 @@ local function get_demander_formspec(pos)
return "formspec_version[4]" ..
"size[10.6,7]" ..
logistica.ui.background..
logistica.ui.push_list_picker(PUSH_LIST_PICKER, 7.5, 0.7, pushPos, selectedList)..
logistica.ui.on_off_btn(isOn, 9.5, 0.5, ON_OFF_BUTTON)..
logistica.ui.push_list_picker(PUSH_LIST_PICKER, 6.5, 0.7, pushPos, selectedList, "Put items in:")..
logistica.ui.on_off_btn(isOn, 9.3, 0.5, ON_OFF_BUTTON)..
"list["..posForm..";filter;0.5,0.5;"..NUM_DEMAND_SLOTS..",1;0]"..
"list[current_player;main;0.5,2;8,4;0]"
end
@ -41,7 +41,7 @@ local function on_player_receive_fields(player, formname, fields)
local pos = demanderForms[playerName].position
if not pos then return false end
if logistica.toggle_machine_on_off(pos) then
logistica.start_demander_timer(pos, 1)
logistica.start_demander_timer(pos)
end
show_demander_formspec(player:get_player_name(), pos)
elseif fields[PUSH_LIST_PICKER] then
@ -55,7 +55,6 @@ local function on_player_receive_fields(player, formname, fields)
return true
end
local function on_demander_punch(pos, node, puncher, pointed_thing)
local targetPos = logistica.get_demander_target(pos)
if targetPos and puncher:is_player() and puncher:get_player_control().sneak then
@ -86,6 +85,7 @@ local function allow_demander_storage_inv_put(pos, listname, index, stack, playe
local slotStack = inv:get_stack(listname, index)
slotStack:add_item(stack)
inv:set_stack(listname, index, slotStack)
logistica.start_demander_timer(pos, 1)
return 0
end
@ -112,8 +112,9 @@ minetest.register_on_player_receive_fields(on_player_receive_fields)
----------------------------------------------------------------
-- Public Registration API
----------------------------------------------------------------
function logistica.register_demander(simpleName)
-- `simpleName` is used for the description and for the name (can contain spaces)
-- transferRate is how many items per tick this demander can transfer, -1 for unlimited
function logistica.register_demander(simpleName, transferRate)
local lname = string.lower(simpleName:gsub(" ", "_"))
local demander_name = "logistica:demander_"..lname
logistica.demanders[demander_name] = true
@ -135,7 +136,7 @@ function logistica.register_demander(simpleName)
is_ground_content = false,
groups = grps,
drop = demander_name,
sounds = default.node_sound_metal_defaults(),
sounds = logistica.node_sound_metallic(),
on_timer = logistica.on_demander_timer,
after_place_node = function (pos, placer, itemstack)
after_place_demander(pos, placer, itemstack, NUM_DEMAND_SLOTS)
@ -146,6 +147,7 @@ function logistica.register_demander(simpleName)
allow_metadata_inventory_take = allow_demander_inv_take,
allow_metadata_inventory_move = allow_demander_inv_move,
logistica = {
demander_transfer_rate = transferRate,
on_connect_to_network = function(pos, networkId)
logistica.start_demander_timer(pos)
end
@ -171,4 +173,5 @@ function logistica.register_demander(simpleName)
end
logistica.register_demander("Basic")
logistica.register_demander("Basic", 1)
logistica.register_demander("Stack", 99)

View File

@ -172,7 +172,7 @@ function logistica.register_mass_storage(simpleName, numSlots, numItemsPerSlot,
description = simpleName.." Mass Storage\n(Empty)",
tiles = { "logistica_"..lname.."_mass_storage.png" },
groups = grps,
sounds = default.node_sound_metal_defaults(),
sounds = logistica.node_sound_metallic(),
after_place_node = function(pos, placer, itemstack)
after_place_mass_storage(pos, placer, itemstack, numSlots, numUpgradeSlots)
end,

View File

@ -1,8 +1,6 @@
local TIMER_DURATION_SHORT = 2.0
local TIMER_DURATION_LONG = 4.0
local TIMER_DURATION_SHORT = 1.0
local TIMER_DURATION_LONG = 3.0
local META_DEMANDER_LISTNAME = "demtarlist"
local MASS_STORAGE_LIST_NAME = "storage"
local ITEM_STORAGE_LIST_NAME = "main"
local TARGET_NODES_REQUIRING_TIMER = {}
TARGET_NODES_REQUIRING_TIMER["default:furnace"] = true
TARGET_NODES_REQUIRING_TIMER["gravelsieve:auto_sieve3"] = true
@ -12,6 +10,18 @@ local function get_meta(pos)
return minetest.get_meta(pos)
end
local function get_max_rate_for_demander(pos)
local node = minetest.get_node_or_nil(pos)
if not node then return 0 end
local nodeDef = minetest.registered_nodes[node.name]
if nodeDef and nodeDef.logistica and nodeDef.logistica.demander_transfer_rate then
if nodeDef.logistica.demander_transfer_rate <= 0 then return 9999
else return nodeDef.logistica.demander_transfer_rate end
else
return 0
end
end
local function get_valid_demander_and_target_inventory(demanderPos)
local meta = get_meta(demanderPos)
local targetList = meta:get_string(META_DEMANDER_LISTNAME)
@ -27,6 +37,7 @@ local function get_valid_demander_and_target_inventory(demanderPos)
if not targetInv:get_list(targetList) then return end
return {
demanderPos = demanderPos,
demanderInventory = meta:get_inventory(),
targetInventory = targetInv,
targetList = targetList,
@ -34,10 +45,10 @@ local function get_valid_demander_and_target_inventory(demanderPos)
}
end
local function get_actual_demand_for_item(demandStack, storageInv, storageListName)
local storageList = storageInv:get_list(storageListName)
local function get_actual_demand_for_item(demandStack, invs)
local storageList = invs.targetInventory:get_list(invs.targetList)
local remaining = demandStack:get_count()
for i,v in ipairs(storageList) do
for i,_ in ipairs(storageList) do
local stored = storageList[i]
if demandStack:get_name() == stored:get_name() then
remaining = remaining - stored:get_count()
@ -53,71 +64,35 @@ local function get_actual_demand_for_item(demandStack, storageInv, storageListNa
end
end
local function try_to_fulfil_demanded_item_from_network_item_storage(pos, filterStack, network, inventories)
for storageHash, _ in pairs(network.item_storage) do
local storagePos = minetest.get_position_from_hash(storageHash)
local storageInv = get_meta(storagePos):get_inventory()
if storageInv:contains_item(ITEM_STORAGE_LIST_NAME, filterStack) then
local leftover =
logistica.insert_itemstack_for_demander(pos, filterStack)
if leftover == 0 then -- stack max is 1, so just take the whole itemstack out
storageInv:remove_item(ITEM_STORAGE_LIST_NAME, filterStack)
return true
end -- otherwise, the insert failed for some reason..
end
end
return false
end
local function try_to_fulfil_demanded_item_from_locations(pos, filterStack, locations, inventories)
local filterStackName = filterStack:get_name()
local remainingDemand = filterStack:get_count()
for storageHash, _ in pairs(locations) do
if filterStack:get_count() == 0 then return end
local storagePos = minetest.get_position_from_hash(storageHash)
local storageInv = get_meta(storagePos):get_inventory()
local storageList = storageInv:get_list(MASS_STORAGE_LIST_NAME)
-- we can't use the usual take/put methods because mass storage exceeds max stack
for i = #storageList, 1, -1 do -- traverse backwards for taking items
local storageStack = storageList[i]
if filterStackName == storageStack:get_name() then
local numTaken = math.min(storageStack:get_count(), remainingDemand)
local takenStack = ItemStack(filterStack)
takenStack:set_count(numTaken)
local leftover =
logistica.insert_itemstack_for_demander(pos, takenStack)
numTaken = numTaken - leftover
storageStack:set_count(storageStack:get_count() - numTaken)
remainingDemand = remainingDemand - numTaken
if remainingDemand <= 0 then
storageInv:set_list(MASS_STORAGE_LIST_NAME, storageList)
return true
end
end
i = i - 1
end
storageInv:set_list(MASS_STORAGE_LIST_NAME, storageList)
end
return false
-- returns:
-- nil: nothing in inventory?
-- 0: no item has demand
-- ItemStack: the next demanded item
local function get_next_demanded_stack(pos)
local inventories = get_valid_demander_and_target_inventory(pos)
if not inventories then return nil end
local demandStack = nil
local nextSlot = logistica.get_next_filled_item_slot(get_meta(pos), "filter")
local startingSlot = nextSlot
repeat
if nextSlot <= 0 then return nil end
local filterStack = inventories.demanderInventory:get_list("filter")[nextSlot]
demandStack = get_actual_demand_for_item(filterStack, inventories)
if demandStack:get_count() > 0 then return demandStack end
nextSlot = logistica.get_next_filled_item_slot(get_meta(pos), "filter")
until( nextSlot == startingSlot ) -- until we get back to the starting slot
return 0 -- we had filled slots, but none had demand
end
local function take_demanded_items_from_network(pos, network)
local inventories = get_valid_demander_and_target_inventory(pos)
if not inventories then return true end
for _, filterStack in pairs(inventories.demanderInventory:get_list("filter")) do
local demandStack =
get_actual_demand_for_item(filterStack, inventories.targetInventory, inventories.targetList)
local filterStackName = demandStack:get_name()
local isTool = demandStack:get_stack_max() <= 1
if isTool then
try_to_fulfil_demanded_item_from_network_item_storage(pos, demandStack, network, inventories)
else -- check chaced mass-storage
local locations = network.storage_cache[filterStackName]
if not locations then return true end
try_to_fulfil_demanded_item_from_locations(pos, demandStack, locations, inventories)
end
end
local demandStack = get_next_demanded_stack(pos)
if demandStack == nil then return false end
if demandStack == 0 then return true end -- had items but nothing in demand
-- limiting the number of items requested
demandStack:set_count(math.min(get_max_rate_for_demander(pos), demandStack:get_count()))
local collect = function(st) return logistica.insert_itemstack_for_demander(pos, st) end
logistica.take_stack_from_network(demandStack, network, collect)
return true
end
----------------------------------------------------------------
@ -127,17 +102,17 @@ end
function logistica.start_demander_timer(pos, duration)
if duration == nil then duration = TIMER_DURATION_SHORT end
logistica.start_node_timer(pos, duration)
logistica.set_logistica_node_infotext(pos, true)
logistica.set_node_on_off_state(pos, true)
end
function logistica.on_demander_timer(pos, elapsed)
local network = logistica.get_network_or_nil(pos)
if not network or not logistica.is_machine_on(pos) then
logistica.set_logistica_node_infotext(pos, false)
logistica.set_node_on_off_state(pos, false)
return false
end
take_demanded_items_from_network(pos, network)
return true
if take_demanded_items_from_network(pos, network) then return true
else return false end
end
function logistica.set_demander_target_list(pos, listName)

View File

@ -1,7 +1,9 @@
local path = logistica.MODPATH .. "/logic"
-- once again, order is important
dofile(path .. "/processing_queue.lua")
dofile(path .. "/groups.lua")
dofile(path .. "/network_logic.lua")
dofile(path .. "/network_storage.lua")
dofile(path .. "/controller.lua")
dofile(path .. "/storage.lua")
dofile(path .. "/supplier.lua")

View File

@ -28,6 +28,9 @@ local function has_machine(network, id)
end
end
function logistica.get_network_by_id_or_nil(networkId)
return networks[networkId]
end
function logistica.get_network_or_nil(pos)
local hash = p2h(pos)

77
logic/network_storage.lua Normal file
View File

@ -0,0 +1,77 @@
local MASS_STORAGE_LIST_NAME = "storage"
local ITEM_STORAGE_LIST_NAME = "main"
local function get_meta(pos)
logistica.load_position(pos)
return minetest.get_meta(pos)
end
-- tries to take a stack from the network locations
-- 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)
if stackToTake:get_stack_max() <= 1 then
logistica.take_stack_from_item_storage(stackToTake, network, collectorFunc)
else
logistica.take_stack_from_mass_storage(stackToTake, network, collectorFunc)
end
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(filterStack, network, collectorFunc)
for storageHash, _ in pairs(network.item_storage) do
local storagePos = minetest.get_position_from_hash(storageHash)
local storageInv = get_meta(storagePos):get_inventory()
if storageInv:contains_item(ITEM_STORAGE_LIST_NAME, filterStack) then
local leftover = collectorFunc(filterStack)
if leftover == 0 then -- stack max is 1, so just take the whole itemstack out
storageInv:remove_item(ITEM_STORAGE_LIST_NAME, filterStack)
return true
end -- otherwise, the insert failed for some reason..
end
end
return false
end
-- tries to take a stack from the given network
-- 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
-- returns true if item successfully found and given to collector, false otherwise
function logistica.take_stack_from_mass_storage(stackToTake, network, collectorFunc)
local stackToTakeName = stackToTake:get_name()
local remainingDemand = stackToTake:get_count()
local massLocations = network.storage_cache[stackToTake:get_name()]
if massLocations == nil then return end
for storageHash, _ in pairs(massLocations) do
if stackToTake:get_count() == 0 then return end
local storagePos = minetest.get_position_from_hash(storageHash)
local storageInv = get_meta(storagePos):get_inventory()
local storageList = storageInv:get_list(MASS_STORAGE_LIST_NAME)
-- we can't use the usual take/put methods because mass storage exceeds max stack
for i = #storageList, 1, -1 do -- traverse backwards for taking items
local storageStack = storageList[i]
if stackToTakeName == storageStack:get_name() then
local numTaken = math.min(storageStack:get_count(), remainingDemand)
local takenStack = ItemStack(stackToTake)
takenStack:set_count(numTaken)
local leftover = collectorFunc(takenStack)
numTaken = numTaken - leftover
storageStack:set_count(storageStack:get_count() - numTaken)
remainingDemand = remainingDemand - numTaken
if remainingDemand <= 0 then
storageInv:set_list(MASS_STORAGE_LIST_NAME, storageList)
return true
end
end
i = i - 1
end
storageInv:set_list(MASS_STORAGE_LIST_NAME, storageList)
end
return false
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -104,12 +104,36 @@ function logistica.toggle_machine_on_off(pos)
end
-- isOn is optional
function logistica.set_logistica_node_infotext(pos, isOn)
-- extraText is optional
function logistica.set_node_on_off_state(pos, isOn, extraText)
if extraText == nil then extraText = "" else extraText = "\n"..extraText end
if isOn == nil then isOn = logistica.is_machine_on(pos) end
logistica.load_position(pos)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local text = minetest.registered_nodes[node.name].description..
"\n"..(isOn and "Running" or "Stopped")
extraText.."\n"..(isOn and "Running" or "Stopped")
meta:set_string("infotext", text)
end
-- returns a value of [1,#listSize], incrementing the slot each
-- time this is called, and returining a slot that has an item
-- if there's no item in the list, it will return 0
function logistica.get_next_filled_item_slot(nodeMeta, listName)
local metaKey = listName.."rot"
local inv = nodeMeta:get_inventory()
local listSize = inv:get_list(listName)
if not listSize then return 0 end
listSize = #listSize
local startPos = nodeMeta:get_int(metaKey) or 0
for i = startPos, startPos + listSize do
i = (i % listSize) + 1
local items = inv:get_stack(listName, i)
if items:get_count() > 0 then
nodeMeta:set_int(metaKey, i)
return i
end
end
nodeMeta:set_int(metaKey, 0)
return 0
end

11
util/sound.lua Normal file
View File

@ -0,0 +1,11 @@
function logistica.node_sound_metallic()
local tbl = {
footstep = {name = "logistica_node_footstep", gain = 0.2},
dig = {name = "logistica_node_dig", gain = 0.5},
dug = {name = "logsitica_node_dug", gain = 0.5},
place = {name = "default_place_node_hard", gain = 0.5},
}
default.node_sound_defaults(tbl)
return tbl
end

View File

@ -16,30 +16,36 @@ local function get_lists(pushToPos, allowedLists)
end
local function list_dropdown(name, itemTable, x, y, default)
local function list_dropdown(name, itemTable, x, y, default, label)
local labelField = ""
if label then labelField = "label["..x..","..(y-0.2)..";"..label.."]" end
local defaultIndex = 0
for i, v in ipairs(itemTable) do if default == v then defaultIndex = i end end
local items = table.concat(itemTable, ",")
return "dropdown["..x..","..y..";2,0.6;"..name..";"..items..";"..defaultIndex..";false]"
return "dropdown["..x..","..y..";2,0.6;"..name..";"..items..";"..defaultIndex..";false]"..labelField
end
--------------------------------
-- public functions
--------------------------------
function logistica.ui.on_off_btn(isOn, x, y, name, w, h)
function logistica.ui.on_off_btn(isOn, x, y, name, showLabel, w, h)
if showLabel == nil then showLabel = true end
if not w or not h then
w = 1; h = 1
end
local label=""
if showLabel then label = "label["..(x+0.2)..","..y..";Power]" end
local texture = (isOn and "logistica_icon_on.png" or "logistica_icon_off.png")
return "image_button["..x..","..y..";"..w..","..h..";"..
""..texture..";"..name..";;false;false;"..texture.."]"
""..texture..";"..name..";;false;false;"..texture.."]"..label
end
function logistica.ui.pull_list_picker(name, x, y, pullFromPos, default)
return list_dropdown(name, get_lists(pullFromPos, logistica.ALLOWED_PULL_LISTS), x, y, default)
function logistica.ui.pull_list_picker(name, x, y, pullFromPos, default, label)
return list_dropdown(name, get_lists(pullFromPos, logistica.ALLOWED_PULL_LISTS), x, y, default, label)
end
function logistica.ui.push_list_picker(name, x, y, pushToPos, default)
return list_dropdown(name, get_lists(pushToPos, logistica.ALLOWED_PUSH_LISTS), x, y, default)
function logistica.ui.push_list_picker(name, x, y, pushToPos, default, label)
return list_dropdown(name, get_lists(pushToPos, logistica.ALLOWED_PUSH_LISTS), x, y, default, label)
end

View File

@ -5,3 +5,4 @@ dofile(path.."/rotations.lua")
dofile(path.."/hud.lua")
dofile(path.."/ui_logic.lua")
dofile(path.."/ui.lua")
dofile(path.."/sound.lua")