4 ltool
.VERSION
.MAJOR
= 1
5 ltool
.VERSION
.MINOR
= 4
6 ltool
.VERSION
.PATCH
= 1
7 ltool
.VERSION
.STRING
= ltool
.VERSION
.MAJOR
.. "." .. ltool
.VERSION
.MINOR
.. "." .. ltool
.VERSION
.PATCH
10 ltool
.default_edit_fields
= {
30 local mod_select_item
= minetest
.get_modpath("select_item") ~= nil
32 local sapling_base_name
= "L-system tree sapling"
33 local sapling_format_string
= "L-system tree sapling (%s)"
35 local place_tree
= function(pos
)
37 local meta
= minetest
.get_meta(pos
)
38 local treedef
= minetest
.deserialize(meta
:get_string("treedef"))
39 minetest
.remove_node(pos
)
40 minetest
.spawn_tree(pos
, treedef
)
43 --[[ This registers the sapling for planting the trees ]]
44 minetest
.register_node("ltool:sapling", {
45 description
= sapling_base_name
,
46 _doc_items_longdesc
= "This artificial sapling does not come from nature and contains the genome of a genetically engineered L-system tree. Every sapling of this kind is unique. Who knows what might grow from it when you plant it?",
47 _doc_items_usagehelp
= "Place the sapling on any floor and wait 5 seconds for the tree to appear. If you have the “lplant” privilege, you can grow it instantly by rightclicking it. If you hold down the sneak key while placing it, you will keep a copy of the sapling in your inventory. To create your own saplings, you need to have the “lplant” privilege and pick a tree from the L-System Tree Utility (accessed with the server command “treeform”).",
48 drawtype
= "plantlike",
49 tiles
= { "ltool_sapling.png" },
50 inventory_image
= "ltool_sapling.png",
53 fixed
= { -10/32, -0.5, -10/32, 10/32, 12/32, 10/32 },
55 wield_image
= "ltool_sapling.png",
57 paramtype2
= "wallmounted",
59 groups
= { dig_immediate
= 3, not_in_creative_inventory
=1, },
61 sunlight_propagates
= true,
62 is_ground_content
= false,
63 after_place_node
= function(pos
, placer
, itemstack
, pointed_thing
)
64 -- Transfer metadata and start timer
65 local nodemeta
= minetest
.get_meta(pos
)
66 local itemmeta
= itemstack
:get_meta()
67 local itemtreedef
= itemmeta
:get_string("treedef")
69 -- Legacy support for saplings with legacy metadata
70 if itemtreedef
== nil or itemtreedef
== "" then
71 itemtreedef
= itemstack
:get_metadata()
72 if itemtreedef
== nil or itemtreedef
== "" then
76 nodemeta
:set_string("treedef", itemtreedef
)
77 local timer
= minetest
.get_node_timer(pos
)
79 if placer
:get_player_control().sneak
== true then
85 -- Insta-grow when sapling got rightclicked
86 on_rightclick
= function(pos
, node
, clicker
, itemstack
, pointed_thing
)
87 if minetest
.get_player_privs(clicker
:get_player_name()).lplant
then
91 -- Grow after timer elapsed
92 on_timer
= place_tree
,
93 can_dig
= function(pos
, player
)
94 return minetest
.get_player_privs(player
:get_player_name()).lplant
98 --[[ Register privileges ]]
99 minetest
.register_privilege("ledit", {
100 description
= "Can add, edit, rename and delete own L-system tree definitions of the ltool mod",
101 give_to_singleplayer
= false,
103 minetest
.register_privilege("lplant", {
104 description
= "Can place L-system trees and get L-system tree samplings of the ltool mod",
105 give_to_singleplayer
= false,
108 --[[ Load previously saved data from file or initialize an empty tree table ]]
110 local filepath
= minetest
.get_worldpath().."/ltool.mt"
111 local file
= io
.open(filepath
, "r")
113 local string = file
:read()
115 if(string ~= nil) then
116 local savetable
= minetest
.deserialize(string)
117 if(savetable
~= nil) then
118 ltool
.trees
= savetable
.trees
119 ltool
.next_tree_id
= savetable
.next_tree_id
120 ltool
.number_of_trees
= savetable
.number_of_trees
121 minetest
.log("action", "[ltool] Tree data loaded from "..filepath
..".")
123 minetest
.log("error", "[ltool] Failed to load tree data from "..filepath
..".")
126 minetest
.log("error", "[ltool] Failed to load tree data from "..filepath
..".")
129 --[[ table of all trees ]]
131 --[[ helper variables to ensure unique IDs ]]
132 ltool
.number_of_trees
= 0
133 ltool
.next_tree_id
= 1
137 --[[ Adds a tree to the tree table.
138 name: The tree’s name.
139 author: The author’s / owners’ name
140 treedef: The full tree definition, see lua_api.txt
142 returns the tree ID of the new tree
144 function ltool
.add_tree(name
, author
, treedef
)
145 local id
= ltool
.next_tree_id
146 ltool
.trees
[id
] = {name
= name
, author
= author
, treedef
= treedef
}
147 ltool
.next_tree_id
= ltool
.next_tree_id
+ 1
148 ltool
.number_of_trees
= ltool
.number_of_trees
+ 1
152 --[[ Removes a tree from the database
153 tree_id: ID of the tree to be removed
157 function ltool
.remove_tree(tree_id
)
158 ltool
.trees
[tree_id
] = nil
159 ltool
.number_of_trees
= ltool
.number_of_trees
- 1
160 for k
,v
in pairs(ltool
.playerinfos
) do
161 if(v
.dbsel
~= nil) then
162 if(v
.dbsel
> ltool
.number_of_trees
) then
163 v
.dbsel
= ltool
.number_of_trees
172 --[[ Renames a tree in the database
173 tree_id: ID of the tree to be renamed
174 new_name: The name of the tree
178 function ltool
.rename_tree(tree_id
, new_name
)
179 ltool
.trees
[tree_id
].name
= new_name
182 --[[ Copies a tree in the database
183 tree_id: ID of the tree to be copied
185 returns: the ID of the copy on success;
186 false on failure (tree does not exist)
188 function ltool
.copy_tree(tree_id
)
189 local tree
= ltool
.trees
[tree_id
]
193 return ltool
.add_tree(tree
.name
, tree
.author
, tree
.treedef
)
196 --[[ Gives a L-system tree sapling to a player
197 treedef: L-system tree definition table of tree the sapling will grow
198 seed: Seed of the tree (optional; can be nil)
199 playername: name of the player to which
200 ignore_priv: if true, player’s lplant privilige is not checked (optional argument; default: false)
201 treename: Descriptive name of the tree for the item description (optional, is ignored if nil or empty string)
205 false, 1 if privilege is not sufficient
206 false, 2 if player’s inventory is full
208 function ltool
.give_sapling(treedef
, seed
, player_name
, ignore_priv
, treename
)
209 local privs
= minetest
.get_player_privs(player_name
)
210 if(ignore_priv
== nil) then ignore_priv
= false end
211 if(ignore_priv
== false and privs
.lplant
~= true) then
215 local sapling
= ItemStack("ltool:sapling")
216 local player
= minetest
.get_player_by_name(player_name
)
218 local smeta
= sapling
:get_meta()
219 smeta
:set_string("treedef", minetest
.serialize(treedef
))
220 if treename
and treename
~= "" then
221 smeta
:set_string("description", string.format(sapling_format_string
, treename
))
224 local leftover
= player
:get_inventory():add_item("main", sapling
)
225 if(not leftover
:is_empty()) then
232 --[[ Plants a tree as the specified position
233 tree_id: ID of tree to be planted
234 pos: Position of tree, in format {x=?, y=?, z=?}
235 seed: Optional seed for randomness, equal seed makes equal trees
237 returns false on failure, nil otherwise
239 function ltool
.plant_tree(tree_id
, pos
, seed
)
240 local tree
= ltool
.trees
[tree_id
]
246 treedef
= table.copy(tree
.treedef
)
249 treedef
= tree
.treedef
251 minetest
.spawn_tree(pos
, treedef
)
254 --[[ Tries to return a tree data structure for a given tree_id
256 tree_id: ID of tee to be returned
258 returns false on failure, a tree otherwise
260 function ltool
.get_tree(tree_id
)
261 local tree
= ltool
.trees
[tree_id
]
269 ltool
.seed
= os
.time()
272 --[=[ Here come the functions to build the main formspec.
273 They
do not build the entire formspec ]=]
275 ltool
.formspec_size
= "size[12,9]"
277 --[[ This is a part of the main formspec: Tab header ]]
278 function ltool
.formspec_header(index
)
279 return "tabheader[0,0;ltool_tab;Edit,Database,Plant,Help;"..tostring(index
)..";true;false]"
282 --[[ This creates the edit tab of the formspec
283 fields: A template used to fill the default values of the formspec. ]]
284 function ltool
.tab_edit(fields
, has_ledit_priv
, has_lplant_priv
)
286 fields
= ltool
.default_edit_fields
288 local s
= function(input
)
293 ret
= minetest
.formspec_escape(tostring(input
))
298 -- Show save/clear buttons depending on privs
300 if has_ledit_priv
then
301 leditbuttons
= "button[0,8.7;4,0;edit_save;Save tree to database]"..
302 "button[4,8.7;4,0;edit_clear;Clear fields]"
303 if has_lplant_priv
then
304 leditbuttons
= leditbuttons
.. "button[8,8.7;4,0;edit_sapling;Give me a sapling]"
307 leditbuttons
= "label[0,8.3;Read-only mode. You need the “ledit” privilege to save trees to the database.]"
311 local fields_select_item
= ""
312 if mod_select_item
then
314 fields_select_item
= ""..
315 "button[2.4,5.7;0.5,0;edit_trunk;>]"..
316 "button[5.4,5.7;0.5,0;edit_leaves;>]"..
317 "button[8.4,5.7;0.5,0;edit_leaves2;>]"..
318 "button[11.4,5.7;0.5,0;edit_fruit;>]"..
319 "tooltip[edit_trunk;Select node]"..
320 "tooltip[edit_leaves;Select node]"..
321 "tooltip[edit_leaves2;Select node]"..
322 "tooltip[edit_fruit;Select node]"
326 "field[0.2,1;11,0;axiom;Axiom;"..s(fields
.axiom
).."]"..
327 "button[11,0.7;1,0;edit_axiom;+]"..
328 "tooltip[edit_axiom;Opens larger text field for Axiom]"..
329 "field[0.2,2;11,0;rules_a;Rules set A;"..s(fields
.rules_a
).."]"..
330 "button[11,1.7;1,0;edit_rules_a;+]"..
331 "tooltip[edit_rules_a;Opens larger text field for Rules set A]"..
332 "field[0.2,3;11,0;rules_b;Rules set B;"..s(fields
.rules_b
).."]"..
333 "button[11,2.7;1,0;edit_rules_b;+]"..
334 "tooltip[edit_rules_b;Opens larger text field for Rules set B]"..
335 "field[0.2,4;11,0;rules_c;Rules set C;"..s(fields
.rules_c
).."]"..
336 "button[11,3.7;1,0;edit_rules_c;+]"..
337 "tooltip[edit_rules_c;Opens larger text field for Rules set C]"..
338 "field[0.2,5;11,0;rules_d;Rules set D;"..s(fields
.rules_d
).."]"..
339 "button[11,4.7;1,0;edit_rules_d;+]"..
340 "tooltip[edit_rules_d;Opens larger text field for Rules set D]"..
342 "field[0.2,6;"..nlength
..",0;trunk;Trunk node name;"..s(fields
.trunk
).."]"..
343 "field[3.2,6;"..nlength
..",0;leaves;Leaves node name;"..s(fields
.leaves
).."]"..
344 "field[6.2,6;"..nlength
..",0;leaves2;Secondary leaves node name;"..s(fields
.leaves2
).."]"..
345 "field[9.2,6;"..nlength
..",0;fruit;Fruit node name;"..s(fields
.fruit
).."]"..
348 "field[0.2,7;3,0;trunk_type;Trunk type (single/double/crossed);"..s(fields
.trunk_type
).."]"..
349 "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).]"..
350 "field[3.2,7;3,0;thin_branches;Thin branches? (true/false);"..s(fields
.thin_branches
).."]"..
351 "tooltip[thin_branches;\"true\": All branches are just 1 node wide. \"false\": Branches can be larger.]"..
352 "field[6.2,7;3,0;leaves2_chance;Secondary leaves chance (%);"..s(fields
.leaves2_chance
).."]"..
353 "tooltip[leaves2_chance;Chance (in percent) to replace a leaves node by a secondary leaves node]"..
354 "field[9.2,7;3,0;fruit_chance;Fruit chance (%);"..s(fields
.fruit_chance
).."]"..
355 "tooltip[fruit_chance;Chance (in percent) to replace a leaves node by a fruit node.]"..
357 "field[0.2,8;3,0;iterations;Iterations;"..s(fields
.iterations
).."]"..
358 "tooltip[iterations;Maximum number of iterations, usually between 2 and 5.]"..
359 "field[3.2,8;3,0;random_level;Randomness level;"..s(fields
.random_level
).."]"..
360 "tooltip[random_level;Factor to lower number of iterations, usually between 0 and 3.]"..
361 "field[6.2,8;3,0;angle;Angle (in °);"..s(fields
.angle
).."]"..
362 "field[9.2,8;3,0;name;Name;"..s(fields
.name
).."]"..
363 "tooltip[name;Descriptive name for this tree, only used for convenience.]"..
367 --[[ This creates the database tab of the formspec.
368 index: Selected index of the textlist
369 playername: To whom the formspec is shown
371 function ltool
.tab_database(index
, playername
)
372 local treestr
, tree_ids
= ltool
.build_tree_textlist(index
, playername
)
373 if(treestr
~= nil) then
375 if(index
== nil) then
378 indexstr
= tostring(index
)
380 ltool
.playerinfos
[playername
].treeform
.database
.textlist
= tree_ids
383 if minetest
.get_player_privs(playername
).ledit
then
384 leditbuttons
= "button[3,7.5;3,1;database_rename;Rename tree]"..
385 "button[6,7.5;3,1;database_delete;Delete tree]"
387 leditbuttons
= "label[0.2,7.2;Read-only mode. You need the “ledit” privilege to edit trees.]"
391 "textlist[0,0;11,7;treelist;"..treestr
..";"..tostring(index
)..";false]"..
393 "button[3,8.5;3,1;database_copy;Copy tree to editor]"..
394 "button[6,8.5;3,1;database_update;Reload database]"
396 return "label[0,0;The tree database is empty.]"..
397 "button[6.5,8.5;3,1;database_update;Reload database]"
401 --[[ This creates the "Plant" tab part of the main formspec ]]
402 function ltool
.tab_plant(tree
, fields
, has_lplant_priv
)
404 local seltree
= "label[0,-0.2;Selected tree: "..minetest
.formspec_escape(tree
.name
).."]"
405 if not has_lplant_priv
then
407 "label[0,0.3;Planting of trees is not allowed. You need to have the “lplant” privilege.]"
412 local s
= function(i
)
413 if(i
==nil) then return ""
414 else return tostring(minetest
.formspec_escape(i
))
418 if(fields
.seed
== nil) then
419 seed
= tostring(ltool
.seed
)
424 if(fields
.plantmode
== "Absolute coordinates") then
426 elseif(fields
.plantmode
== "Relative coordinates") then
428 elseif(fields
.plantmode
== "Distance in viewing direction") then
436 "dropdown[-0.1,0.5;5;plantmode;Absolute coordinates,Relative coordinates,Distance in viewing direction;"..dropdownindex
.."]"..
437 --[[ NOTE: This tooltip does not work for the dropdown list in 0.4.10,
438 but it is added anyways in case this gets fixed in later Minetest versions. ]]
439 "tooltip[plantmode;"..
440 "- \"Absolute coordinates\": Fields \"x\", \"y\" and \"z\" specify the absolute world coordinates where to plant the tree\n"..
441 "- \"Relative coordinates\": Fields \"x\", \"y\" and \"z\" specify the relative position from your position\n"..
442 "- \"Distance in viewing direction\": Plant tree relative from your position in the direction you look to, at the specified distance"..
444 "field[0.2,-2;6,10;x;x;"..s(fields
.x
).."]"..
445 "tooltip[x;Field is only used by absolute and relative coordinates.]"..
446 "field[0.2,-1;6,10;y;y;"..s(fields
.y
).."]"..
447 "tooltip[y;Field is only used by absolute and relative coordinates.]"..
448 "field[0.2,0;6,10;z;z;"..s(fields
.z
).."]"..
449 "tooltip[z;Field is only used by absolute and relative coordinates.]"..
450 "field[0.2,1;6,10;distance;Distance;"..s(fields
.distance
).."]"..
451 "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.]"..
452 "field[0.2,2;6,10;seed;Randomness seed;"..seed
.."]"..
453 "tooltip[seed;A number used for the random number generators. Identical randomness seeds will produce identical trees. This field is optional.]"..
454 "button[3.5,8;3,1;plant_plant;Plant tree]"..
455 "tooltip[plant_plant;Immediately place the tree at the specified position]"..
456 "button[6.5,8;3,1;sapling;Give me a sapling]"..
457 "tooltip[sapling;This gives you an item which you can place manually in the world later]"
459 local notreestr
= "No tree in database selected or database is empty."
460 if has_lplant_priv
then
461 return "label[0,0;"..notreestr
.."]"
463 return "label[0,0;"..notreestr
.."\nYou are not allowed to plant trees anyway as you don't have the “lplant” privilege.]"
469 --[[ This creates the cheat sheet tab ]]
470 function ltool
.tab_cheat_sheet()
472 "tablecolumns[text;text]"..
473 "tableoptions[background=#000000;highlight=#000000;border=false]"..
474 "table[-0.15,0.75;12,8;cheat_sheet;"..
476 "G,Move forward one unit with the pen up,"..
477 "F,Move forward one unit with the pen down drawing trunks and branches,"..
478 "f,Move forward one unit with the pen down drawing leaves,"..
479 "T,Move forward one unit with the pen down drawing trunks,"..
480 "R,Move forward one unit with the pen down placing fruit,"..
481 "A,Replace with rules set A,"..
482 "B,Replace with rules set B,"..
483 "C,Replace with rules set C,"..
484 "D,Replace with rules set D,"..
485 "a,Replace with rules set A\\, chance 90%,"..
486 "b,Replace with rules set B\\, chance 80%,"..
487 "c,Replace with rules set C\\, chance 70%,"..
488 "d,Replace with rules set D\\, chance 60%,"..
489 "+,Yaw the turtle right by angle parameter,"..
490 "-,Yaw the turtle left by angle parameter,"..
491 "&,Pitch the turtle down by angle parameter,"..
492 "^,Pitch the turtle up by angle parameter,"..
493 "/,Roll the turtle to the right by angle parameter,"..
494 "*,Roll the turtle to the left by angle parameter,"..
495 "\\[,Save in stack current state info,"..
496 "\\],Recover from stack state info]"
499 function ltool
.tab_help_intro()
501 "tablecolumns[text]"..
502 "tableoptions[background=#000000;highlight=#000000;border=false]"..
503 "table[-0.15,0.75;12,7;help_intro;"..
504 string.format("You are using the L-System Tree Utility mod version %s.,", ltool
.VERSION
.STRING
)..
506 "The purpose of this mod is to aid with the creation of L-system trees.,"..
507 "With this mod you can create\\, save\\, manage and plant L-system trees.,"..
508 "All trees are saved into <world path>/ltool.mt on server shutdown.,"..
509 "This mod assumes you already understand the concept of L-systems\\;,"..
510 "this mod is mainly aimed towards modders.,"..
512 "The usual workflow in this mod goes like this:,"..
514 "1. Create a new tree in the \"Edit\" tab and save it,"..
515 "2. Select it in the database,"..
518 "To help you get started\\, you can create an example tree for the \"Edit\" tab,"..
519 "by pressing this button:]"..
520 "button[4,8;4,1;create_template;Create template]"
523 function ltool
.tab_help_edit()
525 "tablecolumns[text]"..
526 "tableoptions[background=#000000;highlight=#000000;border=false]"..
527 "table[-0.15,0.75;12,8;help_edit;"..
528 "To create a L-system tree\\, switch to the \"Edit\" tab.,"..
529 "When you are done\\, hit \"Save tree to database\". The tree will be stored in,"..
530 "the database. The \"Clear fields\" button empties all the input fields.,"..
531 "To understand the meaning of the fields\\, read the introduction to L-systems.,"..
532 "All trees must have an unique name. You are notified in case there is a name,"..
533 "clash. If the name clash is with one of your own trees\\, you can choose to,"..
537 function ltool
.tab_help_database()
539 "tablecolumns[text]"..
540 "tableoptions[background=#000000;highlight=#000000;border=false]"..
541 "table[-0.15,0.75;12,8;help_database;"..
542 "The database contains a server-wide list of all created trees.,"..
543 "Each tree has an \"owner\". In this mod\\, the concept of ownership is a very,"..
544 "weak one: The owner may rename\\, change and delete his/her own trees\\,,"..
545 "everyone else is prevented from doing that. In contrast\\, all trees can be,"..
546 "copied freely\\;,"..
547 "To do so\\, simply hit \"Copy tree to editor\"\\, change the name and hit,"..
548 "\"Save tree to database\". If you like someone else's tree definition\\,,"..
549 "it is recommended to make a copy for yourself\\, since the original owner,"..
550 "can at any time choose to delete or edit the tree. The trees which you \"own\","..
551 "are written in a yellow font\\, all other trees in a white font.,"..
552 "In order to plant a tree\\, you have to select a tree in the database first.]"
555 function ltool
.tab_help_plant()
557 "tablecolumns[text]"..
558 "tableoptions[background=#000000;highlight=#000000;border=false]"..
559 "table[-0.15,0.75;12,8;help_plant;"..
560 "To plant a tree from a previously created tree definition\\, first select,"..
561 "it in the database\\, then open the \"Plant\" tab.,"..
562 "In this tab\\, you can directly place the tree or request a sapling.,"..
563 "If you choose to directly place the tree\\, you can either specify absolute,"..
564 "or relative coordinates or specify that the tree should be planted in your,"..
565 "viewing direction. Absolute coordinates are the world coordinates as specified,"..
566 "by the \"x\"\\, \"y\"\\, and \"z\" fields. Relative coordinates are relative,"..
567 "to your position and use the same fields. When you choose to plant the tree,"..
568 "based on your viewing direction\\, the tree will be planted at a distance,"..
569 "specified by the field \"distance\" away from you in the direction you look to.,"..
570 "When using coordinates\\, the \"distance\" field is ignored\\, when using,"..
571 "direction\\, the coordinate fields are ignored.,"..
573 "You can also use the “lplant” server command to plant trees.,"..
575 "If you got a sapling\\, you can place it practically anywhere you like to.,"..
576 "After placing it\\, the sapling will be replaced by the L-system tree after,"..
577 "5 seconds\\, unless it was destroyed in the meantime.,"..
578 "All requested saplings are independent from the moment they are created.,"..
579 "The sapling will still work\\, even if the original tree definiton has been,"..
583 function ltool
.tab_help(index
)
584 local formspec
= "tabheader[0.1,1;ltool_help_tab;Introduction,Creating Trees,Managing Trees,Planting Trees,Cheat Sheet;"..tostring(index
)..";true;false]"
586 formspec
= formspec
.. ltool
.tab_help_intro()
587 elseif(index
==2) then
588 formspec
= formspec
.. ltool
.tab_help_edit()
589 elseif(index
==3) then
590 formspec
= formspec
.. ltool
.tab_help_database()
591 elseif(index
==4) then
592 formspec
= formspec
.. ltool
.tab_help_plant()
593 elseif(index
==5) then
594 formspec
= formspec
.. ltool
.tab_cheat_sheet()
600 function ltool
.formspec_editplus(fragment
)
601 local formspec
= ""..
603 "textarea[0.2,0.5;12,3;"..fragment
.."]"..
604 "label[0,3.625;Draw:]"..
605 "button[2,3.5;1,1;editplus_c_G;G]"..
606 "tooltip[editplus_c_G;Move forward one unit with the pen up]"..
607 "button[3,3.5;1,1;editplus_c_F;F]"..
608 "tooltip[editplus_c_F;Move forward one unit with the pen down drawing trunks and branches]"..
609 "button[4,3.5;1,1;editplus_c_f;f]"..
610 "tooltip[editplus_c_f;Move forward one unit with the pen down drawing leaves]"..
611 "button[5,3.5;1,1;editplus_c_T;T]"..
612 "tooltip[editplus_c_T;Move forward one unit with the pen down drawing trunks]"..
613 "button[6,3.5;1,1;editplus_c_R;R]"..
614 "tooltip[editplus_c_R;Move forward one unit with the pen down placing fruit]"..
616 "label[0,4.625;Rules:]"..
617 "button[2,4.5;1,1;editplus_c_A;A]"..
618 "tooltip[editplus_c_A;Replace with rules set A]"..
619 "button[3,4.5;1,1;editplus_c_B;B]"..
620 "tooltip[editplus_c_B;Replace with rules set B]"..
621 "button[4,4.5;1,1;editplus_c_C;C]"..
622 "tooltip[editplus_c_C;Replace with rules set C]"..
623 "button[5,4.5;1,1;editplus_c_D;D]"..
624 "tooltip[editplus_c_D;Replace with rules set D]"..
625 "button[6.5,4.5;1,1;editplus_c_a;a]"..
626 "tooltip[editplus_c_a;Replace with rules set A, chance 90%]"..
627 "button[7.5,4.5;1,1;editplus_c_b;b]"..
628 "tooltip[editplus_c_b;Replace with rules set B, chance 80%]"..
629 "button[8.5,4.5;1,1;editplus_c_c;c]"..
630 "tooltip[editplus_c_c;Replace with rules set C, chance 70%]"..
631 "button[9.5,4.5;1,1;editplus_c_d;d]"..
632 "tooltip[editplus_c_d;Replace with rules set D, chance 60%]"..
634 "label[0,5.625;Rotate:]"..
635 "button[3,5.5;1,1;editplus_c_+;+]"..
636 "tooltip[editplus_c_+;Yaw the turtle right by the value specified in \"Angle\"]"..
637 "button[2,5.5;1,1;editplus_c_-;-]"..
638 "tooltip[editplus_c_-;Yaw the turtle left by the value specified in \"Angle\"]"..
639 "button[4.5,5.5;1,1;editplus_c_&;&]"..
640 "tooltip[editplus_c_&;Pitch the turtle down by the value specified in \"Angle\"]"..
641 "button[5.5,5.5;1,1;editplus_c_^;^]"..
642 "tooltip[editplus_c_^;Pitch the turtle up by the value specified in \"Angle\"]"..
643 "button[8,5.5;1,1;editplus_c_/;/]"..
644 "tooltip[editplus_c_/;Roll the turtle to the right by the value specified in \"Angle\"]"..
645 "button[7,5.5;1,1;editplus_c_*;*]"..
646 "tooltip[editplus_c_*;Roll the turtle to the left by the value specified in \"Angle\"]"..
648 "label[0,6.625;Stack:]"..
649 "button[2,6.5;1,1;editplus_c_P;\\[]"..
650 "tooltip[editplus_c_P;Save current state info into stack]"..
651 "button[3,6.5;1,1;editplus_c_p;\\]]"..
652 "tooltip[editplus_c_p;Recover from current stack state info]"..
654 "button[2.5,7.5;3,1;editplus_save;Save]"..
655 "button[5.5,7.5;3,1;editplus_cancel;Cancel]"
660 --[[ creates the content of a textlist which contains all trees.
661 index: Selected entry
662 playername: To which the main formspec is shown to. Used for highlighting owned trees
664 returns (string to be used in the text list, table of tree IDs)
666 function ltool
.build_tree_textlist(index
, playername
)
669 if(ltool
.number_of_trees
== 0) then
672 local tree_ids
= ltool
.get_tree_ids()
674 local tree_id
= tree_ids
[i
]
675 local tree
= ltool
.trees
[tree_id
]
676 if(tree
.author
== playername
) then
677 colorstring
= "#FFFF00"
681 string = string .. colorstring
.. tostring(tree_id
) .. ": " .. minetest
.formspec_escape(tree
.name
)
682 if(i
~=#tree_ids
) then
683 string = string .. ","
686 return string, tree_ids
689 --[=[ Here come functions which show formspecs to players ]=]
691 --[[ Shows the main tree form to the given player, starting with the "Edit" tab ]]
692 function ltool
.show_treeform(playername
)
693 local privs
= minetest
.get_player_privs(playername
)
694 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(ltool
.playerinfos
[playername
].treeform
.edit
.fields
, privs
.ledit
, privs
.lplant
)
695 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
698 --[[ spawns a simple dialog formspec to a player ]]
699 function ltool
.show_dialog(playername
, formname
, message
)
700 local formspec
= "size[12,2;]label[0,0.2;"..message
.."]"..
701 "button[4.5,1.5;3,1;okay;OK]"
702 minetest
.show_formspec(playername
, formname
, formspec
)
707 --[=[ End of formspec-relatec functions ]=]
709 --[[ This function does a lot of parameter checks and returns (tree, tree_name) on success.
710 If ANY parameter check fails, the whole function fails.
711 On failure, it returns (nil, <error message string>).]]
712 function ltool
.evaluate_edit_fields(fields
, ignore_name
)
714 -- Validation helper: Checks for invalid characters for the fields “axiom” and the 4 rule sets
715 local v
= function(str
)
716 local match
= string.match(str
, "[^][abcdfABCDFGTR+-/*&^]")
723 -- Validation helper: Checks for balanced brackets
724 local b
= function(str
)
726 for c
=1, string.len(str
) do
727 local char
= string.sub(str
, c
, c
)
729 brackets
= brackets
+ 1
730 elseif char
== "]" then
731 brackets
= brackets
- 1
740 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
741 if(b(fields
.axiom
) and b(fields
.rules_a
) and b(fields
.rules_b
) and b(fields
.rules_c
) and b(fields
.rules_d
)) then
742 treedef
.rules_a
= fields
.rules_a
743 treedef
.rules_b
= fields
.rules_b
744 treedef
.rules_c
= fields
.rules_c
745 treedef
.rules_d
= fields
.rules_d
746 treedef
.axiom
= fields
.axiom
748 return nil, "The brackets are unbalanced! For each of the axiom and the rule sets, each opening bracket must be matched by a closing bracket."
751 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."
753 treedef
.trunk
= fields
.trunk
754 treedef
.leaves
= fields
.leaves
755 treedef
.leaves2
= fields
.leaves2
756 treedef
.leaves2_chance
= fields
.leaves2_chance
757 treedef
.angle
= tonumber(fields
.angle
)
758 if(treedef
.angle
== nil) then
759 return nil, "The field \"Angle\" must contain a number."
761 treedef
.iterations
= tonumber(fields
.iterations
)
762 if(treedef
.iterations
== nil) then
763 return nil, "The field \"Iterations\" must contain a natural number greater or equal to 0."
764 elseif(treedef
.iterations
< 0) then
765 return nil, "The field \"Iterations\" must contain a natural number greater or equal to 0."
767 treedef
.random_level
= tonumber(fields
.random_level
)
768 if(treedef
.random_level
== nil) then
769 return nil, "The field \"Randomness level\" must contain a number."
771 treedef
.fruit
= fields
.fruit
772 treedef
.fruit_chance
= tonumber(fields
.fruit_chance
)
773 if(treedef
.fruit_chance
== nil) then
774 return nil, "The field \"Fruit chance\" must contain a number."
775 elseif(treedef
.fruit_chance
> 100 or treedef
.fruit_chance
< 0) then
776 return nil, "Fruit chance must be between 0% and 100%."
778 if(fields
.trunk_type
== "single" or fields
.trunk_type
== "double" or fields
.trunk_type
== "crossed") then
779 treedef
.trunk_type
= fields
.trunk_type
781 return nil, "Trunk type must be \"single\", \"double\" or \"crossed\"."
783 treedef
.thin_branches
= fields
.thin_branches
784 if(fields
.thin_branches
== "true") then
785 treedef
.thin_branches
= true
786 elseif(fields
.thin_branches
== "false") then
787 treedef
.thin_branches
= false
789 return nil, "Field \"Thin branches?\" must be \"true\" or \"false\"."
791 local name
= fields
.name
792 if(ignore_name
~= true and name
== "") then
793 return nil, "Name is empty."
799 --[=[ Here come several utility functions ]=]
801 --[[ converts a given tree to field names, as if they were given to a
802 minetest.register_on_plyer_receive_fields callback function ]]
803 function ltool
.tree_to_fields(tree
)
804 local s
= function(i
)
812 fields
.axiom
= s(tree
.treedef
.axiom
)
813 fields
.rules_a
= s(tree
.treedef
.rules_a
)
814 fields
.rules_b
= s(tree
.treedef
.rules_b
)
815 fields
.rules_c
= s(tree
.treedef
.rules_c
)
816 fields
.rules_d
= s(tree
.treedef
.rules_d
)
817 fields
.trunk
= s(tree
.treedef
.trunk
)
818 fields
.leaves
= s(tree
.treedef
.leaves
)
819 fields
.leaves2
= s(tree
.treedef
.leaves2
)
820 fields
.leaves2_chance
= s(tree
.treedef
.leaves2
)
821 fields
.fruit
= s(tree
.treedef
.fruit
)
822 fields
.fruit_chance
= s(tree
.treedef
.fruit_chance
)
823 fields
.angle
= s(tree
.treedef
.angle
)
824 fields
.iterations
= s(tree
.treedef
.iterations
)
825 fields
.random_level
= s(tree
.treedef
.random_level
)
826 fields
.trunk_type
= s(tree
.treedef
.trunk_type
)
827 fields
.thin_branches
= s(tree
.treedef
.thin_branches
)
828 fields
.name
= s(tree
.name
)
834 -- returns a simple table of all the tree IDs
835 function ltool
.get_tree_ids()
837 for tree_id
, _
in pairs(ltool
.trees
) do
838 table.insert(ids
, tree_id
)
844 --[[ In a table of tree IDs (returned by ltool.get_tree_ids, parameter tree_ids), this function
845 searches for the first occourance of the value searched_tree_id and returns its index.
846 This is basically a reverse lookup utility. ]]
847 function ltool
.get_tree_id_index(searched_tree_id
, tree_ids
)
848 for i
=1, #tree_ids
do
849 local table_tree_id
= tree_ids
[i
]
850 if(searched_tree_id
== table_tree_id
) then
856 -- Returns the selected tree of the given player
857 function ltool
.get_selected_tree(playername
)
858 local sel
= ltool
.playerinfos
[playername
].dbsel
860 local tree_id
= ltool
.playerinfos
[playername
].treeform
.database
.textlist
[sel
]
861 if(tree_id
~= nil) then
862 return ltool
.trees
[tree_id
]
868 -- Returns the ID of the selected tree of the given player
869 function ltool
.get_selected_tree_id(playername
)
870 local sel
= ltool
.playerinfos
[playername
].dbsel
872 return ltool
.playerinfos
[playername
].treeform
.database
.textlist
[sel
]
878 ltool
.treeform
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit()
880 minetest
.register_chatcommand("treeform",
883 description
= "Open L-System Tree Utility.",
885 func
= function(playername
, param
)
886 ltool
.show_treeform(playername
)
890 minetest
.register_chatcommand("lplant",
892 description
= "Plant a L-system tree at the specified position",
893 privs
= { lplant
= true },
894 params
= "<tree ID> <x> <y> <z> [<seed>]",
895 func
= function(playername
, param
)
897 local tree_id
, x
, y
, z
, seed
= string.match(param
, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *([%d.-]*)")
898 tree_id
, p
.x
, p
.y
, p
.z
, seed
= tonumber(tree_id
), tonumber(x
), tonumber(y
), tonumber(z
), tonumber(seed
)
899 if not tree_id
or not p
.x
or not p
.y
or not p
.z
then
900 return false, "Invalid usage, see /help lplant."
902 local lm
= tonumber(minetest
.settings
:get("map_generation_limit") or 31000)
903 if p
.x
< -lm
or p
.x
> lm
or p
.y
< -lm
or p
.y
> lm
or p
.z
< -lm
or p
.z
> lm
then
904 return false, "Cannot plant tree out of map bounds!"
907 local success
= ltool
.plant_tree(tree_id
, p
, seed
)
908 if success
== false then
909 return false, "Unknown tree ID!"
916 function ltool
.dbsel_to_tree(dbsel
, playername
)
917 return ltool
.trees
[ltool
.playerinfos
[playername
].treeform
.database
.textlist
[dbsel]]
920 function ltool
.save_fields(playername
,formname
,fields
)
921 if(formname
=="ltool:treeform_edit") then
922 ltool
.playerinfos
[playername
].treeform
.edit
.fields
= fields
923 elseif(formname
=="ltool:treeform_database") then
924 ltool
.playerinfos
[playername
].treeform
.database
.fields
= fields
925 elseif(formname
=="ltool:treeform_plant") then
926 ltool
.playerinfos
[playername
].treeform
.plant
.fields
= fields
930 --[=[ Callback functions start here ]=]
932 function ltool
.process_form(player
,formname
,fields
)
933 local playername
= player
:get_player_name()
935 local seltree
= ltool
.get_selected_tree(playername
)
936 local seltree_id
= ltool
.get_selected_tree_id(playername
)
937 local privs
= minetest
.get_player_privs(playername
)
938 local s
= function(input
)
943 ret
= minetest
.formspec_escape(tostring(input
))
947 --[[ process clicks on the tab header ]]
948 if(formname
== "ltool:treeform_edit" or formname
== "ltool:treeform_database" or formname
== "ltool:treeform_plant" or formname
== "ltool:treeform_help") then
949 if fields
.ltool_tab
~= nil then
950 ltool
.save_fields(playername
, formname
, fields
)
951 local tab
= tonumber(fields
.ltool_tab
)
952 local formspec
, subformname
, contents
954 contents
= ltool
.tab_edit(ltool
.playerinfos
[playername
].treeform
.edit
.fields
, privs
.ledit
, privs
.lplant
)
957 contents
= ltool
.tab_database(ltool
.playerinfos
[playername
].dbsel
, playername
)
958 subformname
= "database"
960 if(ltool
.number_of_trees
> 0) then
961 contents
= ltool
.tab_plant(seltree
, ltool
.playerinfos
[playername
].treeform
.plant
.fields
, privs
.lplant
)
963 contents
= ltool
.tab_plant(nil, nil, privs
.lplant
)
965 subformname
= "plant"
967 contents
= ltool
.tab_help(ltool
.playerinfos
[playername
].treeform
.help
.tab
)
970 formspec
= ltool
.formspec_size
..ltool
.formspec_header(tab
)..contents
971 minetest
.show_formspec(playername
, "ltool:treeform_" .. subformname
, formspec
)
976 if(formname
== "ltool:treeform_plant") then
977 if(fields
.plant_plant
) then
978 if(seltree
~= nil) then
979 if(privs
.lplant
~= true) then
980 ltool
.save_fields(playername
, formname
, fields
)
981 local message
= "You can't plant trees, you need to have the \"lplant\" privilege."
982 ltool
.show_dialog(playername
, "ltool:treeform_error_lplant", message
)
985 minetest
.log("action","[ltool] Planting tree")
986 local treedef
= seltree
.treedef
988 local x
,y
,z
= tonumber(fields
.x
), tonumber(fields
.y
), tonumber(fields
.z
)
989 local distance
= tonumber(fields
.distance
)
991 local fail_coordinates
= function()
992 ltool
.save_fields(playername
, formname
, fields
)
993 ltool
.show_dialog(playername
, "ltool:treeform_error_badplantfields", "Error: When using coordinates, you have to specifiy numbers in the fields \"x\", \"y\", \"z\".")
995 local fail_distance
= function()
996 ltool
.save_fields(playername
, formname
, fields
)
997 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\".")
999 if(fields
.plantmode
== "Absolute coordinates") then
1000 if(type(x
)~="number" or type(y
) ~= "number" or type(z
) ~= "number") then
1004 tree_pos
= {x
=x
, y
=y
, z
=z
}
1005 elseif(fields
.plantmode
== "Relative coordinates") then
1006 if(type(x
)~="number" or type(y
) ~= "number" or type(z
) ~= "number") then
1010 tree_pos
= player
:getpos()
1011 tree_pos
.x
= tree_pos
.x
+ x
1012 tree_pos
.y
= tree_pos
.y
+ y
1013 tree_pos
.z
= tree_pos
.z
+ z
1014 elseif(fields
.plantmode
== "Distance in viewing direction") then
1015 if(type(distance
)~="number") then
1019 tree_pos
= vector
.round(vector
.add(player
:getpos(), vector
.multiply(player
:get_look_dir(), distance
)))
1021 minetest
.log("error", "[ltool] fields.plantmode = "..tostring(fields
.plantmode
))
1024 if(tonumber(fields
.seed
)~=nil) then
1025 treedef
.seed
= tonumber(fields
.seed
)
1028 ltool
.plant_tree(seltree_id
, tree_pos
)
1032 elseif(fields
.sapling
) then
1033 if(seltree
~= nil) then
1034 if(privs
.lplant
~= true) then
1035 ltool
.save_fields(playername
, formname
, fields
)
1036 local message
= "You can't request saplings, you need to have the \"lplant\" privilege."
1037 ltool
.show_dialog(playername
, "ltool:treeform_error_sapling", message
)
1041 if(tonumber(fields
.seed
)~=nil) then
1042 seed
= tonumber(fields
.seed
)
1044 local ret
, ret2
= ltool
.give_sapling(ltool
.trees
[seltree_id
].treedef
, seed
, playername
, true, ltool
.trees
[seltree_id
].name
)
1045 if(ret
==false and ret2
==2) then
1046 ltool
.save_fields(playername
, formname
, fields
)
1047 ltool
.show_dialog(playername
, "ltool:treeform_error_sapling", "Error: The sapling could not be given to you. Probably your inventory is full.")
1052 elseif(formname
== "ltool:treeform_edit") then
1053 if(fields
.edit_save
or fields
.edit_sapling
) then
1054 local param1
, param2
1055 param1
, param2
= ltool
.evaluate_edit_fields(fields
, fields
.edit_sapling
~= nil)
1056 if(fields
.edit_save
and privs
.ledit
~= true) then
1057 ltool
.save_fields(playername
, formname
, fields
)
1058 local message
= "You can't save trees, you need to have the \"ledit\" privilege."
1059 ltool
.show_dialog(playername
, "ltool:treeform_error_ledit", message
)
1062 if(fields
.edit_sapling
and privs
.lplant
~= true) then
1063 ltool
.save_fields(playername
, formname
, fields
)
1064 local message
= "You can't request saplings, you need to have the \"lplant\" privilege."
1065 ltool
.show_dialog(playername
, "ltool:treeform_error_ledit", message
)
1068 local tree_ok
= true
1070 if(param1
~= nil) then
1073 for k
,v
in pairs(ltool
.trees
) do
1074 if(fields
.edit_save
and v
.name
== name
) then
1075 ltool
.save_fields(playername
, formname
, fields
)
1076 if(v
.author
== playername
) then
1077 local formspec
= "size[6,2;]label[0,0.2;You already have a tree with this name.\nDo you want to replace it?]"..
1078 "button[0,1.5;3,1;replace_yes;Yes]"..
1079 "button[3,1.5;3,1;replace_no;No]"
1080 minetest
.show_formspec(playername
, "ltool:treeform_replace", formspec
)
1082 ltool
.show_dialog(playername
, "ltool:treeform_error_nameclash", "Error: This name is already taken by someone else.\nPlease choose a different name.")
1090 ltool
.save_fields(playername
, formname
, fields
)
1091 if(tree_ok
== true) then
1092 if fields
.edit_save
then
1093 ltool
.add_tree(name
, playername
, treedef
)
1094 elseif fields
.edit_sapling
then
1095 local ret
, ret2
= ltool
.give_sapling(treedef
, tostring(ltool
.seed
), playername
, true, fields
.name
)
1096 if(ret
==false and ret2
==2) then
1097 ltool
.save_fields(playername
, formname
, fields
)
1098 ltool
.show_dialog(playername
, "ltool:treeform_error_sapling", "Error: The sapling could not be given to you. Probably your inventory is full.")
1102 local message
= "Error: The tree definition is invalid.\n"..
1103 minetest
.formspec_escape(param2
)
1104 ltool
.show_dialog(playername
, "ltool:treeform_error_badtreedef", message
)
1107 if(fields
.edit_clear
) then
1108 local privs
= minetest
.get_player_privs(playername
)
1109 ltool
.save_fields(playername
, formname
, {})
1110 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(nil, privs
.ledit
, privs
.lplant
)
1111 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1113 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
1115 if(fields
.edit_axiom
) then
1116 fragment
= "axiom;Axiom;"..s(fields
.axiom
)
1117 elseif(fields
.edit_rules_a
) then
1118 fragment
= "rules_a;Rules set A;"..s(fields
.rules_a
)
1119 elseif(fields
.edit_rules_b
) then
1120 fragment
= "rules_b;Rules set B;"..s(fields
.rules_b
)
1121 elseif(fields
.edit_rules_c
) then
1122 fragment
= "rules_c;Rules set C;"..s(fields
.rules_c
)
1123 elseif(fields
.edit_rules_d
) then
1124 fragment
= "rules_d;Rules set D;"..s(fields
.rules_d
)
1127 ltool
.save_fields(playername
, formname
, fields
)
1128 local formspec
= ltool
.formspec_editplus(fragment
)
1129 minetest
.show_formspec(playername
, "ltool:treeform_editplus", formspec
)
1131 if(mod_select_item
and (fields
.edit_trunk
or fields
.edit_leaves
or fields
.edit_leaves2
or fields
.edit_fruit
)) then
1132 ltool
.save_fields(playername
, formname
, fields
)
1133 select_item
.show_dialog(playername
, "ltool:node", function(itemstring
)
1134 if itemstring
~= "air" and minetest
.registered_nodes
[itemstring
] ~= nil then
1139 --[[ Larger edit fields for axiom and rules fields ]]
1140 elseif(formname
== "ltool:treeform_editplus") then
1141 local editfields
= ltool
.playerinfos
[playername
].treeform
.edit
.fields
1142 local function addchar(c
)
1144 if(c
=="P") then c
= "[" end
1145 if(c
=="p") then c
= "]" end
1146 if(fields
.axiom
) then
1147 fragment
= "axiom;Axiom;"..s(fields
.axiom
..c
)
1148 elseif(fields
.rules_a
) then
1149 fragment
= "rules_a;Rules set A;"..s(fields
.rules_a
..c
)
1150 elseif(fields
.rules_b
) then
1151 fragment
= "rules_b;Rules set B;"..s(fields
.rules_b
..c
)
1152 elseif(fields
.rules_c
) then
1153 fragment
= "rules_c;Rules set C;"..s(fields
.rules_c
..c
)
1154 elseif(fields
.rules_d
) then
1155 fragment
= "rules_d;Rules set D;"..s(fields
.rules_d
..c
)
1157 local formspec
= ltool
.formspec_editplus(fragment
)
1158 minetest
.show_formspec(playername
, "ltool:treeform_editplus", formspec
)
1160 if(fields
.editplus_save
) then
1161 local function o(writed
, writer
)
1162 if(writer
~=nil) then
1168 editfields
.axiom
= o(editfields
.axiom
, fields
.axiom
)
1169 editfields
.rules_a
= o(editfields
.rules_a
, fields
.rules_a
)
1170 editfields
.rules_b
= o(editfields
.rules_b
, fields
.rules_b
)
1171 editfields
.rules_c
= o(editfields
.rules_c
, fields
.rules_c
)
1172 editfields
.rules_d
= o(editfields
.rules_d
, fields
.rules_d
)
1173 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(editfields
, privs
.ledit
, privs
.lplant
)
1174 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1175 elseif(fields
.editplus_cancel
or fields
.quit
) then
1176 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(editfields
, privs
.ledit
, privs
.lplant
)
1177 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1179 for id
, field
in pairs(fields
) do
1180 if(string.sub(id
,1,11) == "editplus_c_") then
1181 local char
= string.sub(id
,12,12)
1186 --[[ "Database" tab ]]
1187 elseif(formname
== "ltool:treeform_database") then
1188 if(fields
.treelist
) then
1189 local event
= minetest
.explode_textlist_event(fields
.treelist
)
1190 if(event
.type == "CHG") then
1191 ltool
.playerinfos
[playername
].dbsel
= event
.index
1192 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(2)..ltool
.tab_database(event
.index
, playername
)
1193 minetest
.show_formspec(playername
, "ltool:treeform_database", formspec
)
1195 elseif(fields
.database_copy
) then
1196 if(seltree
~= nil) then
1197 if(ltool
.playerinfos
[playername
] ~= nil) then
1198 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(ltool
.tree_to_fields(seltree
), privs
.ledit
, privs
.lplant
)
1199 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1202 ltool
.show_dialog(playername
, "ltool:treeform_error_nodbsel", "Error: No tree is selected.")
1204 elseif(fields
.database_update
) then
1205 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(2)..ltool
.tab_database(ltool
.playerinfos
[playername
].dbsel
, playername
)
1206 minetest
.show_formspec(playername
, "ltool:treeform_database", formspec
)
1208 elseif(fields
.database_delete
) then
1209 if(privs
.ledit
~= true) then
1210 ltool
.save_fields(playername
, formname
, fields
)
1211 local message
= "You can't delete trees, you need to have the \"ledit\" privilege."
1212 ltool
.show_dialog(playername
, "ltool:treeform_error_ledit_db", message
)
1215 if(seltree
~= nil) then
1216 if(playername
== seltree
.author
) then
1217 local remove_id
= ltool
.get_selected_tree_id(playername
)
1218 if(remove_id
~= nil) then
1219 ltool
.remove_tree(remove_id
)
1220 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(2)..ltool
.tab_database(ltool
.playerinfos
[playername
].dbsel
, playername
)
1221 minetest
.show_formspec(playername
, "ltool:treeform_database", formspec
)
1224 ltool
.show_dialog(playername
, "ltool:treeform_error_delete", "Error: This tree is not your own. You may only delete your own trees.")
1227 ltool
.show_dialog(playername
, "ltool:treeform_error_nodbsel", "Error: No tree is selected.")
1229 elseif(fields
.database_rename
) then
1230 if(seltree
~= nil) then
1231 if(privs
.ledit
~= true) then
1232 ltool
.save_fields(playername
, formname
, fields
)
1233 local message
= "You can't rename trees, you need to have the \"ledit\" privilege."
1234 ltool
.show_dialog(playername
, "ltool:treeform_error_ledit_db", message
)
1237 if(playername
== seltree
.author
) then
1238 local formspec
= "field[newname;New name:;"..minetest
.formspec_escape(seltree
.name
).."]"
1239 minetest
.show_formspec(playername
, "ltool:treeform_rename", formspec
)
1241 ltool
.show_dialog(playername
, "ltool:treeform_error_rename_forbidden", "Error: This tree is not your own. You may only rename your own trees.")
1244 ltool
.show_dialog(playername
, "ltool:treeform_error_nodbsel", "Error: No tree is selected.")
1247 --[[ Process "Do you want to replace this tree?" dialog ]]
1248 elseif(formname
== "ltool:treeform_replace") then
1249 local editfields
= ltool
.playerinfos
[playername
].treeform
.edit
.fields
1250 local newtreedef
, newname
= ltool
.evaluate_edit_fields(editfields
)
1251 if(privs
.ledit
~= true) then
1252 local message
= "You can't overwrite trees, you need to have the \"ledit\" privilege."
1253 minetest
.show_dialog(playername
, "ltool:treeform_error_ledit", message
)
1256 if(fields
.replace_yes
) then
1257 for tree_id
,tree
in pairs(ltool
.trees
) do
1258 if(tree
.name
== newname
) then
1259 --[[ The old tree is deleted and a
1260 new one with a new ID is created ]]
1261 local new_tree_id
= ltool
.next_tree_id
1262 ltool
.trees
[new_tree_id
] = {}
1263 ltool
.trees
[new_tree_id
].treedef
= newtreedef
1264 ltool
.trees
[new_tree_id
].name
= newname
1265 ltool
.trees
[new_tree_id
].author
= tree
.author
1266 ltool
.next_tree_id
= ltool
.next_tree_id
+ 1
1267 ltool
.trees
[tree_id
] = nil
1268 ltool
.playerinfos
[playername
].dbsel
= ltool
.number_of_trees
1272 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(editfields
, privs
.ledit
, privs
.lplant
)
1273 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1274 elseif(formname
== "ltool:treeform_help") then
1275 local tab
= tonumber(fields
.ltool_help_tab
)
1277 ltool
.playerinfos
[playername
].treeform
.help
.tab
= tab
1278 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(4)..ltool
.tab_help(tab
)
1279 minetest
.show_formspec(playername
, "ltool:treeform_help", formspec
)
1281 if(fields
.create_template
) then
1284 rules_a
="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]",
1285 rules_b
="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]",
1286 trunk
="mapgen_tree",
1287 leaves
="mapgen_leaves",
1291 trunk_type
="single",
1292 thin_branches
="true",
1294 fruit
="mapgen_apple",
1295 name
= "Example Tree "..ltool
.next_tree_id
1297 ltool
.save_fields(playername
, formname
, newfields
)
1298 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(newfields
, privs
.ledit
, privs
.lplant
)
1299 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1301 --[[ Tree renaming dialog ]]
1302 elseif(formname
== "ltool:treeform_rename") then
1303 if(privs
.ledit
~= true) then
1304 ltool
.save_fields(playername
, formname
, fields
)
1305 local message
= "You can't delete trees, you need to have the \"ledit\" privilege."
1306 ltool
.show_dialog(playername
, "ltool:treeform_error_ledit_delete", message
)
1309 if(fields
.newname
~= "" and fields
.newname
~= nil) then
1310 ltool
.rename_tree(ltool
.get_selected_tree_id(playername
), fields
.newname
)
1311 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(2)..ltool
.tab_database(ltool
.playerinfos
[playername
].dbsel
, playername
)
1312 minetest
.show_formspec(playername
, "ltool:treeform_database", formspec
)
1314 ltool
.show_dialog(playername
, "ltool:treeform_error_bad_rename", "Error: This name is empty. The tree name must be non-empty.")
1316 --[[ Here come various error messages to handle ]]
1317 elseif(formname
== "ltool:treeform_error_badtreedef" or formname
== "ltool:treeform_error_nameclash" or formname
== "ltool:treeform_error_ledit") then
1318 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(1)..ltool
.tab_edit(ltool
.playerinfos
[playername
].treeform
.edit
.fields
, privs
.ledit
, privs
.lplant
)
1319 minetest
.show_formspec(playername
, "ltool:treeform_edit", formspec
)
1320 elseif(formname
== "ltool:treeform_error_badplantfields" or formname
== "ltool:treeform_error_sapling" or formname
== "ltool:treeform_error_lplant") then
1321 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(3)..ltool
.tab_plant(seltree
, ltool
.playerinfos
[playername
].treeform
.plant
.fields
, privs
.lplant
)
1322 minetest
.show_formspec(playername
, "ltool:treeform_plant", formspec
)
1323 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
1324 local formspec
= ltool
.formspec_size
..ltool
.formspec_header(2)..ltool
.tab_database(ltool
.playerinfos
[playername
].dbsel
, playername
)
1325 minetest
.show_formspec(playername
, "ltool:treeform_database", formspec
)
1326 elseif(formname
== "ltool:treeform_error_bad_rename") then
1327 local formspec
= "field[newname;New name:;"..minetest
.formspec_escape(seltree
.name
).."]"
1328 minetest
.show_formspec(playername
, "ltool:treeform_rename", formspec
)
1330 -- Action for Inventory++ button
1331 if fields
.ltool
and minetest
.get_modpath("inventory_plus") then
1332 ltool
.show_treeform(playername
)
1338 if mod_select_item
then
1339 select_item
.register_on_select_item(function(playername
, dialogname
, itemstring
)
1340 if dialogname
== "ltool:node" then
1342 local f
= ltool
.playerinfos
[playername
].treeform
.edit
.fields
1343 if f
.edit_trunk
then
1344 f
.trunk
= itemstring
1345 elseif f
.edit_leaves
then
1346 f
.leaves
= itemstring
1347 elseif f
.edit_leaves2
then
1348 f
.leaves2
= itemstring
1349 elseif f
.edit_fruit
then
1350 f
.fruit
= itemstring
1353 ltool
.show_treeform(playername
)
1359 --[[ These 2 functions are basically just table initializions and cleanups ]]
1360 function ltool
.leave(player
)
1361 ltool
.playerinfos
[player
:get_player_name()] = nil
1364 function ltool
.join(player
)
1365 local infotable
= {}
1366 infotable
.dbsel
= nil
1367 infotable
.treeform
= {}
1368 infotable
.treeform
.database
= {}
1369 --[[ This table stores a mapping of the textlist IDs in the database formspec and the tree IDs.
1370 It is updated each time ltool.tab_database is called. ]]
1371 infotable
.treeform
.database
.textlist
= {}
1372 --[[ the “fields” tables store the values of the input fields of a formspec. It is updated
1373 whenever the formspec is changed, i.e. on tab change ]]
1374 infotable
.treeform
.database
.fields
= {}
1375 infotable
.treeform
.plant
= {}
1376 infotable
.treeform
.plant
.fields
= {}
1377 infotable
.treeform
.edit
= {}
1378 infotable
.treeform
.edit
.fields
= {}
1379 infotable
.treeform
.help
= {}
1380 infotable
.treeform
.help
.tab
= 1
1381 ltool
.playerinfos
[player
:get_player_name()] = infotable
1383 -- Add Inventory++ support
1384 if minetest
.get_modpath("inventory_plus") then
1385 inventory_plus
.register_button(player
, "ltool", "L-System Tree Utility")
1389 function ltool
.save_to_file()
1390 local savetable
= {}
1391 savetable
.trees
= ltool
.trees
1392 savetable
.number_of_trees
= ltool
.number_of_trees
1393 savetable
.next_tree_id
= ltool
.next_tree_id
1394 local savestring
= minetest
.serialize(savetable
)
1395 local filepath
= minetest
.get_worldpath().."/ltool.mt"
1396 local file
= io
.open(filepath
, "w")
1398 file
:write(savestring
)
1400 minetest
.log("action", "[ltool] Tree data saved to "..filepath
..".")
1402 minetest
.log("error", "[ltool] Failed to write ltool data to "..filepath
".")
1407 minetest
.register_on_player_receive_fields(ltool
.process_form
)
1409 minetest
.register_on_leaveplayer(ltool
.leave
)
1411 minetest
.register_on_joinplayer(ltool
.join
)
1413 minetest
.register_on_shutdown(ltool
.save_to_file
)
1415 local button_action
= function(player
)
1416 ltool
.show_treeform(player
:get_player_name())
1419 if minetest
.get_modpath("unified_inventory") ~= nil then
1420 unified_inventory
.register_button("ltool", {
1422 image
= "ltool_sapling.png",
1423 tooltip
= "L-System Tree Utility",
1424 action
= button_action
,
1428 if minetest
.get_modpath("sfinv_buttons") ~= nil then
1429 sfinv_buttons
.register_button("ltool", {
1430 title
= "L-System Tree Utility",
1431 tooltip
= "Invent your own trees and plant them",
1432 image
= "ltool_sapling.png",
1433 action
= button_action
,