From df14c3200b3e3e102857d48fba0019cec3166b74 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Mon, 16 May 2022 20:42:46 +0200 Subject: [PATCH] working test version that does not set the replacer yet --- depends.txt | 2 + fs_history.lua | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 70 +++++++++++++++++ mod.conf | 5 ++ 4 files changed, 284 insertions(+) create mode 100644 depends.txt create mode 100644 fs_history.lua create mode 100644 init.lua create mode 100644 mod.conf diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..7017fbb --- /dev/null +++ b/depends.txt @@ -0,0 +1,2 @@ +default +replacer diff --git a/fs_history.lua b/fs_history.lua new file mode 100644 index 0000000..e179b84 --- /dev/null +++ b/fs_history.lua @@ -0,0 +1,207 @@ +-- 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_setter.max_hist_size = 30 + + +-- turn stored pattern string ( ) into something readable by human beeings +replacer_setter.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_setter.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_setter.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_setter.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_setter.add_to_hist = function(player_name, pattern) + if(not(player_name) or not(pattern) or pattern == "") then + return + end + if(not(replacer_setter.history)) then + replacer_setter.history = {} + end + if(not(replacer_setter.history[ player_name ])) then + replacer_setter.history[ player_name ] = {} + end + local index = table.indexof(replacer_setter.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_setter.history[ player_name ] >= replacer_setter.max_hist_size) then + table.remove(replacer_setter.history[ player_name ], 1) + end + -- insert at the beginning of the list + table.insert(replacer_setter.history[ player_name ], 1, pattern) +end + + +-- show a formspec with a history of stored patterns to select from +replacer_setter.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})) + -- make sure all variables exist and the current entry is stored + replacer_setter.add_to_hist(player_name, current_pattern) + -- 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_setter.user_mode and replacer_setter.user_mode[ player_name ]) then + current_mode = table.indexof(replacer_setter.mode_names, replacer_setter.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: This selection is valid only for the replacer to the ".. + "*left* of this tool here in your inventory.]".. + "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_setter.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;" + local hist_entries = {} + local selected = 1 + for i, v in ipairs(replacer_setter.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_setter.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_setter.history and replacer_setter.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_setter.history[ player_name ]) then + local itemstack = player:get_wielded_item() + itemstack = replacer_setter.set_to(player_name, + replacer_setter.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_setter.mode_descriptions, + minetest.formspec_escape(fields.select_mode)) + if(index and index > -1 and replacer_setter.mode_names[ index ]) then + replacer_setter.user_mode[ player_name ] = replacer_setter.mode_names[ index ] + end + end + return true +end) diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..655ae69 --- /dev/null +++ b/init.lua @@ -0,0 +1,70 @@ +--[[ + Helper Tool for setting the Replacer to new values when using + mobile devices. Comes with a history. + Copyright (C) 2022 Sokomine + + License: GPLv3.0 +--]] + +replacer_setter = {} +replacer_setter.history = {} + +-- no idea which other modes other replacers may support... +replacer_setter.mode_descriptions = { + "[ normal ] replace material, shape and orientation according to the stored pattern" +} +-- internal names for the above modes (will be stored in replacer.user_mode[ player_name ]) +replacer_setter.mode_names = {"normal"} + +-- make sure all mode descriptions are properly escaped for the dropdown menu +for i, v in ipairs(replacer_setter.mode_descriptions) do + replacer_setter.mode_descriptions[i] = minetest.formspec_escape(v) +end + +replacer_setter.user_mode = {} + +dofile(minetest.get_modpath("replacer_setter").."/fs_history.lua"); + + +minetest.register_tool("replacer_setter:setter", +{ + description = "Replacer Set Tool", + groups = {}, + inventory_image = "default_sign_wall_wood.png^replacer_replacer.png", + wield_image = "", + wield_scale = {x=1,y=1,z=1}, + stack_max = 1, -- there is no reason to have more than one + liquids_pointable = true, -- it is ok to painit in/with water + + node_placement_prediction = nil, + + on_use = function(itemstack, user, pointed_thing) + if(not(user) or not(pointed_thing)) then + return + end + local new_pattern = "" + if(pointed_thing.type == "node") then + local pos = minetest.get_pointed_thing_position( pointed_thing, false ) + local node = minetest.get_node_or_nil( pos ) + new_pattern = "default:dirt 0 0"; + if( node ~= nil and node.name ) then + new_pattern = node.name..' '..node.param1..' '..node.param2; + end + end + local pname = user:get_player_name() + if(not(replacer_setter.history[pname])) then + replacer_setter.history[pname] = {} + end + minetest.show_formspec(pname, "replacer:menu", replacer_setter.get_formspec(pname, new_pattern, user)) + return nil + end, +}) + + +minetest.register_craft({ + output = 'replacer_setter:setter', + recipe = { + {"replacer:replacer"}, + } +}) + diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..f79814c --- /dev/null +++ b/mod.conf @@ -0,0 +1,5 @@ +name = replacer_setter +description = Tool for storing new patterns in a replacer when using mobile devices. +depends = replacer, default +author = Sokomine +title = Replacer Setting Tool