Merge remote-tracking branch 'upstream/master'

This commit is contained in:
bri cassa 2021-10-04 21:45:45 +02:00
commit c0fa835364
7 changed files with 488 additions and 45 deletions

View File

@ -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. 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. 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. When in creative mode, the node will just be replaced. Your inventory will not be changed.

View File

@ -1,2 +1,5 @@
default? default?
dye? dye?
moreblocks?
stairs?
stairsplus?

206
fs_history.lua Normal file
View File

@ -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 (<node_name> <param1> <param2>) 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 "<nodename> <param1> <param2>"
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)

View File

@ -21,6 +21,12 @@
-- Version 3.0 -- Version 3.0
-- Changelog: -- 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 -- 09.12.2017 * Got rid of outdated minetest.env
-- * Fixed error in protection function. -- * Fixed error in protection function.
-- * Fixed minor bugs. -- * Fixed minor bugs.
@ -58,6 +64,13 @@ replacer.blacklist[ "protector:protect2"] = true;
-- adds a tool for inspecting nodes and entities -- adds a tool for inspecting nodes and entities
dofile(minetest.get_modpath("replacer").."/inspect.lua"); 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", minetest.register_tool( "replacer:replacer",
{ {
description = "Node replacement tool", description = "Node replacement tool",
@ -81,7 +94,6 @@ minetest.register_tool( "replacer:replacer",
}, },
--]] --]]
node_placement_prediction = nil, node_placement_prediction = nil,
metadata = "default:dirt", -- default replacement: common dirt
on_place = function(itemstack, placer, pointed_thing) on_place = function(itemstack, placer, pointed_thing)
@ -93,8 +105,8 @@ minetest.register_tool( "replacer:replacer",
local keys=placer:get_player_control(); local keys=placer:get_player_control();
-- just place the stored node if now new one is to be selected -- just place the stored node if no new one is to be selected
if( not( keys["sneak"] )) then if( not( keys["sneak"] ) and not( keys["aux1"])) then
return replacer.replace( itemstack, placer, pointed_thing, true ); end return replacer.replace( itemstack, placer, pointed_thing, true ); end
@ -104,24 +116,18 @@ minetest.register_tool( "replacer:replacer",
return nil; return nil;
end 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 ); local node = minetest.get_node_or_nil( pos );
--minetest.chat_send_player( name, " Target node: "..minetest.serialize( node ).." at pos "..minetest.serialize( pos ).."."); local pattern = "default:dirt 0 0";
local metadata = "default:dirt 0 0";
if( node ~= nil and node.name ) then if( node ~= nil and node.name ) then
metadata = node.name..' '..node.param1..' '..node.param2; pattern = node.name..' '..node.param1..' '..node.param2;
end end
itemstack:set_metadata( metadata );
minetest.chat_send_player( name, "Node replacement tool set to: '"..metadata.."'."); return replacer.set_to(name, pattern, placer, itemstack) -- nothing consumed but data changed
return itemstack; -- nothing consumed but data changed
end, end,
-- on_drop = func(itemstack, dropper, pos),
on_use = function(itemstack, user, pointed_thing) on_use = function(itemstack, user, pointed_thing)
return replacer.replace( itemstack, user, pointed_thing, false ); return replacer.replace( itemstack, user, pointed_thing, false );
@ -135,7 +141,6 @@ replacer.replace = function( itemstack, user, pointed_thing, mode )
return nil; return nil;
end end
local name = user:get_player_name(); 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 if( pointed_thing.type ~= "node" ) then
minetest.chat_send_player( name, " Error: No node."); 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 pos = minetest.get_pointed_thing_position( pointed_thing, mode );
local node = minetest.get_node_or_nil( pos ); 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 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."); 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 end
local item = itemstack:to_table(); local meta = itemstack:get_meta()
local pattern = meta:get_string("pattern")
-- make sure it is defined -- make sure it is defined
if( not( item[ "metadata"] ) or item["metadata"]=="" ) then if(not(pattern) or pattern == "") then
item["metadata"] = "default:dirt 0 0"; 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 end
-- regain information about nodename, param1 and param2 -- regain information about nodename, param1 and param2
local daten = item[ "metadata"]:split( " " ); local daten = pattern:split( " " );
-- the old format stored only the node name -- the old format stored only the node name
if( #daten < 3 ) then if( #daten < 3 ) then
daten[2] = 0; daten[2] = 0;
@ -175,6 +185,12 @@ replacer.replace = function( itemstack, user, pointed_thing, mode )
return nil; return nil;
end 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 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 "?" ).. minetest.chat_send_player( name, "Replacing blocks of the type '"..( node.name or "?" )..
"' is not allowed on this server. Replacement failed."); "' is not allowed on this server. Replacement failed.");
@ -198,7 +214,6 @@ replacer.replace = function( itemstack, user, pointed_thing, mode )
return nil; return nil;
end end
-- in survival mode, the player has to provide the node he wants to place -- in survival mode, the player has to provide the node he wants to place
if( not(minetest.settings:get_bool("creative_mode") ) if( not(minetest.settings:get_bool("creative_mode") )
and not( minetest.check_player_privs( name, {creative=true}))) then 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 -- fortunately, dirt and dirt_with_grass does not make use of rotation
if( daten[1] == "default:dirt_with_grass" ) then if( daten[1] == "default:dirt_with_grass" ) then
daten[1] = "default:dirt"; daten[1] = "default:dirt";
item["metadata"] = "default:dirt 0 0"; pattern = "default:dirt 0 0";
end end
-- does the player carry at least one of the desired nodes with him? -- 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 end
-- give the player the item by simulating digging if possible -- give the player the item by simulating digging if possible
if( node.name ~= "air" if( node.name ~= "air"
and node.name ~= "ignore" and node.name ~= "ignore") then
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
minetest.node_dig( pos, node, user ); minetest.node_dig( pos, node, user );
@ -240,21 +244,20 @@ replacer.replace = function( itemstack, user, pointed_thing, mode )
if( not( digged_node ) if( not( digged_node )
or digged_node.name == node.name ) then 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."); -- some nodes - like liquids - cannot be digged. but they are buildable_to and
return nil; -- 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
end end
-- consume the item -- consume the item
user:get_inventory():remove_item("main", daten[1].." 1"); user:get_inventory():remove_item("main", daten[1].." 1");
--user:get_inventory():add_item( "main", node.name.." 1");
end 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] } ); minetest.add_node( pos, { name = daten[1], param1 = daten[2], param2 = daten[3] } );
return nil; -- no item shall be removed from inventory return nil; -- no item shall be removed from inventory
end end

View File

@ -58,7 +58,7 @@ replacer.inspect = function( itemstack, user, pointed_thing, mode, show_receipe
end end
local name = user:get_player_name(); local name = user:get_player_name();
local keys = user:get_player_control(); local keys = user:get_player_control();
if( keys["sneak"] ) then if( keys["sneak"] or keys["aux1"]) then
show_receipe = true; show_receipe = true;
end end

5
mod.conf Normal file
View File

@ -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

226
mode_of_replacement.lua Normal file
View File

@ -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