--- title: Grundlegende Kartenoperationen layout: default root: ../.. idx: 3.1 description: Grundlegende Operationen wie set_node und get_node redirect_from: /en/chapters/environment.html --- ## Einleitung In diesem Kapitel erfahren Sie, wie Sie grundlegende Aktionen auf der Karte durchführen können. - [Karten Struktur](#karten-struktur) - [Lesen](#lesen) - [Blöcke lesen](#blöcke-lesen) - [Finding Nodes](#finding-nodes) - [Writing](#writing) - [Writing Nodes](#writing-nodes) - [Removing Nodes](#removing-nodes) - [Loading Blocks](#loading-blocks) - [Deleting Blocks](#deleting-blocks) ## Karten Struktur Die Minetest-Karte ist in Map-Blöcke(nicht zu verwechseln mit Blöcken in deutschen) aufgeteilt, wobei jeder Map-Block ein Würfel der Kantenlänge 16 ist. Während die Spieler auf der Karte unterwegs sind, werden Map-Blöcke erstellt, geladen, aktiv und entladen. Bereiche der Karte, die noch nicht geladen sind, sind voll von *ignore*-Blöcken, einem unpassierbaren, nicht auswählbaren Platzhalterblock. Leerer Raum ist voll von *Luft*-Blöcken, einem unsichtbaren Block, durch den man hindurchgehen kann. Ein aktiver Map-Block ist ein Block, der geladen ist und für den Aktualisierungen durchgeführt werden. Geladene Map-Blöcke werden oft als *aktive Blöcke* bezeichnet. Aktive Blöcke können von Mods oder Spielern gelesen oder beschrieben werden und haben aktive Entities. Die Engine führt auch Operationen auf der Karte durch, wie z. B. die Ausführung der Flüssigkeitsphysik. Map-Blöcke können entweder aus der Weltdatenbank geladen oder generiert werden. Map-Blöcke werden bis zum Limit der Kartengenerierung (`mapgen_limit`) generiert, das standardmäßig auf den Maximalwert von 31000 gesetzt ist. Vorhandene Map-Blöcke können jedoch außerhalb des Generierungslimits aus der Weltdatenbank geladen werden. ## Lesen ### Blöcke lesen Sobald Sie eine Position haben, können Sie diese auf der Karte ablesen: ```lua local node = minetest.get_node({ x = 1, y = 3, z = 4 }) --Warnung: Im Englischen ist mit block der Map-Block gemeint. Daher emphielt sich für die Variabelnamen node(Knoten) zu nehmen print(dump(node)) --> { name=.., param1=.., param2=.. } ``` Handelt es sich bei der Position um eine Dezimalzahl, so wird sie auf den enthaltenen Knoten gerundet. Die Funktion gibt immer eine Tabelle mit den Blockinformationen zurück: * `name` - Der Knotenname, der beim Entladen des Bereichs *ignoriert* wird. * `param1` - Siehe Block-Definition. Dieser ist in der Regel light. * `param2` - Siehe Block-Definition. Es ist erwähnenswert, dass die Funktion den enthaltenen Block nicht lädt, wenn der Block inaktiv ist, sondern stattdessen eine Tabelle zurückgibt, in der `name` `ignore` ist. Sie können stattdessen `minetest.get_node_or_nil` verwenden, was `nil` zurückgibt und nicht eine Tabelle mit dem Namen `ignore`. Allerdings wird der Block dann immer noch nicht geladen. Dies kann immer noch `ignore` zurückgeben, wenn ein Block tatsächlich ignore enthält. Dies wird in der Nähe des Randes der Karte passieren, wie es durch die Kartengenerierung definiert ist Grenze (`mapgen_limit`) definiert ist. ### Finding Nodes Minetest offers a number of helper functions to speed up common map actions. The most commonly used of these are for finding nodes. For example, say we wanted to make a certain type of plant that grows better near mese; you would need to search for any nearby mese nodes, and adapt the growth rate accordingly. `minetest.find_node_near` will return the first found node in a certain radius which matches the node names or groups given. In the following example, we look for a mese node within 5 nodes of the position: ```lua local grow_speed = 1 local node_pos = minetest.find_node_near(pos, 5, { "default:mese" }) if node_pos then minetest.chat_send_all("Node found at: " .. dump(node_pos)) grow_speed = 2 end ``` Let's say, for example, that the growth rate increases the more mese there is nearby. You should then use a function that can find multiple nodes in the area: ```lua local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 }) local pos2 = vector.add(pos, { x = 5, y = 5, z = 5 }) local pos_list = minetest.find_nodes_in_area(pos1, pos2, { "default:mese" }) local grow_speed = 1 + #pos_list ``` The above code finds the number of nodes in a *cuboid volume*. This is different to `find_node_near`, which uses the distance to the position (ie: a *sphere*). In order to fix this, we will need to manually check the range ourselves: ```lua local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 }) local pos2 = vector.add(pos, { x = 5, y = 5, z = 5 }) local pos_list = minetest.find_nodes_in_area(pos1, pos2, { "default:mese" }) local grow_speed = 1 for i=1, #pos_list do local delta = vector.subtract(pos_list[i], pos) if delta.x*delta.x + delta.y*delta.y + delta.z*delta.z <= 5*5 then grow_speed = grow_speed + 1 end end ``` Now the code will correctly increase `grow_speed` based on mese nodes in range. Note how we compared the squared distance from the position, rather than square rooting it to obtain the actual distance. This is because computers find square roots computationally expensive, so they should avoided as much as possible. There are more variations of the above two functions, such as `find_nodes_with_meta` and `find_nodes_in_area_under_air`, which work similarly and are useful in other circumstances. ## Writing ### Writing Nodes You can use `set_node` to write to the map. Each call to set_node will cause lighting to be recalculated and node callbacks to run, which means that set_node is fairly slow for large numbers of nodes. ```lua minetest.set_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" }) local node = minetest.get_node({ x = 1, y = 3, z = 4 }) print(node.name) --> default:mese ``` set_node will remove any associated metadata or inventory from that position. This isn't desirable in all circumstances, especially if you're using multiple node definitions to represent one conceptual node. An example of this is the furnace node - whilst you conceptually think of it as one node, it's actually two. You can set a node without deleting metadata or the inventory like so: ```lua minetest.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" }) ``` ### Removing Nodes A node must always be present. To remove a node, you set the position to `air`. The following two lines will both remove a node, and are both identical: ```lua minetest.remove_node(pos) minetest.set_node(pos, { name = "air" }) ``` In fact, remove_node is just a helper function that calls set_node with `"air"`. ## Loading Blocks You can use `minetest.emerge_area` to load map blocks. Emerge area is asynchronous, meaning the blocks won't be loaded instantly. Instead, they will be loaded soon in the future, and the callback will be called each time. ```lua -- Load a 20x20x20 area local halfsize = { x = 10, y = 10, z = 10 } local pos1 = vector.subtract(pos, halfsize) local pos2 = vector.add (pos, halfsize) local context = {} -- persist data between callback calls minetest.emerge_area(pos1, pos2, emerge_callback, context) ``` Minetest will call `emerge_callback` whenever it loads a block, with some progress information. ```lua local function emerge_callback(pos, action, num_calls_remaining, context) -- On first call, record number of blocks if not context.total_blocks then context.total_blocks = num_calls_remaining + 1 context.loaded_blocks = 0 end -- Increment number of blocks loaded context.loaded_blocks = context.loaded_blocks + 1 -- Send progress message if context.total_blocks == context.loaded_blocks then minetest.chat_send_all("Finished loading blocks!") end local perc = 100 * context.loaded_blocks / context.total_blocks local msg = string.format("Loading blocks %d/%d (%.2f%%)", context.loaded_blocks, context.total_blocks, perc) minetest.chat_send_all(msg) end end ``` This is not the only way of loading blocks; using an [Lua Voxel Manipulator (LVM)](../advmap/lvm.html) will also cause the encompassed blocks to be loaded synchronously. ## Deleting Blocks You can use delete_blocks to delete a range of map blocks: ```lua -- Delete a 20x20x20 area local halfsize = { x = 10, y = 10, z = 10 } local pos1 = vector.subtract(pos, halfsize) local pos2 = vector.add (pos, halfsize) minetest.delete_area(pos1, pos2) ``` This will delete all map blocks in that area, *inclusive*. This means that some nodes will be deleted outside the area as they will be on a mapblock which overlaps the area bounds.