diff --git a/README.md b/README.md index 5f263f1..25905f8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Or just use /giveme replacer:replacer Usage: Right-click on a node of that type you want to replace other nodes with. Left-click (normal usage) on any nodes you want to replace with the type you previously right-clicked on. - SHIFT-Right-click in order to store a new pattern. + Sneak-Right-click (alternatively: aux1-Right-click) in order to store a new pattern. When in creative mode, the node will just be replaced. Your inventory will not be changed. diff --git a/depends.txt b/depends.txt index fef1434..fe0f5de 100644 --- a/depends.txt +++ b/depends.txt @@ -1,2 +1,5 @@ default? dye? +moreblocks? +stairs? +stairsplus? diff --git a/fs_history.lua b/fs_history.lua new file mode 100644 index 0000000..cbe83be --- /dev/null +++ b/fs_history.lua @@ -0,0 +1,206 @@ +-- apart from the history, the formspec also handles mode switches + +-- how many patterns are stored in the history? those don't take up much space, +-- but a too long list might not be overly helpful for the players either +replacer.max_hist_size = 20 + + +-- turn stored pattern string ( ) into something readable by human beeings +replacer.human_readable_pattern = function(pattern) + if(not(pattern)) then + return "(nothing)" + end + -- data is stored in the form " " + local parts = string.split(pattern, " ") + if(not(parts) or #parts < 3) then + return "(corrupted data)" + end + local node_name = parts[1] + local param2 = parts[3] + + local def = minetest.registered_nodes[ node_name ] + if(not(def)) then + return "(unknown node)" + end + local text = "'"..tostring(def.description or "- no description -").."'" + if(not(def.description) or def.description == "") then + text = "- no description -" + end + -- facedir is probably the most commonly used rotation variant + if( def.paramtype2 == "facedir" + or def.paramtype2 == "colorfacedir") then + local axis_names = {"y+ (Ground)", "z+ (North)", "z- (South)", + "x+ (East)", "x- (West)", "y- (Sky)"} + text = text.." Rotated: "..tostring(param2 % 4).. + " around axis: "..tostring( axis_names[ math.floor( (param2%24) / 4 ) + 1 ]) + -- wallmounted is diffrent + elseif( def.paramtype2 == "wallmounted" + or def.paramtype2 == "colorwallmounted") then + local axis_names = {"y+ (Ground)", "y- (Sky)", + "z+ (North)", "z- (South)", + "x+ (East)", "x- (West)"} + text = text.." Mounted at wall: "..tostring( axis_names[ (param2 % 6)+ 1 ]) + end + return text +end + + +-- set the replacer to a new pattern +replacer.set_to = function(player_name, pattern, player, itemstack) + if(not(player_name) or not(player) or not(itemstack)) then + return itemstack + end + -- fallback if nothing is given + if(not(pattern)) then + pattern = "default:dirt 0 0" + end + + local set_to = replacer.human_readable_pattern(pattern) + -- change the description of the tool so that it's easier to see which replacer (if you + -- have more than one in your inv) is set to which node + local meta = itemstack:get_meta() + -- actually store the new pattern + meta:set_string("pattern", pattern ) + + meta:set_string("description", "Node replacement tool set to:\n"..set_to.. + "\n["..tostring(pattern).."]") + + minetest.chat_send_player(player_name, "Node replacement tool set to: "..set_to.. + "["..tostring(pattern).."].") + + replacer.add_to_hist(player_name, pattern) + return itemstack -- nothing consumed but data changed +end + + +-- keep a history of stored patterns for each player (not for each replacer); +-- this history is not saved over server shutdown +replacer.add_to_hist = function(player_name, pattern) + if(not(player_name) or not(pattern) or pattern == "") then + return + end + if(not(replacer.history)) then + replacer.history = {} + end + if(not(replacer.history[ player_name ])) then + replacer.history[ player_name ] = {} + end + local index = table.indexof(replacer.history[ player_name ], pattern) + -- only add new entries; do not store duplicates + if(index and index > -1) then + return + end + -- remove the oldest entry + if(#replacer.history[ player_name ] >= replacer.max_hist_size) then + table.remove(replacer.history[ player_name ], 1) + end + table.insert(replacer.history[ player_name ], pattern) +end + + +-- show a formspec with a history of stored patterns to select from +replacer.get_formspec = function(player_name, current_pattern, player) + -- is the player in creative mode? + local in_creative_mode = (minetest.settings:get_bool("creative_mode") + or minetest.check_player_privs(player_name, {creative=true})) + -- count how many blocks of each type the player has in his inventory + local counted_inv = {} + if(not(in_creative_mode)) then + local inv_main = player:get_inventory():get_list("main") + for i, v in ipairs(inv_main) do + local stack_name = v:get_name() + if(not(counted_inv[ stack_name ])) then + counted_inv[ stack_name ] = 0 + end + counted_inv[ stack_name ] = counted_inv[ stack_name ] + v:get_count() + end + end + + -- find out which mode the player has currently selected + local current_mode = 1 + if(replacer.user_mode and replacer.user_mode[ player_name ]) then + current_mode = table.indexof(replacer.mode_names, replacer.user_mode[ player_name ]) + if(current_mode == -1) then + current_mode = 1 + end + end + + local formspec = "size[18,10]".. + "label[6,0;Node Replacement Tool Setup and History]".. + "button_exit[8,9.4;2,0.8;quit;Exit]".. + "label[0.2,8.5;Note: Selected mode and history are reset on server restart.\n".. + "Note: The selected mode is valid for *all* replacers you use. ".. + "The stored pattern is valid for *this particular* replacer only.]".. + "label[0.2,0.6;Select mode: When replacing (punching, left-click) or ".. + "placing (right-click) a block, ..]".. + "dropdown[0.2,1.0;17;select_mode;".. + table.concat(replacer.mode_descriptions, ",").. + ";"..tostring(current_mode)..";]".. + "label[0.2,2.1;Click here to set the replacer to a pattern you have stored before:]".. + "tablecolumns[color;".. + "text,align=right,tooltip=Amount of nodes of this type left in your inventory:".. + ";color;text,align=left,tooltip=Stored pattern:]".. + "table[0.2,2.5;17,6;replacer_history;" + -- make sure all variables exist and the current entry is stored + replacer.add_to_hist(player_name, current_pattern) + local hist_entries = {} + local selected = 1 + for i, v in ipairs(replacer.history[ player_name ]) do + if(v == current_pattern) then + selected = i + end + local amount_left = "#00FF00,infinite supply:,#00FF00" + if(not(in_creative_mode)) then + -- which item are we looking for? + local parts = v:split(" ") + if(not(parts) or #parts<1) then + parts = {"does not exist"} + -- TODO: handle this in a more general way + elseif(parts[1] == "default:dirt_with_grass") then + parts[1] = "default:dirt" + end + if(counted_inv[ parts[1] ]) then + amount_left = "#00FF00,"..tostring(counted_inv[ parts[1] ]).." available:".. + ",#00FF00" + else + amount_left = "#FF0000,none left!,#CFCFCF" + end + end + hist_entries[ i ] = tostring(amount_left)..",".. + minetest.formspec_escape(replacer.human_readable_pattern(v).." ["..v.."]") + end + return formspec..table.concat(hist_entries, ",")..";"..tostring(selected).."]" +end + + +-- the player has interacted with our formspec +minetest.register_on_player_receive_fields( function(player, formname, fields) + if(not(formname) or formname ~= "replacer:menu") then + return false + end + local player_name = player:get_player_name() + -- the player clicked on an entry in the history + if(fields and fields.replacer_history + and replacer.history and replacer.history[ player_name ]) then + -- find out which line it was + local selected = minetest.explode_table_event(fields.replacer_history) + if(selected and (selected.type == "CHG" or selected.type == "DLC") + and selected.row <= #replacer.history[ player_name ]) then + local itemstack = player:get_wielded_item() + itemstack = replacer.set_to(player_name, + replacer.history[ player_name ][ selected.row ], + player, itemstack) + player:set_wielded_item(itemstack) + return true + end + end + -- the player selected a mode + if(fields and fields.select_mode) then + local index = table.indexof(replacer.mode_descriptions, + minetest.formspec_escape(fields.select_mode)) + if(index and index > -1 and replacer.mode_names[ index ]) then + replacer.user_mode[ player_name ] = replacer.mode_names[ index ] + end + end + return true +end) diff --git a/init.lua b/init.lua index bfecb8c..c1aa311 100644 --- a/init.lua +++ b/init.lua @@ -21,6 +21,12 @@ -- Version 3.0 -- Changelog: +-- 02.09.2021 * Added a history of stored patterns (not saved over server restart) +-- * Added a menu to select a history entry. It is accessable via AUX1 + left click. +-- * Removed deprecated functions get/set_metadata(..) and renamed metadata to pattern +-- 29.09.2021 * AUX1 key works now same as SNEAK key for storing new pattern (=easier when flying) +-- * The description of the tool now shows which pattern is stored +-- * The description of the stored pattern is more human readable -- 09.12.2017 * Got rid of outdated minetest.env -- * Fixed error in protection function. -- * Fixed minor bugs. @@ -58,6 +64,13 @@ replacer.blacklist[ "protector:protect2"] = true; -- adds a tool for inspecting nodes and entities dofile(minetest.get_modpath("replacer").."/inspect.lua"); +-- adds a formspec with a history function (accessible with AUX1 + left click) +dofile(minetest.get_modpath("replacer").."/fs_history.lua"); + +-- adds support for the circular saw from moreblocks and similar nodes +-- and allows to replace the *material* while keeping shape - or vice versa +dofile(minetest.get_modpath("replacer").."/mode_of_replacement.lua"); + minetest.register_tool( "replacer:replacer", { description = "Node replacement tool", @@ -81,7 +94,6 @@ minetest.register_tool( "replacer:replacer", }, --]] node_placement_prediction = nil, - metadata = "default:dirt", -- default replacement: common dirt on_place = function(itemstack, placer, pointed_thing) @@ -93,8 +105,8 @@ minetest.register_tool( "replacer:replacer", local keys=placer:get_player_control(); - -- just place the stored node if now new one is to be selected - if( not( keys["sneak"] )) then + -- just place the stored node if no new one is to be selected + if( not( keys["sneak"] ) and not( keys["aux1"])) then return replacer.replace( itemstack, placer, pointed_thing, true ); end @@ -104,24 +116,18 @@ minetest.register_tool( "replacer:replacer", return nil; end - local pos = minetest.get_pointed_thing_position( pointed_thing, false ); + local pos = minetest.get_pointed_thing_position( pointed_thing, false ); -- node under local node = minetest.get_node_or_nil( pos ); - --minetest.chat_send_player( name, " Target node: "..minetest.serialize( node ).." at pos "..minetest.serialize( pos ).."."); - local metadata = "default:dirt 0 0"; + local pattern = "default:dirt 0 0"; if( node ~= nil and node.name ) then - metadata = node.name..' '..node.param1..' '..node.param2; + pattern = node.name..' '..node.param1..' '..node.param2; end - itemstack:set_metadata( metadata ); - minetest.chat_send_player( name, "Node replacement tool set to: '"..metadata.."'."); - - return itemstack; -- nothing consumed but data changed + return replacer.set_to(name, pattern, placer, itemstack) -- nothing consumed but data changed end, --- on_drop = func(itemstack, dropper, pos), - on_use = function(itemstack, user, pointed_thing) return replacer.replace( itemstack, user, pointed_thing, false ); @@ -135,7 +141,6 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) return nil; end local name = user:get_player_name(); - --minetest.chat_send_player( name, "You USED this on "..minetest.serialize( pointed_thing ).."."); if( pointed_thing.type ~= "node" ) then minetest.chat_send_player( name, " Error: No node."); @@ -145,8 +150,6 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) local pos = minetest.get_pointed_thing_position( pointed_thing, mode ); local node = minetest.get_node_or_nil( pos ); - --minetest.chat_send_player( name, " Target node: "..minetest.serialize( node ).." at pos "..minetest.serialize( pos ).."."); - if( node == nil ) then minetest.chat_send_player( name, "Error: Target node not yet loaded. Please wait a moment for the server to catch up."); @@ -154,15 +157,22 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) end - local item = itemstack:to_table(); + local meta = itemstack:get_meta() + local pattern = meta:get_string("pattern") -- make sure it is defined - if( not( item[ "metadata"] ) or item["metadata"]=="" ) then - item["metadata"] = "default:dirt 0 0"; + if(not(pattern) or pattern == "") then + pattern = "default:dirt 0 0"; + end + + local keys=user:get_player_control(); + if( keys["aux1"]) then + minetest.show_formspec(name, "replacer:menu", replacer.get_formspec(name, pattern, user)) + return nil end -- regain information about nodename, param1 and param2 - local daten = item[ "metadata"]:split( " " ); + local daten = pattern:split( " " ); -- the old format stored only the node name if( #daten < 3 ) then daten[2] = 0; @@ -175,6 +185,12 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) return nil; end + local daten = replacer.get_new_node_data(node, daten, name) + -- nothing to replace + if(not(daten)) then + return + end + if( node.name and node.name ~= "" and replacer.blacklist[ node.name ]) then minetest.chat_send_player( name, "Replacing blocks of the type '"..( node.name or "?" ).. "' is not allowed on this server. Replacement failed."); @@ -198,7 +214,6 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) return nil; end - -- in survival mode, the player has to provide the node he wants to place if( not(minetest.settings:get_bool("creative_mode") ) and not( minetest.check_player_privs( name, {creative=true}))) then @@ -207,7 +222,7 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) -- fortunately, dirt and dirt_with_grass does not make use of rotation if( daten[1] == "default:dirt_with_grass" ) then daten[1] = "default:dirt"; - item["metadata"] = "default:dirt 0 0"; + pattern = "default:dirt 0 0"; end -- does the player carry at least one of the desired nodes with him? @@ -219,20 +234,9 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) end - -- give the player the item by simulating digging if possible - if( node.name ~= "air" - and node.name ~= "ignore" - and node.name ~= "default:lava_source" - and node.name ~= "default:lava_flowing" - and node.name ~= "default:river_water_source" - and node.name ~= "default:river_water_flowing" - and node.name ~= "default:water_source" - and node.name ~= "default:water_flowing" - and node.name ~= "nalc:acid_source" - and node.name ~= "nalc:acid_flowing" - and node.name ~= "nalc:sand_source" - and node.name ~= "nalc:sand_flowing" ) then + if( node.name ~= "air" + and node.name ~= "ignore") then minetest.node_dig( pos, node, user ); @@ -240,21 +244,20 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) if( not( digged_node ) or digged_node.name == node.name ) then - minetest.chat_send_player( name, "Replacing '"..( node.name or "air" ).."' with '"..( item[ "metadata"] or "?" ).."' failed. Unable to remove old node."); - return nil; + -- some nodes - like liquids - cannot be digged. but they are buildable_to and + -- thus can be replaced + local node_def = minetest.registered_nodes[node.name] + if(not(node_def) or not(node_def.buildable_to)) then + minetest.chat_send_player( name, "Replacing '"..( node.name or "air" ).."' with '"..( pattern or "?" ).."' failed. Unable to remove old node."); + return nil; + end end end -- consume the item user:get_inventory():remove_item("main", daten[1].." 1"); - - --user:get_inventory():add_item( "main", node.name.." 1"); end - - --minetest.chat_send_player( name, "Replacing node '"..( node.name or "air" ).."' with '"..( item[ "metadata"] or "?" ).."'."); - - --minetest.place_node( pos, { name = item[ "metadata" ] } ); minetest.add_node( pos, { name = daten[1], param1 = daten[2], param2 = daten[3] } ); return nil; -- no item shall be removed from inventory end diff --git a/inspect.lua b/inspect.lua index bfc89d8..a0efced 100644 --- a/inspect.lua +++ b/inspect.lua @@ -58,7 +58,7 @@ replacer.inspect = function( itemstack, user, pointed_thing, mode, show_receipe end local name = user:get_player_name(); local keys = user:get_player_control(); - if( keys["sneak"] ) then + if( keys["sneak"] or keys["aux1"]) then show_receipe = true; end diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..f642ade --- /dev/null +++ b/mod.conf @@ -0,0 +1,5 @@ +name = replacer +description = Essential tool for builders. Allows to store material and rotation and place more blocks the same way without having to turn them all individually with the screwdriver. +optional_depends = default, dye, moreblocks, stairs, stairsplus +author = Sokomine +title = Replacer - Node Replace Tool diff --git a/mode_of_replacement.lua b/mode_of_replacement.lua new file mode 100644 index 0000000..5633d27 --- /dev/null +++ b/mode_of_replacement.lua @@ -0,0 +1,226 @@ + +-- store for each user which mode the user has selected +replacer.user_mode = {} + +-- descriptions for the dropdown menu (accessible via AUX1 + left-click) +replacer.mode_descriptions = { + "[ normal ] replace material, shape and orientation according to the stored pattern", + "[ material ] replace the material only (if possible), but keep shape and orientation", + "[ shape ] replace shape and orientation (if possible), but keep the material"} + +-- internal names for the above modes (will be stored in replacer.user_mode[ player_name ]) +replacer.mode_names = {"normal", "material", "shape"} + +-- make sure all mode descriptions are properly escaped for the dropdown menu +for i, v in ipairs(replacer.mode_descriptions) do + replacer.mode_descriptions[i] = minetest.formspec_escape(v) +end + +-- sometimes some few node names for on or two materials do not follow the +-- pattern of node names for that type; offer a way to translate them here +replacer.node_name_alternatives = {} +replacer.node_name_alternatives[ "default:mese_post_light_wood" ] = "default:mese_post_light" +replacer.node_name_alternatives[ "walls:desert_cobble" ] = "walls:desertcobble" + +-- support for the circular saw; table contains prefixes as indices plus +-- a list of known suffixes for that prefix +replacer.saw_prefixes = {} + +-- populate replacer.saw_prefixes +if(minetest.global_exists("circular_saw") and circular_saw.names) then + -- this is fixed for the saw and does not depend on which actual nodes are + -- registered for it later on; + -- we build this table replacer.saw_prefixes for faster lookup because + -- many shapes share the same prefix + for i, v in ipairs(circular_saw.names) do + local prefix = v[1].."_" + if(not(replacer.saw_prefixes[ prefix ])) then + replacer.saw_prefixes[ prefix ] = {v[2]} + else + table.insert(replacer.saw_prefixes[ prefix ], v[2]) + end + end +end + + +-- functions + +-- does node_name match any name from the circular saw? +-- or is it a node from the cnc machine? +-- and if so, which material is it? +-- returns: {created_by_machine, source node name, mod_name, prefix, material, suffix} +-- (the last four form the name of the node) +replacer.identify_shape_and_material = function(full_node_name) + local parts = full_node_name:split(":") + if(not(parts) or #parts < 2) then + return nil + end + local mod_name = parts[1] + local node_name = parts[2] + + -- it might be a full block - or any other drawtype not really covered here + local def = minetest.registered_nodes[ full_node_name ] + if(def and def.drawtype and def.drawtype == "normal") then + return {"normal", full_node_name, + mod_name, "", node_name, ""} + end + + -- a wooden fence rail + local prefix = "fence_rail_" + if(mod_name == "default" and node_name:sub(1, #prefix) == prefix) then + local material = node_name:sub(#prefix + 1) + return {"fence_rail", "default:"..material, "default", prefix, material, ""} + end + -- a wooden fence + prefix = "fence_" + if(mod_name == "default" and node_name:sub(1, #prefix) == prefix) then + local material = node_name:sub(#prefix + 1) + return {"fence", "default:"..material, "default", prefix, material, ""} + end + -- gates come in diffrent wood types as well + prefix = "gate_" + if(mod_name == "doors" and node_name:sub(1, #prefix) == prefix) then + local gate_suffixes = {"_open", "_closed"} + for i, suffix in ipairs(gate_suffixes) do + if(node_name:sub(-#suffix) == suffix) then + local material = node_name:sub(#prefix+1, -#suffix-1) + return {"gate", "default:"..material, "doors", prefix, material, suffix} + end + end + end + -- a mese post (comes in diffrent wood types) + prefix = "mese_post_light_" + if(full_node_name == "default:mese_post_light") then + return {"mese_post", "default:wood", "default", "mese_post_light_", "", ""} + elseif(mod_name == "default" and node_name:sub(1, #prefix) == prefix) then + local material = node_name:sub(#prefix + 1) + return {"mese_post", "default:"..material, "default", prefix, material, ""} + end + -- walls (usually made out of stone) + if(full_node_name == "walls:cobble") then + return {"walls", "default:cobble", "walls", "", "cobble", ""} + elseif(full_node_name == "walls:mossycobble") then + return {"walls", "default:mossycobble", "walls", "", "mossycobble", ""} + elseif(full_node_name == "walls:desertcobble") then + return {"walls", "default:desert_cobble", "walls", "", "desert_cobble", ""} + end + + -- it might be a regular stair (or similar node) from MinetestGame's stair mod + if(mod_name == "stairs") then + -- stair_inner_ and stair_outer_ need to be checked before stair_ is checked + -- because they are more specific + local stair_prefixes = {"stair_inner_", "stair_outer_", "stair_", "slab_"} + for i, prefix in ipairs(stair_prefixes) do + if(node_name:sub(1,#prefix) == prefix) then + local material = node_name:sub(#prefix+1) + return {"stairs", "default:"..material, + mod_name, prefix, material, ""} + end + end + return nil + end + + -- check if we are dealing with a node from the circular saw from moreblocks + for prefix, suffixes in pairs(replacer.saw_prefixes) do + -- the prefix matches; does any suffix match? + if(node_name:sub(1, #prefix) == prefix) then + for i, suffix in ipairs(suffixes) do + if(suffix == "" or node_name:sub(-#suffix) == suffix) then + local material = node_name:sub(#prefix + 1, -#suffix-1) + for m_name, m_parts in pairs(circular_saw.known_nodes) do + if(m_parts[2] == material) then + return {"circular_saw", m_name, + m_parts[1], prefix, material, suffix} + end + end + end + end + -- no need to check the other prefixes; they won't match either + return "" + end + end + + -- check if we are dealing with a node from the cnc machine + if(minetest.global_exists("technic_cnc") and technic_cnc.programs) then + for i, data in ipairs(technic_cnc.programs) do + if(full_node_name:sub(-#data.suffix-1) == "_"..data.suffix) then + local source_node = full_node_name:sub(1,-#data.suffix-2) + local p = source_node:split(":") + return {"tecnic_cnc", source_node, + p[1], "", p[2], "_"..data.suffix} + end + end + end + + -- TODO: pkarcs, mymillworks etc. mods, pillars, castle, .. + return nil +end + + +-- try to convert the old node into the desired new node +-- the mode is stored in replacer.user_mode[ player_name ] (fallback: normal) +replacer.get_new_node_data = function(old_node, stored_pattern, player_name) + if(not(old_node) or not(stored_pattern) or not(old_node.name)) then + return nil + end + + -- normal mode of operation: replace material, shape and orientation + if(not(replacer.user_mode) + or not(replacer.user_mode[ player_name ]) + or replacer.user_mode[ player_name ] == "normal") then + return stored_pattern + end + + -- what type of node does the stored pattern represent? + local new_data = replacer.identify_shape_and_material(stored_pattern[1]) + -- if the type of the stored pattern cannot be identified, then abort here + if(not(new_data)) then + return nil + end + local old_data = replacer.identify_shape_and_material(old_node.name) + -- if the type of the node that is to be replaced cannot be identified, then abort here + if(not(old_data)) then + return nil + end + + -- replace material, but keep shape and orientation + if(replacer.user_mode[ player_name ] == "material") then + -- now try to replace the *material* of the old node while keeping prefix and postfix + local new_name = old_data[3]..":"..old_data[4]..new_data[5]..old_data[6] + -- handle some exceptions like mese post light wood and desert_cobble walls + if(not(minetest.registered_nodes[ new_name ]) + and replacer.node_name_alternatives[ new_name ]) then + new_name = replacer.node_name_alternatives[ new_name ] + end + -- perhaps we need to change the mod name to the new material as well + if(not(minetest.registered_nodes[ new_name ])) then + new_name = new_data[3]..":"..old_data[4]..new_data[5]..old_data[6] + end + -- if the node still doesn't exist: give up + if(not(minetest.registered_nodes[ new_name ])) then + return nil + end + -- keep param1 and param2, but change the node name + return {new_name, old_node.param1, old_node.param2} + + -- replace shape and orientation, but keep material + elseif(replacer.user_mode[ player_name ] == "shape") then + -- now try to replace the *material* of the old node while keeping prefix and postfix + local new_name = new_data[3]..":"..new_data[4]..old_data[5]..new_data[6] + -- handle some exceptions like mese post light wood and desert_cobble walls + if(not(minetest.registered_nodes[ new_name ]) + and replacer.node_name_alternatives[ new_name ]) then + new_name = replacer.node_name_alternatives[ new_name ] + end + -- perhaps we need to change the mod name to the new material as well + if(not(minetest.registered_nodes[ new_name ])) then + new_name = old_data[3]..":"..new_data[4]..old_data[5]..new_data[6] + end + -- if the node still doesn't exist: give up + if(not(minetest.registered_nodes[ new_name ])) then + return nil + end + -- keep material, but change shape and orientation + return {new_name, stored_pattern[2], stored_pattern[3]} + end +end