diff --git a/init.lua b/init.lua
index c03beb8..d4c3e4d 100644
--- a/init.lua
+++ b/init.lua
@@ -60,6 +60,7 @@ dofile(modpath.."/machines/mass_fabricator.lua")
dofile(modpath.."/machines/miner.lua")
dofile(modpath.."/machines/nuclear_reactor.lua")
dofile(modpath.."/machines/power_storage.lua")
+dofile(modpath.."/machines/pump.lua")
dofile(modpath.."/machines/recycler.lua")
dofile(modpath.."/machines/rotary_macerator.lua")
dofile(modpath.."/machines/tool_workshop.lua")
diff --git a/machines/pump.lua b/machines/pump.lua
new file mode 100644
index 0000000..4221550
--- /dev/null
+++ b/machines/pump.lua
@@ -0,0 +1,407 @@
+-- IndustrialTest
+-- Copyright (C) 2025 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 S=minetest.get_translator("industrialtest")
+industrialtest.Pump=table.copy(industrialtest.ActivatedElectricMachine)
+industrialtest.internal.unpackTableInto(industrialtest.Pump,{
+ name="industrialtest:pump",
+ description=S("Pump"),
+ tiles={
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png^industrialtest_pump_front.png"
+ },
+ sounds="metal",
+ facedir=true,
+ storageLists={
+ "src",
+ "dst",
+ "powerStorage"
+ },
+ powerLists={
+ {
+ list="powerStorage",
+ direction="i"
+ }
+ },
+ active={
+ tiles={
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png",
+ "industrialtest_machine_block.png^industrialtest_pump_front_active.png"
+ }
+ },
+ capacity=industrialtest.api.lvPowerFlow*2,
+ flow=industrialtest.api.lvPowerFlow,
+ ioConfig="iiiiii",
+ requiresWrench=true,
+ hasPowerInput=true,
+ _fluidCapacity=5000,
+ _opPower=300,
+ _pumpTime=10
+})
+
+function industrialtest.Pump.onConstruct(self,pos)
+ local meta=minetest.get_meta(pos)
+ local inv=meta:get_inventory()
+ inv:set_size("src",1)
+ inv:set_size("dst",1)
+ inv:set_size("powerStorage",1)
+ meta:set_float("srcTime",0)
+ industrialtest.api.addFluidStorage(meta,self._fluidCapacity)
+ self.determinePumpTargets(pos)
+ industrialtest.ActivatedElectricMachine.onConstruct(self,pos)
+end
+
+function industrialtest.Pump.getFormspec(self,pos)
+ local parentFormspec=industrialtest.ActivatedElectricMachine.getFormspec(self,pos)
+ local meta=minetest.get_meta(pos)
+ local powerPercent=meta:get_int("industrialtest.powerAmount")/meta:get_int("industrialtest.powerCapacity")*100
+ local srcPercent=meta:get_float("srcTime")/self._pumpTime*100
+ local fluidType=meta:get_string("industrialtest.fluidType")
+ local fluidPercent=0
+ if meta:contains("industrialtest.fluidAmount") and meta:contains("industrialtest.fluidCapacity") then
+ fluidPercent=meta:get_int("industrialtest.fluidAmount")/meta:get_int("industrialtest.fluidCapacity")*100
+ end
+ local pumpFluid=industrialtest.api.getPumpFluid(fluidType)
+ local tile=(pumpFluid and pumpFluid.texture or "industrialtest_gui_fluid_bg.png")
+ local formspec={
+ "list[context;src;3.2,1.7;1,1]",
+ industrialtest.internal.getItemSlotBg(3.2,1.7,1,1),
+ "list[context;dst;4.6,1.7;1,1]",
+ industrialtest.internal.getItemSlotBg(4.6,1.7,1,1),
+ "list[context;powerStorage;3.9,3.7;1,1]",
+ industrialtest.internal.getItemSlotBg(3.9,3.7,1,1),
+ (powerPercent>0 and "image[3.9,2.7;1,1;industrialtest_gui_electricity_bg.png^[lowpart:"..powerPercent..":industrialtest_gui_electricity_fg.png]"
+ or "image[3.9,2.7;1,1;industrialtest_gui_electricity_bg.png]"),
+ (srcPercent>0 and "image[6.7,2.7;1,1;gui_furnace_arrow_bg.png^[lowpart:"..srcPercent..":gui_furnace_arrow_fg.png]"
+ or "image[6.7,2.7;1,1;gui_furnace_arrow_bg.png]"),
+ (fluidPercent>0 and "image[7.7,2.7;1,1;industrialtest_gui_fluid_bg.png^[lowpart:"..fluidPercent..":"..tile.."]" or "image[7.7,2.7;1,1;industrialtest_gui_fluid_bg.png]"),
+ "label[3.2,1.5;"..S("Input").."]",
+ "label[4.6,1.5;"..S("Output").."]",
+ "listring[context;src]",
+ "listring[context;dst]"
+ }
+ return parentFormspec..table.concat(formspec,"")
+end
+
+function industrialtest.Pump.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,count)
+ if toList=="dst" then
+ return 0
+ end
+ return industrialtest.ActivatedElectricMachine.allowMetadataInventoryMove(self,pos,fromList,fromIndex,toList,count)
+end
+
+function industrialtest.Pump.allowMetadataInventoryPut(self,pos,listname,index,stack,player)
+ if listname=="dst" then
+ return 0
+ end
+ return industrialtest.ActivatedElectricMachine.allowMetadataInventoryPut(self,pos,listname,index,stack,player)
+end
+
+function industrialtest.Pump.allowMetadataInventoryTake(self,pos,listname,index,stack,player)
+ if listname=="src" then
+ local meta=minetest.get_meta(pos)
+ local inv=meta:get_inventory()
+ local srcSlot=inv:get_stack("src",1)
+ if stack:get_count()==srcSlot:get_count() and not meta:get_int("hasOutputTarget") then
+ meta:set_float("srcTime",0)
+ self:updateFormspec(pos)
+ end
+ end
+ return industrialtest.ActivatedElectricMachine.allowMetadataInventoryTake(self,pos,listname,index,stack,player)
+end
+
+function industrialtest.Pump.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
+ if fromList=="src" then
+ local meta=minetest.get_meta(pos)
+ local inv=meta:get_inventory()
+ local srcSlot=inv:get_stack("src",1)
+ if count==srcSlot:get_count() and not meta:get_int("hasOutputTarget") then
+ meta:set_float("srcTime",0)
+ self:updateFormspec(pos)
+ end
+ elseif toList=="src" then
+ self:triggerIfNeeded(pos)
+ end
+ industrialtest.ActivatedElectricMachine.onMetadataInventoryMove(self,pos,fromList,fromIndex,toList,toIndex,count)
+end
+
+function industrialtest.Pump.onMetadataInventoryPut(self,pos,listname,index,stack)
+ if listname=="src" then
+ self:triggerIfNeeded(pos)
+ end
+ industrialtest.ActivatedElectricMachine.onMetadataInventoryPut(self,pos,listname,index,stack)
+end
+
+function industrialtest.Miner.onMetadataInventoryTake(self,pos,listname,index,stack)
+ if listname=="dst" then
+ self:triggerIfNeeded(pos)
+ end
+end
+
+function industrialtest.Pump.shouldActivate(self,pos)
+ local meta=minetest.get_meta(pos)
+ if meta:get_int("industrialtest.powerAmount")0 then
+ hasInput=true
+ else
+ -- Check if there is node that can be pumped under pump
+ if industrialtest.api.getPumpFluid(nodeUnder.name) then
+ hasInput=true
+ end
+ end
+
+ -- Check if any input target can provide with fluid
+ if not hasInput and meta:get_int("hasInputTarget")>0 then
+ local polledTargets=self.pollInputTargets(pos)
+ hasInput=#polledTargets>0
+ end
+
+ if not hasInput then
+ return false
+ end
+
+ local hasOutput=false
+ -- First check if pump can push into any neighour node
+ if meta:get_int("hasOutputTarget") then
+ local outputTargets=minetest.deserialize(meta:get_string("outputTargets"))
+ for _,target in ipairs(outputTargets) do
+ local node=minetest.get_node(target)
+ local def=minetest.registered_nodes[node.name]
+ hasOutput=(def and def._industrialtest_self and def._industrialtest_self:canPushFluid(target,nodeUnder.name,fluidAmount))
+ if hasOutput then
+ break
+ end
+ end
+ end
+
+ -- Check if it's possible to pump fluid into item
+ if not hasOutput then
+ local inv=meta:get_inventory()
+ local srcSlot=inv:get_stack("src",1)
+ if not srcSlot:is_empty() then
+ local def=srcSlot:get_definition()
+ if def.groups._industrialtest_simpleFluidStorage and def._industrialtest_simpleFluidStorageCapacity and fluidAmount>=def._industrialtest_simpleFluidStorageCapacity and
+ def._industrialtest_getResultingFluidStorageItemByNode then
+ local fluidType=meta:get_string("industrialtest.fluidType")
+ local resulting=def._industrialtest_getResultingFluidStorageItemByNode(fluidType)
+ if resulting then
+ local dstSlot=inv:get_stack("dst",1)
+ hasOutput=dstSlot:item_fits(ItemStack(resulting.name))
+ end
+ end
+ end
+ end
+
+ -- Check if pump storage is not full
+ if not hasOutput then
+ local fluidCapacity=meta:get_int("industrialtest.fluidCapacity")
+ hasOutput=fluidCapacity-fluidAmount>=industrialtest.api.nodeFluidCapacity
+ end
+
+ return hasOutput
+end
+
+function industrialtest.Pump.shouldDeactivate(self,pos)
+ return not self:shouldActivate(pos)
+end
+
+function industrialtest.Pump.afterDeactivation(self,pos)
+ -- If machine was deactivated then make sure to update formspec
+ local meta=minetest.get_meta(pos)
+ meta:set_float("srcTime",0)
+ self:updateFormspec(pos)
+end
+
+function industrialtest.Pump.activeUpdate(self,pos,elapsed,meta,inv)
+ local nodeUnderPos=vector.offset(pos,0,-1,0)
+ local nodeUnder=minetest.get_node(nodeUnderPos)
+ local fluidAmount=meta:get_int("industrialtest.fluidAmount")
+ local fluidCapacity=meta:get_int("industrialtest.fluidCapacity")
+ local shouldUpdateFormspec=false
+
+ -- First try to pump fluid under
+ -- Check if there is node that can be pumped under pump
+ if fluidCapacity-fluidAmount>=industrialtest.api.nodeFluidCapacity and industrialtest.api.getPumpFluid(nodeUnder.name) then
+ local srcTime=meta:get_float("srcTime")+elapsed*industrialtest.api.getMachineSpeed(meta)
+ if srcTime>=self._pumpTime then
+ fluidAmount=fluidAmount+industrialtest.api.nodeFluidCapacity
+ meta:set_string("industrialtest.fluidType",nodeUnder.name)
+ meta:set_int("industrialtest.fluidAmount",fluidAmount)
+ minetest.remove_node(nodeUnderPos)
+ srcTime=0
+ end
+ industrialtest.api.addPower(meta,-self._opPower)
+ meta:set_float("srcTime",srcTime)
+ shouldUpdateFormspec=true
+ end
+
+ if meta:get_int("hasInputTarget")>0 then
+ local polledTargets=self.pollInputTargets(pos)
+ local fluidType=meta:get_string("industrialtest.fluidType")
+ for _,target in ipairs(polledTargets) do
+ local moved=math.min(fluidCapacity-fluidAmount,target.fluidInfo.remaining)
+ fluidAmount=fluidAmount+moved
+ target.pullFluid(moved)
+ meta:set_string("industrialtest.fluidType",target.fluidInfo.fluidType)
+ shouldUpdateFormspec=true
+ if fluidCapacity-fluidAmount<=0 then
+ break
+ end
+ end
+ meta:set_int("industrialtest.fluidAmount",fluidAmount)
+ end
+
+ -- Try to push fluid into item if available
+ local inv=meta:get_inventory()
+ local srcSlot=inv:get_stack("src",1)
+ if not srcSlot:is_empty() then
+ local def=srcSlot:get_definition()
+ if def.groups._industrialtest_simpleFluidStorage and def._industrialtest_simpleFluidStorageCapacity and fluidAmount>=def._industrialtest_simpleFluidStorageCapacity and
+ def._industrialtest_getResultingFluidStorageItemByNode then
+ local fluidType=meta:get_string("industrialtest.fluidType")
+ local resulting=def._industrialtest_getResultingFluidStorageItemByNode(fluidType)
+ if resulting then
+ local dstSlot=inv:get_stack("dst",1)
+ local resultingStack=ItemStack(resulting.name)
+ if dstSlot:item_fits(resultingStack) then
+ dstSlot:add_item(resultingStack)
+ inv:set_stack("dst",1,dstSlot)
+ srcSlot:take_item()
+ inv:set_stack("src",1,srcSlot)
+ fluidAmount=fluidAmount-def._industrialtest_simpleFluidStorageCapacity
+ meta:set_int("industrialtest.fluidAmount",fluidAmount)
+ shouldUpdateFormspec=true
+ end
+ end
+ end
+ end
+
+ -- Try to push fluid into neighbour target
+ if meta:get_int("hasOutputTarget")>0 then
+ local outputTargets=minetest.deserialize(meta:get_string("outputTargets"))
+ for _,targetPos in ipairs(outputTargets) do
+ local targetNode=minetest.get_node(targetPos)
+ local targetDef=minetest.registered_nodes[targetNode.name]
+ local fluidType=meta:get_string("industrialtest.fluidType")
+ if targetDef and targetDef._industrialtest_self and targetDef._industrialtest_self.canPushFluid and
+ targetDef._industrialtest_self.onPumpFluidPush and targetDef._industrialtest_self:canPushFluid(targetPos,fluidType,fluidAmount) then
+ fluidAmount=targetDef._industrialtest_self:onPumpFluidPush(targetPos,pos,fluidType,fluidAmount)
+ end
+ end
+ meta:set_int("industrialtest.fluidAmount",fluidAmount)
+ end
+
+ return shouldUpdateFormspec
+end
+
+function industrialtest.Pump.action(self,pos)
+ self.determinePumpTargets(pos)
+ self:triggerIfNeeded(pos)
+end
+
+-- Scans neighbour positions for pump targets
+function industrialtest.Pump.determinePumpTargets(pos)
+ 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 inputTargets={}
+ local outputTargets={}
+ for _,neighbour in ipairs(neighbourPositions) do
+ local node=minetest.get_node(neighbour)
+ local targetDef=industrialtest.api.getPumpTarget(node.name)
+ if targetDef then
+ if targetDef.direction=="i" then
+ table.insert(inputTargets,neighbour)
+ elseif targetDef.direction=="o" then
+ table.insert(outputTargets,neighbour)
+ end
+ end
+ end
+ local meta=minetest.get_meta(pos)
+ meta:set_string("inputTargets",minetest.serialize(inputTargets))
+ meta:set_int("hasInputTarget",#inputTargets>0 and 1 or 0)
+ meta:set_string("outputTargets",minetest.serialize(outputTargets))
+ meta:set_int("hasOutputTarget",#outputTargets>0 and 1 or 0)
+end
+
+-- \brief Checks all input targets if they have any fluid incoming
+-- \param pos vector
+-- \returns table
+function industrialtest.Pump.pollInputTargets(pos)
+ local meta=minetest.get_meta(pos)
+ local inputTargets=minetest.deserialize(meta:get_string("inputTargets"))
+ local fluidType=meta:get_string("industrialtest.fluidType")
+ local result={}
+ for _,targetPos in ipairs(inputTargets) do
+ local targetNode=minetest.get_node(targetPos)
+ local targetDef=minetest.registered_nodes[targetNode.name]
+ if targetDef and targetDef._industrialtest_self and targetDef._industrialtest_self.pullFluid then
+ local fluidInfo=targetDef._industrialtest_self:pullFluid(targetPos,0)
+ if fluidInfo and (fluidInfo.fluidType==fluidType or fluidType=="ignore") then
+ table.insert(result,{
+ pos=targetPos,
+ fluidInfo=fluidInfo,
+ pullFluid=function(amount)
+ return targetDef._industrialtest_self:pullFluid(targetPos,amount)
+ end
+ })
+ fluidType=fluidInfo.fluidType
+ end
+ end
+ end
+ return result
+end
+
+industrialtest.Pump:register()
+
+minetest.register_abm({
+ label="Pump pumping",
+ nodenames={"industrialtest:pump"},
+ interval=industrialtest.config.updateDelay,
+ chance=1,
+ action=function(pos)
+ industrialtest.Pump:action(pos)
+ end
+})
+
+minetest.register_craft({
+ type="shaped",
+ output="industrialtest:pump",
+ recipe={
+ {"industrialtest:empty_cell","industrialtest:electronic_circuit","industrialtest:empty_cell"},
+ {"industrialtest:empty_cell","industrialtest:machine_block","industrialtest:empty_cell"},
+ {"industrialtest:mining_pipe",industrialtest.elementKeys.treetap,"industrialtest:mining_pipe"}
+ }
+})