From fe55ade9b046366515a4fef7fdb6f71b8fba3996 Mon Sep 17 00:00:00 2001 From: mrkubax10 Date: Thu, 20 Nov 2025 20:41:20 +0100 Subject: [PATCH] Implement Magnetizer --- init.lua | 1 + machines/magnetizer.lua | 272 ++++++++++++++++++++++++++++++++++++++++ nodes.lua | 125 ++++++++++++++++++ 3 files changed, 398 insertions(+) create mode 100644 machines/magnetizer.lua diff --git a/init.lua b/init.lua index d4c3e4d..d084ff4 100644 --- a/init.lua +++ b/init.lua @@ -56,6 +56,7 @@ dofile(modpath.."/machines/generator.lua") dofile(modpath.."/machines/induction_furnace.lua") dofile(modpath.."/machines/iron_furnace.lua") dofile(modpath.."/machines/macerator.lua") +dofile(modpath.."/machines/magnetizer.lua") dofile(modpath.."/machines/mass_fabricator.lua") dofile(modpath.."/machines/miner.lua") dofile(modpath.."/machines/nuclear_reactor.lua") diff --git a/machines/magnetizer.lua b/machines/magnetizer.lua new file mode 100644 index 0000000..200c28f --- /dev/null +++ b/machines/magnetizer.lua @@ -0,0 +1,272 @@ +-- 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") + +local function findFenceRailEnd(initialPosition,direction) + local y=initialPosition.y + local railNode=minetest.get_node(vector.new(initialPosition.x,y,initialPosition.z)) + while minetest.get_item_group(railNode.name,"_industrialtest_metalFence")>0 and math.abs(initialPosition.y-y)<=19 do + y=y+direction + railNode=minetest.get_node(vector.new(initialPosition.x,y,initialPosition.z)) + end + return y-direction +end + +local function hasMetalBoots(player) + local inv + if industrialtest.mtgAvailable then + _,inv=armor:get_valid_player(player,"") + elseif industrialtest.mclAvailable then + inv=player:get_inventory() + end + + if inv then + local armorList=inv:get_list("armor") + assert(armorList) + local requiredGroups={ + "armor_iron", + "armor_gold", + "armor_bronze", + "_industrialtest_electricArmor" + } + -- 3D armor boots have to be hardcoded here because they don't provide any group depending on material + local requiredNames={ + "3d_armor:boots_steel", + "3d_armor:boots_gold", + "3d_armor:boots_bronze" + } + for _,itemstack in ipairs(armorList) do + local def=itemstack:get_definition() + if def and def.groups and def.groups.armor_feet then + for _,group in ipairs(requiredGroups) do + if def.groups[group] then + -- Matching group succeeded + return true + end + end + end + for _,itemname in ipairs(requiredNames) do + if itemname==itemstack:get_name() then + -- Matching itemname succeeded + return true + end + end + end + end + + return false +end + +industrialtest.Magnetizer=table.copy(industrialtest.ElectricMachine) +industrialtest.internal.unpackTableInto(industrialtest.Magnetizer,{ + name="industrialtest:magnetizer", + description=S("Magnetizer"), + 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_magnetizer_front.png" + }, + sounds="metal", + facedir=true, + storageLists={ + "powerStorage" + }, + powerLists={ + { + list="powerStorage", + direction="i" + } + }, + capacity=industrialtest.api.lvPowerFlow*2, + flow=industrialtest.api.lvPowerFlow, + ioConfig="iiiiii", + requiresWrench=true, + hasPowerInput=true, + _capacityPerFence=15, + _opPower=4 +}) + +function industrialtest.Magnetizer.onConstruct(self,pos) + local meta=minetest.get_meta(pos) + local inv=meta:get_inventory() + inv:set_size("powerStorage",1) + self:determineFenceRail(pos) + self:determinePowerCapacity(pos) + industrialtest.ElectricMachine.onConstruct(self,pos) +end + +function industrialtest.Magnetizer.onDestruct(self,pos) + self.detachFenceRail(pos) + industrialtest.ElectricMachine.onDestruct(self,pos) +end + +function industrialtest.Magnetizer.onDig(self,pos,node,digger) + self.detachFenceRail(pos) + return industrialtest.ElectricMachine.onDig(self,pos,node,digger) +end + +function industrialtest.Magnetizer.getFormspec(self,pos) + local parentFormspec=industrialtest.ElectricMachine.getFormspec(self,pos) + local meta=minetest.get_meta(pos) + local powerPercent=meta:get_int("industrialtest.powerAmount")/meta:get_int("industrialtest.powerCapacity")*100 + local formspec={ + (powerPercent>0 and "image[4.7,2.7;1,1;industrialtest_gui_electricity_bg.png^[lowpart:"..powerPercent..":industrialtest_gui_electricity_fg.png]" + or "image[4.7,2.7;1,1;industrialtest_gui_electricity_bg.png]"), + "list[context;powerStorage;4.7,3.7;1,1]", + "listring[context;powerStorage]" + } + return parentFormspec..table.concat(formspec,"") +end + +function industrialtest.Magnetizer.determinePowerCapacity(self,pos) + local meta=minetest.get_meta(pos) + if meta:contains("lowY") and meta:contains("highY") then + local lowY=meta:get_int("lowY") + local highY=meta:get_int("highY") + local capacity=self.capacity+(highY-lowY)*self._capacityPerFence + meta:set_int("industrialtest.powerCapacity",capacity) + end +end + +-- Checks all sides on the same Y level if there is rail made from metal fences attached +function industrialtest.Magnetizer.determineFenceRail(self,pos) + local neighbourPositions={ + vector.offset(pos,-1,0,0), + vector.offset(pos,1,0,0), + vector.offset(pos,0,0,-1), + vector.offset(pos,0,0,1) + } + local railPosition + for _,neighbourPosition in ipairs(neighbourPositions) do + local neighbourNode=minetest.get_node(neighbourPosition) + if minetest.get_item_group(neighbourNode.name,"_industrialtest_metalFence")>0 then + railPosition=neighbourPosition + break + end + end + if not railPosition then + return + end + + local direction=vector.subtract(railPosition,pos) + minetest.swap_node(pos,{ + name=self.name, + -- Some cryptic code that converts direction vector to param2 + param2=direction.z+1*math.abs(direction.z)+direction.x+2*math.abs(direction.x) + }) + + -- Find low and high points of fence rail + local lowY=findFenceRailEnd(railPosition,-1) + local highY=findFenceRailEnd(railPosition,1) + + -- Keep magnetizer position in fence metadata so new fences can be attached easily + for y=lowY,highY,1 do + local meta=minetest.get_meta(vector.new(railPosition.x,y,railPosition.z)) + meta:set_string("magnetizerPosition",minetest.serialize(pos)) + end + + local meta=minetest.get_meta(pos) + meta:set_string("railPosition",minetest.serialize(railPosition)) + meta:set_int("lowY",lowY) + meta:set_int("highY",highY) +end + +function industrialtest.Magnetizer.detachFenceRail(pos) + local meta=minetest.get_meta(pos) + if not meta:contains("railPosition") or not meta:contains("lowY") or not meta:contains("highY") then + return + end + + local railPosition=minetest.deserialize(meta:get_string("railPosition")) + local lowY=meta:get_int("lowY") + local highY=meta:get_int("highY") + for y=lowY,highY,1 do + local fenceMeta=minetest.get_meta(vector.new(railPosition.x,y,railPosition.z)) + fenceMeta:set_string("magnetizerPosition","") + end + meta:set_string("railPosition","") +end + +industrialtest.Magnetizer:register() + +minetest.register_craft({ + type="shaped", + output="industrialtest:magnetizer", + recipe={ + {industrialtest.elementKeys.powerCarrier,"industrialtest:iron_fence",industrialtest.elementKeys.powerCarrier}, + {industrialtest.elementKeys.powerCarrier,"industrialtest:machine_block",industrialtest.elementKeys.powerCarrier}, + {industrialtest.elementKeys.powerCarrier,"industrialtest:iron_fence",industrialtest.elementKeys.powerCarrier} + } +}) + +-- Players to which Y velocity should be added +local validatedPlayers={} +-- Time since last check to which players the effect should be applied +local validationDelta=0.2 +minetest.register_globalstep(function(dtime) + validationDelta=validationDelta+dtime + if validationDelta>=0.2 then + validatedPlayers={} + local players=minetest.get_connected_players() + for _,player in ipairs(players) do + local control=player:get_player_control() + if (control.jump or control.sneak) and hasMetalBoots(player) then + local props=player:get_properties() + local pos=player:get_pos() + local offsets={ + vector.new(props.collisionbox[1],0,0), + vector.new(props.collisionbox[4],0,0), + vector.new(0,0,props.collisionbox[3]), + vector.new(0,0,props.collisionbox[6]) + } + for _,offset in ipairs(offsets) do + local targetPos=vector.add(pos,offset) + local targetNode=minetest.get_node(targetPos) + if minetest.get_item_group(targetNode.name,"_industrialtest_metalFence")>0 then + local fenceMeta=minetest.get_meta(targetPos) + if fenceMeta:contains("magnetizerPosition") then + table.insert(validatedPlayers,{ + playerName=player:get_player_name(), + magnetizerPosition=minetest.deserialize(fenceMeta:get_string("magnetizerPosition")), + multiplier=(control.sneak and 0.55 or 1) + }) + break + end + end + end + end + end + end + + for _,validatedPlayer in ipairs(validatedPlayers) do + local player=minetest.get_player_by_name(validatedPlayer.playerName) + if player then + local magnetizerMeta=minetest.get_meta(validatedPlayer.magnetizerPosition) + local physicsOverride=player:get_physics_override() + local requiredPower=industrialtest.Magnetizer._opPower*physicsOverride.gravity + if magnetizerMeta:get_int("industrialtest.powerAmount")>=requiredPower then + industrialtest.api.addPower(magnetizerMeta,-requiredPower) + industrialtest.Magnetizer:requestPower(validatedPlayer.magnetizerPosition) + industrialtest.Magnetizer:updateFormspec(validatedPlayer.magnetizerPosition) + industrialtest.internal.addYVelocityClamped(player,30*dtime*physicsOverride.gravity*validatedPlayer.multiplier,5*physicsOverride.gravity) + end + end + end +end) diff --git a/nodes.lua b/nodes.lua index a5fec6e..060dd59 100644 --- a/nodes.lua +++ b/nodes.lua @@ -489,3 +489,128 @@ minetest.register_craft({ {industrialtest.elementKeys.glass,"industrialtest:advanced_alloy",industrialtest.elementKeys.glass} } }) + +-- \brief Function which should be called after iron fence was constructed +-- \param pos vector +-- \returns nil +local function ironFenceOnConstruct(pos) + local neighbours={ + vector.offset(pos,0,-1,0), + vector.offset(pos,0,1,0) + } + for _,neighbourPosition in ipairs(neighbours) do + local meta=minetest.get_meta(neighbourPosition) + if meta:contains("magnetizerPosition") then + local magnetizerPosition=minetest.deserialize(meta:get_string("magnetizerPosition")) + industrialtest.Magnetizer:determineFenceRail(magnetizerPosition) + industrialtest.Magnetizer:determinePowerCapacity(magnetizerPosition) + industrialtest.Magnetizer:updateFormspec(magnetizerPosition) + industrialtest.Magnetizer:requestPower(magnetizerPosition) + break + end + end + + neighbours={ + vector.offset(pos,1,0,0), + vector.offset(pos,-1,0,0), + vector.offset(pos,0,0,1), + vector.offset(pos,0,0,-1) + } + for _,neighbourPosition in ipairs(neighbours) do + local neighbourNode=minetest.get_node(neighbourPosition) + if neighbourNode.name=="industrialtest:magnetizer" then + local meta=minetest.get_meta(neighbourPosition) + if not meta:contains("railPosition") then + industrialtest.Magnetizer:determineFenceRail(neighbourPosition) + industrialtest.Magnetizer:determinePowerCapacity(neighbourPosition) + industrialtest.Magnetizer:updateFormspec(neighbourPosition) + industrialtest.Magnetizer:requestPower(neighbourPosition) + break + end + end + end +end + +-- \brief Function which should be called before iron fence was removed +-- \param pos vector +-- \returns nil +local function ironFenceOnDestruct(pos) + local meta=minetest.get_meta(pos) + if not meta:contains("magnetizerPosition") then + return + end + local magnetizerPosition=minetest.deserialize(meta:get_string("magnetizerPosition")) + industrialtest.Magnetizer.detachFenceRail(magnetizerPosition) +end + +-- \brief Function which should be called after iron fence was removed +-- \param meta table +-- \returns nil +local function ironFenceDetach(meta) + if not meta or not meta.fields or not meta.fields.magnetizerPosition then + return + end + local magnetizerPosition=minetest.deserialize(meta.fields.magnetizerPosition) + industrialtest.Magnetizer:determineFenceRail(magnetizerPosition) + industrialtest.Magnetizer:determinePowerCapacity(magnetizerPosition) + industrialtest.Magnetizer:updateFormspec(magnetizerPosition) +end + +if industrialtest.mtgAvailable then + local inventoryImage="default_fence_overlay.png^default_steel_block.png^default_fence_overlay.png^[makealpha:255,126,126" + default.register_fence("industrialtest:iron_fence",{ + description=S("Iron Fence"), + texture="default_steel_block.png", + inventory_image=inventoryImage, + wield_image=inventoryImage, + groups={ + cracky=1, + level=2, + _industrialtest_metalFence=1 + }, + sounds=default.node_sound_metal_defaults(), + connects_to={ + "group:fence", + "group:wood", + "group:tree", + "group:wall", + "industrialtest:magnetizer" + }, + on_construct=ironFenceOnConstruct, + on_destruct=ironFenceOnDestruct, + after_dig_node=function(pos,oldnode,oldmeta) + ironFenceDetach(oldmeta) + end + }) + minetest.register_craft({ + type="shaped", + output="industrialtest:iron_fence 4", + recipe={ + {"industrialtest:iron_plate","industrialtest:iron_plate","industrialtest:iron_plate"}, + {"industrialtest:iron_plate","industrialtest:iron_plate","industrialtest:iron_plate"} + } + }) +elseif industrialtest.mclAvailable then + mcl_fences.register_fence_def("iron_fence",{ + description=S("Iron Fence"), + tiles={"default_steel_block.png"}, + groups={ + _industrialtest_metalFence=1 + }, + connects_to={ + "group:fence", + "group:fence_gate", + "group:solid", + "industrialtest:magnetizer" + }, + on_construct=ironFenceOnConstruct, + on_destruct=ironFenceOnDestruct, + after_destruct=function(pos,oldnode,oldmeta) + ironFenceDetach(oldmeta) + end, + _mcl_fences_baseitem="industrialtest:iron_plate", + _mcl_fences_stickreplacer="industrialtest:iron_plate" + }) + -- mcl_fences.register_fence_def registers fences in it's own namespace so register alias here to keep compatibility + minetest.register_alias("industrialtest:iron_fence","mcl_fences:iron_fence") +end