226 lines
7.7 KiB
Lua
226 lines
7.7 KiB
Lua
local EMPTY_BUCKET = logistica.itemstrings.empty_bucket -- sort of weird, but we move liquids around in buckets, so we need some known "empty" bucket
|
|
|
|
local PUMP_MAX_RANGE = logistica.settings.pump_max_range
|
|
local PUMP_MAX_DEPTH = logistica.settings.pump_max_depth
|
|
|
|
local PUMP_NODES_PER_ROW = 2 * PUMP_MAX_RANGE + 1
|
|
local PUMP_NODES_PER_LAYER = PUMP_NODES_PER_ROW * PUMP_NODES_PER_ROW
|
|
local PUMP_INDEX_MAX = PUMP_NODES_PER_LAYER * PUMP_MAX_DEPTH
|
|
|
|
local META_PUMP_INDEX = "pumpix"
|
|
local META_OWNER = "pumpowner"
|
|
local META_LAST_LAYER = "pumplsl"
|
|
local META_LAST_LAYER_HAD_SUCCESS = "pumplss"
|
|
local META_MAX_AMOUNT_ON_NETWORK = "pumpmxn"
|
|
|
|
local ON_SUFFIX = "_on"
|
|
|
|
local MAX_CHECKS_PER_TIMER = PUMP_NODES_PER_LAYER -- limits how many nodes the index can advance per timer
|
|
|
|
local TIMER_SHORT = 1.0
|
|
local TIMER_LONG = 3.0
|
|
|
|
local SOURCES_TO_NAMES = nil
|
|
|
|
local PUMP_NEIGHBORS = {
|
|
vector.new(-1, 0, 0),
|
|
vector.new( 1, 0, 0),
|
|
vector.new( 0, 0, 1),
|
|
vector.new( 0, 0, -1),
|
|
}
|
|
|
|
local function ends_with(str, ending)
|
|
return str:sub(-#ending) == ending
|
|
end
|
|
|
|
local function sources_to_names()
|
|
if not SOURCES_TO_NAMES then SOURCES_TO_NAMES = logistica.reservoir_get_all_sources_to_names_map() end
|
|
return SOURCES_TO_NAMES
|
|
end
|
|
|
|
local function pump_get_index(meta)
|
|
return meta:get_int(META_PUMP_INDEX)
|
|
end
|
|
|
|
local function pump_set_index(meta, newIndex)
|
|
meta:set_int(META_PUMP_INDEX, newIndex)
|
|
end
|
|
|
|
local function get_owner_name(meta)
|
|
return meta:get_string(META_OWNER)
|
|
end
|
|
|
|
local function get_last_layer(meta)
|
|
return meta:get_int(META_LAST_LAYER)
|
|
end
|
|
|
|
local function set_last_layer(meta, layerInt)
|
|
meta:set_int(META_LAST_LAYER, layerInt)
|
|
end
|
|
|
|
local function get_last_layer_success(meta)
|
|
return meta:get_int(META_LAST_LAYER_HAD_SUCCESS) == 0
|
|
end
|
|
|
|
local function set_last_layer_success(meta, success)
|
|
meta:set_int(META_LAST_LAYER_HAD_SUCCESS, success and 0 or 1)
|
|
end
|
|
|
|
local function get_max_level(meta)
|
|
return meta:get_int(META_MAX_AMOUNT_ON_NETWORK)
|
|
end
|
|
|
|
local function set_max_level(meta, maxLevel)
|
|
meta:set_int(META_MAX_AMOUNT_ON_NETWORK, maxLevel)
|
|
end
|
|
|
|
-- returns nil if target position does not have a valid liquid source
|
|
-- otherwise returns table {nodeName = "name", isRenewable = true/false, bucketName = "bucket_itemstack_name"}
|
|
local function get_valid_source(targetPosition, ownerName)
|
|
logistica.load_position(targetPosition)
|
|
|
|
if minetest.is_protected(targetPosition, ownerName) then return nil end
|
|
|
|
local node = minetest.get_node_or_nil(targetPosition)
|
|
if not node then return nil end
|
|
|
|
local liquidName = sources_to_names()[node.name]
|
|
if not liquidName then return nil end
|
|
|
|
-- ensure it's really a source node
|
|
local nodeDef = minetest.registered_nodes[node.name]
|
|
if nodeDef.liquidtype ~= "source" then return nil end
|
|
|
|
local bucketsName = logistica.reservoir_get_full_buckets_for_liquid(liquidName)
|
|
local bucketName = nil
|
|
for potentialBucket, _ in pairs(bucketsName) do
|
|
-- basically, try to pick the EMPTY_BUCKET, but if not present for some weird reason, pick any valid filled bucket
|
|
if bucketName ~= EMPTY_BUCKET then bucketName = potentialBucket end
|
|
end
|
|
|
|
-- otherwise its valid
|
|
local isRenewable = nodeDef.liquid_renewable ; if isRenewable == nil then isRenewable = true end -- default value is true, per api docs
|
|
return {
|
|
nodeName = node.name,
|
|
isRenewable = isRenewable,
|
|
bucketName = bucketName,
|
|
}
|
|
end
|
|
|
|
-- returns a vector of the position associated with this index
|
|
local function pump_index_to_position(pumpPosition, pumpIndex)
|
|
local x = pumpIndex % PUMP_NODES_PER_LAYER % PUMP_NODES_PER_ROW
|
|
local y = -math.floor(pumpIndex / PUMP_NODES_PER_LAYER)
|
|
local z = math.floor((pumpIndex % PUMP_NODES_PER_LAYER) / PUMP_NODES_PER_ROW)
|
|
return vector.add(pumpPosition, vector.new(x - PUMP_MAX_RANGE, y - 1, z - PUMP_MAX_RANGE))
|
|
end
|
|
|
|
-- returns true if succeeded, false if not
|
|
local function put_liquid_in_neighboring_reservoirs(pumpPosition, bucketItemStack)
|
|
for _, v in ipairs(PUMP_NEIGHBORS) do
|
|
local neighborPos = vector.add(pumpPosition, v)
|
|
logistica.load_position(neighborPos)
|
|
local neighborNode = minetest.get_node_or_nil(neighborPos)
|
|
if neighborNode and logistica.GROUPS.reservoirs.is(neighborNode.name) then
|
|
local resultStack = logistica.reservoir_use_item_on(neighborPos, bucketItemStack, neighborNode)
|
|
if resultStack ~= nil then return true end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- returns true if succeeded, false if not
|
|
local function put_liquid_in_network_reservoirs(pumpPosition, bucketItemStack, network)
|
|
if not network then return false end
|
|
local liquidName = logistica.reservoir_get_liquid_name_for_filled_bucket(bucketItemStack:get_name())
|
|
if not liquidName then return false end
|
|
local amountInNetwork = logistica.get_liquid_info_in_network(pumpPosition, liquidName)
|
|
local maxAmount = get_max_level(minetest.get_meta(pumpPosition))
|
|
if maxAmount > 0 and amountInNetwork.curr >= maxAmount then return false end
|
|
local resultStack = logistica.use_bucket_for_liquid_in_network(pumpPosition, bucketItemStack)
|
|
return resultStack ~= nil -- if we got a replacement, it was successfully emptied into network
|
|
end
|
|
|
|
----------------------------------------------------------------
|
|
-- public functions
|
|
----------------------------------------------------------------
|
|
|
|
function logistica.pump_on_power(pos, power)
|
|
local node = minetest.get_node_or_nil(pos)
|
|
if power then
|
|
logistica.start_node_timer(pos, TIMER_SHORT)
|
|
if node and not ends_with(node.name, ON_SUFFIX) then
|
|
logistica.swap_node(pos, node.name..ON_SUFFIX)
|
|
end
|
|
else
|
|
if node and ends_with(node.name, ON_SUFFIX) then
|
|
logistica.swap_node(pos, node.name:sub(1, #node.name - #ON_SUFFIX))
|
|
end
|
|
end
|
|
logistica.set_node_tooltip_from_state(pos, nil, power)
|
|
end
|
|
|
|
function logistica.pump_timer(pos, _)
|
|
local network = logistica.get_network_or_nil(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
|
|
local count = 1
|
|
local success = false
|
|
local index = pump_get_index(meta)
|
|
local owner = get_owner_name(meta)
|
|
|
|
local lastLayer = get_last_layer(meta)
|
|
local lastLayerSuccess = get_last_layer_success(meta)
|
|
|
|
repeat
|
|
local targetPosition = pump_index_to_position(pos, index)
|
|
if targetPosition.y ~= lastLayer then
|
|
-- new layer reached
|
|
if lastLayerSuccess then -- let index continue as normal, but reset last layer success
|
|
set_last_layer_success(meta, false)
|
|
else -- reset index back to 0, and target position with it
|
|
index = 0
|
|
targetPosition = pump_index_to_position(pos, index)
|
|
end
|
|
set_last_layer(meta, targetPosition.y)
|
|
end
|
|
|
|
local sourceInfo = get_valid_source(targetPosition, owner)
|
|
if sourceInfo then
|
|
local bucketItemStack = ItemStack(sourceInfo.bucketName)
|
|
success = put_liquid_in_neighboring_reservoirs(pos, bucketItemStack)
|
|
if not success then
|
|
success = put_liquid_in_network_reservoirs(pos, bucketItemStack, network)
|
|
end
|
|
|
|
if success then
|
|
set_last_layer_success(meta, true)
|
|
if not sourceInfo.isRenewable then -- renewable liquids are not removed to reduce lag
|
|
minetest.remove_node(targetPosition)
|
|
end
|
|
end
|
|
end
|
|
index = (index + 1) % PUMP_INDEX_MAX
|
|
count = count + 1
|
|
until (count > MAX_CHECKS_PER_TIMER or success)
|
|
|
|
if success then logistica.start_node_timer(pos, TIMER_SHORT)
|
|
else logistica.start_node_timer(pos, TIMER_LONG) end
|
|
|
|
pump_set_index(meta, index) -- save index even if no success
|
|
|
|
return false
|
|
end
|
|
|
|
function logistica.pump_change_max_network_level(pos, change)
|
|
local meta = minetest.get_meta(pos)
|
|
local currMax = get_max_level(meta)
|
|
local newMax = logistica.clamp(currMax + change, 0, 10000) -- 10k arbitrary max
|
|
set_max_level(meta, newMax)
|
|
end
|
|
|
|
function logistica.pump_get_max_network_level(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
return get_max_level(meta)
|
|
end
|