Add input help to editplus dialog
[minetest_ltool.git] / init.lua
blob3e6c1e22fd4107e26742f497b90b3ee6a5458e5a
1 ltool = {}
3 ltool.VERSION = {}
4 ltool.VERSION.MAJOR = 1
5 ltool.VERSION.MINOR = 0
6 ltool.VERSION.PATCH = 0
7 ltool.VERSION.STRING = "1.0.0"
9 ltool.playerinfos = {}
10 ltool.default_edit_fields = {
11 axiom="",
12 rules_a="",
13 rules_b="",
14 rules_c="",
15 rules_d="",
16 trunk="",
17 leaves="",
18 leaves2="",
19 leaves2_chance="",
20 fruit="",
21 fruit_chance="",
22 angle="",
23 iterations="",
24 random_level="",
25 trunk_type="",
26 thin_branches="",
27 name = "",
30 --[[ This registers the sapling for planting the trees ]]
31 minetest.register_node("ltool:sapling", {
32 description = "custom L-system tree sapling",
33 stack_max = 1,
34 drawtype = "plantlike",
35 tiles = { "ltool_sapling.png" },
36 inventory_image = "ltool_sapling.png",
37 wield_image = "ltool_sapling.png",
38 paramtype = "light",
39 paramtype2= "wallmounted",
40 walkable = false,
41 buildable_to = true,
42 groups = { dig_immediate = 3, not_in_creative_inventory=1 },
43 drop = "",
44 after_place_node = function(pos, placer, itemstack, pointed_thing)
45 -- Transfer metadata and start timer
46 local nodemeta = minetest.get_meta(pos)
47 local itemmeta = itemstack:get_metadata()
48 nodemeta:set_string("treedef", itemmeta)
49 local timer = minetest.get_node_timer(pos)
50 timer:start(5)
51 end,
52 on_timer = function(pos, elapsed)
53 -- Place tree
54 local meta = minetest.get_meta(pos)
55 local treedef = minetest.deserialize(meta:get_string("treedef"))
56 minetest.remove_node(pos)
57 minetest.spawn_tree(pos, treedef)
58 end,
59 can_dig = function(pos, player)
60 return minetest.get_player_privs(player:get_player_name()).lplant
61 end,
64 --[[ Register privileges ]]
65 minetest.register_privilege("ledit", {
66 description = "Can add, edit, rename and delete own L-system tree definitions of the ltool mod",
67 give_to_singleplayer = false,
69 minetest.register_privilege("lplant", {
70 description = "Can place L-system trees and get L-system tree samplings of the ltool mod",
71 give_to_singleplayer = false,
74 --[[ Load previously saved data from file or initialize an empty tree table ]]
76 local filepath = minetest.get_worldpath().."/ltool.mt"
77 local file = io.open(filepath, "r")
78 if(file) then
79 local string = file:read()
80 io.close(file)
81 if(string ~= nil) then
82 local savetable = minetest.deserialize(string)
83 if(savetable ~= nil) then
84 ltool.trees = savetable.trees
85 ltool.next_tree_id = savetable.next_tree_id
86 ltool.number_of_trees = savetable.number_of_trees
87 minetest.log("action", "[ltool] Tree data loaded from "..filepath..".")
88 else
89 minetest.log("error", "[ltool] Failed to load tree data from "..filepath..".")
90 end
91 else
92 minetest.log("error", "[ltool] Failed to load tree data from "..filepath..".")
93 end
94 else
95 --[[ table of all trees ]]
96 ltool.trees = {}
97 --[[ helper variables to ensure unique IDs ]]
98 ltool.number_of_trees = 0
99 ltool.next_tree_id = 1
103 --[[ Adds a tree to the tree table.
104 name: The tree’s name.
105 author: The author’s / owners’ name
106 treedef: The full tree definition, see lua_api.txt
108 returns the tree ID of the new tree
110 function ltool.add_tree(name, author, treedef)
111 local id = ltool.next_tree_id
112 ltool.trees[id] = {name = name, author = author, treedef = treedef}
113 ltool.next_tree_id = ltool.next_tree_id + 1
114 ltool.number_of_trees = ltool.number_of_trees + 1
115 return id
118 --[[ Removes a tree from the database
119 tree_id: ID of the tree to be removed
121 returns nil
123 function ltool.remove_tree(tree_id)
124 ltool.trees[tree_id] = nil
125 ltool.number_of_trees = ltool.number_of_trees - 1
126 for k,v in pairs(ltool.playerinfos) do
127 if(v.dbsel ~= nil) then
128 if(v.dbsel > ltool.number_of_trees) then
129 v.dbsel = ltool.number_of_trees
131 if(v.dbsel < 1) then
132 v.dbsel = 1
138 --[[ Renames a tree in the database
139 tree_id: ID of the tree to be renamed
140 new_name: The name of the tree
142 returns nil
144 function ltool.rename_tree(tree_id, new_name)
145 ltool.trees[tree_id].name = new_name
148 --[[ Copies a tree in the database
149 tree_id: ID of the tree to be copied
151 returns: the ID of the copy on success;
152 false on failure (tree does not exist)
154 function ltool.copy_tree(tree_id)
155 local tree = ltool.trees[tree_id]
156 if(tree == nil) then
157 return false
159 return ltool.add_tree(tree.name, tree.author, tree.treedef)
162 --[[ Generates a sapling as an ItemStack to mess around later with
163 tree_id: ID of tree the sapling will grow
164 seed: Seed of the tree the sapling will grow (optional, can be nil)
166 returns: an ItemStack which contains one sapling of the specified tree, on success
167 false on failure (if tree does not exist)
169 function ltool.generate_sapling(tree_id, seed)
170 local tree = ltool.trees[tree_id]
171 if(tree == nil) then
172 return false
174 local sapling = ItemStack("ltool:sapling")
175 tree.treedef.seed = seed
176 sapling:set_metadata(minetest.serialize(tree.treedef))
177 tree.treedef.seed = nil
178 return sapling
181 --[[ Gives a L-system tree sapling to a player
182 tree_id: ID of tree the sapling will grow
183 seed: Seed of the tree (optional; can be nil)
184 playername: name of the player to which
185 ignore_priv: if true, player’s lplant privilige is not checked (optional argument; default: false)
187 returns:
188 true on success
189 false, 1 if privilege is not sufficient
190 false, 2 if player’s inventory is full
191 false, 3 if tree does not exist
193 function ltool.give_sapling(tree_id, seed, player_name, ignore_priv)
194 if(ignore_priv == nil) then ignore_priv = false end
195 if(ignore_priv == false and privs.lplant ~= true) then
196 return false, 1
198 local sapling = ItemStack("ltool:sapling")
199 local player = minetest.get_player_by_name(player_name)
200 local tree = ltool.trees[tree_id]
201 if(tree == nil) then
202 return false, 3
204 tree.treedef.seed = seed
205 sapling:set_metadata(minetest.serialize(tree.treedef))
206 tree.treedef.seed = nil
207 local leftover = player:get_inventory():add_item("main", sapling)
208 if(not leftover:is_empty()) then
209 return false, 2
210 else
211 return true
215 --[[ Plants a tree as the specified position
216 tree_id: ID of tree to be planted
217 pos: Position of tree, in format {x=?, y=?, z=?}
219 returns false on failure, nil otherwise
221 function ltool.plant_tree(tree_id, pos)
222 local tree = ltool.trees[tree_id]
223 if(tree==nil) then
224 return false
226 minetest.spawn_tree(pos, tree.treedef)
229 --[[ Tries to return a tree data structure for a given tree_id
231 tree_id: ID of tee to be returned
233 returns false on failure, a tree otherwise
235 function ltool.get_tree(tree_id)
236 local tree = ltool.trees[tree_id]
237 if(tree==nil) then
238 return false
240 return tree
244 ltool.seed = os.time()
247 --[=[ Here come the functions to build the main formspec.
248 They do not build the entire formspec ]=]
250 ltool.formspec_size = "size[12,9]"
252 --[[ This is a part of the main formspec: Tab header ]]
253 function ltool.formspec_header(index)
254 return "tabheader[0,0;ltool_tab;Edit,Database,Plant,Help;"..tostring(index)..";true;false]"
257 --[[ This creates the edit tab of the formspec
258 fields: A template used to fill the default values of the formspec. ]]
259 function ltool.tab_edit(fields)
260 if(fields==nil) then
261 fields = ltool.default_edit_fields
263 local s = function(input)
264 if(input==nil) then
265 ret = ""
266 else
267 ret = minetest.formspec_escape(tostring(input))
269 return ret
271 return ""..
272 "field[0.2,-3.5;11,10;axiom;Axiom;"..s(fields.axiom).."]"..
273 "button[11,0.2;1,1;edit_axiom;+]"..
274 "tooltip[edit_axiom;Opens larger text field for Axiom]"..
275 "field[0.2,-2.5;11,10;rules_a;Rules set A;"..s(fields.rules_a).."]"..
276 "button[11,1.2;1,1;edit_rules_a;+]"..
277 "tooltip[edit_rules_a;Opens larger text field for Rules set A]"..
278 "field[0.2,-1.5;11,10;rules_b;Rules set B;"..s(fields.rules_b).."]"..
279 "button[11,2.2;1,1;edit_rules_b;+]"..
280 "tooltip[edit_rules_b;Opens larger text field for Rules set B]"..
281 "field[0.2,-0.5;11,10;rules_c;Rules set C;"..s(fields.rules_c).."]"..
282 "button[11,3.2;1,1;edit_rules_c;+]"..
283 "tooltip[edit_rules_c;Opens larger text field for Rules set C]"..
284 "field[0.2,0.5;11,10;rules_d;Rules set D;"..s(fields.rules_d).."]"..
285 "button[11,4.2;1,1;edit_rules_d;+]"..
286 "tooltip[edit_rules_d;Opens larger text field for Rules set D]"..
288 "field[0.2,1.5;3,10;trunk;Trunk node name;"..s(fields.trunk).."]"..
289 "field[3.2,1.5;3,10;leaves;Leaves node name;"..s(fields.leaves).."]"..
290 "field[6.2,1.5;3,10;leaves2;Secondary leaves node name;"..s(fields.leaves2).."]"..
291 "field[9.2,1.5;3,10;fruit;Fruit node name;"..s(fields.fruit).."]"..
293 "field[0.2,2.5;3,10;trunk_type;Trunk type (single/double/crossed);"..s(fields.trunk_type).."]"..
294 "tooltip[trunk_type;This field specifies the shape of the tree trunk. Possible values:\n- \"single\": trunk of size 1×1\n- \"double\": trunk of size 2×2\n- \"crossed\": trunk in cross shape (3×3).]"..
295 "field[3.2,2.5;3,10;thin_branches;Thin branches? (true/false);"..s(fields.thin_branches).."]"..
296 "tooltip[thin_branches;\"true\": All branches are just 1 node wide. \"false\": Branches can be larger.]"..
297 "field[6.2,2.5;3,10;leaves2_chance;Secondary leaves chance (in percent);"..s(fields.leaves2_chance).."]"..
298 "tooltip[leaves2_chance;Chance (in percent) to replace a leaves node by a secondary leaves node]"..
299 "field[9.2,2.5;3,10;fruit_chance;Fruit chance (in percent);"..s(fields.fruit_chance).."]"..
300 "tooltip[fruit_chance;Change (in percent) to replace a leaves node by a fruit node.]"..
302 "field[0.2,3.5;3,10;iterations;Iterations;"..s(fields.iterations).."]"..
303 "tooltip[iterations;Maximum number of iterations, usually between 2 and 5.]"..
304 "field[3.2,3.5;3,10;random_level;Randomness level;"..s(fields.random_level).."]"..
305 "tooltip[random_level;Factor to lower number of iterations, usually between 0 and 3.]"..
306 "field[6.2,3.5;3,10;angle;Angle (in degrees);"..s(fields.angle).."]"..
307 "field[9.2,3.5;3,10;name;Name;"..s(fields.name).."]"..
308 "tooltip[name;An unique name for this tree, only used for convenience.]"..
310 "button[3.5,8.5;3,1;edit_save;Save tree to database]"..
311 "button[6.5,8.5;3,1;edit_clear;Clear fields]"
314 --[[ This creates the database tab of the formspec.
315 index: Selected index of the textlist
316 playername: To whom the formspec is shown
318 function ltool.tab_database(index, playername)
319 local treestr, tree_ids = ltool.build_tree_textlist(index, playername)
320 if(treestr ~= nil) then
321 local indexstr
322 if(index == nil) then
323 indexstr = ""
324 else
325 indexstr = tostring(index)
327 ltool.playerinfos[playername].treeform.database.textlist = tree_ids
328 return ""..
329 "textlist[0,0;11,7;treelist;"..treestr..";"..tostring(index)..";false]"..
330 "button[3.5,7.5;3,1;database_rename;Rename tree]"..
331 "button[6.5,7.5;3,1;database_delete;Delete tree]"..
332 "button[3.5,8.5;3,1;database_copy;Copy tree to editor]"..
333 "button[6.5,8.5;3,1;database_update;Reload database]"
334 else
335 return "label[0,0;The tree database is empty.]"..
336 "button[6.5,8.5;3,1;database_update;Reload database]"
340 --[[ This creates the "Plant" tab part of the main formspec ]]
341 function ltool.tab_plant(tree, fields)
342 if(tree ~= nil) then
343 if(fields==nil) then
344 fields = {}
346 local s = function(i)
347 if(i==nil) then return ""
348 else return tostring(minetest.formspec_escape(i))
351 local seed
352 if(fields.seed == nil) then
353 seed = tostring(ltool.seed)
354 else
355 seed = fields.seed
357 local dropdownindex
358 if(fields.plantmode == "Absolute coordinates") then
359 dropdownindex = 1
360 elseif(fields.plantmode == "Relative coordinates") then
361 dropdownindex = 2
362 elseif(fields.plantmode == "Distance in viewing direction") then
363 dropdownindex = 3
364 else
365 dropdownindex = 1
368 return ""..
369 "label[0,-0.2;Selected tree: "..minetest.formspec_escape(tree.name).."]"..
370 "dropdown[-0.1,0.5;5;plantmode;Absolute coordinates,Relative coordinates,Distance in viewing direction;"..dropdownindex.."]"..
371 --[[ NOTE: This tooltip does not work for the dropdown list in 0.4.10,
372 but it is added anyways in case this gets fixed in later Minetest versions. ]]
373 "tooltip[plantmode;"..
374 "- \"Absolute coordinates\": Fields \"x\", \"y\" and \"z\" specify the absolute world coordinates where to plant the tree\n"..
375 "- \"Relative coordinates\": Fields \"x\", \"y\" and \"z\" specify the relative position from your position\n"..
376 "- \"Distance in viewing direction\": Plant tree relative from your position in the direction you look to, at the specified distance"..
377 "]"..
378 "field[0.2,-2;6,10;x;x;"..s(fields.x).."]"..
379 "tooltip[x;Field is only used by absolute and relative coordinates.]"..
380 "field[0.2,-1;6,10;y;y;"..s(fields.y).."]"..
381 "tooltip[y;Field is only used by absolute and relative coordinates.]"..
382 "field[0.2,0;6,10;z;z;"..s(fields.z).."]"..
383 "tooltip[z;Field is only used by absolute and relative coordinates.]"..
384 "field[0.2,1;6,10;distance;Distance;"..s(fields.distance).."]"..
385 "tooltip[distance;This field is used to specify the distance (in node lengths) from your position\nin the viewing direction. It is ignored if you use coordinates.]"..
386 "field[0.2,2;6,10;seed;Randomness seed;"..seed.."]"..
387 "tooltip[seed;A number used for the random number generators. Identical randomness seeds will produce identical trees. This field is optional.]"..
388 "button[3.5,8;3,1;plant_plant;Plant tree]"..
389 "tooltip[plant_plant;Immediately place the tree at the specified position]"..
390 "button[6.5,8;3,1;sapling;Give me a sapling]"..
391 "tooltip[sapling;This gives you an item which you can place manually in the world later..]"
392 else
393 return "label[0,0;No tree in database selected or database is empty.]"
398 --[[ This creates the cheat sheet tab ]]
399 function ltool.tab_cheat_sheet()
400 return ""..
401 "tablecolumns[text;text]"..
402 "tableoptions[background=#000000;highlight=#000000;border=false]"..
403 "table[-0.15,0.75;12,8;cheat_sheet;"..
404 "Symbol,Action,"..
405 "G,Move forward one unit with the pen up,"..
406 "F,Move forward one unit with the pen down drawing trunks and branches,"..
407 "f,Move forward one unit with the pen down drawing leaves (100% chance),"..
408 "T,Move forward one unit with the pen down drawing trunks only,"..
409 "R,Move forward one unit with the pen down placing fruit,"..
410 "A,Replace with rules set A,"..
411 "B,Replace with rules set B,"..
412 "C,Replace with rules set C,"..
413 "D,Replace with rules set D,"..
414 "a,Replace with rules set A\\, chance 90%,"..
415 "b,Replace with rules set B\\, chance 80%,"..
416 "c,Replace with rules set C\\, chance 70%,"..
417 "d,Replace with rules set D\\, chance 60%,"..
418 "+,Yaw the turtle right by angle parameter,"..
419 "-,Yaw the turtle left by angle parameter,"..
420 "&,Pitch the turtle down by angle parameter,"..
421 "^,Pitch the turtle up by angle parameter,"..
422 "/,Roll the turtle to the right by angle parameter,"..
423 "*,Roll the turtle to the left by angle parameter,"..
424 "\\[,Save in stack current state info,"..
425 "\\],Recover from stack state info]"
428 function ltool.tab_help_intro()
429 return ""..
430 "tablecolumns[text]"..
431 "tableoptions[background=#000000;highlight=#000000;border=false]"..
432 "table[-0.15,0.75;12,7;help_intro;"..
433 string.format("You are using the L-System Tree Utility mod version %s.,", ltool.VERSION.STRING)..
434 ","..
435 "The purpose of this mod is to aid with the creation of L-system trees.,"..
436 "With this mod you can create\\, save\\, manage and plant L-system trees.,"..
437 "All trees are saved into <world path>/ltool.mt on server shutdown.,"..
438 "This mod assumes you already understand the concept of L-systems\\;,"..
439 "this mod is mainly aimed towards modders.,"..
440 ","..
441 "The usual workflow in this mod goes like this:,"..
442 ","..
443 "1. Create a new tree in the \"Edit\" tab and save it,"..
444 "2. Select it in the database,"..
445 "3. Plant it,"..
446 ","..
447 "To help you get started\\, you can create an example tree for the \"Edit\" tab,"..
448 "by pressing this button:]"..
449 "button[4,8;4,1;create_template;Create template]"
452 function ltool.tab_help_edit()
453 return ""..
454 "tablecolumns[text]"..
455 "tableoptions[background=#000000;highlight=#000000;border=false]"..
456 "table[-0.15,0.75;12,8;help_edit;"..
457 "To create a L-system tree\\, switch to the \"Edit\" tab.,"..
458 "When you are done\\, hit \"Save tree to database\". The tree will be stored in,"..
459 "the database. The \"Clear fields\" button empties all the input fields.,"..
460 "To understand the meaning of the fields\\, read the introduction to L-systems.,"..
461 "All trees must have an unique name. You are notified in case there is a name,"..
462 "clash. If the name clash is with one of your own trees\\, you can choose to,"..
463 "replace it.]"
466 function ltool.tab_help_database()
467 return ""..
468 "tablecolumns[text]"..
469 "tableoptions[background=#000000;highlight=#000000;border=false]"..
470 "table[-0.15,0.75;12,8;help_database;"..
471 "The database contains a server-wide list of all created trees.,"..
472 "Each tree has an \"owner\". In this mod\\, the concept of ownership is a very,"..
473 "weak one: The owner may rename\\, change and delete his/her own trees\\,,"..
474 "everyone else is prevented from doing that. In contrast\\, all trees can be,"..
475 "copied freely\\;,"..
476 "To do so\\, simply hit \"Copy tree to editor\"\\, change the name and hit,"..
477 "\"Save tree to database\". If you like someone else's tree definition\\,,"..
478 "it is recommended to make a copy for yourself\\, since the original owner,"..
479 "can at any time choose to delete or edit the tree. The trees which you \"own\","..
480 "are written in a yellow font\\, all other trees in a white font.,"..
481 "In order to plant a tree\\, you have to select a tree in the database first.]"
484 function ltool.tab_help_plant()
485 return ""..
486 "tablecolumns[text]"..
487 "tableoptions[background=#000000;highlight=#000000;border=false]"..
488 "table[-0.15,0.75;12,8;help_plant;"..
489 "To plant a previously tree from a previous created tree definition\\,,"..
490 "first select it in the database\\, then open the \"Plant\" tab.,"..
491 "In this tab\\, you can directly place the tree or request a sapling.,"..
492 "If you choose to directly place the tree\\, you can either specify absolute,"..
493 "or relative coordinates or specify that the tree should be planted in your,"..
494 "viewing direction. Absolute coordinates are the world coordinates as specified,"..
495 "by the \"x\"\\, \"y\"\\, and \"z\" fields. Relative coordinates are relative,"..
496 "to your position and use the same fields. When you choose to plant the tree,"..
497 "based on your viewing direction\\, the tree will be planted at a distance,"..
498 "specified by the field \"distance\" away from you in the direction you look to.,"..
499 "When using coordinates\\, the \"distance\" field is ignored\\, when using,"..
500 "direction\\, the coordinate fields are ignored.,"..
501 ","..
502 "If you got a sapling\\, you can place it practically anywhere you like to.,"..
503 "After placing it\\, the sapling will be replaced by the L-system tree after,"..
504 "5 seconds\\, unless it was destroyed in the meantime.,"..
505 "All requested saplings are independent from the moment they are created.,"..
506 "The sapling will still work\\, even if the original tree definiton has been,"..
507 "deleted.]"
510 function ltool.tab_help(index)
511 local formspec = "tabheader[0.1,1;ltool_help_tab;Introduction,Creating Trees,Managing Trees,Planting Trees,Cheat Sheet;"..tostring(index)..";true;false]"
512 if(index==1) then
513 formspec = formspec .. ltool.tab_help_intro()
514 elseif(index==2) then
515 formspec = formspec .. ltool.tab_help_edit()
516 elseif(index==3) then
517 formspec = formspec .. ltool.tab_help_database()
518 elseif(index==4) then
519 formspec = formspec .. ltool.tab_help_plant()
520 elseif(index==5) then
521 formspec = formspec .. ltool.tab_cheat_sheet()
524 return formspec
527 function ltool.formspec_editplus(fragment)
528 local formspec = ""..
529 "size[12,8]"..
530 "textarea[0.2,0.5;12,3;"..fragment.."]"..
531 "label[0,3.625;Draw:]"..
532 "button[2,3.5;1,1;editplus_c_G;G]"..
533 "tooltip[editplus_c_G;Move forward one unit with the pen up]"..
534 "button[3,3.5;1,1;editplus_c_F;F]"..
535 "tooltip[editplus_c_F;Move forward one unit with the pen down drawing trunks and branches]"..
536 "button[4,3.5;1,1;editplus_c_f;f]"..
537 "tooltip[editplus_c_f;Move forward one unit with the pen down drawing leaves (100% chance)]"..
538 "button[5,3.5;1,1;editplus_c_T;T]"..
539 "tooltip[editplus_c_T;Move forward one unit with the pen down drawing trunks only)]"..
540 "button[6,3.5;1,1;editplus_c_R;R]"..
541 "tooltip[editplus_c_R;Move forward one unit with the pen down placing fruit)]"..
543 "label[0,4.625;Rules:]"..
544 "button[2,4.5;1,1;editplus_c_A;A]"..
545 "tooltip[editplus_c_A;Replace with rules set A]"..
546 "button[3,4.5;1,1;editplus_c_B;B]"..
547 "tooltip[editplus_c_B;Replace with rules set B]"..
548 "button[4,4.5;1,1;editplus_c_C;C]"..
549 "tooltip[editplus_c_C;Replace with rules set C]"..
550 "button[5,4.5;1,1;editplus_c_D;D]"..
551 "tooltip[editplus_c_D;Replace with rules set D]"..
552 "button[6.5,4.5;1,1;editplus_c_a;a]"..
553 "tooltip[editplus_c_a;Replace with rules set A, chance 90%]"..
554 "button[7.5,4.5;1,1;editplus_c_b;b]"..
555 "tooltip[editplus_c_b;Replace with rules set B, chance 80%]"..
556 "button[8.5,4.5;1,1;editplus_c_c;c]"..
557 "tooltip[editplus_c_c;Replace with rules set C, chance 70%]"..
558 "button[9.5,4.5;1,1;editplus_c_d;d]"..
559 "tooltip[editplus_c_d;Replace with rules set D, chance 60%]"..
561 "label[0,5.625;Rotate:]"..
562 "button[3,5.5;1,1;editplus_c_+;+]"..
563 "tooltip[editplus_c_+;Yaw the turtle right by the value specified in \"Angle\"]"..
564 "button[2,5.5;1,1;editplus_c_-;-]"..
565 "tooltip[editplus_c_-;Yaw the turtle left by the value specified in \"Angle\"]"..
566 "button[4.5,5.5;1,1;editplus_c_&;&]"..
567 "tooltip[editplus_c_&;Pitch the turtle down by the value specified in \"Angle\"]"..
568 "button[5.5,5.5;1,1;editplus_c_^;^]"..
569 "tooltip[editplus_c_^;Pitch the turtle up by the value specified in \"Angle\"]"..
570 "button[8,5.5;1,1;editplus_c_/;/]"..
571 "tooltip[editplus_c_/;Roll the turtle to the right by the value specified in \"Angle\"]"..
572 "button[7,5.5;1,1;editplus_c_*;*]"..
573 "tooltip[editplus_c_*;Roll the turtle to the left by the value specified in \"Angle\"]"..
575 "label[0,6.625;Stack:]"..
576 "button[2,6.5;1,1;editplus_c_P;\\[]"..
577 "tooltip[editplus_c_P;Save current state info into stack]"..
578 "button[3,6.5;1,1;editplus_c_p;\\]]"..
579 "tooltip[editplus_c_p;Recover from current stack state info]"..
581 "button[2.5,7.5;3,1;editplus_save;Save]"..
582 "button[5.5,7.5;3,1;editplus_cancel;Cancel]"
584 return formspec
587 --[[ creates the content of a textlist which contains all trees.
588 index: Selected entry
589 playername: To which the main formspec is shown to. Used for highlighting owned trees
591 returns (string to be used in the text list, table of tree IDs)
593 function ltool.build_tree_textlist(index, playername)
594 local string = ""
595 local colorstring
596 if(ltool.number_of_trees == 0) then
597 return nil
599 local tree_ids = ltool.get_tree_ids()
600 for i=1,#tree_ids do
601 local tree_id = tree_ids[i]
602 local tree = ltool.trees[tree_id]
603 if(tree.author == playername) then
604 colorstring = "#FFFF00"
605 else
606 colorstring = ""
608 string = string .. colorstring .. tostring(tree_id) .. ": " .. minetest.formspec_escape(tree.name)
609 if(i~=#tree_ids) then
610 string = string .. ","
613 return string, tree_ids
616 --[=[ Here come functions which show formspecs to players ]=]
618 --[[ Shows the main tree formular to the given player, starting with the "Edit" tab ]]
619 function ltool.show_treeform(playername)
620 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.playerinfos[playername].treeform.edit.fields)
621 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
624 --[[ spawns a simple dialog formspec to a player ]]
625 function ltool.show_dialog(playername, formname, message)
626 local formspec = "size[12,2;]label[0,0.2;"..message.."]"..
627 "button[4.5,1.5;3,1;okay;OK]"
628 minetest.show_formspec(playername, formname, formspec)
633 --[=[ End of formspec-relatec functions ]=]
635 --[[ This function does a lot of parameter checks and returns (tree, tree_name) on success.
636 If ANY parameter check fails, the whole function fails.
637 On failure, it returns (nil, <error message string>).]]
638 function ltool.evaluate_edit_fields(fields)
639 local treedef = {}
640 -- Validation helper: Checks for invalid characters for the fields “axiom” and the 4 rule sets
641 local v = function(str)
642 local match = string.match(str, "[^][abcdfABCDFGTR+-/*&^]")
643 if(match==nil) then
644 return true
645 else
646 return false
649 if(v(fields.axiom) and v(fields.rules_a) and v(fields.rules_b) and v(fields.rules_c) and v(fields.rules_d)) then
650 treedef.rules_a = fields.rules_a
651 treedef.rules_b = fields.rules_b
652 treedef.rules_c = fields.rules_c
653 treedef.rules_d = fields.rules_d
654 treedef.axiom = fields.axiom
655 else
656 return nil, "The axiom or one of the rule sets contains at least one invalid character.\nSee the cheat sheet for a list of allowed characters."
658 treedef.trunk = fields.trunk
659 treedef.leaves = fields.leaves
660 treedef.leaves2 = fields.leaves2
661 treedef.leaves2_chance = fields.leaves2_chance
662 treedef.angle = tonumber(fields.angle)
663 if(treedef.angle == nil) then
664 return nil, "The field \"Angle\" must contain a number."
666 treedef.iterations = tonumber(fields.iterations)
667 if(treedef.iterations == nil) then
668 return nil, "The field \"Iterations\" must contain a natural number greater or equal to 0."
669 elseif(treedef.iterations < 0) then
670 return nil, "The field \"Iterations\" must contain a natural number greater or equal to 0."
672 treedef.random_level = tonumber(fields.random_level)
673 if(treedef.random_level == nil) then
674 return nil, "The field \"Randomness level\" must contain a number."
676 treedef.fruit = fields.fruit
677 treedef.fruit_chance = tonumber(fields.fruit_chance)
678 if(treedef.fruit_chance == nil) then
679 return nil, "The field \"Fruit chance\" must contain a number."
680 elseif(treedef.fruit_chance > 100 or treedef.fruit_chance < 0) then
681 return nil, "Fruit chance must be between 0% and 100%."
683 if(fields.trunk_type == "single" or fields.trunk_type == "double" or fields.trunk_type == "crossed") then
684 treedef.trunk_type = fields.trunk_type
685 else
686 return nil, "Trunk type must be \"single\", \"double\" or \"crossed\"."
688 treedef.thin_branches = fields.thin_branches
689 if(fields.thin_branches == "true") then
690 treedef.thin_branches = true
691 elseif(fields.thin_branches == "false") then
692 treedef.thin_branches = false
693 else
694 return nil, "Field \"Thin branches?\" must be \"true\" or \"false\"."
696 local name = fields.name
697 if(name == "") then
698 return nil, "Name is empty."
700 return treedef, name
704 --[=[ Here come several utility functions ]=]
706 --[[ converts a given tree to field names, as if they were given to a
707 minetest.register_on_plyer_receive_fields callback function ]]
708 function ltool.tree_to_fields(tree)
709 local s = function(i)
710 if(i==nil) then
711 return ""
712 else
713 return tostring(i)
716 local fields = {}
717 fields.axiom = s(tree.treedef.axiom)
718 fields.rules_a = s(tree.treedef.rules_a)
719 fields.rules_b = s(tree.treedef.rules_b)
720 fields.rules_c = s(tree.treedef.rules_c)
721 fields.rules_d = s(tree.treedef.rules_d)
722 fields.trunk = s(tree.treedef.trunk)
723 fields.leaves = s(tree.treedef.leaves)
724 fields.leaves2 = s(tree.treedef.leaves2)
725 fields.leaves2_chance = s(tree.treedef.leaves2)
726 fields.fruit = s(tree.treedef.leaves2)
727 fields.fruit_chance = s(tree.treedef.fruit_chance)
728 fields.angle = s(tree.treedef.angle)
729 fields.iterations = s(tree.treedef.iterations)
730 fields.random_level = s(tree.treedef.random_level)
731 fields.trunk_type = s(tree.treedef.trunk_type)
732 fields.thin_branches = s(tree.treedef.thin_branches)
733 fields.name = s(tree.name)
734 return fields
739 -- returns a simple table of all the tree IDs
740 function ltool.get_tree_ids()
741 local ids = {}
742 for tree_id, _ in pairs(ltool.trees) do
743 table.insert(ids, tree_id)
745 table.sort(ids)
746 return ids
749 --[[ In a table of tree IDs (returned by ltool.get_tree_ids, parameter tree_ids), this function
750 searches for the first occourance of the value searched_tree_id and returns its index.
751 This is basically a reverse lookup utility. ]]
752 function ltool.get_tree_id_index(searched_tree_id, tree_ids)
753 for i=1, #tree_ids do
754 local table_tree_id = tree_ids[i]
755 if(searched_tree_id == table_tree_id) then
756 return i
761 -- Returns the selected tree of the given player
762 function ltool.get_selected_tree(playername)
763 local sel = ltool.playerinfos[playername].dbsel
764 if(sel ~= nil) then
765 local tree_id = ltool.playerinfos[playername].treeform.database.textlist[sel]
766 if(tree_id ~= nil) then
767 return ltool.trees[tree_id]
770 return nil
773 -- Returns the ID of the selected tree of the given player
774 function ltool.get_selected_tree_id(playername)
775 local sel = ltool.playerinfos[playername].dbsel
776 if(sel ~= nil) then
777 return ltool.playerinfos[playername].treeform.database.textlist[sel]
779 return nil
783 ltool.treeform = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit()
785 minetest.register_chatcommand("treeform",
787 params = "",
788 description = "Open L-system tree builder formular.",
789 privs = {},
790 func = function(playername, param)
791 ltool.show_treeform(playername)
795 function ltool.dbsel_to_tree(dbsel, playername)
796 return ltool.trees[ltool.playerinfos[playername].treeform.database.textlist[dbsel]]
799 function ltool.save_fields(playername,formname,fields)
800 if(formname=="ltool:treeform_edit") then
801 ltool.playerinfos[playername].treeform.edit.fields = fields
802 elseif(formname=="ltool:treeform_database") then
803 ltool.playerinfos[playername].treeform.database.fields = fields
804 elseif(formname=="ltool:treeform_plant") then
805 ltool.playerinfos[playername].treeform.plant.fields = fields
809 --[=[ Callback functions start here ]=]
811 function ltool.process_form(player,formname,fields)
812 local playername = player:get_player_name()
813 local seltree = ltool.get_selected_tree(playername)
814 local seltree_id = ltool.get_selected_tree_id(playername)
815 local privs = minetest.get_player_privs(playername)
816 local s = function(input)
817 if(input==nil) then
818 ret = ""
819 else
820 ret = minetest.formspec_escape(tostring(input))
822 return ret
824 --[[ process clicks on the tab header ]]
825 if(formname == "ltool:treeform_edit" or formname == "ltool:treeform_database" or formname == "ltool:treeform_plant" or formname == "ltool:treeform_help") then
826 if fields.ltool_tab ~= nil then
827 ltool.save_fields(playername, formname, fields)
828 local tab = tonumber(fields.ltool_tab)
829 local formspec, subformname, contents
830 if(tab==1) then
831 contents = ltool.tab_edit(ltool.playerinfos[playername].treeform.edit.fields)
832 subformname = "edit"
833 elseif(tab==2) then
834 contents = ltool.tab_database(ltool.playerinfos[playername].dbsel, playername)
835 subformname = "database"
836 elseif(tab==3) then
837 if(ltool.number_of_trees > 0) then
838 contents = ltool.tab_plant(seltree, ltool.playerinfos[playername].treeform.plant.fields)
839 else
840 contents = ltool.tab_plant(nil)
842 subformname = "plant"
843 elseif(tab==4) then
844 contents = ltool.tab_help(ltool.playerinfos[playername].treeform.help.tab)
845 subformname = "help"
847 formspec = ltool.formspec_size..ltool.formspec_header(tab)..contents
848 minetest.show_formspec(playername, "ltool:treeform_" .. subformname, formspec)
849 return
852 --[[ "Plant" tab ]]
853 if(formname == "ltool:treeform_plant") then
854 if(fields.plant_plant) then
855 if(seltree ~= nil) then
856 if(privs.lplant ~= true) then
857 ltool.save_fields(playername, formname, fields)
858 local message = "You can't plant trees, you need to have the \"lplant\" privilege."
859 ltool.show_dialog(playername, "ltool:treeform_error_lplant", message)
860 return
862 minetest.log("action","[ltool] Planting tree")
863 local treedef = seltree.treedef
865 local x,y,z = tonumber(fields.x), tonumber(fields.y), tonumber(fields.z)
866 local distance = tonumber(fields.distance)
867 local tree_pos
868 local fail_coordinates = function()
869 ltool.save_fields(playername, formname, fields)
870 ltool.show_dialog(playername, "ltool:treeform_error_badplantfields", "Error: When using coordinates, you have to specifiy numbers in the fields \"x\", \"y\", \"z\".")
872 local fail_distance = function()
873 ltool.save_fields(playername, formname, fields)
874 ltool.show_dialog(playername, "ltool:treeform_error_badplantfields", "Error: When using viewing direction for planting trees,\nyou must specify how far away you want the tree to be placed in the field \"Distance\".")
876 if(fields.plantmode == "Absolute coordinates") then
877 if(type(x)~="number" or type(y) ~= "number" or type(z) ~= "number") then
878 fail_coordinates()
879 return
881 tree_pos = {x=x, y=y, z=z}
882 elseif(fields.plantmode == "Relative coordinates") then
883 if(type(x)~="number" or type(y) ~= "number" or type(z) ~= "number") then
884 fail_coordinates()
885 return
887 tree_pos = player:getpos()
888 tree_pos.x = tree_pos.x + x
889 tree_pos.y = tree_pos.y + y
890 tree_pos.z = tree_pos.z + z
891 elseif(fields.plantmode == "Distance in viewing direction") then
892 if(type(distance)~="number") then
893 fail_distance()
894 return
896 tree_pos = vector.round(vector.add(player:getpos(), vector.multiply(player:get_look_dir(), distance)))
897 else
898 minetest.log("error", "[ltool] fields.plantmode = "..tostring(fields.plantmode))
901 if(tonumber(fields.seed)~=nil) then
902 treedef.seed = tonumber(fields.seed)
905 ltool.plant_tree(seltree_id, tree_pos)
907 treedef.seed = nil
909 elseif(fields.sapling) then
910 if(seltree ~= nil) then
911 if(privs.lplant ~= true) then
912 ltool.save_fields(playername, formname, fields)
913 local message = "You can't request saplings, you need to have the \"lplant\" privilege."
914 ltool.show_dialog(playername, "ltool:treeform_error_sapling", message)
915 return
917 local seed = nil
918 if(tonumber(fields.seed)~=nil) then
919 seed = tonumber(fields.seed)
921 local ret, ret2 = ltool.give_sapling(seltree_id, seed, playername, true)
922 if(ret==false and ret2==2) then
923 ltool.save_fields(playername, formname, fields)
924 ltool.show_dialog(playername, "ltool:treeform_error_sapling", "Error: The sapling could not be given to you. Probably your inventory is full.")
928 --[[ "Edit" tab ]]
929 elseif(formname == "ltool:treeform_edit") then
930 if(fields.edit_save) then
931 local param1, param2
932 param1, param2 = ltool.evaluate_edit_fields(fields)
933 if(privs.ledit ~= true) then
934 ltool.save_fields(playername, formname, fields)
935 local message = "You can't save trees, you need to have the \"ledit\" privilege."
936 ltool.show_dialog(playername, "ltool:treeform_error_ledit", message)
937 return
939 if(param1 ~= nil) then
940 local treedef = param1
941 local name = param2
942 local add = true
943 for k,v in pairs(ltool.trees) do
944 if(v.name == name) then
945 ltool.save_fields(playername, formname, fields)
946 if(v.author == playername) then
947 local formspec = "size[6,2;]label[0,0.2;You already have a tree with this name.\nDo you want to replace it?]"..
948 "button[0,1.5;3,1;replace_yes;Yes]"..
949 "button[3,1.5;3,1;replace_no;No]"
950 minetest.show_formspec(playername, "ltool:treeform_replace", formspec)
951 else
952 ltool.show_dialog(playername, "ltool:treeform_error_nameclash", "Error: This name is already taken by someone else.\nPlease choose a different name.")
954 add = false
957 if(add == true) then
958 ltool.add_tree(name, playername, treedef)
960 else
961 ltool.save_fields(playername, formname, fields)
962 local message = "Error: The tree definition is invalid.\n"..
963 minetest.formspec_escape(param2)
964 ltool.show_dialog(playername, "ltool:treeform_error_badtreedef", message)
967 if(fields.edit_clear) then
968 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit()
969 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
971 if(fields.edit_axiom or fields.edit_rules_a or fields.edit_rules_b or fields.edit_rules_c or fields.edit_rules_d) then
972 local fragment
973 if(fields.edit_axiom) then
974 fragment = "axiom;Axiom;"..s(fields.axiom)
975 elseif(fields.edit_rules_a) then
976 fragment = "rules_a;Rules set A;"..s(fields.rules_a)
977 elseif(fields.edit_rules_b) then
978 fragment = "rules_b;Rules set B;"..s(fields.rules_b)
979 elseif(fields.edit_rules_c) then
980 fragment = "rules_c;Rules set C;"..s(fields.rules_c)
981 elseif(fields.edit_rules_d) then
982 fragment = "rules_d;Rules set D;"..s(fields.rules_d)
985 ltool.save_fields(playername, formname, fields)
986 local formspec = ltool.formspec_editplus(fragment)
987 minetest.show_formspec(playername, "ltool:treeform_editplus", formspec)
989 --[[ Larger edit fields for axiom and rules fields ]]
990 elseif(formname == "ltool:treeform_editplus") then
991 local editfields = ltool.playerinfos[playername].treeform.edit.fields
992 local function addchar(c)
993 local fragment
994 if(c=="P") then c = "[" end
995 if(c=="p") then c = "]" end
996 if(fields.axiom) then
997 fragment = "axiom;Axiom;"..s(fields.axiom..c)
998 elseif(fields.rules_a) then
999 fragment = "rules_a;Rules set A;"..s(fields.rules_a..c)
1000 elseif(fields.rules_b) then
1001 fragment = "rules_b;Rules set B;"..s(fields.rules_b..c)
1002 elseif(fields.rules_c) then
1003 fragment = "rules_c;Rules set C;"..s(fields.rules_c..c)
1004 elseif(fields.rules_d) then
1005 fragment = "rules_d;Rules set D;"..s(fields.rules_d..c)
1007 local formspec = ltool.formspec_editplus(fragment)
1008 minetest.show_formspec(playername, "ltool:treeform_editplus", formspec)
1010 if(fields.editplus_save) then
1011 local function o(writed, writer)
1012 if(writer~=nil) then
1013 return writer
1014 else
1015 return writed
1018 editfields.axiom = o(editfields.axiom, fields.axiom)
1019 editfields.rules_a = o(editfields.rules_a, fields.rules_a)
1020 editfields.rules_b = o(editfields.rules_b, fields.rules_b)
1021 editfields.rules_c = o(editfields.rules_c, fields.rules_c)
1022 editfields.rules_d = o(editfields.rules_d, fields.rules_d)
1023 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(editfields)
1024 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
1025 elseif(fields.editplus_cancel) then
1026 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(editfields)
1027 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
1028 else
1029 for id, field in pairs(fields) do
1030 if(string.sub(id,1,11) == "editplus_c_") then
1031 local char = string.sub(id,12,12)
1032 addchar(char)
1036 --[[ "Database" tab ]]
1037 elseif(formname == "ltool:treeform_database") then
1038 if(fields.treelist) then
1039 local event = minetest.explode_textlist_event(fields.treelist)
1040 if(event.type == "CHG") then
1041 ltool.playerinfos[playername].dbsel = event.index
1042 local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(event.index, playername)
1043 minetest.show_formspec(playername, "ltool:treeform_database", formspec)
1045 elseif(fields.database_copy) then
1046 if(seltree ~= nil) then
1047 if(ltool.playerinfos[playername] ~= nil) then
1048 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.tree_to_fields(seltree))
1049 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
1051 else
1052 ltool.show_dialog(playername, "ltool:treeform_error_nodbsel", "Error: No tree is selected.")
1054 elseif(fields.database_update) then
1055 local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername)
1056 minetest.show_formspec(playername, "ltool:treeform_database", formspec)
1058 elseif(fields.database_delete) then
1059 if(privs.ledit ~= true) then
1060 ltool.save_fields(playername, formname, fields)
1061 local message = "You can't delete trees, you need to have the \"ledit\" privilege."
1062 ltool.show_dialog(playername, "ltool:treeform_error_ledit_db", message)
1063 return
1065 if(seltree ~= nil) then
1066 if(playername == seltree.author) then
1067 local remove_id = ltool.get_selected_tree_id(playername)
1068 if(remove_id ~= nil) then
1069 ltool.remove_tree(remove_id)
1070 local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername)
1071 minetest.show_formspec(playername, "ltool:treeform_database", formspec)
1073 else
1074 ltool.show_dialog(playername, "ltool:treeform_error_delete", "Error: This tree is not your own. You may only delete your own trees.")
1076 else
1077 ltool.show_dialog(playername, "ltool:treeform_error_nodbsel", "Error: No tree is selected.")
1079 elseif(fields.database_rename) then
1080 if(seltree ~= nil) then
1081 if(privs.ledit ~= true) then
1082 ltool.save_fields(playername, formname, fields)
1083 local message = "You can't rename trees, you need to have the \"ledit\" privilege."
1084 ltool.show_dialog(playername, "ltool:treeform_error_ledit_db", message)
1085 return
1087 if(playername == seltree.author) then
1088 local formspec = "field[newname;New name:;"..minetest.formspec_escape(seltree.name).."]"
1089 minetest.show_formspec(playername, "ltool:treeform_rename", formspec)
1090 else
1091 ltool.show_dialog(playername, "ltool:treeform_error_rename_forbidden", "Error: This tree is not your own. You may only rename your own trees.")
1093 else
1094 ltool.show_dialog(playername, "ltool:treeform_error_nodbsel", "Error: No tree is selected.")
1097 --[[ Process "Do you want to replace this tree?" dialog ]]
1098 elseif(formname == "ltool:treeform_replace") then
1099 local editfields = ltool.playerinfos[playername].treeform.edit.fields
1100 local newtreedef, newname = ltool.evaluate_edit_fields(editfields)
1101 if(privs.ledit ~= true) then
1102 local message = "You can't overwrite trees, you need to have the \"ledit\" privilege."
1103 minetest.show_dialog(playername, "ltool:treeform_error_ledit", message)
1104 return
1106 if(fields.replace_yes) then
1107 for tree_id,tree in pairs(ltool.trees) do
1108 if(tree.name == newname) then
1109 --[[ The old tree is deleted and a
1110 new one with a new ID is created ]]
1111 local new_tree_id = ltool.next_tree_id
1112 ltool.trees[new_tree_id] = {}
1113 ltool.trees[new_tree_id].treedef = newtreedef
1114 ltool.trees[new_tree_id].name = newname
1115 ltool.trees[new_tree_id].author = tree.author
1116 ltool.next_tree_id = ltool.next_tree_id + 1
1117 ltool.trees[tree_id] = nil
1118 ltool.playerinfos[playername].dbsel = ltool.number_of_trees
1122 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(editfields)
1123 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
1124 elseif(formname == "ltool:treeform_help") then
1125 local tab = tonumber(fields.ltool_help_tab)
1126 if(tab ~= nil) then
1127 ltool.playerinfos[playername].treeform.help.tab = tab
1128 local formspec = ltool.formspec_size..ltool.formspec_header(4)..ltool.tab_help(tab)
1129 minetest.show_formspec(playername, "ltool:treeform_help", formspec)
1131 if(fields.create_template) then
1132 local newfields = {
1133 axiom="FFFFFAFFBF",
1134 rules_a="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]",
1135 rules_b="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]",
1136 trunk="default:tree",
1137 leaves="default:leaves",
1138 angle="30",
1139 iterations="2",
1140 random_level="0",
1141 trunk_type="single",
1142 thin_branches="true",
1143 fruit_chance="10",
1144 fruit="default:apple",
1145 name = "Example Tree "..ltool.next_tree_id
1147 ltool.save_fields(playername, formname, newfields)
1148 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(newfields)
1149 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
1151 --[[ Tree renaming dialog ]]
1152 elseif(formname == "ltool:treeform_rename") then
1153 if(privs.ledit ~= true) then
1154 ltool.save_fields(playername, formname, fields)
1155 local message = "You can't delete trees, you need to have the \"ledit\" privilege."
1156 ltool.show_dialog(playername, "ltool:treeform_error_ledit_delete", message)
1157 return
1159 if(fields.newname ~= "") then
1160 ltool.rename_tree(ltool.get_selected_tree_id(playername), fields.newname)
1161 local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername)
1162 minetest.show_formspec(playername, "ltool:treeform_database", formspec)
1163 else
1164 ltool.show_dialog(playername, "ltool:treeform_error_bad_rename", "Error: This name is empty. The tree name must be non-empty.")
1166 --[[ Here come various error messages to handle ]]
1167 elseif(formname == "ltool:treeform_error_badtreedef" or formname == "ltool:treeform_error_nameclash" or formname == "ltool:treeform_error_ledit") then
1168 local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.playerinfos[playername].treeform.edit.fields)
1169 minetest.show_formspec(playername, "ltool:treeform_edit", formspec)
1170 elseif(formname == "ltool:treeform_error_badplantfields" or formname == "ltool:treeform_error_sapling" or formname == "ltool:treeform_error_lplant") then
1171 local formspec = ltool.formspec_size..ltool.formspec_header(3)..ltool.tab_plant(seltree, ltool.playerinfos[playername].treeform.plant.fields)
1172 minetest.show_formspec(playername, "ltool:treeform_plant", formspec)
1173 elseif(formname == "ltool:treeform_error_delete" or formname == "ltool:treeform_error_rename_forbidden" or formname == "ltool:treeform_error_nodbsel" or formname == "ltool:treeform_error_ledit_db") then
1174 local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername)
1175 minetest.show_formspec(playername, "ltool:treeform_database", formspec)
1176 elseif(formname == "ltool:treeform_error_bad_rename") then
1177 local formspec = "field[newname;New name:;"..minetest.formspec_escape(seltree.name).."]"
1178 minetest.show_formspec(playername, "ltool:treeform_rename", formspec)
1182 --[[ These 2 functions are basically just table initializions and cleanups ]]
1183 function ltool.leave(player)
1184 ltool.playerinfos[player:get_player_name()] = nil
1187 function ltool.join(player)
1188 local infotable = {}
1189 infotable.dbsel = nil
1190 infotable.treeform = {}
1191 infotable.treeform.database = {}
1192 --[[ This table stores a mapping of the textlist IDs in the database formspec and the tree IDs.
1193 It is updated each time ltool.tab_database is called. ]]
1194 infotable.treeform.database.textlist = nil
1195 --[[ the “fields” tables store the values of the input fields of a formspec. It is updated
1196 whenever the formspec is changed, i.e. on tab change ]]
1197 infotable.treeform.database.fields = {}
1198 infotable.treeform.plant = {}
1199 infotable.treeform.plant.fields = {}
1200 infotable.treeform.edit = {}
1201 infotable.treeform.edit.fields = {}
1202 infotable.treeform.help = {}
1203 infotable.treeform.help.tab = 1
1204 ltool.playerinfos[player:get_player_name()] = infotable
1207 function ltool.save_to_file()
1208 local savetable = {}
1209 savetable.trees = ltool.trees
1210 savetable.number_of_trees = ltool.number_of_trees
1211 savetable.next_tree_id = ltool.next_tree_id
1212 local savestring = minetest.serialize(savetable)
1213 local filepath = minetest.get_worldpath().."/ltool.mt"
1214 local file = io.open(filepath, "w")
1215 if(file) then
1216 file:write(savestring)
1217 io.close(file)
1218 minetest.log("action", "[ltool] Tree data saved to "..filepath..".")
1219 else
1220 minetest.log("error", "[ltool] Failed to write ltool data to "..filepath".")
1225 minetest.register_on_player_receive_fields(ltool.process_form)
1227 minetest.register_on_leaveplayer(ltool.leave)
1229 minetest.register_on_joinplayer(ltool.join)
1231 minetest.register_on_shutdown(ltool.save_to_file)