From c4fed6eb5c954a32f700d1199cc6192ab607f521 Mon Sep 17 00:00:00 2001 From: "A.C.M" Date: Thu, 28 Feb 2019 15:48:29 +0100 Subject: [PATCH 01/12] Fixed Warnings for global Vars. --- init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/init.lua b/init.lua index ddb7478..4544930 100644 --- a/init.lua +++ b/init.lua @@ -104,7 +104,7 @@ minetest.register_tool( "replacer:replacer", return nil; end - local pos = minetest.get_pointed_thing_position( pointed_thing, under ); + 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 ).."."); @@ -124,7 +124,7 @@ minetest.register_tool( "replacer:replacer", on_use = function(itemstack, user, pointed_thing) - return replacer.replace( itemstack, user, pointed_thing, above ); + return replacer.replace( itemstack, user, pointed_thing, false ); end, }) From c030a3beb8440efd0f3c4ca183da0cbedcb3f7b5 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Wed, 29 Sep 2021 20:19:28 +0200 Subject: [PATCH 02/12] aux1 key works now as well instead of only sneak key for storing a new pattern --- README.md | 2 +- init.lua | 3 +-- inspect.lua | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) 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/init.lua b/init.lua index e1f41b9..315f56b 100644 --- a/init.lua +++ b/init.lua @@ -94,7 +94,7 @@ 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 + if( not( keys["sneak"] ) and not( keys["aux1"])) then return replacer.replace( itemstack, placer, pointed_thing, 0 ); end @@ -265,4 +265,3 @@ minetest.register_craft({ } }) - diff --git a/inspect.lua b/inspect.lua index 6c038a3..d9ea96c 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 From 62d7ad06bb48c98578667ed14f716f610a773e93 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Wed, 29 Sep 2021 22:37:05 +0200 Subject: [PATCH 03/12] the replacers' description now shows which node is stored, and the description is more human readable --- init.lua | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/init.lua b/init.lua index 315f56b..d9098e3 100644 --- a/init.lua +++ b/init.lua @@ -21,6 +21,9 @@ -- Version 3.0 -- Changelog: +-- 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. @@ -114,7 +117,13 @@ minetest.register_tool( "replacer:replacer", end itemstack:set_metadata( metadata ); - minetest.chat_send_player( name, "Node replacement tool set to: '"..metadata.."'."); + local set_to = replacer.human_readable_metadata(metadata).." ["..tostring(metadata).."]" + -- 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() + meta:set_string("description", "Replacer set to: "..set_to) + + minetest.chat_send_player( name, "Node replacement tool set to: "..set_to.."."); return itemstack; -- nothing consumed but data changed end, @@ -256,6 +265,46 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) end +-- turn stored metadata string ( ) into something readable by human beeings +replacer.human_readable_metadata = function(metadata) + if(not(metadata)) then + return "(nothing)" + end + -- data is stored in the form " " + local parts = string.split(metadata, " ") + 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 + + minetest.register_craft({ output = 'replacer:replacer', recipe = { From fe8955378059c602492abb8cb626049e5a2b55b8 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Thu, 30 Sep 2021 00:18:34 +0200 Subject: [PATCH 04/12] modified PR from coil0: used buildable_to instead of node names in order to determine if replacing can progress even if digging of the node doesn't work --- init.lua | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/init.lua b/init.lua index cf6b1c6..4827831 100644 --- a/init.lua +++ b/init.lua @@ -228,16 +228,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" ) then + and node.name ~= "ignore") then minetest.node_dig( pos, node, user ); @@ -245,8 +238,13 @@ 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 '"..( item[ "metadata"] or "?" ).."' failed. Unable to remove old node."); + return nil; + end end end From fbad089ff1d83f1345d17376b3f76f8a6b5b1d38 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sat, 2 Oct 2021 12:04:56 +0200 Subject: [PATCH 05/12] added newlines in description --- init.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/init.lua b/init.lua index 4827831..09e400d 100644 --- a/init.lua +++ b/init.lua @@ -117,13 +117,15 @@ minetest.register_tool( "replacer:replacer", end itemstack:set_metadata( metadata ); - local set_to = replacer.human_readable_metadata(metadata).." ["..tostring(metadata).."]" + local set_to = replacer.human_readable_metadata(metadata) -- 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() - meta:set_string("description", "Replacer set to: "..set_to) + meta:set_string("description", "Node replacement tool set to:\n"..set_to.. + "\n["..tostring(metadata).."]") - minetest.chat_send_player( name, "Node replacement tool set to: "..set_to.."."); + minetest.chat_send_player( name, "Node replacement tool set to: "..set_to.. + "["..tostring(metadata).."].") return itemstack; -- nothing consumed but data changed end, From 9ef8940bc411ac296a48fc650f7342ee6f048a53 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sat, 2 Oct 2021 14:21:42 +0200 Subject: [PATCH 06/12] added a history and a formspec to select stored patterns from said history --- fs_history.lua | 176 +++++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 64 ++++-------------- 2 files changed, 188 insertions(+), 52 deletions(-) create mode 100644 fs_history.lua diff --git a/fs_history.lua b/fs_history.lua new file mode 100644 index 0000000..5bc35c5 --- /dev/null +++ b/fs_history.lua @@ -0,0 +1,176 @@ +-- 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 metadata string ( ) into something readable by human beeings +replacer.human_readable_metadata = function(metadata) + if(not(metadata)) then + return "(nothing)" + end + -- data is stored in the form " " + local parts = string.split(metadata, " ") + 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, metadata, player, itemstack) + if(not(player_name) or not(player) or not(itemstack)) then + return itemstack + end + -- fallback if nothing is given + if(not(metadata)) then + metadata = "default:dirt 0 0" + end + itemstack:set_metadata( metadata ) + + local set_to = replacer.human_readable_metadata(metadata) + -- 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() + meta:set_string("description", "Node replacement tool set to:\n"..set_to.. + "\n["..tostring(metadata).."]") + + minetest.chat_send_player(player_name, "Node replacement tool set to: "..set_to.. + "["..tostring(metadata).."].") + + replacer.add_to_hist(player_name, metadata) + 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, metadata ) + if(not(player_name) or not(metadata) or metadata == "") 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 ], metadata ) + -- 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 ], metadata) +end + + +-- show a formspec with a history of stored patterns to select from +replacer.get_formspec = function(player_name, current_metadata, 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 + + local formspec = "size[14,8]".. + "label[4,0;Node Replacement Tool Setup and History]".. + "button_exit[6,7.2;2,0.8;quit;Exit]".. + "label[0.2,0.6;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:".. + ";text,align=left,tooltip=Stored pattern:]".. + "table[0.2,1.0;13,6;replacer_history;" + -- make sure all variables exist and the current entry is stored + replacer.add_to_hist(player_name, current_metadata) + local hist_entries = {} + local selected = 1 + for i, v in ipairs(replacer.history[ player_name ]) do + if(v == current_metadata) then + selected = i + end + local amount_left = "#00FF00,infinite supply:" + 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:" + else + amount_left = "#FF0000,none left!" + end + end + hist_entries[ i ] = tostring(amount_left)..",".. + minetest.formspec_escape(replacer.human_readable_metadata(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 + return true +end) diff --git a/init.lua b/init.lua index 09e400d..71740bc 100644 --- a/init.lua +++ b/init.lua @@ -21,6 +21,8 @@ -- 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. -- 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 @@ -61,6 +63,9 @@ 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"); + minetest.register_tool( "replacer:replacer", { description = "Node replacement tool", @@ -115,19 +120,8 @@ minetest.register_tool( "replacer:replacer", if( node ~= nil and node.name ) then metadata = node.name..' '..node.param1..' '..node.param2; end - itemstack:set_metadata( metadata ); - local set_to = replacer.human_readable_metadata(metadata) - -- 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() - meta:set_string("description", "Node replacement tool set to:\n"..set_to.. - "\n["..tostring(metadata).."]") - - minetest.chat_send_player( name, "Node replacement tool set to: "..set_to.. - "["..tostring(metadata).."].") - - return itemstack; -- nothing consumed but data changed + return replacer.set_to(name, metadata, placer, itemstack) -- nothing consumed but data changed end, @@ -172,6 +166,12 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) item["metadata"] = "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, item["metadata"], user)) + return nil + end + -- regain information about nodename, param1 and param2 local daten = item[ "metadata"]:split( " " ); -- the old format stored only the node name @@ -265,46 +265,6 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) end --- turn stored metadata string ( ) into something readable by human beeings -replacer.human_readable_metadata = function(metadata) - if(not(metadata)) then - return "(nothing)" - end - -- data is stored in the form " " - local parts = string.split(metadata, " ") - 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 - - minetest.register_craft({ output = 'replacer:replacer', recipe = { From 76fcea22e0aaf72cd63c4d0ae87bac3049ad0f82 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sat, 2 Oct 2021 14:46:15 +0200 Subject: [PATCH 07/12] removed deprecated functions get_/set_metadata and renamed metadata to pattern --- fs_history.lua | 42 ++++++++++++++++++++++-------------------- init.lua | 27 ++++++++++++++------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/fs_history.lua b/fs_history.lua index 5bc35c5..149bb56 100644 --- a/fs_history.lua +++ b/fs_history.lua @@ -3,13 +3,13 @@ replacer.max_hist_size = 20 --- turn stored metadata string ( ) into something readable by human beeings -replacer.human_readable_metadata = function(metadata) - if(not(metadata)) then +-- 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(metadata, " ") + local parts = string.split(pattern, " ") if(not(parts) or #parts < 3) then return "(corrupted data)" end @@ -44,35 +44,37 @@ end -- set the replacer to a new pattern -replacer.set_to = function(player_name, metadata, player, itemstack) +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(metadata)) then - metadata = "default:dirt 0 0" + if(not(pattern)) then + pattern = "default:dirt 0 0" end - itemstack:set_metadata( metadata ) - local set_to = replacer.human_readable_metadata(metadata) + 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(metadata).."]") + "\n["..tostring(pattern).."]") minetest.chat_send_player(player_name, "Node replacement tool set to: "..set_to.. - "["..tostring(metadata).."].") + "["..tostring(pattern).."].") - replacer.add_to_hist(player_name, metadata) + 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, metadata ) - if(not(player_name) or not(metadata) or metadata == "") then +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 @@ -81,7 +83,7 @@ replacer.add_to_hist = function(player_name, metadata ) if(not(replacer.history[ player_name ])) then replacer.history[ player_name ] = {} end - local index = table.indexof( replacer.history[ player_name ], metadata ) + local index = table.indexof(replacer.history[ player_name ], pattern) -- only add new entries; do not store duplicates if(index and index > -1) then return @@ -90,12 +92,12 @@ replacer.add_to_hist = function(player_name, metadata ) if(#replacer.history[ player_name ] >= replacer.max_hist_size) then table.remove(replacer.history[ player_name ], 1) end - table.insert(replacer.history[ player_name ], metadata) + 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_metadata, player) +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})) @@ -121,11 +123,11 @@ replacer.get_formspec = function(player_name, current_metadata, player) ";text,align=left,tooltip=Stored pattern:]".. "table[0.2,1.0;13,6;replacer_history;" -- make sure all variables exist and the current entry is stored - replacer.add_to_hist(player_name, current_metadata) + 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_metadata) then + if(v == current_pattern) then selected = i end local amount_left = "#00FF00,infinite supply:" @@ -145,7 +147,7 @@ replacer.get_formspec = function(player_name, current_metadata, player) end end hist_entries[ i ] = tostring(amount_left)..",".. - minetest.formspec_escape(replacer.human_readable_metadata(v).." ["..v.."]") + minetest.formspec_escape(replacer.human_readable_pattern(v).." ["..v.."]") end return formspec..table.concat(hist_entries, ",")..";"..tostring(selected).."]" end diff --git a/init.lua b/init.lua index 71740bc..9371f4c 100644 --- a/init.lua +++ b/init.lua @@ -23,6 +23,7 @@ -- 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 @@ -89,7 +90,6 @@ minetest.register_tool( "replacer:replacer", }, --]] node_placement_prediction = nil, - metadata = "default:dirt", -- default replacement: common dirt on_place = function(itemstack, placer, pointed_thing) @@ -116,12 +116,12 @@ minetest.register_tool( "replacer:replacer", 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 - return replacer.set_to(name, metadata, placer, itemstack) -- nothing consumed but data changed + return replacer.set_to(name, pattern, placer, itemstack) -- nothing consumed but data changed end, @@ -159,21 +159,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, item["metadata"], user)) + 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; @@ -218,7 +219,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? @@ -244,7 +245,7 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) -- 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 '"..( item[ "metadata"] or "?" ).."' failed. Unable to remove old node."); + minetest.chat_send_player( name, "Replacing '"..( node.name or "air" ).."' with '"..( pattern or "?" ).."' failed. Unable to remove old node."); return nil; end end @@ -257,9 +258,9 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) --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.chat_send_player( name, "Replacing node '"..( node.name or "air" ).."' with '"..( pattern or "?" ).."'."); - --minetest.place_node( pos, { name = item[ "metadata" ] } ); + --minetest.place_node( pos, { name = pattern } ); minetest.add_node( pos, { name = daten[1], param1 = daten[2], param2 = daten[3] } ); return nil; -- no item shall be removed from inventory end From 4f028a17010560cbe48aa279ce4266ab44986ecf Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sat, 2 Oct 2021 14:48:04 +0200 Subject: [PATCH 08/12] removed old obsolete lines which where already commented out --- init.lua | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/init.lua b/init.lua index 9371f4c..4351e33 100644 --- a/init.lua +++ b/init.lua @@ -115,7 +115,6 @@ minetest.register_tool( "replacer:replacer", 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 pattern = "default:dirt 0 0"; if( node ~= nil and node.name ) then pattern = node.name..' '..node.param1..' '..node.param2; @@ -125,8 +124,6 @@ minetest.register_tool( "replacer:replacer", end, --- on_drop = func(itemstack, dropper, pos), - on_use = function(itemstack, user, pointed_thing) return replacer.replace( itemstack, user, pointed_thing, false ); @@ -140,7 +137,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."); @@ -150,8 +146,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."); @@ -254,13 +248,7 @@ replacer.replace = function( itemstack, user, pointed_thing, mode ) -- 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 '"..( pattern or "?" ).."'."); - - --minetest.place_node( pos, { name = pattern } ); minetest.add_node( pos, { name = daten[1], param1 = daten[2], param2 = daten[3] } ); return nil; -- no item shall be removed from inventory end From aaa9bdd35ac8b37bd9461491bab57a8b9b06cac7 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sat, 2 Oct 2021 15:19:52 +0200 Subject: [PATCH 09/12] improved readability --- fs_history.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fs_history.lua b/fs_history.lua index 149bb56..d33d966 100644 --- a/fs_history.lua +++ b/fs_history.lua @@ -114,14 +114,14 @@ replacer.get_formspec = function(player_name, current_pattern, player) end end - local formspec = "size[14,8]".. - "label[4,0;Node Replacement Tool Setup and History]".. - "button_exit[6,7.2;2,0.8;quit;Exit]".. + local formspec = "size[18,8]".. + "label[6,0;Node Replacement Tool Setup and History]".. + "button_exit[8,7.2;2,0.8;quit;Exit]".. "label[0.2,0.6;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:".. - ";text,align=left,tooltip=Stored pattern:]".. - "table[0.2,1.0;13,6;replacer_history;" + ";color;text,align=left,tooltip=Stored pattern:]".. + "table[0.2,1.0;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 = {} @@ -130,7 +130,7 @@ replacer.get_formspec = function(player_name, current_pattern, player) if(v == current_pattern) then selected = i end - local amount_left = "#00FF00,infinite supply:" + local amount_left = "#00FF00,infinite supply:,#00FF00" if(not(in_creative_mode)) then -- which item are we looking for? local parts = v:split(" ") @@ -141,9 +141,10 @@ replacer.get_formspec = function(player_name, current_pattern, player) parts[1] = "default:dirt" end if(counted_inv[ parts[1] ]) then - amount_left = "#00FF00,"..tostring(counted_inv[ parts[1] ]).." available:" + amount_left = "#00FF00,"..tostring(counted_inv[ parts[1] ]).." available:".. + ",#00FF00" else - amount_left = "#FF0000,none left!" + amount_left = "#FF0000,none left!,#CFCFCF" end end hist_entries[ i ] = tostring(amount_left)..",".. From 4d3dd7b51e6f42380ee714d9c1feafa6b76fee9a Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sun, 3 Oct 2021 13:17:01 +0200 Subject: [PATCH 10/12] allow mode switches --- depends.txt | 3 + fs_history.lua | 35 ++++++- init.lua | 11 +- mode_of_replacement.lua | 226 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 mode_of_replacement.lua 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 index d33d966..cbe83be 100644 --- a/fs_history.lua +++ b/fs_history.lua @@ -1,3 +1,5 @@ +-- 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 @@ -114,14 +116,31 @@ replacer.get_formspec = function(player_name, current_pattern, player) end end - local formspec = "size[18,8]".. + -- 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,7.2;2,0.8;quit;Exit]".. - "label[0.2,0.6;Click here to set the replacer to a pattern you have stored before:]".. + "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,1.0;17,6;replacer_history;" + "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 = {} @@ -175,5 +194,13 @@ minetest.register_on_player_receive_fields( function(player, formname, fields) 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 4351e33..f867f34 100644 --- a/init.lua +++ b/init.lua @@ -67,6 +67,10 @@ 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", @@ -181,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."); @@ -204,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 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 From ef93ee2494a19f89c3d17cb3ea1a59b465759239 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sun, 3 Oct 2021 13:49:57 +0200 Subject: [PATCH 11/12] added mod.conf --- mod.conf | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 mod.conf 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 From e4464c3faa9c4fcb724e8da3b89d01e3f6ca0338 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Sun, 3 Oct 2021 13:56:47 +0200 Subject: [PATCH 12/12] corrected typo --- init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init.lua b/init.lua index f867f34..60fbd30 100644 --- a/init.lua +++ b/init.lua @@ -105,7 +105,7 @@ minetest.register_tool( "replacer:replacer", 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"] ) and not( keys["aux1"])) then return replacer.replace( itemstack, placer, pointed_thing, 0 ); end