diff --git a/elepower_dynamics/conduits.lua b/elepower_dynamics/conduits.lua index 10fc5df..d2988d6 100644 --- a/elepower_dynamics/conduits.lua +++ b/elepower_dynamics/conduits.lua @@ -1,6 +1,22 @@ +-- Electric power ele.register_conduit("elepower_dynamics:conduit", { description = "Power Conduit", tiles = {"elepower_conduit.png"}, groups = {oddly_breakable_by_hand = 1, cracky = 1} }) + +-- Fluids +elefluid.register_transfer_node("elepower_dynamics:fluid_transfer_node", { + description = "Fluid Transfer Node", + tiles = {"elepower_fluid_transporter_side.png", "elepower_fluid_transporter_side.png^[transformR180", + "elepower_fluid_transporter_side.png^[transformR270", "elepower_fluid_transporter_side.png^[transformFXR90", + "elepower_fluid_transporter_back.png", "elepower_fluid_transporter_front.png"}, + groups = {oddly_breakable_by_hand = 1, cracky = 1} +}) + +elefluid.register_transfer_duct("elepower_dynamics:fluid_duct", { + description = "Fluid Duct", + tiles = {"elepower_duct.png"}, + groups = {oddly_breakable_by_hand = 1, cracky = 1} +}) diff --git a/elepower_dynamics/mod.conf b/elepower_dynamics/mod.conf index dd369b1..22fdbe4 100644 --- a/elepower_dynamics/mod.conf +++ b/elepower_dynamics/mod.conf @@ -1,4 +1,4 @@ name = elepower_dynamics description = Elepower Dynamics. Conduits and materials! -depends = elepower_papi,default +depends = elepower_papi,elepower_fapi,default optional_depends = moreores diff --git a/elepower_dynamics/textures/elepower_duct.png b/elepower_dynamics/textures/elepower_duct.png new file mode 100644 index 0000000..c067c92 Binary files /dev/null and b/elepower_dynamics/textures/elepower_duct.png differ diff --git a/elepower_dynamics/textures/elepower_fluid_transporter_back.png b/elepower_dynamics/textures/elepower_fluid_transporter_back.png new file mode 100644 index 0000000..4f688b8 Binary files /dev/null and b/elepower_dynamics/textures/elepower_fluid_transporter_back.png differ diff --git a/elepower_dynamics/textures/elepower_fluid_transporter_front.png b/elepower_dynamics/textures/elepower_fluid_transporter_front.png new file mode 100644 index 0000000..ab1f3fb Binary files /dev/null and b/elepower_dynamics/textures/elepower_fluid_transporter_front.png differ diff --git a/elepower_dynamics/textures/elepower_fluid_transporter_side.png b/elepower_dynamics/textures/elepower_fluid_transporter_side.png new file mode 100644 index 0000000..5804ce4 Binary files /dev/null and b/elepower_dynamics/textures/elepower_fluid_transporter_side.png differ diff --git a/elepower_fapi/buffer.lua b/elepower_fapi/buffer.lua new file mode 100644 index 0000000..e69de29 diff --git a/elepower_fapi/init.lua b/elepower_fapi/init.lua new file mode 100644 index 0000000..7a85dd0 --- /dev/null +++ b/elepower_fapi/init.lua @@ -0,0 +1,14 @@ +-- A Elepower Mod +-- Copyright 2018 Evert "Diamond" Prants + +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +elefluid = rawget(_G, "elefluid") or {} +elefluid.modpath = modpath + +elefluid.unit = "mB" +elefluid.unit_description = "milli-bucket" + +dofile(modpath.."/transfer.lua") +dofile(modpath.."/transfer_node.lua") +dofile(modpath.."/buffer.lua") diff --git a/elepower_fapi/mod.conf b/elepower_fapi/mod.conf new file mode 100644 index 0000000..983d9d2 --- /dev/null +++ b/elepower_fapi/mod.conf @@ -0,0 +1,4 @@ +name = elepower_fapi +description = Elepower Fluid Transfer API +depends = elepower_papi +optional_depends = fluidity diff --git a/elepower_fapi/transfer.lua b/elepower_fapi/transfer.lua new file mode 100644 index 0000000..98fd084 --- /dev/null +++ b/elepower_fapi/transfer.lua @@ -0,0 +1,368 @@ +-- Network graphs are built eminating from provider nodes. + +-- Network cache +elefluid.graphcache = {nodes = {}} + +local fluidmod = minetest.get_modpath("fluidity") ~= nil + +--------------------- +-- Graph Functions -- +--------------------- + +local function clear_networks_from_node(pos) + local meta = minetest.get_meta(pos) + meta:set_string("network_id", "") +end + +local function add_node(nodes, pos, pnodeid) + local node_id = minetest.hash_node_position(pos) + if elefluid.graphcache.nodes[node_id] and elefluid.graphcache.nodes[node_id] ~= pnodeid then return end + elefluid.graphcache.nodes[node_id] = pnodeid + if nodes[node_id] then + return false + end + nodes[node_id] = pos + return true +end + +local function add_duct_node(nodes, pos, pnodeid, queue) + if add_node(nodes, pos, pnodeid) then + queue[#queue + 1] = pos + end +end + +local function check_node(targets, all_nodes, pos, p_pos, pnodeid, queue) + ele.helpers.get_or_load_node(pos) + local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) + + if ele.helpers.get_item_group(node.name, "elefluid_transport") then + add_duct_node(all_nodes, pos, pnodeid, queue) + return + end + + if not ele.helpers.get_item_group(node.name, "ele_fluid_container") then + return + end + + -- Don't add already networked nodes to this network + if meta:get_string("network_id") ~= "" and meta:get_string("network_id") ~= pnodeid then + return + end + + meta:set_string("network_id", pnodeid) + + add_node(targets, pos, pnodeid) +end + +local function traverse_network(targets, all_nodes, pos, p_pos, pnodeid, queue) + local positions = { + {x=pos.x+1, y=pos.y, z=pos.z}, + {x=pos.x-1, y=pos.y, z=pos.z}, + {x=pos.x, y=pos.y+1, z=pos.z}, + {x=pos.x, y=pos.y-1, z=pos.z}, + {x=pos.x, y=pos.y, z=pos.z+1}, + {x=pos.x, y=pos.y, z=pos.z-1}} + for _, cur_pos in pairs(positions) do + check_node(targets, all_nodes, cur_pos, p_pos, pnodeid, queue) + end +end + +local function fluid_targets(p_pos, positions) + local provider = minetest.get_node(p_pos) + local pnodeid = minetest.pos_to_string(p_pos) + + if elefluid.graphcache[pnodeid] then + local cached = elefluid.graphcache[pnodeid] + return cached.targets + end + + local targets = {} + local queue = {} + local all_nodes = {} + + for pos in pairs(positions) do + queue = {} + + local node = minetest.get_node(pos) + if node and ele.helpers.get_item_group(node.name, "elefluid_transport") then + add_duct_node(all_nodes, pos, pnodeid, queue) + elseif node and ele.helpers.get_item_group(node.name, "ele_fluid_container") then + queue = {p_pos} + end + + while next(queue) do + local to_visit = {} + for _, posi in ipairs(queue) do + traverse_network(targets, all_nodes, posi, p_pos, pnodeid, to_visit) + end + queue = to_visit + end + end + + local prov_id = minetest.hash_node_position(p_pos) + elefluid.graphcache.nodes[prov_id] = pnodeid + + targets = ele.helpers.flatten(targets) + all_nodes = ele.helpers.flatten(all_nodes) + + elefluid.graphcache[pnodeid] = {all_nodes = all_nodes, targets = targets} + + return targets +end + +----------------------- +-- Main Transfer ABM -- +----------------------- + +minetest.register_abm({ + nodenames = {"group:elefluid_transport_source"}, + label = "elefluitFluidGraphSource", + interval = 1, + chance = 1, + action = function(pos, node, active_object_count, active_object_count_wider) + local meta = minetest.get_meta(pos) + local meta1 = nil + + -- Check if a source is attached to a network. + -- If that network has been abolished, we will use this node as the network's root this time. + local netwrkto = meta:get_string("ele_network") + if netwrkto ~= "" and netwrkto ~= nil then + local lpos = minetest.string_to_pos(netwrkto) + if ele.helpers.get_item_group(minetest.get_node(lpos).name, "elefluid_transport_source") then + return + else + elefluid.graphcache[netwrkto] = nil + end + meta:set_string("ele_network", "") + end + + local targets = {} + local source = minetest.registered_nodes[node.name] + + local positions = {vector.add(minetest.facedir_to_dir(node.param2), pos)} + + local ntwks = {} + local errored = false + local nw_branches = 0 + for _,pos1 in pairs(positions) do + local name = node.name + local networked = ele.helpers.get_item_group(name, "elefluid_transport_source") or + ele.helpers.get_item_group(name, "elefluid_transport") + if networked then + ntwks[pos1] = true + nw_branches = nw_branches + 1 + end + end + + if errored then + return + end + + if nw_branches == 0 then + minetest.forceload_free_block(pos) + return + else + minetest.forceload_block(pos) + end + + targets = fluid_targets(pos, ntwks) + + -- Begin transfer + local srcpos = ele.helpers.face_front(pos, node.param2) + local srcnode = minetest.get_node(srcpos) + + -- Make sure source node is not air + if not srcnode or srcnode.name == "air" then + return + end + + -- Make sure source node is a registered fluid container + if not ele.helpers.get_item_group(srcnode.name, "ele_fluid_container") then + return + end + + local srcmeta = minetest.get_meta(srcpos) + local srcdef = minetest.registered_nodes[srcnode.name] + local buffers = srcdef['ele_fluid_buffers'] + if not buffers then return nil end + + local pcapability = ele.helpers.get_node_property(meta, pos, "fluid_pump_capacity") + + -- Transfer some fluid here + for _,pos in pairs(targets) do + if not vector.equals(pos, srcpos) then + local nm = minetest.get_meta(pos) + local nd = minetest.get_node(pos) + local nc = minetest.registered_nodes[nd.name] + local pp = nc['ele_fluid_buffers'] + + local changed = false + + if pp ~= nil then + for name, data in pairs(pp) do + for bname, buf in pairs(buffers) do + local target_fluid = nm:get_string(name.."_fluid") + local buffer_fluid = srcmeta:get_string(bname.."_fluid") + + local target_count = nm:get_int(name.."_fluid_storage") + local buffer_count = srcmeta:get_int(bname.."_fluid_storage") + + local apply = false + + if (target_fluid == buffer_fluid or target_fluid == "") and buffer_fluid ~= "" and buffer_count > 0 and + (data.accepts == true or data.accepts == buffer_fluid) then + local can_transfer_amnt = 0 + if buffer_count > pcapability then + can_transfer_amnt = pcapability + else + can_transfer_amnt = buffer_count + end + + if can_transfer_amnt > 0 then + local can_accept_amnt = 0 + if target_count + can_transfer_amnt > data.capacity then + can_accept_amnt = data.capacity - target_count + else + can_accept_amnt = can_transfer_amnt + end + + if can_accept_amnt > 0 then + target_count = target_count + can_accept_amnt + target_fluid = buffer_fluid + buffer_count = buffer_count - can_accept_amnt + + if buffer_count == 0 then + buffer_fluid = "" + end + + apply = true + end + end + end + + if apply then + nm:set_string(name.."_fluid", target_fluid) + srcmeta:set_string(bname.."_fluid", buffer_fluid) + + nm:set_int(name.."_fluid_storage", target_count) + srcmeta:set_string(bname.."_fluid_storage", buffer_count) + + changed = true + end + end + end + end + + if changed then + minetest.get_node_timer(srcpos):start(1.0) + minetest.get_node_timer(pos):start(1.0) + end + end + end + end, +}) + +local function check_connections(pos) + local connections = {} + local positions = { + {x=pos.x+1, y=pos.y, z=pos.z}, + {x=pos.x-1, y=pos.y, z=pos.z}, + {x=pos.x, y=pos.y+1, z=pos.z}, + {x=pos.x, y=pos.y-1, z=pos.z}, + {x=pos.x, y=pos.y, z=pos.z+1}, + {x=pos.x, y=pos.y, z=pos.z-1}} + + for _,connected_pos in pairs(positions) do + local name = minetest.get_node(connected_pos).name + if ele.helpers.get_item_group(name, "elefluid_transport") or + ele.helpers.get_item_group(name, "ele_fluid_container") or + ele.helpers.get_item_group(name, "elefluid_transport_source") then + table.insert(connections, connected_pos) + end + end + return connections +end + +-- Update networks when a node has been placed or removed +function elefluid.clear_networks(pos) + local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) + local name = node.name + local placed = name ~= "air" + local positions = check_connections(pos) + if #positions < 1 then return end + local dead_end = #positions == 1 + for _,connected_pos in pairs(positions) do + local net = elefluid.graphcache.nodes[minetest.hash_node_position(connected_pos)] or minetest.pos_to_string(connected_pos) + if net and elefluid.graphcache[net] then + if dead_end and placed then + -- Dead end placed, add it to the network + -- Get the network + local node_at = minetest.get_node(positions[1]) + local network_id = elefluid.graphcache.nodes[minetest.hash_node_position(positions[1])] or minetest.pos_to_string(positions[1]) + + if not network_id or not elefluid.graphcache[network_id] then + -- We're evidently not on a network, nothing to add ourselves to + return + end + local c_pos = minetest.string_to_pos(network_id) + local network = elefluid.graphcache[network_id] + + -- Actually add it to the (cached) network + -- This is similar to check_node_subp + elefluid.graphcache.nodes[minetest.hash_node_position(pos)] = network_id + pos.visited = 1 + + if ele.helpers.get_item_group(name, "elefluid_transport") then + table.insert(network.all_nodes, pos) + end + + if ele.helpers.get_item_group(name, "ele_fluid_container") then + meta:set_string("ele_network", network_id) + table.insert(network.targets, pos) + end + elseif dead_end and not placed then + -- Dead end removed, remove it from the network + -- Get the network + local network_id = elefluid.graphcache.nodes[minetest.hash_node_position(positions[1])] or minetest.pos_to_string(positions[1]) + if not network_id or not elefluid.graphcache[network_id] then + -- We're evidently not on a network, nothing to remove ourselves from + return + end + local network = elefluid.graphcache[network_id] + + -- The network was deleted. + if network_id == minetest.pos_to_string(pos) then + for _,v in ipairs(network.all_nodes) do + local pos1 = minetest.hash_node_position(v) + clear_networks_from_node(v) + elefluid.graphcache.nodes[pos1] = nil + end + elefluid.graphcache[network_id] = nil + return + end + + -- Search for and remove device + elefluid.graphcache.nodes[minetest.hash_node_position(pos)] = nil + for tblname,table in pairs(network) do + if type(table) == "table" then + for devicenum,device in pairs(table) do + if vector.equals(device, pos) then + table[devicenum] = nil + end + end + end + end + else + -- Not a dead end, so the whole network needs to be recalculated + for _,v in ipairs(elefluid.graphcache[net].all_nodes) do + local pos1 = minetest.hash_node_position(v) + clear_networks_from_node(v) + elefluid.graphcache.nodes[pos1] = nil + end + elefluid.graphcache[net] = nil + end + end + end +end diff --git a/elepower_fapi/transfer_node.lua b/elepower_fapi/transfer_node.lua new file mode 100644 index 0000000..3ac0625 --- /dev/null +++ b/elepower_fapi/transfer_node.lua @@ -0,0 +1,98 @@ +-- Nodes for transferring fluids +-- All units are in millibuckets (1 bucket) + +-- This is the node that takes fluid from another node. +function elefluid.register_transfer_node(nodename, nodedef) + if not nodedef.groups then + nodedef.groups = {} + end + + nodedef.groups["elefluid_transport_source"] = 1 + nodedef.paramtype2 = "facedir" + + local orig_construct = nodedef.on_construct + nodedef.on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_int("fluid_store", 0) + meta:set_string("fluid", "") + + elefluid.clear_networks(pos) + + if orig_construct then + orig_construct(pos) + end + end + + nodedef.after_destruct = elefluid.clear_networks + + -- Default transfer capacity + if not nodedef.ele_fluid_pump_capacity then + nodedef.ele_fluid_pump_capacity = 1000 + end + + minetest.register_node(nodename, nodedef) +end + +-- This is the node that allows for fluid transfer. +function elefluid.register_transfer_duct(nodename, nodedef) + if not nodedef.groups then + nodedef.groups = {} + end + + nodedef.groups["elefluid_transport"] = 1 + + -- Duct node density + local cd = 1/7 + + if nodedef.ele_duct_density then + cd = nodedef.ele_duct_density + end + + -- Default values, including the nodebox + local defaults = { + drawtype = "nodebox", + node_box = { + type = "connected", + fixed = { + {-cd, -cd, -cd, cd, cd, cd} + }, + connect_front = { + {-cd, -cd, -1/2, cd, cd, -cd} + }, + connect_back = { + {-cd, -cd, cd, cd, cd, 1/2} + }, + connect_top = { + {-cd, cd, -cd, cd, 1/2, cd} + }, + connect_bottom = { + {-cd, -1/2, -cd, cd, -cd, cd} + }, + connect_left = { + {-1/2, -cd, -cd, cd, cd, cd} + }, + connect_right = { + {cd, -cd, -cd, 1/2, cd, cd} + }, + }, + paramtype = "light", + connect_sides = { "top", "bottom", "front", "left", "back", "right" }, + is_ground_content = false, + connects_to = { + "group:elefluid_transport", + "group:elefluid_transport_source", + "group:ele_fluid_container", + }, + } + + for k,v in pairs(defaults) do + if not nodedef[k] then + nodedef[k] = v + end + end + + nodedef.on_construct = elefluid.clear_networks + nodedef.after_destruct = elefluid.clear_networks + + minetest.register_node(nodename, nodedef) +end diff --git a/elepower_papi/helpers.lua b/elepower_papi/helpers.lua index 11bbe4b..77e34ef 100644 --- a/elepower_papi/helpers.lua +++ b/elepower_papi/helpers.lua @@ -87,3 +87,14 @@ function ele.helpers.scan_item_list(item_name) return found end + +function ele.helpers.face_front(pos, fd) + local back = minetest.facedir_to_dir(fd) + local front = ele.helpers.table_copy(back) + + front.x = front.x * -1 + pos.x + front.y = front.y * -1 + pos.y + front.z = front.z * -1 + pos.z + + return front +end diff --git a/elepower_papi/network.lua b/elepower_papi/network.lua index bd9ca34..52223d2 100644 --- a/elepower_papi/network.lua +++ b/elepower_papi/network.lua @@ -364,7 +364,7 @@ function ele.clear_networks(pos) -- The network was deleted. if network_id == minetest.pos_to_string(pos) then - for _,v in pairs(network.all_nodes) do + for _,v in ipairs(network.all_nodes) do local pos1 = minetest.hash_node_position(v) clear_networks_from_node(v) ele.graphcache.devices[pos1] = nil @@ -386,7 +386,7 @@ function ele.clear_networks(pos) end else -- Not a dead end, so the whole network needs to be recalculated - for _,v in pairs(ele.graphcache[net].all_nodes) do + for _,v in ipairs(ele.graphcache[net].all_nodes) do local pos1 = minetest.hash_node_position(v) clear_networks_from_node(v) ele.graphcache.devices[pos1] = nil