From 8ca2a44070230e9b5c147fda453a12fe671d25cb Mon Sep 17 00:00:00 2001 From: mrkubax10 Date: Tue, 11 Nov 2025 19:52:30 +0100 Subject: [PATCH] Implement Pump --- init.lua | 1 + machines/pump.lua | 407 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 408 insertions(+) create mode 100644 machines/pump.lua 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"} + } +})