Files
industrialtest/machines/pump.lua
2025-11-11 19:52:30 +01:00

408 lines
14 KiB
Lua

-- 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 <http://www.gnu.org/licenses/>.
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")<self._opPower then
return false
end
local hasInput=false
local nodeUnder=minetest.get_node(vector.offset(pos,0,-1,0))
local fluidAmount=meta:get_int("industrialtest.fluidAmount")
if fluidAmount>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"}
}
})