-- IndustrialTest -- Copyright (C) 2023 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.api={} industrialtest.api.maceratorRecipes={} industrialtest.api.compressorRecipes={} industrialtest.api.extractorRecipes={} industrialtest.api.geothermalGeneratorFuels={} industrialtest.api.waterMillFuels={} industrialtest.api.storageCells={} industrialtest.api.lvPowerFlow=600 industrialtest.api.mvPowerFlow=2400 industrialtest.api.hvPowerFlow=10200 industrialtest.api.evPowerFlow=40800 industrialtest.api.ivPowerFlow=163800 industrialtest.internal.clamp=function(num,min,max) return math.max(math.min(num,max),min) end -- \brief Adds power storage to metadata -- \param capacity How much EU item/node can store -- \param flow How much EU can flow in or out item/node per industrialtest.updateDelay -- \param ioConfig Input/Output configuration in following side order: -X, +X, -Y, +Y, -Z, +Z -- a - bidirectional, i - input, o - output -- \returns nil industrialtest.api.addPowerStorage=function(meta,capacity,flow,ioConfig) meta:set_int("industrialtest.powerCapacity",capacity) meta:set_int("industrialtest.powerFlow",flow) meta:set_int("industrialtest.powerAmount",0) meta:set_string("industrialtest.ioConfig",ioConfig) end -- \brief Takes rotated node and side and outputs normalized side that can be used for ioConfig lookups -- \param pos Vector with node position -- \param side Node side. See industrialtest.api.addPowerStorage for possible values -- \returns Normalized side or in case of failure side argument back industrialtest.api.normalizeSide=function(pos,side) local node=minetest.get_node(pos) -- FIXME: improve code quality there local translation={ [0]={ 1,2,3,4,5,6 }, [1]={ 5,6,3,4,2,1 }, [2]={ 1,2,3,4,6,5 }, [3]={ 6,5,3,4,1,2 } } if node.param2>3 then return side end return translation[node.param2][side] end -- \brief Checks if metadata contains power storage -- \param meta MetaDataRef which should be checked -- \returns true if metadata contains power storage, false otherwise industrialtest.api.hasPowerStorage=function(meta) local values={"industrialtest.powerCapacity","industrialtest.powerFlow","industrialtest.powerAmount","industrialtest.ioConfig"} for _,value in ipairs(values) do if not meta:contains(value) then return false end end return true end -- \brief Updates itemstack description to show current power storage information, additionally updates item wear bar. -- Function doesn't check if itemstack contains power storage so you should be sure that it does before calling this function -- \param itemstack ItemStack which should be updated -- \returns nil industrialtest.api.updateItemPowerText=function(itemstack) local meta=itemstack:get_meta() local def=minetest.registered_tools[itemstack:get_name()] meta:set_string("description",S("@1\n@2 / @3 EU",def.description,meta:get_int("industrialtest.powerAmount"),meta:get_int("industrialtest.powerCapacity"))) itemstack:set_wear(65535-meta:get_int("industrialtest.powerAmount")/meta:get_int("industrialtest.powerCapacity")*65534) end -- \brief Adds power storage to item depending on it's definition -- \param itemstack ItemStack to which item storage should be added -- \returns true if power storage was successfully added, false otherwise industrialtest.api.preparePowerStorageItem=function(itemstack) local meta=itemstack:get_meta() local def=minetest.registered_tools[itemstack:get_name()] if industrialtest.api.hasPowerStorage(meta) or not def or not def._industrialtest_powerStorage or not def._industrialtest_powerCapacity or not def._industrialtest_powerFlow then return false end industrialtest.api.addPowerStorage(meta,def._industrialtest_powerCapacity,def._industrialtest_powerFlow,"n/a") industrialtest.api.updateItemPowerText(itemstack) return true end -- \brief Sets uses metadata value depending on item's definition -- \param itemstack ItemStack which should be altered -- \returns true if value was successfully added, false otherwise industrialtest.api.prepareToolItem=function(itemstack) local def=minetest.registered_tools[itemstack:get_name()] if not def or not def._industrialtest_tool or not def.tool_capabilities or not def.tool_capabilities.uses then return false end local meta=itemstack:get_meta() meta:set_int("industrialtest.uses",def.tool_capabilities.uses) return true end -- \brief Adds wear to item after it's use -- \param itemstack ItemStack to which wear should be added -- \returns nil industrialtest.api.afterToolUse=function(itemstack) local meta=itemstack:get_meta() local def=minetest.registered_tools[itemstack:get_name()] if not def or not def._industrialtest_tool or not def.tool_capabilities or not def.tool_capabilities.uses then return end if not meta:contains("industrialtest.uses") then industrialtest.prepareToolItem(itemstack) end local uses=meta:get_int("industrialtest.uses")-1 if uses==0 then itemstack:set_count(0) minetest.sound_play({name="default_tool_breaks"},{ gain=1, fade=0, pitch=1 },true) return end meta:set_int("industrialtest.uses",uses) itemstack:set_wear(65535-uses/def.tool_capabilities.uses*65535) end -- \brief Checks if power storage is fully charged -- \param meta MetaDataRef which should be checked -- \returns true if power storage is fully charged, false otherwise industrialtest.api.isFullyCharged=function(meta) return meta:get_int("industrialtest.powerAmount")>=meta:get_int("industrialtest.powerCapacity") end -- \brief Adds power to power storage. Function doesn't check if meta contains power storage so you must be sure that it does. -- \param meta MetaDataRef to which power should be added -- \param amount Amount of power to add -- \returns How much of power was actually added industrialtest.api.addPower=function(meta,amount) local powerAmount=meta:get_int("industrialtest.powerAmount") local powerCapacity=meta:get_int("industrialtest.powerCapacity") local prevPowerAmount=powerAmount powerAmount=industrialtest.internal.clamp(powerAmount+amount,0,powerCapacity) meta:set_int("industrialtest.powerAmount",powerAmount) return powerAmount-prevPowerAmount end -- \brief Adds power to itemstack. Function checks if itemstack has power storage. -- \param itemstack ItemStack to which add power -- \param amount How much power to add -- \returns Amount of power added industrialtest.api.addPowerToItem=function(itemstack,amount) local meta=itemstack:get_meta() if not industrialtest.api.hasPowerStorage(meta) then return 0 end local added=industrialtest.api.addPower(meta,amount) industrialtest.api.updateItemPowerText(itemstack) return added end -- \brief Adds power to destination metadata while subtracting it from source metadata -- \Param srcMeta MetaDataRef from which take power -- \param destMeta MetaDataRef to which add power -- \returns How much of power was actually transferred industrialtest.api.transferPower=function(srcMeta,destMeta,amount) local currentFlow=math.min(srcMeta:get_int("industrialtest.powerAmount"),amount) if currentFlow==0 then return 0 end local actualFlow=industrialtest.api.addPower(destMeta,currentFlow) srcMeta:set_int("industrialtest.powerAmount",srcMeta:get_int("industrialtest.powerAmount")-actualFlow) return actualFlow end -- \brief Adds power to destination itemstack while subtracting it from source metadata -- \param srcMeta MetaDataRef from which take power -- \param itemstack ItemStack to which add power -- \returns How much of power was actually transferred industrialtest.api.transferPowerToItem=function(srcMeta,itemstack,amount) local currentFlow=math.min(srcMeta:get_int("industrialtest.powerAmount"),amount) if currentFlow==0 then return 0 end local actualFlow=industrialtest.api.addPowerToItem(itemstack,currentFlow) srcMeta:set_int("industrialtest.powerAmount",srcMeta:get_int("industrialtest.powerAmount")-actualFlow) return actualFlow end -- \brief Adds power to destination metadata while subtracting it from source itemstack -- \param srcItemstack ItemStack from which subtract power -- \param meta MetaDataRef to which add power -- \param amount How much power should be transferred -- \returns How much of power was actually transferred industrialtest.api.transferPowerFromItem=function(srcItemstack,meta,amount) local srcMeta=srcItemstack:get_meta() local currentFlow=math.min(srcMeta:get_int("industrialtest.powerAmount"),amount) if currentFlow==0 then return 0 end local actualFlow=industrialtest.api.addPower(meta,currentFlow) industrialtest.api.addPowerToItem(srcItemstack,-actualFlow) return actualFlow end -- \brief Transfers power from source node to all neighbouring nodes -- \param pos Vector with position of source node -- \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) local meta=minetest.get_meta(pos) if not industrialtest.api.hasPowerStorage(meta) then return false end 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 neighbours={} for key,value in ipairs(neighbourPositions) do neighbours[key]=minetest.get_meta(value) end local neighboursContainingPower=0 for key,value in ipairs(neighbours) do local side=industrialtest.api.getOppositeSide(key) if industrialtest.api.hasPowerStorage(value) and industrialtest.api.isPowerInput(value,side) and not industrialtest.api.isFullyCharged(value) then neighboursContainingPower=neighboursContainingPower+1 else neighbourPositions[key]=0 neighbours[key]=0 end end if neighboursContainingPower==0 then return false end local powerFlow=meta:get_int("industrialtest.powerFlow") local powerDistribution=math.floor(powerFlow/neighboursContainingPower) -- TODO: if supplying machine power flow is too large for receiving machine to handle then that machine should explode local roomAvailable=false local transferred=false for key,value in ipairs(neighbours) do if value~=0 then local normalizedKey=industrialtest.api.normalizeSide(pos,key) if industrialtest.api.isPowerOutput(meta,normalizedKey) then if industrialtest.api.transferPower(meta,value,powerDistribution)>0 then transferred=true end local def=minetest.registered_nodes[minetest.get_node(neighbourPositions[key]).name] if def then local updateFormspec=def._industrialtest_updateFormspec if updateFormspec then updateFormspec(neighbourPositions[key]) end local onPowerFlow=def._industrialtest_onPowerFlow if onPowerFlow and transferred then onPowerFlow(neighbourPositions[key],industrialtest.api.getOppositeSide(key)) end end minetest.get_node_timer(neighbourPositions[key]):start(industrialtest.updateDelay) if not industrialtest.api.isFullyCharged(value) then roomAvailable=true end end end end return roomAvailable,transferred end -- \brief Starts node timer of neighbouring nodes with center node -- \param pos Vector with position of center node -- \returns nil industrialtest.api.triggerNeighbours=function(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) } for key,value in ipairs(neighbourPositions) do local meta=minetest.get_meta(value) local side=industrialtest.api.getOppositeSide(key) if industrialtest.api.hasPowerStorage(meta) and industrialtest.api.isPowerOutput(meta,side) then minetest.get_node_timer(value):start(industrialtest.updateDelay) end end end -- \brief Returns opposite side of provided one -- \param side Side number. See industrialtest.api.addPowerStorage for order -- \returns Opposite side industrialtest.api.getOppositeSide=function(side) return (side%2==0 and side-1 or side+1) end -- \brief Returns connections of node with power storage -- \param pos Vector with position of node with power storage -- \returns Table with side numbers industrialtest.api.getConnections=function(pos) 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 index=1 for key,value in ipairs(neighbourPositions) do local meta=minetest.get_meta(value) if industrialtest.api.hasPowerStorage(meta) then result[index]=key index=index+1 end end return result end -- \brief Changes node's power IO config. Function doesn't check if meta actually contains power storage. -- \param meta MetaDataRef of node which power IO config should be changed -- \param side Side number. See industrialtest.api.addPowerStorage to check order. -- \param mode Side mode. See industrialtest.api.addPowerStorage for possible values. -- \returns nil industrialtest.api.changeIoConfig=function(meta,side,mode) local ioConfig=meta:get_string("industrialtest.ioConfig") ioConfig=string.sub(ioConfig,1,side-1)..mode..string.sub(ioConfig,side+1) meta:set_string("industrialtest.ioConfig",ioConfig) end -- \brief Checks if provided side is power input -- \param meta MetaDataRef of node -- \param side Side number. See industrialtest.api.addPowerStorage to check order. -- \returns true if provided side is power input, false otherwise industrialtest.api.isPowerInput=function(meta,side) local ioConfig=meta:get_string("industrialtest.ioConfig") local mode=string.sub(ioConfig,side,side) return (mode=="i" or mode=="a") end -- \brief Checks if provided side is power output -- \param meta MetaDataRef of node -- \param side Side number. See industrialtest.api.addPowerStorage to check order. -- \returns true if provided side is power output, false otherwise industrialtest.api.isPowerOutput=function(meta,side) local ioConfig=meta:get_string("industrialtest.ioConfig") local mode=string.sub(ioConfig,side,side) return (mode=="o" or mode=="a") end -- \brief Registers dust of certain resource -- \param name Technical name of resource -- \param displayName Display name of resource -- \param resources List of tables with following keys: , , [count(1)] -- <> - required, [] - optional, () - default value -- \param color HTML color of dust -- \param registerMaceratorRecipe If true macerator recipe for dust will be registered -- \returns nil industrialtest.api.registerResourceDust=function(name,displayName,resources,color,registerMaceratorRecipe) minetest.register_craftitem("industrialtest:"..name.."_dust",{ description=S(displayName.." Dust"), inventory_image="industrialtest_dust.png", color=color }) if registerMaceratorRecipe then for _,value in ipairs(resources) do industrialtest.api.registerMaceratorRecipe({ output="industrialtest:"..name.."_dust "..(value.count or 1), recipe=value.resource }) end end end -- \brief Registers plate of certain resource -- \param name Technical name of resource -- \param displayName Display name of resource -- \param resources List of tables with following keys: , , [count(1)] -- <> - required, [] - optional, () - default value -- \param color HTML color of plate -- \param registerCompressorRecipe If true compressor recipe for plate will be registered -- \returns nil industrialtest.api.registerPlate=function(name,displayName,resources,color,registerCompressorRecipe) minetest.register_craftitem("industrialtest:"..name.."_plate",{ description=S(displayName.." Plate"), inventory_image="industrialtest_plate.png", color=color }) if registerCompressorRecipe then for _,value in ipairs(resources) do industrialtest.api.registerCompressorRecipe({ output="industrialtest:"..name.."_plate "..(value.count or 1), recipe=value.resource }) end end end -- \brief Registers cell with certain fluid -- \param name Technical name of cell -- \param displayName Display name of cell -- \param node Node which can be picked up with this cell -- \returns nil industrialtest.api.registerStorageCell=function(name,displayName,node,modname) if not modname then modname="industrialtest" end minetest.register_craftitem("industrialtest:"..name.."_cell",{ description=S(displayName.." Cell"), inventory_image=modname.."_"..name.."_cell.png", on_place=function(itemstack,user,pointed) if pointed.type~="node" or not user or not user:is_player() then return nil end local node=minetest.get_node_or_nil(pointed.above) local storage=industrialtest.api.getStorageCell("industrialtest:"..name.."_cell") if storage.node then if node.name~="air" and node.name~=storage.node then return nil end minetest.set_node(pointed.above,{name=storage.node}) if itemstack:get_count()==1 then itemstack:set_name("industrialtest:empty_cell") else local inv=user:get_inventory() inv:add_item("main",ItemStack("industrialtest:empty_cell")) itemstack:take_item() end return itemstack end return nil end }) industrialtest.api.storageCells["industrialtest:"..name.."_cell"]={ name="industrialtest:"..name.."_cell", node=node } end -- \brief Returns registred storage cell by name -- \param name Storage cell name -- \returns Table with following keys: name, node or nil in case of failure industrialtest.api.getStorageCell=function(name) return industrialtest.api.storageCells[name] end -- \brief Returns registered storage cells by node -- \param node Node ID -- \returns Table with following keys: name, node or nil in case of failure industrialtest.api.getStorageCellByNode=function(node) for _,value in pairs(industrialtest.api.storageCells) do if value.node==node then return value end end return nil end -- \brief Registers macerator recipe -- \param config Table with keys: , , [time(2)] -- \returns nil industrialtest.api.registerMaceratorRecipe=function(config) local definition={ output=config.output or "", recipe=config.recipe or "", time=config.time or 2 } industrialtest.api.maceratorRecipes[definition.recipe]=definition end -- \brief Returns macerator recipe result -- \param recipe String ID of resulting conten -- \returns Table with following keys: output, recipe, time industrialtest.api.getMaceratorRecipeResult=function(recipe) return industrialtest.api.maceratorRecipes[recipe] end -- \brief Registers compressor recipe -- \param config Table with following keys: , , [time(2)], [count(1)] -- \returns nil industrialtest.api.registerCompressorRecipe=function(config) local definition={ output=config.output or "", recipe=config.recipe or "", time=config.time or 2, count=config.count or 1 } industrialtest.api.compressorRecipes[definition.recipe]=definition end -- \brief Returns macerator recipe result -- \param recipe String ID of resulting conten -- \returns Table with following keys: output, recipe, time industrialtest.api.getCompressorRecipeResult=function(recipe) return industrialtest.api.compressorRecipes[recipe] end industrialtest.api.registerExtractorRecipe=function(config) local definition={ output=config.output or "", recipe=config.recipe or "", time=config.time or 2 } industrialtest.api.extractorRecipes[definition.recipe]=definition end industrialtest.api.getExtractorRecipeResult=function(recipe) return industrialtest.api.extractorRecipes[recipe] end -- \brief Registers fuel that can be used in geothermal generator -- \param fuel Table with following keys: , , -- which is a table containing items which are tables with following keys: , -- \returns nil industrialtest.api.registerGeothermalGeneratorFuel=function(config) local definition={ name=config.name or "", calorificValue=config.calorificValue or 0, texture=config.texture or "industrialtest_gui_fluid_bg.png", storageItems=config.storageItems or {} } industrialtest.api.geothermalGeneratorFuels[definition.name]=definition end -- \brief Returns generator fuel information -- \param name Name of fuel -- \returns Table with following keys: name, calorificValue, storageItems industrialtest.api.getGeothermalGeneratorFuel=function(name) return industrialtest.api.geothermalGeneratorFuels[name] end -- \brief Returns generator fuel information by item name -- \param name ID of item -- \returns Table with following keys: name, calorificValue, storageItems or nil in case of failure industrialtest.api.getGeothermalGeneratorFuelByItem=function(name) for _,value in pairs(industrialtest.api.geothermalGeneratorFuels) do for _,item in ipairs(value.storageItems) do if item.name==name then return value end end end return nil end -- \brief Registers fuel that can be used in water mill -- \param fuel Table with following keys: , , -- which is a table containing items which are tables with following keys: , -- \returns nil industrialtest.api.registerWaterMillFuel=function(config) local definition={ name=config.name or "", calorificValue=config.calorificValue or 0, texture=config.texture or "industrialtest_gui_fluid_bg.png", storageItems=config.storageItems or {} } industrialtest.api.waterMillFuels[definition.name]=definition end -- \brief Returns water mill fuel information -- \param name Name of fuel -- \returns Table with following keys: name, calorificValue, storageItems industrialtest.api.getWaterMillFuel=function(name) return industrialtest.api.waterMillFuels[name] end -- \brief Returns water mill fuel information by item name -- \param name ID of item -- \returns Table with following keys: name, calorificValue, storageItems or nil in case of failure industrialtest.api.getWaterMillFuelByItem=function(name) for _,value in pairs(industrialtest.api.waterMillFuels) do for _,item in ipairs(value.storageItems) do if item.name==name then return value end end end return nil end