diff --git a/api.lua b/api.lua index d0b2fff..c84b594 100644 --- a/api.lua +++ b/api.lua @@ -16,310 +16,6 @@ local S=minetest.get_translator("industrialtest") --- \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 -industrialtest.api.powerFlow=function(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 - -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 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 -industrialtest.api.createNetworkMap=function(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 - -industrialtest.api.removeNodeFromNetwork=function(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 -industrialtest.api.createNetworkMapForNode=function(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 -industrialtest.api.isNetworkMaster=function(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 -industrialtest.api.isAttachedToNetwork=function(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 -industrialtest.api.getNetwork=function(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 -industrialtest.api.getConnections=function(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 -- \brief Registers dust of certain resource -- \param name Technical name of resource -- \param displayName Display name of resource diff --git a/api/network.lua b/api/network.lua new file mode 100644 index 0000000..dd9b7e8 --- /dev/null +++ b/api/network.lua @@ -0,0 +1,320 @@ +-- 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 diff --git a/init.lua b/init.lua index 75efbc7..ed39de0 100644 --- a/init.lua +++ b/init.lua @@ -33,6 +33,7 @@ dofile(modpath.."/minerals.lua") dofile(modpath.."/api/common.lua") dofile(modpath.."/api/fluid.lua") +dofile(modpath.."/api/network.lua") dofile(modpath.."/api/power.lua") dofile(modpath.."/api/side.lua") dofile(modpath.."/api/tool.lua")