pax_global_header00006660000000000000000000000064134444227150014520gustar00rootroot0000000000000052 comment=5efb442de0bee8fba3044118766371a28ca5ad08 minetest_ltool/000077500000000000000000000000001344442271500141255ustar00rootroot00000000000000minetest_ltool/API.md000066400000000000000000000102131344442271500150550ustar00rootroot00000000000000# API documentation The L-System Tree Utility provides a set of simple functions to mess around with the tree database, generate trees from saplings and more. ## Variables The table `ltool.VERSION` exports version information. This mod uses [Semantic Versioning](http://semver.org/). This information comes in handy if you need to do some compability work. * `ltool.VERSION.STRING`: Full version as string * `ltool.VERSION.MAJOR`: Major (=first) version number, as number * `ltool.VERSION.MINOR`: Minor (=second) version number, as number * `ltool.VERSION.PATCH`: Patch (=third) version number, as number ## Data structures ### `treedef` This is identical to the `treedef` parameter of `minetest.spawn_tree`. ### `tree` A `tree` is the basic data structure. It is basically a wrapper around `treedef`, with some additional fields relevant for the L-System-Tree-Utility, which are listed here: #### `tree_id` A tree ID, an identifier of a `tree`. This is an unique number. Many functions require a tree ID. No identifier is used twice, once an identifier is taken, it won’t be occupied again, even if the `tree` occupying the slot has been deleted. #### `name` An unique name of the tree, assigned by the user. #### `author` The name of the player who created the `tree`. The author is also the “owner” of the `tree` and is the only one who can edit it in the mod. ## Functions ### `ltool.get_tree(tree_id)` Tries to return a `tree` for a given `tree_id`. #### Parameter * `tree_id`: Identifier of `tree` to be received. #### Return value A `tree` on success, `false` on success (happens if no tree with this `tree_id` exists). ### `ltool.get_tree_ids()` Returns a sorted table containing all tree IDs. #### Parameters None. #### Return value A sorted table containing all tree IDs, sorted by ID. ### `ltool.add_tree(name, author, treedef)` Adds a tree to the tree table. #### Parameters * `name`: The tree’s name. * `author`: The author’s / owners’ name * `treedef`: The full tree definition, see `lua_api.txt` #### Return value The tree ID of the new tree. ### `ltool.remove_tree(tree_id)` Removes a tree from the tree database. #### Parameter * `tree_id`: ID of the tree to be removed #### Return value Always `nil`. ### `ltool.rename_tree(tree_id, new_name)` Renames a tree in the database #### Parameters * `tree_id`: ID of the tree to be renamed * `new_name`: The name of the tree #### Return value Always `nil`. ### `ltool.copy_tree(tree_id)` Creates a copy of a tree in the database. #### Parameter * `tree_id`: ID of the tree to be copied #### Return value The ID of the copy on success, `false` on failure (tree does not exist). ### `ltool.generate_sapling(tree_id, seed)` Generates a sapling as an `ItemStack` to mess around later with. #### Parameter * `tree_id`: ID of tree the sapling will grow * `seed`: Seed of the tree the sapling will grow (optional, can be nil) #### Return value An `ItemStack` which contains one sapling of the specified `tree`, on success. Returns `false` on failure (happens if tree does not exist). ### `ltool.give_sapling(tree_id, seed, player_name, ignore_priv)` Gives a L-system tree sapling to a player. #### Parameters * `tree_id`: ID of tree the sapling will grow * `seed`: Seed of the tree (optional; can be nil) * `playername`: name of the player to which * `ignore_priv`: if `true`, player’s `lplant` privilige is not checked (optional argument; default: `false`) #### Return value It depends: * `true` on success * `false, 1` if player does not have `lplant` privilege * `false, 2` if player’s inventory is full * `false, 3` if `tree` does not exist ### `ltool.plant_tree(tree_id, pos, seed)` Plants a tree as the specified position. #### Parameters * `tree_id`: ID of tree to be planted * `pos`: Position of tree, in the format `{x=?, y=?, z=?}` * `seed`: Optional randomness seed, equal seed creates equal trees #### Return value `false` on failure, `nil` otherwise. ### `ltool.show_treeform(playername)` Shows the main tree form to the given player, starting with the "Edit" tab. #### Parameters * `playername`: Name of the player to whom the formspec should be shown to #### Return value Always `nil.` minetest_ltool/README.md000066400000000000000000000014631344442271500154100ustar00rootroot00000000000000This mod provides a form to easily spawn L-system trees, aimed at mod developers and power users. Current version: 1.6.1 You can open the menu with an item “L-System Tree Utility” (`ltool:tool`) or the server command `/treeform`. If you use the following mod, there will be buttons to ease node selection: * Select Item [`select_item`] If you use one of the following mods, there's a new button in your inventory menu for opening the form: * Unified Inventory [`unified_inventory`] * Simple Fast Inventory Buttons [`sfinv_buttons`] * Inventory++ [`inventory_plus`] This mod also adds the command `/lplant` to plant trees. Usage help for this form is provided in the form itself. This mod also has an API for managing the tree database. See `API.md` for more information. License of this mod: MIT License. minetest_ltool/init.lua000066400000000000000000001713511344442271500156030ustar00rootroot00000000000000local S = minetest.get_translator("ltool") local N = function(s) return s end local F = minetest.formspec_escape ltool = {} ltool.VERSION = {} ltool.VERSION.MAJOR = 1 ltool.VERSION.MINOR = 6 ltool.VERSION.PATCH = 1 ltool.VERSION.STRING = ltool.VERSION.MAJOR .. "." .. ltool.VERSION.MINOR .. "." .. ltool.VERSION.PATCH ltool.playerinfos = {} ltool.default_edit_fields = { axiom="", rules_a="", rules_b="", rules_c="", rules_d="", trunk="mapgen_tree", leaves="mapgen_leaves", leaves2="mapgen_jungleleaves", leaves2_chance="0", fruit="mapgen_apple", fruit_chance="0", angle="45", iterations="2", random_level="0", trunk_type="single", thin_branches="true", name = "", } local mod_select_item = minetest.get_modpath("select_item") ~= nil local sapling_base_name = S("L-System Tree Sapling") local sapling_format_string = N("L-System Tree Sapling (@1)") local place_tree = function(pos) -- Place tree local meta = minetest.get_meta(pos) local treedef = minetest.deserialize(meta:get_string("treedef")) minetest.remove_node(pos) minetest.spawn_tree(pos, treedef) end --[[ This registers the sapling for planting the trees ]] minetest.register_node("ltool:sapling", { description = sapling_base_name, _doc_items_longdesc = S("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?"), _doc_items_usagehelp = S("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 using it. If you hold down the sneak key while placing it, you will keep a copy of the sapling in your inventory.").."\n"..S("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”)."), drawtype = "plantlike", tiles = { "ltool_sapling.png" }, inventory_image = "ltool_sapling.png", selection_box = { type = "fixed", fixed = { -10/32, -0.5, -10/32, 10/32, 12/32, 10/32 }, }, wield_image = "ltool_sapling.png", paramtype = "light", paramtype2= "wallmounted", walkable = false, groups = { dig_immediate = 3, not_in_creative_inventory=1, }, drop = "", sunlight_propagates = true, is_ground_content = false, after_place_node = function(pos, placer, itemstack, pointed_thing) -- Transfer metadata and start timer local nodemeta = minetest.get_meta(pos) local itemmeta = itemstack:get_meta() local itemtreedef = itemmeta:get_string("treedef") -- Legacy support for saplings with legacy metadata if itemtreedef == nil or itemtreedef == "" then itemtreedef = itemstack:get_metadata() if itemtreedef == nil or itemtreedef == "" then return nil end end nodemeta:set_string("treedef", itemtreedef) local timer = minetest.get_node_timer(pos) timer:start(5) if placer:get_player_control().sneak == true then return true else return nil end end, -- Insta-grow when sapling got rightclicked on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) if minetest.get_player_privs(clicker:get_player_name()).lplant then place_tree(pos) end end, -- Grow after timer elapsed on_timer = place_tree, can_dig = function(pos, player) return minetest.get_player_privs(player:get_player_name()).lplant end, }) minetest.register_craftitem("ltool:tool", { description = S("L-System Tree Utility"), _doc_items_longdesc = S("This gadget allows the aspiring genetic engineer to invent and change L-system trees, create L-system tree saplings and look at the inventions from other players. L-system trees are trees and tree-like strucures which are built by a set of (possibly recursive) production rules."), _doc_items_usagehelp = S("Punch to open the L-System editor. A tabbed form will open. To edit and create trees, you need the “ledit” privilege, to make saplings, you need “lplant”. Detailed usage help can be found in that menu. You can also access the same editor with the server command “treeform”."), inventory_image = "ltool_tool.png", wield_image = "ltool_tool.png", on_use = function(itemstack, user, pointed_thing) ltool.show_treeform(user:get_player_name()) end, }) --[[ Register privileges ]] minetest.register_privilege("ledit", { description = S("Can add, edit, rename and delete own L-system tree definitions of the ltool mod"), give_to_singleplayer = false, }) minetest.register_privilege("lplant", { description = S("Can place L-system trees and get L-system tree saplings of the ltool mod"), give_to_singleplayer = false, }) --[[ Load previously saved data from file or initialize an empty tree table ]] do local filepath = minetest.get_worldpath().."/ltool.mt" local file = io.open(filepath, "r") if(file) then local string = file:read() io.close(file) if(string ~= nil) then local savetable = minetest.deserialize(string) if(savetable ~= nil) then ltool.trees = savetable.trees ltool.next_tree_id = savetable.next_tree_id ltool.number_of_trees = savetable.number_of_trees minetest.log("action", "[ltool] Tree data loaded from "..filepath..".") else minetest.log("error", "[ltool] Failed to load tree data from "..filepath..".") end else minetest.log("error", "[ltool] Failed to load tree data from "..filepath..".") end else --[[ table of all trees ]] ltool.trees = {} --[[ helper variables to ensure unique IDs ]] ltool.number_of_trees = 0 ltool.next_tree_id = 1 end end --[[ Adds a tree to the tree table. name: The tree’s name. author: The author’s / owners’ name treedef: The full tree definition, see lua_api.txt returns the tree ID of the new tree ]] function ltool.add_tree(name, author, treedef) local id = ltool.next_tree_id ltool.trees[id] = {name = name, author = author, treedef = treedef} ltool.next_tree_id = ltool.next_tree_id + 1 ltool.number_of_trees = ltool.number_of_trees + 1 return id end --[[ Removes a tree from the database tree_id: ID of the tree to be removed returns nil ]] function ltool.remove_tree(tree_id) ltool.trees[tree_id] = nil ltool.number_of_trees = ltool.number_of_trees - 1 for k,v in pairs(ltool.playerinfos) do if(v.dbsel ~= nil) then if(v.dbsel > ltool.number_of_trees) then v.dbsel = ltool.number_of_trees end if(v.dbsel < 1) then v.dbsel = 1 end end end end --[[ Renames a tree in the database tree_id: ID of the tree to be renamed new_name: The name of the tree returns nil ]] function ltool.rename_tree(tree_id, new_name) ltool.trees[tree_id].name = new_name end --[[ Copies a tree in the database tree_id: ID of the tree to be copied returns: the ID of the copy on success; false on failure (tree does not exist) ]] function ltool.copy_tree(tree_id) local tree = ltool.trees[tree_id] if(tree == nil) then return false end return ltool.add_tree(tree.name, tree.author, tree.treedef) end --[[ Gives a L-system tree sapling to a player treedef: L-system tree definition table of tree the sapling will grow seed: Seed of the tree (optional; can be nil) playername: name of the player to which ignore_priv: if true, player’s lplant privilige is not checked (optional argument; default: false) treename: Descriptive name of the tree for the item description (optional, is ignored if nil or empty string) returns: true on success false, 1 if privilege is not sufficient false, 2 if player’s inventory is full ]] function ltool.give_sapling(treedef, seed, player_name, ignore_priv, treename) local privs = minetest.get_player_privs(player_name) if(ignore_priv == nil) then ignore_priv = false end if(ignore_priv == false and privs.lplant ~= true) then return false, 1 end local sapling = ItemStack("ltool:sapling") local player = minetest.get_player_by_name(player_name) treedef.seed = seed local smeta = sapling:get_meta() smeta:set_string("treedef", minetest.serialize(treedef)) if treename and treename ~= "" then smeta:set_string("description", S(sapling_format_string, treename)) end treedef.seed = nil local leftover = player:get_inventory():add_item("main", sapling) if(not leftover:is_empty()) then return false, 2 else return true end end --[[ Plants a tree as the specified position tree_id: ID of tree to be planted pos: Position of tree, in format {x=?, y=?, z=?} seed: Optional seed for randomness, equal seed makes equal trees returns false on failure, nil otherwise ]] function ltool.plant_tree(tree_id, pos, seed) local tree = ltool.trees[tree_id] if(tree==nil) then return false end local treedef if seed ~= nil then treedef = table.copy(tree.treedef) treedef.seed = seed else treedef = tree.treedef end minetest.spawn_tree(pos, treedef) end --[[ Tries to return a tree data structure for a given tree_id tree_id: ID of tee to be returned returns false on failure, a tree otherwise ]] function ltool.get_tree(tree_id) local tree = ltool.trees[tree_id] if(tree==nil) then return false end return tree end ltool.seed = os.time() --[=[ Here come the functions to build the main formspec. They do not build the entire formspec ]=] ltool.formspec_size = "size[12,9]" --[[ This is a part of the main formspec: Tab header ]] function ltool.formspec_header(index) return "tabheader[0,0;ltool_tab;"..F(S("Edit"))..","..F(S("Database"))..","..F(S("Plant"))..","..F(S("Help"))..";"..tostring(index)..";true;false]" end --[[ This creates the edit tab of the formspec fields: A template used to fill the default values of the formspec. ]] function ltool.tab_edit(fields, has_ledit_priv, has_lplant_priv) if(fields==nil) then fields = ltool.default_edit_fields end local s = function(input) local ret if(input==nil) then ret = "" else ret = F(tostring(input)) end return ret end -- Show save/clear buttons depending on privs local leditbuttons if has_ledit_priv then leditbuttons = "button[0,8.7;4,0;edit_save;"..F(S("Save tree to database")).."]".. "button[4,8.7;4,0;edit_clear;"..F(S("Reset fields")).."]" if has_lplant_priv then leditbuttons = leditbuttons .. "button[8,8.7;4,0;edit_sapling;"..F(S("Generate sapling")).."]" end else leditbuttons = "label[0,8.3;"..F(S("Read-only mode. You need the “ledit” privilege to save trees to the database.")).."]" end local nlength = "3" local fields_select_item = "" if mod_select_item then nlength = "2.6" fields_select_item = "".. "button[2.4,5.7;0.5,0;edit_trunk;"..F(S(">")).."]".. "button[5.4,5.7;0.5,0;edit_leaves;"..F(S(">")).."]".. "button[8.4,5.7;0.5,0;edit_leaves2;"..F(S(">")).."]".. "button[11.4,5.7;0.5,0;edit_fruit;"..F(S(">")).."]".. "tooltip[edit_trunk;"..F(S("Select node")).."]".. "tooltip[edit_leaves;"..F(S("Select node")).."]".. "tooltip[edit_leaves2;"..F(S("Select node")).."]".. "tooltip[edit_fruit;"..F(S("Select node")).."]" end local trunk_type_mapping_reverse = { ["single"] = 1, ["double"] = 2, ["crossed"] = 3, } local trunk_type_idx if fields.trunk_type then trunk_type_idx = trunk_type_mapping_reverse[fields.trunk_type] else trunk_type_idx = 1 end return "".. "field[0.2,1;11,0;axiom;"..F(S("Axiom"))..";"..s(fields.axiom).."]".. "button[11,0.7;1,0;edit_axiom;"..F(S("+")).."]".. "tooltip[edit_axiom;"..F(S("Opens larger text field for Axiom")).."]".. "field[0.2,2;11,0;rules_a;"..F(S("Rules set A"))..";"..s(fields.rules_a).."]".. "button[11,1.7;1,0;edit_rules_a;"..F(S("+")).."]".. "tooltip[edit_rules_a;"..F(S("Opens larger text field for Rules set A")).."]".. "field[0.2,3;11,0;rules_b;"..F(S("Rules set B"))..";"..s(fields.rules_b).."]".. "button[11,2.7;1,0;edit_rules_b;"..F(S("+")).."]".. "tooltip[edit_rules_b;"..F(S("Opens larger text field for Rules set B")).."]".. "field[0.2,4;11,0;rules_c;"..F(S("Rules set C"))..";"..s(fields.rules_c).."]".. "button[11,3.7;1,0;edit_rules_c;"..F(S("+")).."]".. "tooltip[edit_rules_c;"..F(S("Opens larger text field for Rules set C")).."]".. "field[0.2,5;11,0;rules_d;"..F(S("Rules set D"))..";"..s(fields.rules_d).."]".. "button[11,4.7;1,0;edit_rules_d;"..F(S("+")).."]".. "tooltip[edit_rules_d;"..F(S("Opens larger text field for Rules set D")).."]".. "field[0.2,6;"..nlength..",0;trunk;"..F(S("Trunk node"))..";"..s(fields.trunk).."]".. "field[3.2,6;"..nlength..",0;leaves;"..F(S("Leaves node"))..";"..s(fields.leaves).."]".. "field[6.2,6;"..nlength..",0;leaves2;"..F(S("Secondary leaves node"))..";"..s(fields.leaves2).."]".. "field[9.2,6;"..nlength..",0;fruit;"..F(S("Fruit node"))..";"..s(fields.fruit).."]".. fields_select_item.. "label[-0.075,5.95;"..F(S("Trunk type")).."]".. "dropdown[-0.075,6.35;3;trunk_type;single,double,crossed;"..trunk_type_mapping_reverse[fields.trunk_type].."]".. "tooltip[trunk_type;"..F(S("Tree trunk type. Possible values:\n- \"single\": trunk of size 1×1\n- \"double\": trunk of size 2×2\n- \"crossed\": trunk in cross shape (3×3).")).."]".. "checkbox[2.9,6.2;thin_branches;"..F(S("Thin branches"))..";"..s(fields.thin_branches).."]".. "tooltip[thin_branches;"..F(S("If enabled, all branches are just 1 node wide, otherwise, branches can be larger.")).."]".. "field[6.2,7;3,0;leaves2_chance;"..F(S("Secondary leaves chance (%)"))..";"..s(fields.leaves2_chance).."]".. "tooltip[leaves2_chance;"..F(S("Chance (in percent) to replace a leaves node by a secondary leaves node")).."]".. "field[9.2,7;3,0;fruit_chance;"..F(S("Fruit chance (%)"))..";"..s(fields.fruit_chance).."]".. "tooltip[fruit_chance;"..F(S("Chance (in percent) to replace a leaves node by a fruit node.")).."]".. "field[0.2,8;3,0;iterations;"..F(S("Iterations"))..";"..s(fields.iterations).."]".. "tooltip[iterations;"..F(S("Maximum number of iterations, usually between 2 and 5.")).."]".. "field[3.2,8;3,0;random_level;"..F(S("Randomness level"))..";"..s(fields.random_level).."]".. "tooltip[random_level;"..F(S("Factor to lower number of iterations, usually between 0 and 3.")).."]".. "field[6.2,8;3,0;angle;"..F(S("Angle (°)"))..";"..s(fields.angle).."]".. "field[9.2,8;3,0;name;"..F(S("Name"))..";"..s(fields.name).."]".. "tooltip[name;"..F(S("Descriptive name for this tree, only used for convenience.")).."]".. leditbuttons end --[[ This creates the database tab of the formspec. index: Selected index of the textlist playername: To whom the formspec is shown ]] function ltool.tab_database(index, playername) local treestr, tree_ids = ltool.build_tree_textlist(index, playername) if(treestr ~= nil) then local indexstr if(index == nil) then indexstr = "" else indexstr = tostring(index) end ltool.playerinfos[playername].treeform.database.textlist = tree_ids local leditbuttons, lplantbuttons if minetest.get_player_privs(playername).ledit then leditbuttons = "button[3,7.5;3,1;database_rename;"..F(S("Rename tree")).."]".. "button[6,7.5;3,1;database_delete;"..F(S("Delete tree")).."]" else leditbuttons = "label[0.2,7.2;"..F(S("Read-only mode. You need the “ledit” privilege to edit trees.")).."]" end if minetest.get_player_privs(playername).lplant then lplantbuttons = "button[0,8.5;3,1;sapling;"..F(S("Generate sapling")).."]" else lplantbuttons = "" end return "".. "textlist[0,0;11,7;treelist;"..treestr..";"..tostring(index)..";false]".. lplantbuttons.. leditbuttons.. "button[3,8.5;3,1;database_copy;"..F(S("Copy tree to editor")).."]".. "button[6,8.5;3,1;database_update;"..F(S("Reload database")).."]" else return "label[0,0;"..F(S("The tree database is empty.")).."]".. "button[6.5,8.5;3,1;database_update;"..F(S("Reload database")).."]" end end --[[ This creates the "Plant" tab part of the main formspec ]] function ltool.tab_plant(tree, fields, has_lplant_priv) if(tree ~= nil) then local seltree = "label[0,-0.2;"..F(S("Selected tree: @1", tree.name)).."]" if not has_lplant_priv then return seltree.. "label[0,0.3;"..F(S("Planting of trees is not allowed. You need to have the “lplant” privilege.")).."]" end if(fields==nil) then fields = {} end local s = function(i) if(i==nil) then return "" else return tostring(F(i)) end end local seed if(fields.seed == nil) then seed = tostring(ltool.seed) else seed = fields.seed end local dropdownindex if(fields.plantmode == F(S("Absolute coordinates"))) then dropdownindex = 1 elseif(fields.plantmode == F(S("Relative coordinates"))) then dropdownindex = 2 elseif(fields.plantmode == F(S("Distance in viewing direction"))) then dropdownindex = 3 else dropdownindex = 1 end return "".. seltree.. "dropdown[-0.1,0.5;5;plantmode;"..F(S("Absolute coordinates"))..","..F(S("Relative coordinates"))..","..F(S("Distance in viewing direction"))..";"..dropdownindex.."]".. --[[ NOTE: This tooltip does not work for the dropdown list in 0.4.10, but it is added anyways in case this gets fixed in later Minetest versions. ]] "tooltip[plantmode;".. F(S("- \"Absolute coordinates\": Fields \"x\", \"y\" and \"z\" specify the absolute world coordinates where to plant the tree")).."\n".. F(S("- \"Relative coordinates\": Fields \"x\", \"y\" and \"z\" specify the relative position from your position")).."\n".. F(S("- \"Distance in viewing direction\": Plant tree relative from your position in the direction you look to, at the specified distance")).. "]".. "field[0.2,-2;6,10;x;"..F(S("x"))..";"..s(fields.x).."]".. "tooltip[x;"..F(S("Field is only used by absolute and relative coordinates.")).."]".. "field[0.2,-1;6,10;y;"..F(S("y"))..";"..s(fields.y).."]".. "tooltip[y;"..F(S("Field is only used by absolute and relative coordinates.")).."]".. "field[0.2,0;6,10;z;"..F(S("z"))..";"..s(fields.z).."]".. "tooltip[z;"..F(S("Field is only used by absolute and relative coordinates.")).."]".. "field[0.2,1;6,10;distance;"..F(S("Distance"))..";"..s(fields.distance).."]".. "tooltip[distance;"..F(S("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.")).."]".. "field[0.2,2;6,10;seed;"..F(S("Randomness seed"))..";"..seed.."]".. "tooltip[seed;"..F(S("A number used for the random number generators. Identical randomness seeds will produce identical trees. This field is optional.")).."]".. "button[3.5,8;3,1;plant_plant;"..F(S("Plant tree")).."]".. "tooltip[plant_plant;"..F(S("Immediately place the tree at the specified position")).."]".. "button[6.5,8;3,1;sapling;"..F(S("Generate sapling")).."]".. "tooltip[sapling;"..F(S("This gives you an item which you can place manually in the world later")).."]" else local notreestr = F(S("No tree in database selected or database is empty.")) if has_lplant_priv then return "label[0,0;"..notreestr.."]" else return "label[0,0;"..notreestr.."\n"..F(S("You are not allowed to plant trees anyway as you don't have the “lplant” privilege.")).."]" end end end --[[ This creates the cheat sheet tab ]] function ltool.tab_cheat_sheet() return "".. "tablecolumns[text;text]".. "tableoptions[background=#000000;highlight=#000000;border=false]".. "table[-0.15,0.75;12,8;cheat_sheet;".. F(S("Symbol"))..","..F(S("Action"))..",".. "G,"..F(S("Move forward one unit with the pen up"))..",".. "F,"..F(S("Move forward one unit with the pen down drawing trunks and branches"))..",".. "f,"..F(S("Move forward one unit with the pen down drawing leaves"))..",".. "T,"..F(S("Move forward one unit with the pen down drawing trunks"))..",".. "R,"..F(S("Move forward one unit with the pen down placing fruit"))..",".. "A,"..F(S("Replace with rules set A"))..",".. "B,"..F(S("Replace with rules set B"))..",".. "C,"..F(S("Replace with rules set C"))..",".. "D,"..F(S("Replace with rules set D"))..",".. "a,"..F(S("Replace with rules set A, chance 90%"))..",".. "b,"..F(S("Replace with rules set B, chance 80%"))..",".. "c,"..F(S("Replace with rules set C, chance 70%"))..",".. "d,"..F(S("Replace with rules set D, chance 60%"))..",".. "+,"..F(S("Yaw the turtle right by angle parameter"))..",".. "-,"..F(S("Yaw the turtle left by angle parameter"))..",".. "&,"..F(S("Pitch the turtle down by angle parameter"))..",".. "^,"..F(S("Pitch the turtle up by angle parameter"))..",".. "/,"..F(S("Roll the turtle to the right by angle parameter"))..",".. "*,"..F(S("Roll the turtle to the left by angle parameter"))..",".. "\\[,"..F(S("Save in stack current state info"))..",".. "\\],"..F(S("Recover from stack state info")).."]" end function ltool.tab_help_intro() return "".. "textarea[0.2,0.75;12,8;;;".. F( S("You are using the L-System Tree Utility, version @1.", ltool.VERSION.STRING).."\n\n".. S("The purpose of this utility is to aid with the creation of L-system trees. You can create, save, manage and plant L-system trees. All trees are saved into /ltool.mt on server shutdown.").."\n".. S("It assumes you already understand the concept of L-systems, this utility is mainly aimed towards modders and nerds.").."\n\n".. S("The usual workflow goes like this:").."\n\n".. S("1. Create a new tree in the \"Edit\" tab and save it").."\n".. S("2. Select it in the database").."\n".. S("3. Plant it").."\n\n".. S("To help you get started, you can create an example tree for the \"Edit\" tab by pressing this button:") ).."]".. "button[4,8;4,1;create_template;"..F(S("Create template")).."]" end function ltool.tab_help_edit() return "".. "textarea[0.2,0.75;12,9;;;".. F( S("To create a L-system tree, switch to the \"Edit\" tab.").."\n".. S("When you are done, hit \"Save tree to database\". The tree will be stored in the database. The \"Reset fields\" button resets the input fields to defaults.").."\n\n".. S("To understand the meaning of the fields, read the introduction to L-systems.").."\n\n".. S("All trees must have an unique name. You are notified in case of a name clash. If the name clash is with one of your own trees, you can choose to replace it.") ).."]" end function ltool.tab_help_database() return "".. "textarea[0.2,0.75;12,9;;;".. F( S("The database contains a list of all created trees among all players.").."\n\n".. S("Each tree has an \"owner\". This kind of ownership is limited: The owner may rename, change and delete their own trees, everyone else is prevented from doing that. But all trees can be copied freely by everyone.").."\n".. S("To do so, simply hit \"Copy tree to editor\", change the name and hit \"Save tree to database\". If you like someone else's tree definition, it is recommended to make a copy for yourself, since the original owner can at any time choose to delete or edit the tree. The trees which you \"own\" are written in a yellow font, all other trees in a white font.").."\n\n".. S("In order to plant a tree, you have to select a tree in the database first.") ).."]" end function ltool.tab_help_plant() return "".. "textarea[0.2,0.75;12,9;;;".. F( S("To plant a tree from a previously created tree definition, first select it in the database, then open the \"Plant\" tab. In this tab, you can directly place the tree or request a sapling.").."\n".. S("If you choose to directly place the tree, you can either specify absolute or relative coordinates or specify that the tree should be planted in your viewing direction. Absolute coordinates are the world coordinates as specified by the \"x\", \"y\", and \"z\" fields. Relative coordinates are relative to your position and use the same fields. When you choose to plant the tree based on your viewing direction, the tree will be planted at a distance specified by the field \"distance\" away from you in the direction you look to.").."\n".. S("When using coordinates, the \"distance\" field is ignored, when using direction, the coordinate fields are ignored.").."\n\n".. S("You can also use the “lplant” server command to plant trees.").."\n\n".. S("If you got a sapling, you can place it practically anywhere you like to. After placing it, the sapling will be replaced by the L-system tree after 5 seconds, unless it was destroyed in the meantime.").."\n".. S("All requested saplings are independent from the moment they are created. The sapling will still work, even if the original tree definiton has been deleted.") ).."]" end function ltool.tab_help(index) local formspec = "tabheader[0.1,1;ltool_help_tab;"..F(S("Introduction"))..","..F(S("Creating Trees"))..","..F(S("Managing Trees"))..","..F(S("Planting Trees"))..","..F(S("Cheat Sheet"))..";"..tostring(index)..";true;false]" if(index==1) then formspec = formspec .. ltool.tab_help_intro() elseif(index==2) then formspec = formspec .. ltool.tab_help_edit() elseif(index==3) then formspec = formspec .. ltool.tab_help_database() elseif(index==4) then formspec = formspec .. ltool.tab_help_plant() elseif(index==5) then formspec = formspec .. ltool.tab_cheat_sheet() end return formspec end function ltool.formspec_editplus(fragment) local formspec = "".. "size[12,8]".. "textarea[0.2,0.5;12,3;"..fragment.."]".. "label[0,3.625;"..F(S("Draw:")).."]".. "button[2,3.5;1,1;editplus_c_G;G]".. "tooltip[editplus_c_G;"..F(S("Move forward one unit with the pen up")).."]".. "button[3,3.5;1,1;editplus_c_F;F]".. "tooltip[editplus_c_F;"..F(S("Move forward one unit with the pen down drawing trunks and branches")).."]".. "button[4,3.5;1,1;editplus_c_f;f]".. "tooltip[editplus_c_f;"..F(S("Move forward one unit with the pen down drawing leaves")).."]".. "button[5,3.5;1,1;editplus_c_T;T]".. "tooltip[editplus_c_T;"..F(S("Move forward one unit with the pen down drawing trunks")).."]".. "button[6,3.5;1,1;editplus_c_R;R]".. "tooltip[editplus_c_R;"..F(S("Move forward one unit with the pen down placing fruit")).."]".. "label[0,4.625;"..F(S("Rules:")).."]".. "button[2,4.5;1,1;editplus_c_A;A]".. "tooltip[editplus_c_A;"..F(S("Replace with rules set A")).."]".. "button[3,4.5;1,1;editplus_c_B;B]".. "tooltip[editplus_c_B;"..F(S("Replace with rules set B")).."]".. "button[4,4.5;1,1;editplus_c_C;C]".. "tooltip[editplus_c_C;"..F(S("Replace with rules set C")).."]".. "button[5,4.5;1,1;editplus_c_D;D]".. "tooltip[editplus_c_D;"..F(S("Replace with rules set D")).."]".. "button[6.5,4.5;1,1;editplus_c_a;a]".. "tooltip[editplus_c_a;"..F(S("Replace with rules set A, chance 90%")).."]".. "button[7.5,4.5;1,1;editplus_c_b;b]".. "tooltip[editplus_c_b;"..F(S("Replace with rules set B, chance 80%")).."]".. "button[8.5,4.5;1,1;editplus_c_c;c]".. "tooltip[editplus_c_c;"..F(S("Replace with rules set C, chance 70%")).."]".. "button[9.5,4.5;1,1;editplus_c_d;d]".. "tooltip[editplus_c_d;"..F(S("Replace with rules set D, chance 60%")).."]".. "label[0,5.625;"..F(S("Rotate:")).."]".. "button[3,5.5;1,1;editplus_c_+;+]".. "tooltip[editplus_c_+;"..F(S("Yaw the turtle right by the value specified in \"Angle\"")).."]".. "button[2,5.5;1,1;editplus_c_-;-]".. "tooltip[editplus_c_-;"..F(S("Yaw the turtle left by the value specified in \"Angle\"")).."]".. "button[4.5,5.5;1,1;editplus_c_&;&]".. "tooltip[editplus_c_&;"..F(S("Pitch the turtle down by the value specified in \"Angle\"")).."]".. "button[5.5,5.5;1,1;editplus_c_^;^]".. "tooltip[editplus_c_^;"..F(S("Pitch the turtle up by the value specified in \"Angle\"")).."]".. "button[8,5.5;1,1;editplus_c_/;/]".. "tooltip[editplus_c_/;"..F(S("Roll the turtle to the right by the value specified in \"Angle\"")).."]".. "button[7,5.5;1,1;editplus_c_*;*]".. "tooltip[editplus_c_*;"..F(S("Roll the turtle to the left by the value specified in \"Angle\"")).."]".. "label[0,6.625;"..F(S("Stack:")).."]".. "button[2,6.5;1,1;editplus_c_P;\\[]".. "tooltip[editplus_c_P;"..F(S("Save current state info into stack")).."]".. "button[3,6.5;1,1;editplus_c_p;\\]]".. "tooltip[editplus_c_p;"..F(S("Recover from current stack state info")).."]".. "button[2.5,7.5;3,1;editplus_save;"..F(S("Save")).."]".. "button[5.5,7.5;3,1;editplus_cancel;"..F(S("Cancel")).."]" return formspec end --[[ creates the content of a textlist which contains all trees. index: Selected entry playername: To which the main formspec is shown to. Used for highlighting owned trees returns (string to be used in the text list, table of tree IDs) ]] function ltool.build_tree_textlist(index, playername) local string = "" local colorstring if(ltool.number_of_trees == 0) then return nil end local tree_ids = ltool.get_tree_ids() for i=1,#tree_ids do local tree_id = tree_ids[i] local tree = ltool.trees[tree_id] if(tree.author == playername) then colorstring = "#FFFF00" else colorstring = "" end string = string .. colorstring .. tostring(tree_id) .. ": " .. F(tree.name) if(i~=#tree_ids) then string = string .. "," end end return string, tree_ids end --[=[ Here come functions which show formspecs to players ]=] --[[ Shows the main tree form to the given player, starting with the "Edit" tab ]] function ltool.show_treeform(playername) local privs = minetest.get_player_privs(playername) local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.playerinfos[playername].treeform.edit.fields, privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) end --[[ spawns a simple dialog formspec to a player ]] function ltool.show_dialog(playername, formname, message) local formspec = "size[12,2;]label[0,0.2;"..message.."]".. "button[4.5,1.5;3,1;okay;"..F(S("OK")).."]" minetest.show_formspec(playername, formname, formspec) end --[=[ End of formspec-relatec functions ]=] --[[ This function does a lot of parameter checks and returns (tree, tree_name) on success. If ANY parameter check fails, the whole function fails. On failure, it returns (nil, ).]] function ltool.evaluate_edit_fields(fields, ignore_name) local treedef = {} -- Validation helper: Checks for invalid characters for the fields “axiom” and the 4 rule sets local v = function(str) local match = string.match(str, "[^][abcdfABCDFGTR+-/*&^]") if(match==nil) then return true else return false end end -- Validation helper: Checks for balanced brackets local b = function(str) local brackets = 0 for c=1, string.len(str) do local char = string.sub(str, c, c) if char == "[" then brackets = brackets + 1 elseif char == "]" then brackets = brackets - 1 if brackets < 0 then return false end end end return brackets == 0 end 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 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 treedef.rules_a = fields.rules_a treedef.rules_b = fields.rules_b treedef.rules_c = fields.rules_c treedef.rules_d = fields.rules_d treedef.axiom = fields.axiom else return nil, S("The brackets are unbalanced! For each of the axiom and the rule sets, each opening bracket must be matched by a closing bracket.") end else return nil, S("The axiom or one of the rule sets contains at least one invalid character.\nSee the cheat sheet for a list of allowed characters.") end treedef.trunk = fields.trunk treedef.leaves = fields.leaves treedef.leaves2 = fields.leaves2 treedef.leaves2_chance = fields.leaves2_chance treedef.angle = tonumber(fields.angle) if(treedef.angle == nil) then return nil, S("The field \"Angle\" must contain a number.") end treedef.iterations = tonumber(fields.iterations) if(treedef.iterations == nil) then return nil, S("The field \"Iterations\" must contain a natural number greater or equal to 0.") elseif(treedef.iterations < 0) then return nil, S("The field \"Iterations\" must contain a natural number greater or equal to 0.") end treedef.random_level = tonumber(fields.random_level) if(treedef.random_level == nil) then return nil, S("The field \"Randomness level\" must contain a number.") end treedef.fruit = fields.fruit treedef.fruit_chance = tonumber(fields.fruit_chance) if(treedef.fruit_chance == nil) then return nil, S("The field \"Fruit chance\" must contain a number.") elseif(treedef.fruit_chance > 100 or treedef.fruit_chance < 0) then return nil, S("Fruit chance must be between 0% and 100%.") end if(fields.trunk_type == "single" or fields.trunk_type == "double" or fields.trunk_type == "crossed") then treedef.trunk_type = fields.trunk_type else return nil, S("Trunk type must be \"single\", \"double\" or \"crossed\".") end treedef.thin_branches = fields.thin_branches if(fields.thin_branches == "true") then treedef.thin_branches = true elseif(fields.thin_branches == "false") then treedef.thin_branches = false else return nil, S("Field \"Thin branches\" must be \"true\" or \"false\".") end local name = fields.name if(ignore_name ~= true and name == "") then return nil, S("Name is empty.") end return treedef, name end --[=[ Here come several utility functions ]=] --[[ converts a given tree to field names, as if they were given to a minetest.register_on_plyer_receive_fields callback function ]] function ltool.tree_to_fields(tree) local s = function(i) if(i==nil) then return "" else return tostring(i) end end local fields = {} fields.axiom = s(tree.treedef.axiom) fields.rules_a = s(tree.treedef.rules_a) fields.rules_b = s(tree.treedef.rules_b) fields.rules_c = s(tree.treedef.rules_c) fields.rules_d = s(tree.treedef.rules_d) fields.trunk = s(tree.treedef.trunk) fields.leaves = s(tree.treedef.leaves) fields.leaves2 = s(tree.treedef.leaves2) fields.leaves2_chance = s(tree.treedef.leaves2) fields.fruit = s(tree.treedef.fruit) fields.fruit_chance = s(tree.treedef.fruit_chance) fields.angle = s(tree.treedef.angle) fields.iterations = s(tree.treedef.iterations) fields.random_level = s(tree.treedef.random_level) fields.trunk_type = s(tree.treedef.trunk_type) fields.thin_branches = s(tree.treedef.thin_branches) fields.name = s(tree.name) return fields end -- returns a simple table of all the tree IDs function ltool.get_tree_ids() local ids = {} for tree_id, _ in pairs(ltool.trees) do table.insert(ids, tree_id) end table.sort(ids) return ids end --[[ In a table of tree IDs (returned by ltool.get_tree_ids, parameter tree_ids), this function searches for the first occourance of the value searched_tree_id and returns its index. This is basically a reverse lookup utility. ]] function ltool.get_tree_id_index(searched_tree_id, tree_ids) for i=1, #tree_ids do local table_tree_id = tree_ids[i] if(searched_tree_id == table_tree_id) then return i end end end -- Returns the selected tree of the given player function ltool.get_selected_tree(playername) local sel = ltool.playerinfos[playername].dbsel if(sel ~= nil) then local tree_id = ltool.playerinfos[playername].treeform.database.textlist[sel] if(tree_id ~= nil) then return ltool.trees[tree_id] end end return nil end -- Returns the ID of the selected tree of the given player function ltool.get_selected_tree_id(playername) local sel = ltool.playerinfos[playername].dbsel if(sel ~= nil) then return ltool.playerinfos[playername].treeform.database.textlist[sel] end return nil end ltool.treeform = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit() minetest.register_chatcommand("treeform", { params = "", description = "Open L-System Tree Utility.", privs = {}, func = function(playername, param) ltool.show_treeform(playername) end }) minetest.register_chatcommand("lplant", { description = S("Plant a L-system tree at the specified position"), privs = { lplant = true }, params = S(" []"), func = function(playername, param) local p = {} local tree_id, x, y, z, seed = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *([%d.-]*)") tree_id, p.x, p.y, p.z, seed = tonumber(tree_id), tonumber(x), tonumber(y), tonumber(z), tonumber(seed) if not tree_id or not p.x or not p.y or not p.z then return false, S("Invalid usage, see /help lplant.") end local lm = tonumber(minetest.settings:get("map_generation_limit") or 31000) 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 return false, S("Cannot plant tree out of map bounds!") end local success = ltool.plant_tree(tree_id, p, seed) if success == false then return false, S("Unknown tree ID!") else return true end end }) function ltool.dbsel_to_tree(dbsel, playername) return ltool.trees[ltool.playerinfos[playername].treeform.database.textlist[dbsel]] end function ltool.save_fields(playername,formname,fields) if not fields.thin_branches then fields.thin_branches = ltool.playerinfos[playername].treeform.edit.thin_branches end if(formname=="ltool:treeform_edit") then ltool.playerinfos[playername].treeform.edit.fields = fields elseif(formname=="ltool:treeform_database") then ltool.playerinfos[playername].treeform.database.fields = fields elseif(formname=="ltool:treeform_plant") then ltool.playerinfos[playername].treeform.plant.fields = fields end end local function handle_sapling_button_database_plant(seltree, seltree_id, privs, formname, fields, playername) if(seltree ~= nil) then if(privs.lplant ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't request saplings, you need to have the \"lplant\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_sapling", message) return false end local seed = nil if(tonumber(fields.seed)~=nil) then seed = tonumber(fields.seed) end if ltool.trees[seltree_id] then local ret, ret2 = ltool.give_sapling(ltool.trees[seltree_id].treedef, seed, playername, true, ltool.trees[seltree_id].name) if(ret==false and ret2==2) then ltool.save_fields(playername, formname, fields) ltool.show_dialog(playername, "ltool:treeform_error_sapling", S("Error: The sapling could not be given to you. Probably your inventory is full.")) end end end end --[=[ Callback functions start here ]=] function ltool.process_form(player,formname,fields) local playername = player:get_player_name() local seltree = ltool.get_selected_tree(playername) local seltree_id = ltool.get_selected_tree_id(playername) local privs = minetest.get_player_privs(playername) local s = function(input) local ret if(input==nil) then ret = "" else ret = F(tostring(input)) end return ret end -- Update thin_branches field if(formname == "ltool:treeform_edit") then if(not fields.thin_branches) then fields.thin_branches = ltool.playerinfos[playername].treeform.edit.thin_branches if(not fields.thin_branches) then minetest.log("error", "[ltool] thin_branches field of "..playername.." is nil!") end else ltool.playerinfos[playername].treeform.edit.thin_branches = fields.thin_branches end end --[[ process clicks on the tab header ]] if(formname == "ltool:treeform_edit" or formname == "ltool:treeform_database" or formname == "ltool:treeform_plant" or formname == "ltool:treeform_help") then if fields.ltool_tab ~= nil then ltool.save_fields(playername, formname, fields) local tab = tonumber(fields.ltool_tab) local formspec, subformname, contents if(tab==1) then contents = ltool.tab_edit(ltool.playerinfos[playername].treeform.edit.fields, privs.ledit, privs.lplant) subformname = "edit" elseif(tab==2) then contents = ltool.tab_database(ltool.playerinfos[playername].dbsel, playername) subformname = "database" elseif(tab==3) then if(ltool.number_of_trees > 0) then contents = ltool.tab_plant(seltree, ltool.playerinfos[playername].treeform.plant.fields, privs.lplant) else contents = ltool.tab_plant(nil, nil, privs.lplant) end subformname = "plant" elseif(tab==4) then contents = ltool.tab_help(ltool.playerinfos[playername].treeform.help.tab) subformname = "help" end formspec = ltool.formspec_size..ltool.formspec_header(tab)..contents minetest.show_formspec(playername, "ltool:treeform_" .. subformname, formspec) return end end --[[ "Plant" tab ]] if(formname == "ltool:treeform_plant") then if(fields.plant_plant) then if(seltree ~= nil) then if(privs.lplant ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't plant trees, you need to have the \"lplant\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_lplant", message) return end minetest.log("action","[ltool] Planting tree") local treedef = seltree.treedef local x,y,z = tonumber(fields.x), tonumber(fields.y), tonumber(fields.z) local distance = tonumber(fields.distance) local tree_pos local fail_coordinates = function() ltool.save_fields(playername, formname, fields) ltool.show_dialog(playername, "ltool:treeform_error_badplantfields", S("Error: When using coordinates, you have to specify numbers in the fields \"x\", \"y\", \"z\".")) end local fail_distance = function() ltool.save_fields(playername, formname, fields) ltool.show_dialog(playername, "ltool:treeform_error_badplantfields", S("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\".")) end if(fields.plantmode == F(S("Absolute coordinates"))) then if(type(x)~="number" or type(y) ~= "number" or type(z) ~= "number") then fail_coordinates() return end tree_pos = {x=x, y=y, z=z} elseif(fields.plantmode == F(S("Relative coordinates"))) then if(type(x)~="number" or type(y) ~= "number" or type(z) ~= "number") then fail_coordinates() return end tree_pos = player:get_pos() tree_pos.x = tree_pos.x + x tree_pos.y = tree_pos.y + y tree_pos.z = tree_pos.z + z elseif(fields.plantmode == F(S("Distance in viewing direction"))) then if(type(distance)~="number") then fail_distance() return end tree_pos = vector.round(vector.add(player:get_pos(), vector.multiply(player:get_look_dir(), distance))) else minetest.log("error", "[ltool] fields.plantmode = "..tostring(fields.plantmode)) end if(tonumber(fields.seed)~=nil) then treedef.seed = tonumber(fields.seed) end ltool.plant_tree(seltree_id, tree_pos) treedef.seed = nil end elseif(fields.sapling) then local ret = handle_sapling_button_database_plant(seltree, seltree_id, privs, formname, fields, playername) if ret == false then return end end --[[ "Edit" tab ]] elseif(formname == "ltool:treeform_edit") then if(fields.edit_save or fields.edit_sapling) then local param1, param2 param1, param2 = ltool.evaluate_edit_fields(fields, fields.edit_sapling ~= nil) if(fields.edit_save and privs.ledit ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't save trees, you need to have the \"ledit\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_ledit", message) return end if(fields.edit_sapling and privs.lplant ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't request saplings, you need to have the \"lplant\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_ledit", message) return end local tree_ok = true local treedef, name if(param1 ~= nil) then treedef = param1 name = param2 for k,v in pairs(ltool.trees) do if(fields.edit_save and v.name == name) then ltool.save_fields(playername, formname, fields) if(v.author == playername) then local formspec = "size[6,2;]label[0,0.2;You already have a tree with this name.\nDo you want to replace it?]".. "button[0,1.5;3,1;replace_yes;"..F(S("Yes")).."]".. "button[3,1.5;3,1;replace_no;"..F(S("No")).."]" minetest.show_formspec(playername, "ltool:treeform_replace", formspec) else ltool.show_dialog(playername, "ltool:treeform_error_nameclash", S("Error: This name is already taken by someone else.")) end return end end else tree_ok = false end ltool.save_fields(playername, formname, fields) if(tree_ok == true) then if fields.edit_save then ltool.add_tree(name, playername, treedef) elseif fields.edit_sapling then local ret, ret2 = ltool.give_sapling(treedef, tostring(ltool.seed), playername, true, fields.name) if(ret==false and ret2==2) then ltool.save_fields(playername, formname, fields) ltool.show_dialog(playername, "ltool:treeform_error_sapling", S("Error: The sapling could not be given to you. Probably your inventory is full.")) end end else local message = S("Error: The tree definition is invalid.").."\n".. F(param2) ltool.show_dialog(playername, "ltool:treeform_error_badtreedef", message) end end if(fields.edit_clear) then local privs = minetest.get_player_privs(playername) ltool.save_fields(playername, formname, ltool.default_edit_fields) local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.default_edit_fields, privs.ledit, privs.lplant) --[[ hacky_spaces is part of a workaround, see comment on hacky_spaces in ltool.join. This workaround will slightly change the formspec by adding 0-5 spaces to the end, changing the number of spaces on each send. This forces Minetest to re-send the formspec. Spaces are completely harmless in a formspec.]] -- BEGIN OF WORKAROUND local hacky_spaces = ltool.playerinfos[playername].treeform.hacky_spaces hacky_spaces = hacky_spaces .. " " if string.len(hacky_spaces) > 5 then hacky_spaces = "" end ltool.playerinfos[playername].treeform.hacky_spaces = hacky_spaces local real_formspec = formspec .. hacky_spaces -- END OF WORKAROUND minetest.show_formspec(playername, "ltool:treeform_edit", real_formspec) end 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 local fragment if(fields.edit_axiom) then fragment = "axiom;"..F(S("Axiom"))..";"..s(fields.axiom) elseif(fields.edit_rules_a) then fragment = "rules_a;"..F(S("Rules set A"))..";"..s(fields.rules_a) elseif(fields.edit_rules_b) then fragment = "rules_b;"..F(S("Rules set B"))..";"..s(fields.rules_b) elseif(fields.edit_rules_c) then fragment = "rules_c;"..F(S("Rules set C"))..";"..s(fields.rules_c) elseif(fields.edit_rules_d) then fragment = "rules_d;"..F(S("Rules set D"))..";"..s(fields.rules_d) end ltool.save_fields(playername, formname, fields) local formspec = ltool.formspec_editplus(fragment) minetest.show_formspec(playername, "ltool:treeform_editplus", formspec) end if(mod_select_item and (fields.edit_trunk or fields.edit_leaves or fields.edit_leaves2 or fields.edit_fruit)) then ltool.save_fields(playername, formname, fields) -- Prepare sorting. -- Move tree, leaves, apple/leafdecay nodes to the beginning local compare_group, fruit if fields.edit_trunk then compare_group = "tree" elseif fields.edit_leaves or fields.edit_leaves2 then compare_group = "leaves" elseif fields.edit_fruit or fields.edit_fruit then compare_group = "leafdecay" local alias = minetest.registered_aliases["mapgen_apple"] if alias and minetest.registered_nodes[alias] then fruit = alias end end select_item.show_dialog(playername, "ltool:node", function(itemstring) if itemstring ~= "air" and minetest.registered_nodes[itemstring] ~= nil then return true end end, function(i1, i2) if fruit and i1 == fruit then return true end if fruit and i2 == fruit then return false end local i1t = minetest.get_item_group(i1, compare_group) local i2t = minetest.get_item_group(i2, compare_group) local i1d = minetest.registered_items[i1].description local i2d = minetest.registered_items[i2].description local i1nici = minetest.get_item_group(i1, "not_in_creative_inventory") local i2nici = minetest.get_item_group(i2, "not_in_creative_inventory") if (i1d == "" and i2d ~= "") then return false elseif (i1d ~= "" and i2d == "") then return true end if (i1nici == 1 and i2nici == 0) then return false elseif (i1nici == 0 and i2nici == 1) then return true end if i1t < i2t then return false elseif i1t > i2t then return true end return i1 < i2 end) end --[[ Larger edit fields for axiom and rules fields ]] elseif(formname == "ltool:treeform_editplus") then local editfields = ltool.playerinfos[playername].treeform.edit.fields local function addchar(c) local fragment if(c=="P") then c = "[" end if(c=="p") then c = "]" end if(fields.axiom) then fragment = "axiom;"..F(S("Axiom"))..";"..s(fields.axiom..c) elseif(fields.rules_a) then fragment = "rules_a;"..F(S("Rules set A"))..";"..s(fields.rules_a..c) elseif(fields.rules_b) then fragment = "rules_b;"..F(S("Rules set B"))..";"..s(fields.rules_b..c) elseif(fields.rules_c) then fragment = "rules_c;"..F(S("Rules set C"))..";"..s(fields.rules_c..c) elseif(fields.rules_d) then fragment = "rules_d;"..F(S("Rules set D"))..";"..s(fields.rules_d..c) end local formspec = ltool.formspec_editplus(fragment) minetest.show_formspec(playername, "ltool:treeform_editplus", formspec) end if(fields.editplus_save) then local function o(writed, writer) if(writer~=nil) then return writer else return writed end end editfields.axiom = o(editfields.axiom, fields.axiom) editfields.rules_a = o(editfields.rules_a, fields.rules_a) editfields.rules_b = o(editfields.rules_b, fields.rules_b) editfields.rules_c = o(editfields.rules_c, fields.rules_c) editfields.rules_d = o(editfields.rules_d, fields.rules_d) local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(editfields, privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) elseif(fields.editplus_cancel or fields.quit) then local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(editfields, privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) else for id, field in pairs(fields) do if(string.sub(id,1,11) == "editplus_c_") then local char = string.sub(id,12,12) addchar(char) end end end --[[ "Database" tab ]] elseif(formname == "ltool:treeform_database") then if(fields.treelist) then local event = minetest.explode_textlist_event(fields.treelist) if(event.type == "CHG") then ltool.playerinfos[playername].dbsel = event.index local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(event.index, playername) minetest.show_formspec(playername, "ltool:treeform_database", formspec) end elseif(fields.database_copy) then if(seltree ~= nil) then if(ltool.playerinfos[playername] ~= nil) then local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.tree_to_fields(seltree), privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) end else ltool.show_dialog(playername, "ltool:treeform_error_nodbsel", S("Error: No tree is selected.")) end elseif(fields.database_update) then local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername) minetest.show_formspec(playername, "ltool:treeform_database", formspec) elseif(fields.database_delete) then if(privs.ledit ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't delete trees, you need to have the \"ledit\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_ledit_db", message) return end if(seltree ~= nil) then if(playername == seltree.author) then local remove_id = ltool.get_selected_tree_id(playername) if(remove_id ~= nil) then ltool.remove_tree(remove_id) local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername) minetest.show_formspec(playername, "ltool:treeform_database", formspec) end else ltool.show_dialog(playername, "ltool:treeform_error_delete", S("Error: This tree is not your own. You may only delete your own trees.")) end else ltool.show_dialog(playername, "ltool:treeform_error_nodbsel", S("Error: No tree is selected.")) end elseif(fields.database_rename) then if(seltree ~= nil) then if(privs.ledit ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't rename trees, you need to have the \"ledit\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_ledit_db", message) return end if(playername == seltree.author) then local formspec = "field[newname;"..F(S("New name:"))..";"..F(seltree.name).."]" minetest.show_formspec(playername, "ltool:treeform_rename", formspec) else ltool.show_dialog(playername, "ltool:treeform_error_rename_forbidden", S("Error: This tree is not your own. You may only rename your own trees.")) end else ltool.show_dialog(playername, "ltool:treeform_error_nodbsel", S("Error: No tree is selected.")) end elseif(fields.sapling) then local ret = handle_sapling_button_database_plant(seltree, seltree_id, privs, formname, fields, playername) if ret == false then return end end --[[ Process "Do you want to replace this tree?" dialog ]] elseif(formname == "ltool:treeform_replace") then local editfields = ltool.playerinfos[playername].treeform.edit.fields local newtreedef, newname = ltool.evaluate_edit_fields(editfields) if(privs.ledit ~= true) then local message = S("You can't overwrite trees, you need to have the \"ledit\" privilege.") minetest.show_dialog(playername, "ltool:treeform_error_ledit", message) return end if(fields.replace_yes) then for tree_id,tree in pairs(ltool.trees) do if(tree.name == newname) then --[[ The old tree is deleted and a new one with a new ID is created ]] local new_tree_id = ltool.next_tree_id ltool.trees[new_tree_id] = {} ltool.trees[new_tree_id].treedef = newtreedef ltool.trees[new_tree_id].name = newname ltool.trees[new_tree_id].author = tree.author ltool.next_tree_id = ltool.next_tree_id + 1 ltool.trees[tree_id] = nil ltool.playerinfos[playername].dbsel = ltool.number_of_trees end end end local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(editfields, privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) elseif(formname == "ltool:treeform_help") then local tab = tonumber(fields.ltool_help_tab) if(tab ~= nil) then ltool.playerinfos[playername].treeform.help.tab = tab local formspec = ltool.formspec_size..ltool.formspec_header(4)..ltool.tab_help(tab) minetest.show_formspec(playername, "ltool:treeform_help", formspec) end if(fields.create_template) then local newfields = { axiom="FFFFFAFFBF", rules_a="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]", rules_b="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]", trunk="mapgen_tree", leaves="mapgen_leaves", leaves2_chance="0", angle="30", iterations="2", random_level="0", trunk_type="single", thin_branches="true", fruit_chance="10", fruit="mapgen_apple", name = "Example Tree "..ltool.next_tree_id } ltool.save_fields(playername, formname, newfields) local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(newfields, privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) end --[[ Tree renaming dialog ]] elseif(formname == "ltool:treeform_rename") then if(privs.ledit ~= true) then ltool.save_fields(playername, formname, fields) local message = S("You can't delete trees, you need to have the \"ledit\" privilege.") ltool.show_dialog(playername, "ltool:treeform_error_ledit_delete", message) return end if(fields.newname ~= "" and fields.newname ~= nil) then ltool.rename_tree(ltool.get_selected_tree_id(playername), fields.newname) local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername) minetest.show_formspec(playername, "ltool:treeform_database", formspec) else ltool.show_dialog(playername, "ltool:treeform_error_bad_rename", S("Error: This name is empty. The tree name must be non-empty.")) end --[[ Here come various error messages to handle ]] elseif(formname == "ltool:treeform_error_badtreedef" or formname == "ltool:treeform_error_nameclash" or formname == "ltool:treeform_error_ledit") then local formspec = ltool.formspec_size..ltool.formspec_header(1)..ltool.tab_edit(ltool.playerinfos[playername].treeform.edit.fields, privs.ledit, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_edit", formspec) elseif(formname == "ltool:treeform_error_badplantfields" or formname == "ltool:treeform_error_sapling" or formname == "ltool:treeform_error_lplant") then local formspec = ltool.formspec_size..ltool.formspec_header(3)..ltool.tab_plant(seltree, ltool.playerinfos[playername].treeform.plant.fields, privs.lplant) minetest.show_formspec(playername, "ltool:treeform_plant", formspec) 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 local formspec = ltool.formspec_size..ltool.formspec_header(2)..ltool.tab_database(ltool.playerinfos[playername].dbsel, playername) minetest.show_formspec(playername, "ltool:treeform_database", formspec) elseif(formname == "ltool:treeform_error_bad_rename") then local formspec = "field[newname;"..F(S("New name:"))..";"..F(seltree.name).."]" minetest.show_formspec(playername, "ltool:treeform_rename", formspec) else -- Action for Inventory++ button if fields.ltool and minetest.get_modpath("inventory_plus") then ltool.show_treeform(playername) return end end end if mod_select_item then select_item.register_on_select_item(function(playername, dialogname, itemstring) if dialogname == "ltool:node" then if itemstring then local f = ltool.playerinfos[playername].treeform.edit.fields if f.edit_trunk then f.trunk = itemstring elseif f.edit_leaves then f.leaves = itemstring elseif f.edit_leaves2 then f.leaves2 = itemstring elseif f.edit_fruit then f.fruit = itemstring end end ltool.show_treeform(playername) return false end end) end --[[ These 2 functions are basically just table initializions and cleanups ]] function ltool.leave(player) ltool.playerinfos[player:get_player_name()] = nil end function ltool.join(player) local infotable = {} infotable.dbsel = nil infotable.treeform = {} infotable.treeform.database = {} --[[ This table stores a mapping of the textlist IDs in the database formspec and the tree IDs. It is updated each time ltool.tab_database is called. ]] infotable.treeform.database.textlist = {} --[[ the “fields” tables store the values of the input fields of a formspec. It is updated whenever the formspec is changed, i.e. on tab change ]] infotable.treeform.database.fields = {} infotable.treeform.plant = {} infotable.treeform.plant.fields = {} infotable.treeform.edit = {} infotable.treeform.edit.fields = ltool.default_edit_fields infotable.treeform.edit.thin_branches = "true" infotable.treeform.help = {} infotable.treeform.help.tab = 1 --[[ Workaround for annoying bug in Minetest: When you call the identical formspec twice, Minetest does not send the second one. This is an issue when the player has changed the input fields in the meanwhile, resetting fields will fail sometimes. TODO: Remove workaround when not needed anymore. ]] -- BEGIN OF WORKAROUND infotable.treeform.hacky_spaces = "" -- END OF WORKAROUND ltool.playerinfos[player:get_player_name()] = infotable -- Add Inventory++ support if minetest.get_modpath("inventory_plus") then inventory_plus.register_button(player, "ltool", S("L-System Tree Utility")) end end function ltool.save_to_file() local savetable = {} savetable.trees = ltool.trees savetable.number_of_trees = ltool.number_of_trees savetable.next_tree_id = ltool.next_tree_id local savestring = minetest.serialize(savetable) local filepath = minetest.get_worldpath().."/ltool.mt" local file = io.open(filepath, "w") if(file) then file:write(savestring) io.close(file) minetest.log("action", "[ltool] Tree data saved to "..filepath..".") else minetest.log("error", "[ltool] Failed to write ltool data to "..filepath".") end end minetest.register_on_player_receive_fields(ltool.process_form) minetest.register_on_leaveplayer(ltool.leave) minetest.register_on_joinplayer(ltool.join) minetest.register_on_shutdown(ltool.save_to_file) local button_action = function(player) ltool.show_treeform(player:get_player_name()) end if minetest.get_modpath("unified_inventory") ~= nil then unified_inventory.register_button("ltool", { type = "image", image = "ltool_sapling.png", tooltip = S("L-System Tree Utility"), action = button_action, }) end if minetest.get_modpath("sfinv_buttons") ~= nil then sfinv_buttons.register_button("ltool", { title = S("L-System Tree Utility"), tooltip = S("Invent your own trees and plant them"), image = "ltool_sapling.png", action = button_action, }) end minetest_ltool/locale/000077500000000000000000000000001344442271500153645ustar00rootroot00000000000000minetest_ltool/locale/ltool.de.tr000066400000000000000000000524041344442271500174600ustar00rootroot00000000000000# textdomain: ltool L-System Tree Sapling=L-System-Baumsetzling 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?=Dieser künstliche Setzling stammt nicht aus der Natur und enthält das Genom eines genetisch erzeugten L-System-Baums. Jeder Setzling dieser Art ist einzigartig. Was wohl wachsen wird, wenn Sie ihn pflanzen? 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 using it. If you hold down the sneak key while placing it, you will keep a copy of the sapling in your inventory.=Platzieren Sie den Setzling auf einer belibigen Oberfläche und warten Sie 5 Sekunden, bis der Baum auftaucht. Wenn Sie das „lplant“-Privileg haben, können Sie ihn mit Benutzen sofort zum Wachsen bringen. Wenn Sie die Schleichentaste beim Platzieren gedrückt halten, werden Sie eine Kopie des Setzlings in Ihrem Inventar behalten. 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”).=Um Ihren eigenen Setzlinge zu erzeugen, brauchen Sie das „lplant“-Privileg und müssen sich einen Baum aus dem L-System-Baum-Werkzeug (kann mit dem Serverbefehl „treeform“ geöffnet werden) aussuchen. L-System Tree Utility=L-System-Baum-Werkzeug This gadget allows the aspiring genetic engineer to invent and change L-system trees, create L-system tree saplings and look at the inventions from other players. L-system trees are trees and tree-like strucures which are built by a set of (possibly recursive) production rules.=Dieses Gerät erlaubt dem aufstrebenden Ingenieur, L-System-Bäume zu erfinden und zu ändern, neue L-System-Setzinge zu erzeugen und sich die Erfindungen anderer Spieler anzusehen. L-System-Bäume sind Bäume und baumähnliche Gebäude, die einer Menge von (möglicherweise rekursiven) Produktionsregeln folgen. Punch to open the L-System editor. A tabbed form will open. To edit and create trees, you need the “ledit” privilege, to make saplings, you need “lplant”. Detailed usage help can be found in that menu. You can also access the same editor with the server command “treeform”.=Schlagen Sie, um den L-System-Editor zu öffnen. Eine Anzeige mit Registerkarten taucht auf. Um Bäume zu bearbeiten und zu erzeugen, brauchen sie das „ledit“-Privileg. Um Setzlinge zu erzeugen, brauchen Sie das „lplant“-Privileg. Eine detaillierte Hilfe kann in diesem Menü gefunden werden. Sie können den gleichen Editor auch mit dem Serverbefehl „treeform“ öffnen. Can add, edit, rename and delete own L-system tree definitions of the ltool mod=Kann eigene L-System-Baumdefinitionen hinzufügen, bearbeiten, umbenennen und löschen (aus der ltool-Mod) Can place L-system trees and get L-system tree saplings of the ltool mod=Kann L-System-Bäume platzieren und L-System-Setzlinge erhalten (aus der ltool-Mod) Edit=Bearbeiten Database=Datenbank Plant=Pflanzen Help=Hilfe Save tree to database=Baum in Datenbank speichern Reset fields=Felder zurücksetzen Generate sapling=Setzling erzeugen Read-only mode. You need the “ledit” privilege to save trees to the database.=Nur-Lesen-Modus. Sie brauchen das „ledit“-Privileg zum Speichern in die Datenbank. >=> Select node=Node wählen Axiom=Axiom +=+ Opens larger text field for Axiom=Öffnet größeres Textfeld für Axiom Rules set A=Regelsatz A Opens larger text field for Rules set A=Öffnet größeres Textfeld für Regelsatz A Rules set B=Regelsatz B Opens larger text field for Rules set B=Öffnet größeres Textfeld für Regelsatz B Rules set C=Regelsatz C Opens larger text field for Rules set C=Öffnet größeres Textfeld für Regelsatz C Rules set D=Regelsatz D Opens larger text field for Rules set D=Öffnet größeres Textfeld für Regelsatz D Trunk node=Stamm-Node Leaves node=Blätter-Node Secondary leaves node=Sekundärer Blätter-Node Fruit node=Frucht-Node Trunk type=Stammtyp Tree trunk type. Possible values:@n- "single": trunk of size 1×1@n- "double": trunk of size 2×2@n- "crossed": trunk in cross shape (3×3).=Baumstammtyp. Mögliche Werte:@n- „single“: Stamm von Größe 1×1@n- „double“: Stamm von Größe 2×2@n- „crossed“: Stamm in Kreuzform (3×3). Thin branches=Dünne Äste If enabled, all branches are just 1 node wide, otherwise, branches can be larger.=Wenn aktiv, werden alle Äste nur 1 Node breit sein, sonst können Äste dicker sein Secondary leaves chance (%)=Sekundärblätter-Chance Chance (in percent) to replace a leaves node by a secondary leaves node=Chance (in Prozent), einen Blätter-Node mit einem sekundärem Blätter-Node zu ersetzen Fruit chance (%)=Fruchtchance Chance (in percent) to replace a leaves node by a fruit node.=Chance (in Prozent), einen Blätter-Node mit einem Frucht-Node zu ersetzen Iterations=Iterationen Maximum number of iterations, usually between 2 and 5.=Maximale Anzahl Iterationen, üblicherweise zwischen 2 und 5. Randomness level=Zufälligkeit Factor to lower number of iterations, usually between 0 and 3.=Faktor, um Anzahl der Iterationen zu verringern, üblicherweise zwischen 0 und 3. Angle (°)=Winkel (°) Name=Name Descriptive name for this tree, only used for convenience.=Beschreibender Name für diesen Baum, nur für die Übersicht benutzt Absolute coordinates=Absolutkoordinaten Relative coordinates=Relativkoordinaten Distance in viewing direction=Entfernung in Blickrichtung - "Absolute coordinates": Fields "x", "y" and "z" specify the absolute world coordinates where to plant the tree=- „Absolutkoordinaten“: Felder „x“, „y“ und „z“ legen die absoluten Weltkoordinaten des Baumes fest - "Relative coordinates": Fields "x", "y" and "z" specify the relative position from your position=- „Relativkoordinaten“: Felder „x“, „y“ und „z“ legen die relative Position von Ihrer Position aus fest - "Distance in viewing direction": Plant tree relative from your position in the direction you look to, at the specified distance=- „Entfernung in Blickrichtung“: Baum relativ von Ihrer Position aus in die Richtung, in die Sie blicken, pflanzen, mit der angegebenen Entfernung x=x Field is only used by absolute and relative coordinates.=Feld wird nur von Absolut- und Relativkoordinaten benutzt y=y z=z Distance=Entfernung 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.=Dieses Feld wird benutzt, um die Entfernung (in Node-Längeneinheiten) von Ihrer Position@nin Blickrichtung anzugeben. Es wird ignoriert, wenn Sie Koordinaten benutzen. Randomness seed=Zufalls-Seed A number used for the random number generators. Identical randomness seeds will produce identical trees. This field is optional.=Eine Zahl für die Zufallszahlengeneratoren. Identische Zufalls-Seeds werden identische Bäume erzeugen. Dieses Feld ist optional. Plant tree=Baum pflanzen Immediately place the tree at the specified position=Baum sofort an der angegebenen Position pflanzen This gives you an item which you can place manually in the world later=Das gibt Ihnen einen Gegenstand, den Sie später in der Welt manuell pflanzen können No tree in database selected or database is empty.=Keinen Baum in Datenbank gewählt oder Datenbank ist leer. You are not allowed to plant trees anyway as you don't have the “lplant” privilege.=Sie dürfen sowieso keine Bäume pflanzen, da Sie nicht das „lplant“-Privileg haben. Symbol=Symbol Action=Aktion Move forward one unit with the pen up=Eine Einheit nach vorne bewegen, Stift ist oben Move forward one unit with the pen down drawing trunks and branches=Eine Einheit nach vorne bewegen, Stift zeichnet Stämme und Äste Move forward one unit with the pen down drawing leaves=Eine Einheit nach vorne bewegen, Stift zeichnet Blätter Move forward one unit with the pen down drawing trunks=Eine Einheit nach vorne bewegen, Stift zeichnet Stämme Move forward one unit with the pen down placing fruit=Eine Einheit nach vorne bewegen, Stift zeichnet Früchte Replace with rules set A=Mit Regelsatz A ersetzen Replace with rules set B=Mit Regelsatz B ersetzen Replace with rules set C=Mit Regelsatz C ersetzen Replace with rules set D=Mit Regelsatz D ersetzen Replace with rules set A, chance 90%=Mit Regelsatz A ersetzen, 90% Chance Replace with rules set B, chance 80%=Mit Regelsatz B ersetzen, 80% Chance Replace with rules set C, chance 70%=Mit Regelsatz C ersetzen, 70% Chance Replace with rules set D, chance 60%=Mit Regelsatz D ersetzen, 60% Chance Yaw the turtle right by angle parameter=Schildkröte nach rechts um Winkelparameter gieren Yaw the turtle left by angle parameter=Schildkröte nach links um Winkelparameter gieren Pitch the turtle down by angle parameter=Schildkröte nach unten um Winkelparameter nicken Pitch the turtle up by angle parameter=Schildkröte nach oben um Winkelparameter nicken Roll the turtle to the right by angle parameter=Schildkröte nach rechts um Winkelparameter rollen Roll the turtle to the left by angle parameter=Schildkröte nach links um Winkelparameter rollen Save in stack current state info=Aktuelle Zustandsinfo in Stapelspeicher speichern Recover from stack state info=Vom Stapelspeicher Zustandsinfo holen You are using the L-System Tree Utility, version @1.=Sie benutzen das L-System-Baum-Werkzeug, Version @1. Introduction=Einführung Creating Trees=Bäume erfinden Managing Trees=Bäume verwalten Planting Trees=Bäume pflanzen Cheat Sheet=Spickzettel Draw:=Zeichnen: Rules:=Regeln: Rotate:=Rotieren: Yaw the turtle right by the value specified in "Angle"=Schildkröte nach rechts um den in „Winkel“ angegebenen Wert gieren Yaw the turtle left by the value specified in "Angle"=Schildkröte nach links um den in „Winkel“ angegebenen Wert gieren Pitch the turtle down by the value specified in "Angle"=Schildkröte nach unten um den in „Winkel“ angegebenen Wert nicken Pitch the turtle up by the value specified in "Angle"=Schildkröte nach oben um den in „Winkel“ angegebenen Wert nicken Roll the turtle to the right by the value specified in "Angle"=Schildkröte nach rechts um den in „Winkel“ angegebenen Wert rollen Roll the turtle to the left by the value specified in "Angle"=Schildkröte nach links um den in „Winkel“ angegebenen Wert rollen Stack:=Stapelspeicher: Save current state info into stack=Aktuelle Zustandsinfo in Stapelspeicher speichern Recover from current stack state info=Vom Stapelspeicher Zustandsinfo holen Save=Speichern Cancel=Abbrechen OK=OK The brackets are unbalanced! For each of the axiom and the rule sets, each opening bracket must be matched by a closing bracket.=Die Klammern sind nicht ausbalanciert! Für das Axium und jeden einzelnen Regelsatz muss für jede öffnende Klammer eine passende schließende Klammer existieren. The axiom or one of the rule sets contains at least one invalid character.@nSee the cheat sheet for a list of allowed characters.=Das Axiom oder eines der Regelsätze erhält mindestens ein ungültiges Zeichen.@nSehen Sie sich den Spickzettel für eine Liste der erlaubten Zeichen an. The field "Angle" must contain a number.=Das Feld „Winkel“ muss eine Zahl enthalten. The field "Iterations" must contain a natural number greater or equal to 0.=Das Feld „Iterationen“ muss eine natürliche Zahl größer oder gleich 0 enthalten. The field "Randomness level" must contain a number.=Das Feld „Zufälligkeit“ muss eine Zahl enthalten. The field "Fruit chance" must contain a number.=Das Feld „Fruchtchance“ muss eine Zahl enthalten. Fruit chance must be between 0% and 100%.=Fruchtchance muss zwischen 0% und 100% liegen. Trunk type must be "single", "double" or "crossed".=Stammtyp muss entweder „single“, „double“ oder „crossed“ sein. Field "Thin branches" must be "true" or "false".=Feld „Dünne Äste“ muss „true“ oder „false“ sein. Name is empty.=Name ist leer. Plant a L-system tree at the specified position=Einen L-System-Baum an der angegebenen Position pflanzen []= [] Invalid usage, see /help lplant.=Ungültige Verwendung, siehe /help lplant. Cannot plant tree out of map bounds!=Kann keinen Baum außerhalb der Kartengrenzen platzieren! Unknown tree ID!=Unbekannte Baum-ID! You can't request saplings, you need to have the "lplant" privilege.=Sie können sich keine Setzlinge besorgen, Sie brauchen das „lplant“-Privileg. Error: The sapling could not be given to you. Probably your inventory is full.=Fehler: Der Setzling konnte Ihnen nicht gegeben werden. Vielleicht ist Ihr Inventar voll. You can't plant trees, you need to have the "lplant" privilege.=Sie können keine Bäume pflanzen, Sie brauchen das „lplant“-Privileg. Error: When using coordinates, you have to specify numbers in the fields "x", "y", "z".=Fehler: Wenn Sie Koordinaten benutzen, müssen Sie Zahlen in den Feldern „x“, „y“ und „z“ angeben. 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".=Fehler: Wenn Sie die Blickrichtung zum Bäumepflanzen benutzen,@nmüssen Sie im Feld „Entfernung“ die gewünschte Entfernung angeben. You can't save trees, you need to have the "ledit" privilege.=Sie können keine Bäume speichern, Sie brauchen das „ledit“-Privileg. Yes=Ja No=Nein Error: This name is already taken by someone else.=Fehler: Dieser Name wird bereits von jemand anderem benutzt. Error: The tree definition is invalid.=Fehler: Die Baumdefinition ist ungültig. Error: No tree is selected.=Fehler: Kein Baum ausgewählt. You can't delete trees, you need to have the "ledit" privilege.=Sie können keine Bäume löschen, Sie brauchen das „ledit“-Privileg. Error: This tree is not your own. You may only delete your own trees.=Fehler: Dieser Baum gehört Ihnen nicht. Sie können nur Ihre eigenen Bäume löschen. You can't rename trees, you need to have the "ledit" privilege.=Sie können keine Bäume umbenennen, sie brauchen das „ledit“-Privileg. New name:=Neuer Name: Error: This tree is not your own. You may only rename your own trees.=Fehler: Dieser Baum gehört Ihnen nicht. Sie können nur Ihre eigenen Bäume umbenennen. You can't overwrite trees, you need to have the "ledit" privilege.=Sie können Bäume nicht überschreiben, Sie brauchen das „ledit“-Privileg. Error: This name is empty. The tree name must be non-empty.=Fehler: Dieser Name ist leer. Der Baumname darf nicht leer sein. Invent your own trees and plant them=Erfinden Sie Ihre eignene Bäume und pflanzen Sie sie Copy tree to editor=Baum zum Editor kopieren Reload database=Datenbank neu laden The tree database is empty.=Die Baumdatenbank ist leer. Rename tree=Baum umbenennen Delete tree=Baum löschen Selected tree: @1=Gewählter Baum: @1 Read-only mode. You need the “ledit” privilege to edit trees.=Nur-Lesen-Modus. Sie brauchen das „ledit“-Privileg zum Bearbeiten. Planting of trees is not allowed. You need to have the “lplant” privilege.=Bäumepflanzen ist nicht erlaubt. Sie brauchen das „lplant“-Privileg. The purpose of this utility is to aid with the creation of L-system trees. You can create, save, manage and plant L-system trees. All trees are saved into /ltool.mt on server shutdown.=Der Zweck dieses Werkzeuges ist es, bei der Erstellung von L-System-Bäumen zu helfen. Sie können L-System-Bäume erstellen, speichern, verwalten und pflanzen. Alle Bäume werden in /ltool.mt bei der Serverabschaltung gespeichert. It assumes you already understand the concept of L-systems, this utility is mainly aimed towards modders and nerds.=Es wird angenommen, dass Sie bereits das Konzept von L-System-Bäumen verstehen; dieses Werkzeug ist hauptsächlich an Mod-Programmierer und Nerds gerichtet. The usual workflow goes like this:=Der übliche Arbeitsablauf ist wie folgt: 1. Create a new tree in the "Edit" tab and save it=1. Erstellen Sie einen neuen Baum in der Registerkarte „Bearbeiten“ und speichern Sie ihn 2. Select it in the database=2. Wählen Sie ihn in der Datenbank 3. Plant it=3. Pflanzen Sie ihn To help you get started, you can create an example tree for the "Edit" tab by pressing this button:=Für einen leichteren Start können Sie einen Beispielbaum für die „Bearbeiten“-Registerkarte erzeugen, wenn Sie diesen Knopf drücken: Create template=Vorlage erzeugen The database contains a list of all created trees among all players.=Die Datenbank enthält eine Liste aller erstellten Bäume aller Spieler. Each tree has an "owner". This kind of ownership is limited: The owner may rename, change and delete their own trees, everyone else is prevented from doing that. But all trees can be copied freely by everyone.=Jeder Baum hat einen „Eigentümer“. Diese Art von Eigentum ist begrenzt: Der Eigentümer kann seine eigenen Bäume umbenennen, ändern und löschen, jeder andere kann das nicht tun. Aber alle Bäume können von jedem kopiert werden. To do so, simply hit "Copy tree to editor", change the name and hit "Save tree to database". If you like someone else's tree definition, it is recommended to make a copy for yourself, since the original owner can at any time choose to delete or edit the tree. The trees which you "own" are written in a yellow font, all other trees in a white font.=Um dies zu tun, klicken Sie einfach auf „Baum zum Editor kopieren“, ändern Sie den Namen und klicken sie auf „Baum in Datenbank speichern“. Die Bäume, die Ihnen „gehören“ werden mit einer gelben Schrift geschrieben, alle anderen mit einer weißen Schrift. In order to plant a tree, you have to select a tree in the database first.=Um einen Baum zu pflanzen, müssen Sie zuerst einen Baum in der Datenbank auswählen. To plant a tree from a previously created tree definition, first select it in the database, then open the "Plant" tab. In this tab, you can directly place the tree or request a sapling.=Um einen Baum von einer vormals erstellten Baumdefinition zu pfanzen, wählen Sie ihn zuerst in der Datenbank aus, dann öffnen Sie die Registerkarte „Pflanzen”. In dieser Registerkarte können Sie den Baum direkt platzieren oder sich einen Setzling geben lassen. If you choose to directly place the tree, you can either specify absolute or relative coordinates or specify that the tree should be planted in your viewing direction. Absolute coordinates are the world coordinates as specified by the "x", "y", and "z" fields. Relative coordinates are relative to your position and use the same fields. When you choose to plant the tree based on your viewing direction, the tree will be planted at a distance specified by the field "distance" away from you in the direction you look to.=Wenn Sie sich dafür entschieden, den Baum direkt zu pflanzen, können Sie entweder absolute oder direkte Koordinaten angeben, oder angeben, dass der Baum in Ihrer Blickrichtung platziert werden soll. Absolutkoordinaten sind die Weltkoordinaten, die von den Feldern „x“, „y“ und „z“ angegeben werden. Relativkoordinaten sind relativ zu Ihrer Position und benutzen die selben Felder. Wenn Sie sich dafür entschieden, den Baum basierend auf Ihrer Blickrichtung zu platzieren, wird der Baum in der Entfernung, die im Feld „Entfernung“ angegeben ist, von ihnen aus platziert. When using coordinates, the "distance" field is ignored, when using direction, the coordinate fields are ignored.=Wenn Sie Koordinaten benutzen, wird das Feld „Entfernung“ ignoriert, wenn Sie Richtung benutzen, werden die Koordinatenfelder ignoriert. You can also use the “lplant” server command to plant trees.=Sie können auf den Serverbefehl „lplant“ benutzen, um Bäume zu pflanzen. If you got a sapling, you can place it practically anywhere you like to. After placing it, the sapling will be replaced by the L-system tree after 5 seconds, unless it was destroyed in the meantime.=Wenn Sie einen Setzling haben, können Sie ihn praktisch überall platzieren. Nach dem Platzieren wird der Setzling durch den L-System-Baum nach 5 Sekunden ersetzt, wenn er nicht zuvor zerstört wurde. All requested saplings are independent from the moment they are created. The sapling will still work, even if the original tree definiton has been deleted.=Alle so erhaltenen Setzlinge sind unabhängig, nachdem sie erzeugt wurden. Der Setzling wird immer noch funktionieren, selbst, wenn die Original-Baumdefinition gelöscht wurde. To create a L-system tree, switch to the "Edit" tab.=Um einen L-System-Baum zu erzeugen, wechseln Sie zur Registerkarte „Bearbeiten“. When you are done, hit "Save tree to database". The tree will be stored in the database. The "Reset fields" button resets the input fields to defaults.=Wenn Sie fertig sind, klicken Sie auf „Baum in Datenbank speichern“. Der Baum wird in der Datenbank abgespeichert. Der Knopf „Felder zurücksetzen“ setzt die Eingabefelder auf die Standardwerte zurück. To understand the meaning of the fields, read the introduction to L-systems.=Um die Bedeutung der Felder zu verstehen, lesen Sie die Einführung zu L-Systemen. All trees must have an unique name. You are notified in case of a name clash. If the name clash is with one of your own trees, you can choose to replace it.=Alle Bäume müssen einen einzigartigen Namen haben. Sie werden benachrichtigt, wenn es eine Namenskollision gibt. Wenn der Name mit einem Ihrer eigenen Bäume kollidiert, können Sie ihn ersetzen. minetest_ltool/locale/template.txt000066400000000000000000000241411344442271500177420ustar00rootroot00000000000000# textdomain: ltool L-System Tree Sapling= 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?= 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 using 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”).= L-System Tree Utility= This gadget allows the aspiring genetic engineer to invent and change L-system trees, create L-system tree saplings and look at the inventions from other players. L-system trees are trees and tree-like strucures which are built by a set of (possibly recursive) production rules.= Punch to open the L-System editor. A tabbed form will open. To edit and create trees, you need the “ledit” privilege, to make saplings, you need “lplant”. Detailed usage help can be found in that menu. You can also access the same editor with the server command “treeform”.= Can add, edit, rename and delete own L-system tree definitions of the ltool mod= Can place L-system trees and get L-system tree saplings of the ltool mod= Edit= Database= Plant= Help= Save tree to database= Reset fields= Generate sapling= Read-only mode. You need the “ledit” privilege to save trees to the database.= >= Select node= Axiom= += Opens larger text field for Axiom= Rules set A= Opens larger text field for Rules set A= Rules set B= Opens larger text field for Rules set B= Rules set C= Opens larger text field for Rules set C= Rules set D= Opens larger text field for Rules set D= Trunk node= Leaves node= Secondary leaves node= Fruit node= Trunk type= Tree trunk type. Possible values:@n- "single": trunk of size 1×1@n- "double": trunk of size 2×2@n- "crossed": trunk in cross shape (3×3).= Thin branches= If enabled, all branches are just 1 node wide, otherwise, branches can be larger.= Secondary leaves chance (%)= Chance (in percent) to replace a leaves node by a secondary leaves node= Fruit chance (%)= Chance (in percent) to replace a leaves node by a fruit node.= Iterations= Maximum number of iterations, usually between 2 and 5.= Randomness level= Factor to lower number of iterations, usually between 0 and 3.= Angle (°)= Name= Descriptive name for this tree, only used for convenience.= Absolute coordinates= Relative coordinates= Distance in viewing direction= - "Absolute coordinates": Fields "x", "y" and "z" specify the absolute world coordinates where to plant the tree= - "Relative coordinates": Fields "x", "y" and "z" specify the relative position from your position= - "Distance in viewing direction": Plant tree relative from your position in the direction you look to, at the specified distance= x= Field is only used by absolute and relative coordinates.= y= z= 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.= Randomness seed= A number used for the random number generators. Identical randomness seeds will produce identical trees. This field is optional.= Plant tree= Immediately place the tree at the specified position= This gives you an item which you can place manually in the world later= No tree in database selected or database is empty.= You are not allowed to plant trees anyway as you don't have the “lplant” privilege.= Symbol= Action= Move forward one unit with the pen up= Move forward one unit with the pen down drawing trunks and branches= Move forward one unit with the pen down drawing leaves= Move forward one unit with the pen down drawing trunks= Move forward one unit with the pen down placing fruit= Replace with rules set A= Replace with rules set B= Replace with rules set C= Replace with rules set D= Replace with rules set A, chance 90%= Replace with rules set B, chance 80%= Replace with rules set C, chance 70%= Replace with rules set D, chance 60%= Yaw the turtle right by angle parameter= Yaw the turtle left by angle parameter= Pitch the turtle down by angle parameter= Pitch the turtle up by angle parameter= Roll the turtle to the right by angle parameter= Roll the turtle to the left by angle parameter= Save in stack current state info= Recover from stack state info= You are using the L-System Tree Utility, version @1.= Introduction= Creating Trees= Managing Trees= Planting Trees= Cheat Sheet= Draw:= Rules:= Rotate:= Yaw the turtle right by the value specified in "Angle"= Yaw the turtle left by the value specified in "Angle"= Pitch the turtle down by the value specified in "Angle"= Pitch the turtle up by the value specified in "Angle"= Roll the turtle to the right by the value specified in "Angle"= Roll the turtle to the left by the value specified in "Angle"= Stack:= Save current state info into stack= Recover from current stack state info= Save= Cancel= OK= The brackets are unbalanced! For each of the axiom and the rule sets, each opening bracket must be matched by a closing bracket.= The axiom or one of the rule sets contains at least one invalid character.@nSee the cheat sheet for a list of allowed characters.= The field "Angle" must contain a number.= The field "Iterations" must contain a natural number greater or equal to 0.= The field "Randomness level" must contain a number.= The field "Fruit chance" must contain a number.= Fruit chance must be between 0% and 100%.= Trunk type must be "single", "double" or "crossed".= Field "Thin branches" must be "true" or "false".= Name is empty.= Plant a L-system tree at the specified position= []= Invalid usage, see /help lplant.= Cannot plant tree out of map bounds!= Unknown tree ID!= You can't request saplings, you need to have the "lplant" privilege.= Error: The sapling could not be given to you. Probably your inventory is full.= You can't plant trees, you need to have the "lplant" privilege.= Error: When using coordinates, you have to specify numbers in the fields "x", "y", "z".= 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".= You can't save trees, you need to have the "ledit" privilege.= Yes= No= Error: This name is already taken by someone else.= Error: The tree definition is invalid.= Error: No tree is selected.= You can't delete trees, you need to have the "ledit" privilege.= Error: This tree is not your own. You may only delete your own trees.= You can't rename trees, you need to have the "ledit" privilege.= New name:= Error: This tree is not your own. You may only rename your own trees.= You can't overwrite trees, you need to have the "ledit" privilege.= Error: This name is empty. The tree name must be non-empty.= Invent your own trees and plant them= Copy tree to editor= Reload database= The tree database is empty.= Rename tree= Delete tree= Generate sapling= Selected tree: @1= Read-only mode. You need the “ledit” privilege to edit trees.= Planting of trees is not allowed. You need to have the “lplant” privilege.= You are using the L-System Tree Utility, version @1.= The purpose of this utility is to aid with the creation of L-system trees. You can create, save, manage and plant L-system trees. All trees are saved into /ltool.mt on server shutdown.= It assumes you already understand the concept of L-systems, this utility is mainly aimed towards modders and nerds.= The usual workflow goes like this:= 1. Create a new tree in the "Edit" tab and save it= 2. Select it in the database= 3. Plant it= To help you get started, you can create an example tree for the "Edit" tab by pressing this button:= Create template= The database contains a list of all created trees among all players.= Each tree has an "owner". This kind of ownership is limited: The owner may rename, change and delete their own trees, everyone else is prevented from doing that. But all trees can be copied freely by everyone.= To do so, simply hit "Copy tree to editor", change the name and hit "Save tree to database". If you like someone else's tree definition, it is recommended to make a copy for yourself, since the original owner can at any time choose to delete or edit the tree. The trees which you "own" are written in a yellow font, all other trees in a white font.= In order to plant a tree, you have to select a tree in the database first.= To plant a tree from a previously created tree definition, first select it in the database, then open the "Plant" tab. In this tab, you can directly place the tree or request a sapling.= If you choose to directly place the tree, you can either specify absolute or relative coordinates or specify that the tree should be planted in your viewing direction. Absolute coordinates are the world coordinates as specified by the "x", "y", and "z" fields. Relative coordinates are relative to your position and use the same fields. When you choose to plant the tree based on your viewing direction, the tree will be planted at a distance specified by the field "distance" away from you in the direction you look to.= When using coordinates, the "distance" field is ignored, when using direction, the coordinate fields are ignored.= You can also use the “lplant” server command to plant trees.= If you got a sapling, you can place it practically anywhere you like to. After placing it, the sapling will be replaced by the L-system tree after 5 seconds, unless it was destroyed in the meantime.= All requested saplings are independent from the moment they are created. The sapling will still work, even if the original tree definiton has been deleted.= To create a L-system tree, switch to the "Edit" tab.= When you are done, hit "Save tree to database". The tree will be stored in the database. The "Reset fields" button resets the input fields to defaults.= To understand the meaning of the fields, read the introduction to L-systems.= All trees must have an unique name. You are notified in case of a name clash. If the name clash is with one of your own trees, you can choose to replace it.= minetest_ltool/mod.conf000066400000000000000000000002541344442271500155540ustar00rootroot00000000000000name = ltool optional_depends = unified_inventory, sfinv_buttons, inventory_plus, select_item description = A form to easily spawn L-system trees, aimed at mod developers. minetest_ltool/screenshot.png000066400000000000000000002307271344442271500170230ustar00rootroot00000000000000PNG  IHDR,ݽKsRGB pHYs+tIME 8 iTXtComment1QIDATxgdusgeꮪ  ^CH4@ G_:99$I[AT6˯Q~ѱB^w]#31؛ ̧܏8loT* &'6"93/7==333lY`||[&&&`zz'&P*GD|LLLf~i "2z{O1sw]F(l{;G;쒈xg<ho&lhmcJS!sQG47(:to|z˱wo?(<Q}筽#CkvA{8ܕWZ{DX^yҿ48>1533-I['TJ}/W=4fmu>\nüC߻ oey>G%;J)u{ةw򽧏 ,ĵ(#F)ul5_KodDF^rDA{=^!2Mfw0Z;ϽG~G,>HXY]]q\G8{#<٧+3 "sއF*cG$/qnnaĢ?gw>7ǪVSݭmm/8ʣyԤq9NP̐囄c/^Y9vx~ `ǝvw^4&L/`_8|Dc@R )Nq%qv1lRVJ\n].p%?)g;sShP,٬LϏk /\zl)(67G}W6ABW_8uAjF' ko>N?356wu5`j4&'暭F B$, H>nYv1ccg?W)Ob\{'{ ~Q{Zc//<07|H\Y[=t(Le5ݕݥEwhe"݂̒w{۷}Rݽ8[vmc'Q'B%q`sZWMX@L( ^tu9bHD zQoAv^z-^Jؘ@tCiG'nkit1&Ҽ_rbm7_l틕+/[aT:p_k{ݻ&^3OMLLLZ"R;פFH[]fD\~q~fѮMRZ,mL SNkTׯBl+_7`q㸛[k^[)@rѨ!}F+ &08M4S(tgss-N"@rA\PNXn Tə Vkj{&M;͖tN?T׹n)LoA빷sk[X\CΆDrsF~7cD|u4rzNXAXGkSqm2|P( GƀTTJUi0̢zn[>vV7~Ž$&TUD`oo?˕A ֚BdPC\V慅BX~;^{q\A.Zڙ^t[frB:Js\>CDw6;'8;11&v/jNbLHDڭ7oi!_^2C{QP\g3FqÇ>+[k~P(}'/omO/}j\?(\yll vuk+w6&lb;כ}sLl7Bq{q"[(KA-@!Rz$"" _&o֖"ϧI\q-֛59\E4M°@Q:^8HEȦ ,Q=G)RH6MM,ؽƮ0׮9ݱ!M} [JSNs=ԧobQn,/-^]y^T|ykP|>759ֹiz_./U*UX[]V{0LNN'>111]7zFFFFGp||||v:-?B\bۍbamc ӄM7u#in[۴b gZ &e%IbD~H04d._8 P1,V ⁥o{]9\S~s;kSn?Jy ''gh`Heqv,c,Gs0 zUSk-I6B (dʂQA@9L8-"RMMMHm>7w@;nudtޜݹN%Ͻ𜈐\>ccX`D–X:Xoz}r2޶G=W{VMɉ^4Q!((H!r g)ŦA.W)W6VnVٲ8=zE7 Qڏ NAFI0N{X= ֵ0ꗊrǧ6֍InWx<;a4yf ld|]0~KH7;Yw0jhC80߯8 $ le!0Ö5kJ!)bcc%WUc\.q& T 6vM\dÐ9> >K2$h٩{8Cn$PƩqIG%qIdQaO:>{QWBt}R: X;k{qfv`0(K cu R##'7TGI6en4Kr'qP&VA bMdزr LHYVXl@L }?H\6@ Ċ%}a-Tlw" R.Ȅ HR9 A]ܺ.riih 7`@H y]KrTJh9"W](+IqL5Z{҈ ɖ8ߛ:1Plv/5û0ibq2EdgoݘEAx\|6sO!/2 c kvRh-8.F8fr:¢4"t<' [ -*Ԥ}/t-K!_ f3reAW`SҘk !d,紆$eE#&+!e5 )t"sYFGǾ>hv41U 驙cO/#333˯~#ѣ'Q_;?_XX ~o<—_x86 ""~LLlmj!"DY2)# [!CAg{oDIJ "XJe'pŽB1ĮP3J9b V&H) bR#Qt\;M<ϩVʈdMZoG'g痗4 {^hN/Oᡥç0 "6)XAZ0Mŋvia 30: FaDBSBBR)b*א24 HH<_{jWEO7"K!ظI Fms{;'l@ryT$q %drnnCD4),-'g_եEjm?zy>ȝT^dm |\r98rv${\koV*)"Z__zꉧvwwwϧb|'N;zW_?tG1 =$"d#A./,.9v\-1['x-^{qÙnt776az|VLbA ^|؄+H{ 2")W)M)# 1`=$C #n$)* LzM,hΟ?{}W $7ə{'˭VlG0 >DĉӇ#֮5k,(^9q7|w6Mvm{ksh[^ossv\xrw s3^ܹŋ}XkWEit /Jթѱq͍nY'Wl B !iTr&l4zmw;쇈HHX$@AQ~nrTmo>9:flD$vn B@5A Hdc 6L`vumk9N>aubtH"f]zq0BkY Urյ ˽4o$2OBD ™<050MLRIg?c2Mo1Yl"eaujVndaϘt:}kk$f+P?$F 7ЄbT){=k,)DBk8(/G Mj8qHP)h: /oGCKD\.ŽI hM&"H$+PIRXm-&@G<F7'݆R ۅ|)z 0 vI MTAw RP/W(lII\$& %VaR:9>foz~MS?2hIOD/y?51Wȕ:1ipd&gw66:" AB$`X@Xp{w͝DNK9Thπ&aB$/!6̤H9H<ݑx┵G"VB ]P9$fgB$M&RdMl- gL:&<̑F (re{p`?݄"NTsRE;=db,Q!"`sNyC>0²f{gujrvb|6C$MڝfiSRȱ䊹>FP]ܹv [pgbw4_*,tB.\Nb \߿=f}j;ԥ " )dn$Ilp>_VGS03 3([2dG !$BrrzA"RXFG&B!WT( ,VJv`I{C[q3rDG#?=u} zSZ a43Z̄h"kHb>+@2::>3 (IHCݚ],ZvHx8gI$A]+lyjb #^Ajk a`<Сf*Yo|_Rjo ҈A@f "@R <" dgf歵`Oտ~$zgQ 1C`=qJ[Bj8<օBD`~v-{nR\SSq(orjjwo+EV/ )`scfF $ Jj9L9_W_yaiՕ+ccq-DLMmj4m4($ $-0j$EqDYuNMM)0z#cPoՋƚ~s|vL9T \,}leuFjZ a-',Lxv]%R̬=/,,Q"H0C6:#Ց45Yg=r`,r#Z  8skkۓ3SkW^|ァloŃx|dbm>0L8;16y`~b;?nv7W׮=xCgϾ@'3S333Dأl 𽕕[; l~jzjD0?;ߨJG$=ttrbZ)ii#s sW\3OG~r۾rD=o}իA W^}mZԏ+rֱ'vv7_ek_zsoX+Tׯ^:{kׯ\_~ekgjt{ncA ci f -oqk @6\"t"T+^? cDT+~?~ƹwߙ җRuɉ'|>3bdY>sv{sss9?www>zSS^R]?s/>w$ {;WnRkڭe [[$R(\v#RxA8G?ZÃ<111:2nz⃁fg8p7<{ԩz^1Ͼҁwv{]u9E /03;_Գ>l6ExrrLW=K^2;3*W*[ۛ V܅cV=55 fg«<ϫg:+V)ν}R0 #3|=GX^:zy fFgvV1{,\/M\?zh}j@p 1Gz}+׎=K_C>k/z^x~cs3 G}_](ċ/l̑76vځŅ驳g٧KoZ_ rǾco4jo?~GW]sgδۭLV5R}\X _C={ѯ>Wf|lSN=3Nz/K_ }}?W+V؏^]Fc_Y('~3wΝ;wg>;w. }7Сÿ;˯Tcw8 }-w:Gyxgw_vnۭ/Nl>ckkcc~kfzbk{+N>|w}l{{l~W,~~% G~͍B)ҿwT.Ozw 9s^p?__Xz:3}ԭcޙf +V?O?) F@}Bu7?.]Z;qK^NpvM~wo\LMl\3rP$M*~QtZrn+cAg2*L X%APe?gx^|v8\=Vzޠ7``B+"BH,cQQD@ai|1RžZP BJQG8fu{bh*/BTdL~L\iB f9:6\.oe++(C2zJrjR$H< aZ;RnX8j7B b$){f{2Q}6==tA<19W IR4VoMj䉓̧w}gzybrjqt[[I!|\)UGޕŷthFݪ.y^'kml7f5'?'1,'OnE}vbzZ;66 \;v)4Zk9R'^}͐8tIjT@J\4\oA1Gl-ļ^TT$ޘHp* $D Zc0ѐqC(E@ȢZ [F̐io @*1 ;bfQFXxx@ Y C /׮_%41aoYR͘ NP1&q"")a`LyUhE8, yxlP1gW Jn?(VmqoW' H,ֶ R\0265:yUn퉁nq=nW]/W\Fʻmw@ΰp_%eKز8_LoO6[i|{ؕ4J5[zc@&ƧAZI'eǶ4^G2":ZBZi48VQK.qÊ Ft.4}+HP@;ƌd=@@$rv$k "#,D9&a'P6aTA(Gaz# #(TD֤!"DC41h.eIH@;"H"r=lqX&8~.]ω^:3o$_/vo]Z  ~>?m7 Q_)P jO*'(o_N1 D 'ZWmjvǧo_i^ÞRi$i,&U~CV’/ Bn04oܭ"< eVn -CD .RP悼vt3Ґ0iHZ}"TDQGO?I崯S%I:Z?W4NK++WEO}k׮=s*t' U"l$_4&NNF~ڤ lM@BrƜm\aLZd%FQ{""j-)C#5b#lSN5qʙE%T+@Q54$LK8*VQg^q8#r"Ӟk퀈Р ڱv<蒦,0E]Ѡ"_ 0r`fՙUj HkM24V#[qAmS 8Zc[W P4 (+hB8I+a>2~ GD'x Pwܦ6|b `r֮hC{/~~0ŕSq cP=G]~3{?<;{]_^~W_O籯~/?w.?Gy鍸_g4J*RVeo6/mNIoum}zfzle{n칮X߸%?22+ζvVƀ( l\bSӁ6Ao[$ĒR"," D9E; 5$M#,JaiG1c'l*Ax#H"6F 4+c؈s%֤BJ"3(H$I qH9JD KX bъi!}eRff&$!S+D֎e$rQ^0rHR6 # I)ruNKA@ (AeH  '|~iv;qu\m lDpgsRҁ?y~36ڧW?Hko]x" yP }{ R"Ŝ DF\{g/">O}?18 Rb!wpy[/vV^:^:>yѷ|҅o-v:l]}4BD`kmP'a͜ juvIʤE@vĜ%߄Hld@Ɩ4f"Bw?XCFD*ZT@2:#VvkFTSU đ!J2Dl XA! HD}c Z+65 hvWDHh㤽 WZo.Xk\U.ݘs(($U?6^irEg\V! z[ݶ~ű BJ0x^)Vhϟ6k4S I6RMH{[g]?]hn #9"`KI/,N}n;lmkzj$B[SG/]Y{k;A^DЩ[/_vd?X__;ˋp??/q@KZ `+ĎNfhXAѮ725v)4djfq2,-hC"j$"քBnA+a w!a1kX 0&25+AF3FP$M9X J"ByEDLfT(iXERHXXq< [T sY6kV]9<{0U| q+f !eQ2XP>k i",AMviZc?ƀvc ܭ՗ ?wA${CkĂM:7 [b |{'DDcL{#cR叟.^V/Yz "hE}+/|3O\J91ձ8:?는 _\_6֍5a/31ج:"i}MQ9F I 92Mz<% lϫd@,4aN PnD7 C&,xS5 Cbxdbf0z,v*DH QHH`I!,"`cP9  3Zl ͽ5 0HfR$(iBDlf7GDoցn4#-qk=9RZSJrAj6= &©>j^Z_bIT(E$6$FR&SINN^k~LL_is B@`-+"/[\o(J5i4tUjn7^DSJ?/j!ׯR!60HA$q4yr4"G򥱰߹L8w"`ʋly!ȸg7(_f~I[^2Cr+6CaIb6OBP"d֘` D"9ˬ0 nQI fSgUR@! (@  kOfЬ҃'3s*QIڏHC [qm"Fl4VC[@D)m}WxCU(h P(DLI٦ llVj7$7k8#1crxjju߽j/N^{TsxBيxbbww+|T(4|}<ǽT?p`5c'~a8:2rԙϾ7LO?a}W8a'ڱ'fC/w/ } _y+Zx/~#)Fѣe+[s=wl|YνuSV ;W_~G8 g{\ͷDeO?x믿p`Vo~,6I׮?~ܲoX*"o}ѧ66V]+Jg;zj=W֮_H9TوsԙrZ1ٓ t:fC W^x@fl\|$ ZH3`0FDd ʵ\;{ssi./b#ʃ>:';16+.:g8n]Yw|胯qtu W*5_~41?/v;w}wU0,=WQ-/XW ss>ls׮]r7}o}گ'љg&''{~g{S޹< s .=|^=.]|OiJD)ETZD%R?\0 W1O='T9?ydR=t(9qO1_au}}G i|RN!Ю v8/,,~PX[A<3E+r]GP<Ө)*RJonP^@H+Q9CJ-`e&N[]Pz]4֌NܢMBy' ~MK?uꔈCdۢY>1&Mk+,-˞O}vDZ7:a鶳صze rR}?~m:"j_]H굏폿q(._vW(trYXK9rjo0nwڍF#I^(ztmn+墣ʵGyX,:s#>jk٫Ւ4J-j{ޠGNw{sE? M͠Hc{xwgx;l~BPXF=2:y&k0?/~K_JX{Gs;8'Bk}RW/̝<)rꉓ'/]E+NXUZ.DN՝9pRl^ I,l(MF+ap=g0}5Biwzvk}5Fyuu|@8}_y񓯽ڡC?VG6>\ZZ|r||lIEDA_N_V$di( }uPcvz\rc_}٩~s=̬olx~>c+kk~+NWr9u'&& I)gjZgvv ?m"5/ qC ̒JÐ{8IbnY"^?֔sqB魷J`kwX. {~^r"435l6v(5)"r +á V"gDsյ$D4{$na\, 5oqz9756PI剅c Wο_WkZ;JlTnVΦ ~P(rɉicVoqT"l$[ĉ(ZdJ< (uX,X̊JQ6 Z.nPlo }77ꗟy:IIrLAh]„5AUT3bZ XkLZ E$6B0V)LUZ3ٙYubۿpVN6) I! [XBH4i6_n*`+2BR$`u=AamCZv6}5Q B@YTN6T,(D*Ҥ!,&uԩ7ϞM$H2e $' !HAD=?/X:*IE>tz$2c˥S`7oKWq\f$RrZJAgf{<\iI#ep: "BB("Z!OQʲ uBLȘ'' '8 J)`PJeJJV@`S`fQk`aC BmQيF% , 4fhB\κ 1Sv'@J+&ĮAT lPV8a*NEi5lPV29a@D ˚I$C(5ij?`AQ" i"!EHJƨ12:[ϳaW{, aP(U*UP"]!\ J2 ! !XОfLY{Ԭ -*%$e^l6e`I]L_9dlJb(ٜXdx4@t&C߁dd\D4PͷA CقP)yt-F %E"IJ+T`S1Cq?~B(Md">,hE ܘ#M,XZ&Ee̎`4IiHVy' HMl6ZTLH&r`6qXHiD}"Mi8]#d#ǘ45!( @5IfL1 ~_k +,Z;SS|ݭ56|یR M2 i m2ذ&HZ0"q5Dc"!dPOT )D@9R$]+p5IR27iBtЦTAf2~"(&l'ah˜jB"  !!b.BRqPАR,0 rxdcT! ɍ/<PpBB``M4e_U2)Ԟ`M4x Гn47/P&Z$~E40z+"Ge9I%m>a*-7Ä[?'#?E~~a +c/<\?#[W.U$i4s3ZF%,J^ |jGWn?W.l줵 W.4Knz@HvD[ QH 02E$KX!pHgb2U;Cw=ڑ!<= &Q:I6Œ ih/‹Y"؊a ֕"6_K\nP@rj2kԮ [I)4~: /:">GPP]BH&3 k- Jn1D2e`w Q s(C̳02cv/59L-Ug;$ mI)cѠ+ "2:u ɰ .juo?W7x/~+?C NK^p~.WX\uc>Qy) prl !` -3@"DAT‚%@T&aQ)0EhulH ,ހoj` % S$Y]3GJ*L#Kl,)tB"5ip Edf7P&A2XHEaQ&%&(Ƭd-0VH)7J# D $hL! A'OI, S,Ɩ(KV*+0 G &NN)J00dk%![T V vI]T -[Ap|-*t>)30:zȉU2)N3ڇ3ԐC`6sbĽkϖGg٘/U~w˕s1Y=lk}{v}7M-31d]9 |~BRVB>LQW]ںxbI.L٬kCw?fzno`E9[oѣs?~"zogO]+Ȓ&Inѹ/*95ດD,dNı($CI idn8AD i3-jDsñ fC9SI+b4AFg@E Y(;t'&-%&% )M;nB@dRF`q8D;BzX\_XF)i2yAN2M'/*x'-Few9-NqޜJ( Ր6<-R62 2ERy%ø@ĠB2iٸ&r h1LE]_# 9MSiRDI J@Rnl4V/>w#imکol]}S{oz!/^r-.^y7>kW_W?|+~ŵUw\{A/>txo78J)Bu'0+Jkk:6"$TY D0M &e"fN iAn%pJeEA xc-Sbq (YH`HP!! F`$0dD9iLd,=KCޠ0(#6Ud Ȁm* !TeS=&̤(ӠԁSQZE'M`y%VEؒI1V{J, 8.e٭rT&d=rt6XsEqd dY1B CƈvDn7bůn_'&[ -g#DͿ4rOjPv1 3< bn"@Oˆ E"|(,d#..  &@ A4 ' AIv֮_O.3o!+gׄ<`鰮6pTkgVvGzLeLHkF4śy[wY/VU0pccK=Û(2wԤR dTlo|}MB+ ۻkB6`/j@`oˤ͐n Z<4B P؊@&AR PQ&S!F@eBI;6հHrf$b쳕F,l HI&|ñO<5gaEo4Ym9C9wZ~u%Ւ|\>4.`D\yJHP{w-O [f/Ϋ/" ޿,-ʵXp[\hYfON7{PPn8Y?|oZŦ}`_i8aeW4ihnm߶4.LU:a <A 4YG,&;\TֶI$BQ(7!B"m7 !DҶ_b4 i"D. 8O؉hC f 7!҉BeHE^l|=$W 0 0rӅt;A@tN330 `. ctbNPgaAǰCIG$ǭ$!aehz{+;A$0i;uGQA508df8Յs90CxNGE!$^ĩɗmg<) ݥ9k_wqG`e}(e [E0[ᯔpAoH .k!1i+VFGic=3;1*hP,t 0U$)BĬ?V.-\̂6qۥCNx6gg{ؔISH! !d@ T 0Af"EB6"`OQ|_& 0Up0C}` xq]DY2z0}$R6Z,puj/ Eܡ KTeukp{\Flfl֟/ ;L1[wv^_B9>nT*Wkb:_mptQxϊ LKZx5"bOw__͐B\2}varjfҧ*-Z)ȽhА X$|CO\ \# אM<"Zl6Ļve#RyF"` R!I؃r)mS``$64""X\!p1 X:^PUA@DBEs؀·@@45(&ZX`Q` I Bn!9A&,^E`@Ai·C8i枓:^noCTT{)#DL@;|=88x; wB*p3[^kEVl@+P4Fi65Ǜ~WcC(>B)`\605<8ܹgvvq~q@bm\,on". b_*VC6\AH7?gH?f&>|*&`>>'E!2 f.F" [ϒ@ޅZ0PEG*A| RHBGyK~Nph{6ʜO;|X9J倈^<U\5s($0hϤ3A QG 0eAhIS)4d- [U|@ֳgf .G.(.{ ")(^ر:"r.i;v#" Hf<+<UN eIR( >'33t^B<ڤ=Q  u<FRey&NrT n)tN{$ ‰'B a3K~9) ()( $P X/`H,&v +!(EޱRĞ0Fh ť *ˣAPQ?sz'Ǽ_Ún6_SO񏈸8 UX+&tCx?Ssjn5ZČHPH=ԡnie&nkI+C40C9/=ϭtP9n&h9㞡Ƥxq\ҵ^6< ~3dK [1gh!!/TA&D;Vgzvn^]Y2Cdf/-Cs@43OF œ#zB  <&u=##$gA|@bhAb*"qa;B{e`f)PIca$ Ih$,6p=3nM[/=SÒ9޿fǏW5GY~a*F&Qk*ݏ0YZ.bxu^ѻƪV̹㢑gAaa]Vcu^God\::ZOƊ*[wN@08j&go0G600kmc( hAO 3v hS$2!  !Yr:1HN^ *.񢠭{|š*@U*+pI1!yDA]RDȖ(8R 6|L&D%Xr;|#A!CV5\&,:"~Ĕ2Ў@ '$n {fO@ JGF^M2`DwGQD A7\R$&ҤrC7AGB;1mGjVJڜRZ~P_-g=EE2Y&ח6V&H E ky)h RJkymT@d+`-Ch (.e>bPX:R"9!!)^A 0 {+ư_̋ 63w (khE1Mh=;;"¶H>Z}8 uȯC~ 讈5fs  )4N9i 3}DTO5k [8ODyz1AQyP (X)թe.ϞicuזD~ QzxZ,ygwvC̀9$IӤQ}_18 ?1?v[(7"`Lx.Ί޸P>.ȶ+23g_٫x}yR!)$HEⓨP"jJ1)dc|ATtHHyX Œ @" =)ɭHeޅMD|.@@1R lBՔw^Y8 mJ0M5X[P@2 \'&x @a 1>sXc)"4wy * ZSQٖ u1:"$N2jH (!"b ڶidP ,A"CĎAtA A6"GEx P_KB …Bot$I7;{l{psҠ oeBC^ 4VO$lODҁ[yP?ޔB7=? ?}k_pLs}:v&bϴɴF@z_s[Ryi$VjkGeafωめۍJ>oc̈́>Oi"M>g"rG 7t\#*i1L@傃\0NQp^c@'Αi޴-4YA@$蝰cPxSS:R6w)0]M. |/Nׯӡu$ع$ ҡ`("`RssTqFUPPwxƋrӴ$IjϮ}o:gC7}ڿhfTaD|D2KIwA 8C^8Hbc ;d]JK}[v9U4WO}M7v\|c8>=,]+{+w΁~O8_c؝o}OJ63VMYY,e$%ҘTAf)d Wtŋ5*uhS,%rYuqÖUQZWLk-ձ!O8ފ2`3g[c`|cZiO8YC(lFftL XZ0GE I{0{A.JdX(3Ď"]*. ơ3msTϡD@C}KWKK^w[7֖B}[^z1Ã|;5SkCޡE'%C~xh`CN9%k$".JW?VJ)K13 0̋,mJ]AMI!~D\M(G 3*RZjxʨ] /,羱HfAhl=#H8lІRJQD9:V=Tʱ2wt$ sp0fcHst,e2B@{6ao90`PF`L,uI"BieD$NP{ |ޡmD QUcnl8##rETD~{^s3ΞY]YX_SJ"Xmm,,MTPb9&s?"r+EZi"y LwhDRs'N|oͲX,LOK%Gw^;mtxBg8s*M($\>Ah<eJe-g(MqDXC'mYXX!S -ǖۦ#" wއ K`ͼ4"E Ԝ@$뙽xcSPj DpYei$MEʳHq{Y/N\a$cA,a!akEX5;F+Qʺpe(lžrbA%*[[ ژ<FX1&"imV%)EmsxA8H̦tWxd`ep~])hԗffgN97sflT}U(X2W[H4?qbylg!.]M,`[J&O$6%;(((a^'޲x&D $$uLތ4pTqh}=seYq+Ml fSlYP1 &PG-,4~ D LAi:8&@$YbEG: ]f 25g >*(v ^~雾;s7}%(K-HEi( ض]{ϲlyiqaaQ'IܔHJbeϼrP_>z 'ĉ'&N\S{xi?:p(W/v _#*bTUY=+s"x$@&13Cn IE ;6AVXӑ%v< s"@Y$ $&nYj#b6= <{Ld"pNFe&vXE+ O>gPX$4H'h" ^-2:K]qRw"ڊq @\I;q:"bapD81I* 0`Ȏ=*1(X"B,u #"f ʼnw4m9,M0 *N.KmTAj#yi!08¡ٓʘ]Wu+"%Ef=ąf7?!ѿL/*H!jT۶P\꣸g٤ dҽ]n@T} wnR/³G\ lݲU,Xp[!UisP$tD4B@Aև?0@$uؤ{ 蕴X<"IȖɠImA". "ԇgiж7 Vn ʇז}tCHZ[m&r s@Q6ی.2CD|$d[NTA".5#;ey9><w a| XPPY A&D4;?ح͎rwwpADP+H" 6O-+mRc4M6,[6x?BRgNBSIwkSGf0V-W= Hl9X!b[v B*"nS-j Py v\?ю̝ 'W?NDKR$">ʨ0-(D)0̯CQuس2*tS LmHԡB9ˤm¢PQ摶.4)6qt[$,v|FmȦ/ uȭ'گ_XHp2F r6 [eABTFDP|P 2"Q.`AFZ$w،aϭ= CGϭnҹGv_r[ԍTP3fۨgdNw@y#?,`tp{`AR)ml6JXvJ7؊p܄O!.z`:EFvYm`BE``>`i{@n [PqIaT3L cDbPM Q3t~$7KnW 2mPRc. :0h[;pX%˼ 6D lƤCHX 9.i^w^DTn! Pk%6WhHX@J*\Bܡ ?.(YLa;LöʠZa9' -C&_v+x廿+k@(שr3?udY;Ӫ/dR'm Ğ8QJ&n0"( /R@DH|\ͽ60_$0(9-3&F/{" {i0KjSv\Be 3FQckɒ+ٳm =2&X"(@&c;E1J3R$łs@ L\b_@gLZ#.@5+xPp|@F;6(; By}MxQESny\GHD E^E%ءi/WGuA:)JZkcp^E{@ழ::cӦMZ]KHLFjsD#PD6oyk AdT #2DC|4 g"!u+`.^RC 2")*ϱ3s+hE*FEK++%>њ5@gQaQ1 !i⌑8S@)z[.(X7mm!O˨TECxl[o4nr] 16CIyMS{V(R%W{ ;6鈠[$o Ptr6 zgR@`Diչٱؐ?⻡7/*jrikmaQI!HED{h&&/S R($52 .m6yI}qn"bIބ0qܩ Av{.uwm ;(۬.ʱ k5xDl%ȥP#>%)xыxI# z/aI gxG@;EX$`+QCF m)\ UR A׸QAUsBdE9HP"aCo+*X i0?\va;[BHt:[@7k6q6w< 3RPDR{}w)_sI~+" QĤ4,)HL@.# ΰ>,4אTP:n:=}vsɩo]r^}s,Xq="D.}:x*!j[&>s'%OP f < Ut牂[%SH0bK𤁳qnm{?|;L0׋.VD#}L(@PgfyUD9ar`}ˇW=#pv#SCW/A(>\g^O#=X^Y܌ \y׺XY_,瞡zCqďZ|[Pvm' H9'xH dNK}m,䭊*=筤 .Uc" m &fX&*pҒBa-LmW|C\ErgiMv7e޲2$ L9x! wo0)шN0$!)4i&92N+serqV] jx9;1$Ӡ* YL:4%dMù GZX΋i#gԖ^r3*yNE+hޡCыIyYA$-)}q]V0-Af/Ij4}ѱڥ Erʲ!L`P6<P0à zӷ?H0+7{{IS> @I$du2"c; [ Dט@&S=K' 5Hۯ! (4AsHXMi4Aœ-%vi8h8is@5u@`ń hMMNb ! k|: ^Z\5Y_-b{sva@ RV h||u@zC/~1cE3*WɅcBܗ/Ҟꖽ&ml4w)jllN?]o +e kjD8Hb%x<,$,$>m~@CIBsF,r`^ܾT%|s16Sp/y0]{YNևfԅO"EȖ;9*R-޶LAZ .7 2PyD2yvh1@ދ,**w}-DE  6)i%sJ*U;0(ED(h|  +-_e Ȟ1ÆS vB6c` "4.| Z57 $h׆P!zǛ jKo`#!uSkD p[;2:H.qPSD2s{Dh̍ۼ@ yAyfʜǙIp3ڑ;m aV}1%EpųZ׬!Dpx` Ik>[ڎA@E*T.,u\Kblvnbi(YRUY~ );x2]QJu§}8j5qmSmHi;8zUj=溴od3Z$dvI0$3sÐG{T`&} (/[5Bp " vU sJB6rmҁ,0R!%T9ڶ`==zqATaL,!˅RїQ;R*4iHw>JsܧP-&q 1F1̹%"Dk 6!<;Ұ hYR⽗=9wķ=sDĀ!߈9no~!9!',GO<6g=(pܕQIxD,lYXyd""٬]3VWbsSeF\rYvrJZBዯm:sޑgϟU S(Wjpvbfdw1j{_Y7V KƜ^:rlhZy##C y>33puyb!}it;622zԩg2Q |9nA^޺0J-7pC=T.Un<ݿpe >t}ƶw6gy-wēOLLUW]ug}n{m~! 9t͡'N MMM:swEoĥ3{X@;% 0|_ٳwCowmo}4C|oaޝǵ~+r֑W9{7=o{Wo>|?X(n֧z Lڳw^4=7r#XoLc=89w:ҵZs589240~`=nۻg#=h]z駦g_xnhpE_7@p綝jRy';>wm|']Gͷ?#D (X宬%ESPYSthTK닓/U7zvv ޞdb{{/"#;޲T\uwm=Ç?g=oo}OtV.LLq^{W__>4==C^?I&Irw̩@KpσP@{7Bឿ}p?O<Ԏ;?=ا>g~Qo|<|{vպyxp=:{o7rӞ{X;gy}u}ޯ_w}oO?۽oރ>\Vj6T}u켰"b]sC7}#C[1::~`փ=%V&/;?3?;=3S?Sj#Xִ3?ɟ>y׻uW ]>֛Ͽk^)U.'>T %řd e &KktAOu"bI!1K6KE; #(BEhv_okooV^GcgX-+3$&H ߞbR86F}~yce#)p ,T-JѮ/՛[GzW6+.}ݻwoE5:sVmoywgO<773=3K/:W_u՛oNNLV 5"2 5B2;P0#?4zʯnzٽ@{~oAĺ܅Νg_y[t˙3S$ٽk//-,E0~v|awlOMM---=sֻ-qӍ7?wܱp] /ӟ~GOK/ơᙙN,..-Oj .LNNݳ… /rZG?|rb؉s&22<2|#++o8C6soMϿݻu{j] 'S?_(.efۜ)*]Paɛ"O??W_}%"a,%Rf&ĹWw%WW~FG|[XZ\ZK |ެo4pѻzk_{^sk:Lc/pEۙٙ'ɽ_jW׉ ]ڴ倂w=N|GO>?CΝ>t~ơC Q_-//9;{[_R/8yln~{ff<4)]$8s=Rj׮]@R?{~VTo?~}} R& !DEd[>YXeW$1Vn%z{\…l4֑h~~?0qab>{rɓ˫b[[_-]w)t7<'zBRFT>,VcиLA|%*+TIr\t ?{d: aoUJs"D!@Ƚ/!L41sjeuuTwWI҅Bic}X*9RZ)a.kk+=ӝ6&&&;0=qюOmlUɩj0xqD|[DoR48{SNתZ"BevqǶl鉝cRLNNĆf\Ukg{{{GF/Yw饗Lϳkg].WjݵѝսT jan~O2\>wB}mpwkXu@a"ё/--lݱIر}g@]zI\7[&layaǮV3AA//}+_?5~J˵kC;&u I(R~WR&.N|9D>^LțC̞Hy?0jlv[?uT]\]9;}FD9O*P:S^2QTa Yίd~\H~;{It/hEM| b;ik:Ben! 0/~^TJjcm% g3,l⋅b$y0D3X@n 8>@%ACbD㒲w|D A *P#8:V v \|dH j 1HJ]v> kR`[:`ڮY9;Kn@|ƶw^qKmۯxo"Fy43t ( .%0<p$Ms$8t;6Y @!:Ylԗ y^kbIajb;5h6J8Aџ@oq}S*lD=wc?RhS6+0 Xb2$TPM<c 9Z)tpfpy~՛P\8[:7@D F͜ЁsU jUݕR6jRʋ*q,Z4{KcͳQ }ûf!lw̹r5=ϒrۺQSEF"])dI#c{~)"FsE` 䳰ًJYap( t*e$c4yP1/ .U{ƚw\rpO8RħN='RJurfQh[R,"xD9syw^T$VB <"φ#,-@` *=Bc،s&@&.*8y> |9!H)#{UQ*m[ aa˽#`fXps &c{Y4\={c<, YD+Om VmV}5VAa: a8}Y:B*,c5`!\z3EcS3{!K 6̌dA<0KSYX?) J|il$9Q$kգ̖<0i2Nkq4+2/*/H/$df$$GS@ua$2*di3㘽ض2AAoB9K ֲJ=X_*zS+~/lfז& g/Z o[m'̡hOۘ *i=03i{'t.6.&JQ`G'_m52*T: }Rg6`,@ RKxk3sAطobW UTpֵG?8m,˒VjOd XJ)4^~ T¬E41j]szPXRq`/" PaV>6M j.kDj`˞fc9i!($B9Q.Ϟv?U,t{{<b߃]*nNLw骎M,DF̝ݽcT^+ N]29.kK>;uadxrٝvQT^xɩ)cy(UstD:(|B", P sR*v5<4tɝ;w>s=\ĉJz؉^s]5?}l[WWyn@gMLs`AMo嗯jvR0qaW?{ڃ'N쮸ʓN;;>0\i"@WO[ufRꫫ{afm!zϹ߄ 1 6mڕM|0(d#OCjܽ(;Ob!K;1;m-2@Ǘ]~Y;v>j}\pl^z%/>57^=rtpxhꪫhffӊQ@յgNtM޻'zֿ Jww| j5xiE,wt^"7-K/8oϞx7M늛I顱KW'=MfOWb!i;~j;Q-Z顇2]|rь_~j.'C:~ =X*BW/g}fȖF>==]їծ#?T<]z W\~ɓ'PdÇ;z[nym߳{cE9v5Y+ϛl[|I, }mb R+ط{_w}ߺC7v>srK/+|kO>~#_v\-W}믻>m6 }zȱQy[}_)J)gDR:l8OŒYh;\v]cwq;QM70$QN'7|W/6͸T[X"bD۶,6zz˕J_`\( `T62ťjWo+^Z*w;]253eUĻX8̒:E")DL>DT `Hk"2=5eVkR\*FR5ڰb=ٹ8w^\t8ٲCi{"ڵsѺw~a~n~xhŕV^)A2R85TU(QЈjs^A^׽䩣uFPA5F[]*\Ϩ}-mGQȄhDҨ7`jj֭B\jB!c`0hmعRd3H#7</:B J^|%NX er9wǎݺeKZ}wFӧN z}#* ?#ic[^=rW}(RG'N ?yXMYYFMj|j(r6dT?{QOl,+}Zi".;xnz{q+2zQKR[M2 Q2g'bu-vme 4el#Aݺc֡VcZ˩ЩW QawwRH Af[-zҟ5TΣЖ(tN LijϠ^R@j6=T9rԳ߶m%\b3sWOsg$2=wuy/G̸VG^'J!0\p"xTZef/f\((UV]]YeMFOhLHRml*(B xu T"Vs7kҘ@1UCM-ɼ0^@i!vlقϸwxei6ҕ2dqfZN{z{fgWWFGG/ gq@uWǵ^]h#"'O<~ Ū019q_zZ8GyY(xK=0ɓ$jWʄYI!C1@01CiCJ1&9[Z=}! ,.<=j6#.,Uk3[⊎F}v_yϱXVc@dog`K}mڃ]]X*UfP؅%A`጑1`*hg%i::: (DP(V[U\h}K_i5HEJidvYRڲWDqccitk+Y\* k2&Z9dj/ &jZpd m!(7Y+)r&⹁P'F iS5}Qra+$$l0'Bk#E$xk^rWw{@ k>RxQ07zchx(8z3aum@o*w{kݿQ\RWؗ*"jW⮞aV:RT{;gpkj=~h%e}[6Յ3UcaL$Be$NGہP{ WmHnlo13W7*!bnsgY@HG!ij]f!+2I*VBP%rU8VQ\Jl)e*+²ށmCWtlPVcda5Zh-g9:u;NclJ_Qm*՞(|ж,kZU$YZ-";H R]{F" XCz ","$ʕAd6Y0hGqZ(}CM8Ž}ܤJL=HJњ=>!0xDc5Y]Ű -R:,Þ,Iը3{Q©QI H&FʅZkM."J!9DeF{!GsߥpyAPANB43{¢QQi8.7k9'hp BVwwҸ Q4۹0u={"Z*m՝(IsCz[j@T=ٳe/S_YZ?+.y+{COpz5$I߾!XO+ H{E"2]yfQ3?9p~+mfϞP*WZ5~\yES-6khSZ2.w x֙} DBFOJ!o/߷g,,_J#ۆ{}^kRIᕗ_y ϥYuY[-j޲+ ? ?u|G YEd^g{ţ'gG(q}yju\asozӛ;vjud2kAX.*Slnlı1|=z'Oj45{q|[߂H?VQ&DZ]f8riRHJbHDiwA.3_JZ[B,Q|'9RFFzKγXXGqd~y5[Vbo}}_h%{o t`X/zrc+C~?5&vȉDK3!f;{|'3o"D ~`OǟG?U>ܑ[n<819gXR7.Wz95FNO<≛]AIkݷV??]_7Z˚نm6J]i)jW_cc%XLڨi :ʜ "s)H+*9~}osu7Z(p7TQq=_g>?O^:8 i|P*93g˳/<]#QC'w޵wޯ}kr7>ϽGV{;B{cccrąg{{g?\*}_sرxkšk Jw}u ݽ#s\\)Yto#K3r73~ӍBWXЁ-[/]xkk+/|֛o^Y^a]=wU8yj1߇*ۣI*=c?\1=u}wr?wG]7tٕ>ygeaE-#'O(.ў;wD*8K666ػ ӏ?51#rբ~ٗڧ~džzc۶)%uZTS'니(ڨfcEE{o[WVfX ?+YQ*<q-6rD5>#F+/[){{u]8?csr??;}%ξϿ5.{/?g{eɇMlmlX^8zz;o鏋q `A~l_yk>Oݷ#homC:/NCy\z{x;n}zjStWoۺе| _gyJ}qݳ-oy[Ez׻ݷo۷r-cc۶m۱cGP24+ݻ.n+]}]]qU7qōwݗuU7tUko}ӛm+.ΛЛncnqfzwlˮwQ &\}=wXJ+fzfoxn%BTl؋GN\kdO Q,"Ij]]Z}'YgDyWC[ O<3ã[_ܳw ]~ǗdyYh8'M)hSm 05Q5* I!(:""!+FEdbêX(aյ/N)U }[GO7zۃ|#MR07hԵ6Uvؾc֤(c-wzj́yԅl֪GZX& ]DL]җq|uuyrj3g.-,ol$c鹩?Mo{O>+KksKƪ/r|rZݩ/ݵ@ٗ]w,iwkK"9h]>cffoɅٙfV'OGVB{ԡ2=7S;\17taja>xTzwmMOϞ<;ap_JO叟9ldtԊCf]iϕ츼3cߡr?*9cum‰ kq\̜pF S3sKCgO^%;F~1P\fO>ӡ+_>rb8yfu|䩸P|GRW{'ֵW;?5p~r J鱭#@{H|ػ~)$ly`t}o!y*c_9CJȒVcc--;{LDH@س:hq_ש}ePB.q?7(\jl,΍XDXi3OF!CΙ!Pšum$=[g(BBL[VF3zcMp3r/u6k50W~ǫO~cض@q!Rh:I?R'L)g@f_t;IGyc43;JH;2$2d6Mwժ/ς.!KֹD}{E,Due5}VT |جbpo0{V왙!3G8{Dt+"Z0 H{=swܹz9 (RZ]ezjWk^HR ׃^vn~a=Z<}+^lQ^~hWV-ͱR7QIt ԟ1|׽c{\hMKv( R nPV{xzKmƞً `!f w$*TMN ZO4\jF{۪]ٸTv1'h±$iM vۭacQ>>4V*Ϝ?Wz}~qaFިt466vĉװx^_YTJ+Ҧڽ8sVQWpը/'3͍U74}cmei5DX(WvLz$ (&VݣFaAOooz+PfH5[ǺjNrbccɦj`ř v>*[ Ǚ244}}uR[[tzi{ 7kfś]+W3\ Y"mI|]Ҟ\TmdĺίعM%ɪJ6* ۱&ejfamon'qmԽ,[Չ8,]ɏ~矨uD:1raoݮۖ"ZZ_zvhT6fƜ-vWl\`O֝U\A`tiÉ)g$Q r) .0O[ABR3HDDߨo.L/Mz&nsq_>VDIEGe6"Vq 6vJ ς"=sn5!2G#wBAA +.i>/^-m'"AҾ{}HM^ spպ`蝵iS)AflӬua4?vH_w|9.3B4* "[rrR'n%A8UU= )Z!]Q: x׮lY$o[ 2$6 *,޲HG5چ6Ԃ[SV>Yz/QR.s|F$AF[@tl3DN)bflq.,j))*0Ff{f#/֥r'(^ ]Dt:fJ!W P|~\ A\FP!psFʧvP( J=X&HгD5(t2'Y1LU,UǨL-.!0%/p-j%|gP߱eTahO5a )­)JK5Ki/T9#phݤ㊟o Cp@%j6cJMڅ.b'Y$\&;Ga#;O(A}(=mFG;(SpwDE坏*k K]ijQċw &Mr 6Bbl.Zam KX'k>7v5c++(hSa|9l'Bs/ĩc}c yǏ<3O7gZΝ;vů~ةWz;zO|䞿gϟ]K&hJVm退=+?_ w}w`Oݻ{^^)׎:z?iH|s?>~ _à - )̻{׾՟?{˽?77o#7]{M׽uquڟ~}v9ruHo_ʗy{:t*ҫ ͇Άٹ݁Nu0Oz(e3r0Q/;{u'D_C~f]3|o'Np-=}-?+ e0S0%n䱧&跁Zb`4M@ @ V-r"d Z,4;RKުPFQ| Ε݃~g+O:>h"{G$$Hf,39"Eq%=y# 6jG?'K/{kO\WJyG_x WϟԹs/ίLs/]z?xyweO/s?<=/[3b`C[[s3\/vxxq2R5tX 6,,Dfֶꯞ}'?+my?rs/n_FkkۗLl'󽹩7aea)gyK* χЩtRzϥۗT)&j_.˽"@鬛KWKb2Awvy烄~COl?׌֑wyOu{{@ûLr@  7zjhnṔ5ϊBD\N͟n_eDy`PzqK$M)ķ: "-y&eP`FJXƙ?ϯ̯ A>qҕ }>u{O~k9t?֛ot:-w+_=O=kmlv}⑜ Ŭß$l=gM,555L5՘{r _ot逐ξtv6>g;;M期W/5M.__{moꩧsWz'_w[[{;c #p"8wR89U%^@Q^`X@) Ԑ? Y` 鱼y<$ r&OtvLdt=|R/F%F}&lvqOg^-w 7nn mRԴ Y07)%N".Rv/l4:10}MrDf6iҩ Z:ϑM ذ4!4 {sY@Ui5bNssJNa98RJ'*RDr i~0'$Q#")82'md"m}-TTP!- JD@ 4iDJRB{GL03Xu6ßt:"Й7~_}_2 䠨}o}_SsJ'4BU^yoAaFbkqDNym(gLX&*Er C4mJD1|A}X}q !R)Ka kvE]RV8L )P:3TMc7MJgnp?v#ę8bH1N$?󌥗(P){bW,O2=Ho`jpX ty"I8?@K-R(N/,焀,èW3+XV ]Yj{7鱙%^M!O j͔(h1dAr i~Ovb$;Z;o6R/i s׫vWoCCC'@s#_"\@д^QYU/ǝCkwKE< wz;+fe"ÌḎ^m ݭXcWDoP߬B|kNq בАpT_U[jV ~a ﭜԏ@]kHf m~\o:aBkhk>ڳn тEmA)+MWʦQE܂i!Z1(Q aag龎&rT,#AL RT|byJɗN'@SfW৆R*bZ<T *`>G=C>] rR*T5Sp]bJaXv^ũR +iP 2cj G7(Z'wo}f0~t#1vv|b> CNH C7 ex9ƙWGo901KW' sB)6@4OI b?W,<$jXjB@S3 2M ɠԸ%SТR +)-`Q ra.Ir )wYa!G?:3>/vLtb--vPWF~&d^t%z[D:3KH]s$Y˿gQ,kLTӹ'-@Lqi?̣B\iJkQy`ZSy 7 䦹roi}s_K4M<lUmh8:"p"FMA")'*QVEGtcg(-Wuו KoS93o|[Η ?Q{4>u8ω腗{;Gko]|/O>kyۭ\8w {h1_}ܘU3F Usq*gAfv}@MC*[UJonOTJRAC\dJ4bzXBj+!znmsv7y34p)Fp*j#'ڀFbT Jș%? 7C 9Fq^mv/e1=88 {v>b?9UE ] q-̿sT 1y!&t/ww677\ߏIZܐmfU]!O.5R/1 ^ԆmJc j Vȡ(%J3yr^~G;vęgߛ?ӷt|oq.]84=>n띯low?|N^{gz߻?x;="UF\51q!5Nc2u,TF 3tE󔈱ʰ+Z M,n0OtJ22`e2XȱbHv7A'R)!Pѥ7.q6o~?q!d0s1eˌ bjUsb,ksg !2 6,t6tϥtj7Cen=ؕtE:z零_&ҋVϵZO$hbF}2+a?fAk  MS;`}m,?u-]zo}^줔B+B*5b 0"뤔XnuհE>Yq&3M elڣ^ޢ痯^s%Ҷ'ny~x Xa$ vȱkO:<".d_xqhhw=6W, Mjt+)$ &BH h "gtO͸VB)..fy.kWJ[Dε[wRKy> ~3h^L Έ3Nq|Z|_voa˗w/u`ZK UCTR Q4S#gE\t+YbƜc /ai~;;tⳟ̩ӧ|wH^سu~BM''!3?brakox;*h_$V.q@i=mK huxH=IJ[*w{h74ҹ?8vsg/D`~ݝoǟ|^~k?tb«C?s7~k^~{߾ ڧ~rxzĮzBd Ti?Yw/@)ɡ4=b7KDDޝ` F2AʉnLeWðP nl 4H2ғ>FL[=49z~ ^/@s;z͉.*cfj% X\JoĠ !Nhfҩiq#5b BTa,b b?/Ș2TSUK 3)\Gq26+ H.V~;kT#=‡AjVA=T{+U..ÑⴛX㄀R8?^粌Gxd-鏬{k 2?,0 ۊD.O Rdqyĸ2\b{h~SuEJBd z eS ]j&QEA-zN@0sUKM aynɴ52ByePG)r$dSxܐRu#@3p<&Rp {&$ KL[us!$SBB;4cθRҔ!YHgFi~eD N+i53vw6$E^X'l]@tI*{3*P|d^;G !J mu-q8G;@xpAB7sJs;.p|?pMEj@5u+bC #' =bX=}Výc5H1$հ0Ӱ' e(}RB_76uYf?)V!YZ]1; !Ey`~shڨ>0vJ%>'0:Rj Dm'Av5,!N~*h 鍡Yp*@;2.Wp!O+S\tsS\ٰ@Tb0 -2(XU"VbLtEUq }KKh@YD0(Y@,T3SoWSO Fhc2G9ԠSH 2ǡA+b`թN8Zͭ{;!!5y4 VwR_3%3ESzAeǙFzx7 !M:~p` UMMa\cD*"8ZoA {W-%iݡNꩴeTɈuȪ I-[0,͔U([BٟKWre@j(54t"vr+/N"h͔(rofbjkE0odPgZ_ U7aePNOP(p؈ ` l]X+N,(zv]$D%\}DWjn7G&¿2V?q`̦11a*F IjtDVR~$'#G&ӕoR3X62f748 kİpѕ!QX^ M Խ8xS< kWQ xZ@瀁)ZZG9Qݨ%( p&ӑ\w qda)*&{0~.-rSjP*&A>^tF;#;R!fGRUzU fM3ʰ)Ax5OeE"$LAtAѭ4!T 1 l( fEf>sNU`tR&j EdFd 6sey'׻}]1R,X4GIH'tp OnP4<%8D9c 4`SqUZ9Ȏuu툆HX>v"fJC@MbM=VB{b__kuTh (YS+eC,r`GUyLJv  v\e |3LM#Ef挙ԒdiRTL:sڪs B8ZzU'y2XF͌2)^r&ۗ|g$5 G!#r&0a!&.V`p?Jee0SJ "R` Q`Z{cw*LoÕH0z k1#ֹJ%CC3̈́\] [LU[\OZ"Rwg_UNvV V ^*9'DX B> 9ue{e12ӾZQ^GNxAaFcXk5,-=GvnolX hvݘwS2)%}fXlPUuW5Mr KW2x R3 B%D+/yUz!lp @%Wjy_ܮbSk' di 3D@9ҊN_<~ۭEj ǯ eDUPkxQ$C*7!eƘA[ {`fHab/u2cVldd@7y8 y L $7e0C(- 9NgʁJ[2q nܨ0rrxdְ̫KEE,J_JvK%-b^5זyȑ &}5aqSy9uAc/}X/˺MqGpyٶ[/9X̟R/7˲ uX!A_ ;/5SՓfU؏ZYfׅ#CHe@3L]ZF軒&V'kR^;q*RF&M}(@yX{0}Bҫ $gLxhދ,^RCi”L RESvynݵJG}b5ba T<T.ڴp }y %[ӧn9ypz]Uw5@"q}7 3 cBd2XT971ʽ`i `@ "?$ ?RٳΝߛ{ѣϞ=cY)塇z'^~z*D$1r߼t|<N& Ǫ.V.fghU @{|p|ZO1j<ϥ/NN )"" 61g7}11k?PJ 07H1," @?@JW$ѡz{uX*k Vwz\kB4L.*73bJ9``ņNM7'Gcj|>vvvvwww3>|ŋ~aG>rȑ^xakkݿ?}u]z׻Ν;wm}޾z<}G}衇UoO>裏~4~wu׏~O|/_766^{~nᆋ/>o{Μ9sw>.\}Ow<7t92iėg}7~7Ν;wpp[o}p½;^{{;zŋ?omm?~?{?{g{{{M?ş0 η SCSvSӔ)Mdg) J rƼ81P͌AN 7҄eO@ S4a$87f͌aTJoG*t+P+P5MKC'|3n6ثɄ=z{{^pg~g~~+R:ş>)}k_ǿoگ{ッb_|3s˗oF3?'O?G}{]wǷ8sL9w?~JM¡rp5lO;vlkkOOnևzr=ꫯ^t#b׿{{{'O|o}СCO~;7Ms~K_L&.]iR.Eo52쩡_ 4ZRb%lRv*0 f€Z Zq}Lh ǚm3fJC sC͌)m7R͌M_/SmylR97M\.9#>/'O\.~|ɭb}}:ӧg5MO@)M3S( OD]zѣN?]J(J}*N=jeP1a[<>ca<2A Q~|; O3Qt,Qj2XP SB=+)- Ck9VVWg3C8ޑZl3bɍ6]ݥ:u&a,oٛm"r 7}ꩧycǎ\tGkOիwq3gJ)]tS/~"r뭷~+_={饗ŋwyٳgo|/ҙ3go}[u+BD/^|~fv5,f_wov2 KEn hhgij0BYW?I?/>7;J>랭1i '!Y,@Q3Bs_<'jPz##eRc%aQ<6%䰪NKoyƩEBG1O;ȟf?WZ6h#ig^1&L-9wXNn)L* -TQrsS15:5ʗj@FnDzcE9aL)c|1_o7Nw_ g̟{Cg?wuO;Pբe|%,QP:9V]\ }n;\HjuP#,o VLE)*me03Zjk }HHai^rr]4]P {%(.w'\ IJ yy8bg3tܓFdWbPd BtaԿx~Q*~?-!6g/Mu(#%h~7}YW 6s\Xx3ߡϫ8Q L]SaE3(0`fc:ӵJ1@ ^- `B]%"cjMaSX~H`T$<%{!1':< :=\'J6,cȊPb f},khiB(I_kvY؅0D,:d@dֿrNMaOb<$tv,5ei!@ՠ"v }C/m}U_Rbc2"a{1-BYu-mXcuhgTϏrYu4 qmqpz޽_gAf{`avtbP39et?ϠP0eF+C~J(D:ĕ¨"0H0GRqT826s_R& )"R%v:k}X)cBdB3ijI׺I:}°!d)t?cb\dpL}|B HNLSZb )4`mBPղZUk,bPQG@j‚~ [:}}Q-Ob/Q LʕΪ #3QDnc72~n`wNuG0zTK#ݲ:>6V]!W+ 0Bgo :R巌c9DlW*5h6SHuHv^X`c4w`@ܐsk + Ŝ/7(7EpN>?0-{XSxD uF/LׂVSKf+‰"7d *8 t Ho3cplZJ/.JNAP9L\}ws9!O?_bePV?5(SSAJVy 0.q΄)Q@*#J%MTX) DRN7ϗ0Jdfk6YK!m0> z~Y,(5j9(Hl$f>b x,`ժFbԲBRCa*>i2 1J6)LOK݊ 3,(u VD})a0QOfA# WvX) :h䉣WEW@fRF+GD1c#9WlV>HHRCH :XWʰ M3*2M5)kД>~ߐq`'iBH RBhn 3)h9Дir8Ň [ D҇'Fu5<=iJEaΈ~rC.@1<GKOuI`̛ GC<`%Eo<H}jhAZcM:,u=Qu36ZS8@ ɛw FJ 8SkDgiu.}cu65tU&`bĺ}θtt3%|$0{Xh5N)Y"S0Wߡ}I9VHHJhJV A:8u2g8 i] sb-3朩-Ǣ$g~ HŜ ä{N:f_l-ܔRTbuzbݏ,WeD8,^c\ɂb}h)QjO*@U۲T{6 KwKiJdbƫP_SrqOMI'>ϊZKbDEFzqc p"4RteQ3 Bҙ :ZJq24Sq,G^\X˷Xjk]paΈS1dQFWu @0T( ͐2RVqub88bT=srGfGju !6# #4CnWȍp0f,5ҳb!G'A5LQLQtӵՁF ?|Ys*ፊ;zӊڎ8dVpQ"btUpa-yMlʙIvjRHFNbnd1d&cQa"tFJA&Tg<rPD$Uјlؓ)#ۇ6qD{ 2i1)#f<0;m(49a7Qtpw?[!NH8%t7b,55wT($@BT Ÿ&2JhBt'9+,L ^'Fh=f0 {3r)aaC 0,a@JD?2& Q%862 O<5jZS]yt1TǷ\pUN:DnWa5/h_Q " C= @պ}<4}io\Uc_`)&FߠXժ3Z!%:5 VZj:\3izNL4wTTx;ksHR8 qRG>w _{#Gϕ3J"JL a/;o7Ӱ)jL;(ҙ,TT~ZSNKg+ݾn&N_ pT{Tlʮ !nH-Y03Ӱa!G&djjf5T32sU)Q;tryJ[c-) Q~k#6`~A1aSU]zD'郛 콢OzbIuA ݰ\s>5R>x!lXhbFqgx6JSvvDXs&8M˻zu=-&T &2hrp`@U=(M!! ]`lwc>Dffl%mWgL:P[2x80΀pP0 : P"[l5r ͼ4iG[GObJ15nr?nDjXT1%t5_˽.H'^s\-[ CVRdx}# x򌿍aR2,qKHt1kL٭V(Έt#O'yuVbV4v#z2AjYP0/4Sq +ns:l<kiRT9Z3bZ 4eSrс@DnBV k$ cZ:WxDiB,@,ehn)Oq*,r %.%W=4 'R%„ 7SvB5yHS4qo6nab_jd:k0d v3c;҉I bc/ ·Q6l0%dXK=n@6 B*E#Jjfɛql03sǩ ܐt&@_(r"ʤbNiAސKtE!dۭ~[DK{IENDB`minetest_ltool/textures/000077500000000000000000000000001344442271500160105ustar00rootroot00000000000000minetest_ltool/textures/inventory_plus_ltool.png000066400000000000000000000002771344442271500230350ustar00rootroot00000000000000PNG  IHDRaIDAT8c`4ltl@N e C It`1\2Ud!0q%X R͕ȮBQSEGV @00=?27jÀXJ)J#XA}QAQIENDB`minetest_ltool/textures/ltool_sapling.png000066400000000000000000000002771344442271500213720ustar00rootroot00000000000000PNG  IHDRaIDAT8c`4ltl@N e C It`1\2Ud!0q%X R͕ȮBQSEGV @00=?27jÀXJ)J#XA}QAQIENDB`minetest_ltool/textures/ltool_tool.png000066400000000000000000000002311344442271500207000ustar00rootroot00000000000000PNG  IHDRRPLTE4h&}:v:}6i2:BIDATxcFAA ``TvuAc5.B.h!D1QRB0A I K IENDB`