-- IndustrialTest -- Copyright (C) 2024 mrkubax10 -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . local function addNodeToNetwork(pos,networkMasterPos) local meta=minetest.get_meta(pos) local networks={} if meta:contains("industrialtest.networks") then networks=minetest.deserialize(meta:get_string("industrialtest.networks")) end for _,network in ipairs(networks) do if network.x==networkMasterPos.x and network.y==networkMasterPos.y and network.z==networkMasterPos.z then return end end table.insert(networks,networkMasterPos) meta:set_string("industrialtest.networks",minetest.serialize(networks)) end local function clampFlow(pos,flow) local def=minetest.registered_nodes[minetest.get_node(pos).name] local newFlow if def.groups and def.groups._industrialtest_cable then newFlow=def._industrialtest_cableFlow else local meta=minetest.get_meta(pos) newFlow=meta:get_int("industrialtest.powerFlow") end return math.min(flow,newFlow) end -- \brief Transfers power from source node to it's network, if sides is set then power will be only transfered to network connected to that sides -- \param pos Vector with position of source node -- \param (optional) sides table with Vectors -- \param (optional) flowOverride number -- \returns two values: true if any neighbouring node has room for more power, false otherwise -- true if any power was transferred, false otherwise function industrialtest.api.powerFlow(pos,sides,flowOverride) local meta=minetest.get_meta(pos) -- if machine doesn't have network map then it's not capable of transferring power local network=industrialtest.api.getNetwork(meta) if not network or #network==0 then return false,false end local endpointCount=0 for _,endpoint in ipairs(network) do local endpointMeta=minetest.get_meta(endpoint.position) if not industrialtest.api.isFullyCharged(endpointMeta) and (not sides or sides[endpoint.sourceSide]) then endpointCount=endpointCount+1 end end if endpointCount==0 then return false,false end local powerDistribution=math.floor((flowOverride and flowOverride or meta:get_int("industrialtest.powerFlow"))/endpointCount) local transferred=false local roomAvailable=false for _,endpoint in ipairs(network) do if not sides or sides[endpoint.sourceSide] then local endpointMeta=minetest.get_meta(endpoint.position) if powerDistribution<=endpoint.flow then local transferredPower=industrialtest.api.transferPower(meta,endpointMeta,powerDistribution) if transferredPower>0 then transferred=true end local def=minetest.registered_nodes[minetest.get_node(endpoint.position).name] if def then local updateFormspec=def._industrialtest_updateFormspec if updateFormspec then updateFormspec(endpoint.position) end local onPowerFlow=def._industrialtest_onPowerFlow if onPowerFlow and transferredPower>0 then onPowerFlow(endpoint.position,industrialtest.api.getOppositeSide(endpoint.side),transferredPower) end end minetest.get_node_timer(endpoint.position):start(industrialtest.updateDelay) if not industrialtest.api.isFullyCharged(endpointMeta) then roomAvailable=true end else minetest.remove_node(endpoint.position) industrialtest.internal.explode(endpoint.position,2) end end end return roomAvailable,transferred end -- \brief Creates network map starting from node at pos, optionally omitting node at omit -- \param pos vector -- \param (optional) addCables bool -- \param (optional) omit Vector -- \returns table with network map function industrialtest.api.createNetworkMap(pos,addCables,omit) local workers={} local map={} local connections=industrialtest.api.getConnections(pos,"i") if #connections==0 then return map end local sides={ ["-1,0,0"]=1, ["1,0,0"]=2, ["0,-1,0"]=3, ["0,1,0"]=4, ["0,0,-1"]=5, ["0,0,1"]=6 } local serializedSourcePos=pos.x..","..pos.y..","..pos.z local visitedNodes={[serializedSourcePos]=true} for _,conn in ipairs(connections) do if not omit or conn.x~=omit.x or conn.y~=omit.y or conn.z~=omit.z then visitedNodes[conn.x..","..conn.y..","..conn.z]=true addNodeToNetwork(conn,pos) local sideVector=vector.subtract(conn,pos) local serializedSideVector=sideVector.x..","..sideVector.y..","..sideVector.z local def=minetest.registered_nodes[minetest.get_node(conn).name] if def.groups._industrialtest_cable then table.insert(workers,{ position=conn, targetPosition=conn, distance=1, flow=def._industrialtest_cableFlow, targetFlow=0, sourceSide=industrialtest.api.normalizeSide(pos,sides[serializedSideVector]) }) if addCables then table.insert(map,{ position=conn, distance=0 }) end else local meta=minetest.get_meta(conn) table.insert(map,{ position=conn, distance=0, flow=meta:get_int("industrialtest.powerFlow"), side=sides[serializedSideVector], sourceSide=industrialtest.api.normalizeSide(pos,sides[serializedSideVector]) }) end end end while #workers>0 do for i=1,#workers do local worker=workers[i] connections=industrialtest.api.getConnections(worker.position,"i") if #connections==0 then table.remove(workers,i) break else local directionAssigned=false local foundNewNode=false for _,conn in ipairs(connections) do if not omit or conn.x~=omit.x or conn.y~=omit.y or conn.z~=omit.z then local serializedPos=conn.x..","..conn.y..","..conn.z if not visitedNodes[serializedPos] then local def=minetest.registered_nodes[minetest.get_node(conn).name] visitedNodes[serializedPos]=true foundNewNode=true addNodeToNetwork(conn,pos) if def.groups._industrialtest_cable then if directionAssigned then table.insert(workers,{ position=conn, targetPosition=conn, distance=worker.distance+1, flow=clampFlow(conn,worker.flow), targetFlow=0, sourceSide=worker.sourceSide }) else worker.targetPosition=conn worker.distance=worker.distance+1 worker.targetFlow=clampFlow(conn,worker.flow) directionAssigned=true end if addCables then table.insert(map,{ position=conn, distance=worker.distance+1, }) end else local sideVector=vector.subtract(conn,worker.position) table.insert(map,{ position=conn, distance=worker.distance, flow=clampFlow(conn,worker.flow), side=sides[sideVector.x..","..sideVector.y..","..sideVector.z], sourceSide=worker.sourceSide }) if #connections==1 then foundNewNode=false break end end end end end if not foundNewNode then table.remove(workers,i) break end worker.position=worker.targetPosition worker.flow=worker.targetFlow end end end return map end function industrialtest.api.removeNodeFromNetwork(pos,nodePos) local meta=minetest.get_meta(pos) if not meta:contains("industrialtest.network") then return end local network=minetest.deserialize(meta:get_string("industrialtest.network")) local removed=false for key,node in ipairs(network) do if node.position.x==nodePos.x and node.position.y==nodePos.y and node.position.z==nodePos.z then table.remove(network,key) removed=true break end end if removed then meta:set_string("industrialtest.network",minetest.serialize(network)) end end -- \brief Creates network map and writes it to node metadata at pos, optionally omitting node at omit -- \param pos Vector -- \param (optional) omit Vector -- \returns nil function industrialtest.api.createNetworkMapForNode(pos,omit) local meta=minetest.get_meta(pos) local network=industrialtest.api.createNetworkMap(pos,false,omit) meta:set_string("industrialtest.network",minetest.serialize(network)) end -- \brief Returns true if meta contains network map, false otherwise -- \param meta MetaDataRef -- \returns bool function industrialtest.api.isNetworkMaster(meta) return meta:contains("industrialtest.network") end -- \brief Returns network table if node containing meta belongs to any networks, false otherwise -- \param meta MetaDataRef -- \returns bool or table function industrialtest.api.isAttachedToNetwork(meta) if not meta:contains("industrialtest.networks") then return false end local networks=minetest.deserialize(meta:get_string("industrialtest.networks")) if #networks==0 then return false end return networks end -- \brief Returns network master network from it's meta, if meta doesn't contain network map then function returns false -- \param meta MetaDataRef -- \returns table or bool function industrialtest.api.getNetwork(meta) if not meta:contains("industrialtest.network") then return false end return minetest.deserialize(meta:get_string("industrialtest.network")) end -- \brief Returns connections of node with power storage. If direction is "i" only input connections will be returned, if direction is "o" only output connections -- will be returned, if it's not provided all connections will be returned. -- \param pos Vector -- \param (optional) direction string -- \returns table function industrialtest.api.getConnections(pos,direction) local result={} local neighbourPositions={ vector.offset(pos,-1,0,0), vector.offset(pos,1,0,0), vector.offset(pos,0,-1,0), vector.offset(pos,0,1,0), vector.offset(pos,0,0,-1), vector.offset(pos,0,0,1) } local sourceMeta=minetest.get_meta(pos) local sourceDef=minetest.registered_nodes[minetest.get_node(pos).name] local directionOutput=(not direction or direction=="o") local directionInput=(not direction or direction=="i") for key,conn in ipairs(neighbourPositions) do local meta=minetest.get_meta(conn) local def=minetest.registered_nodes[minetest.get_node(conn).name] local normalizedKey=industrialtest.api.normalizeSide(pos,key) local powerOutput=(sourceDef.groups._industrialtest_cable or industrialtest.api.isPowerOutput(sourceMeta,normalizedKey)) local powerInput=(sourceDef.groups._industrialtest_cable or industrialtest.api.isPowerInput(sourceMeta,normalizedKey)) if def.groups._industrialtest_cable or industrialtest.api.hasPowerStorage(meta) then local side=industrialtest.api.normalizeSide(conn,industrialtest.api.getOppositeSide(normalizedKey)) if (powerOutput and directionInput and (def.groups._industrialtest_cable or industrialtest.api.isPowerInput(meta,side))) or ((def.groups._industrialtest_cable or industrialtest.api.isPowerOutput(meta,side)) and powerInput and directionOutput) then table.insert(result,conn) end end end return result end