234 lines
8.6 KiB
Markdown
234 lines
8.6 KiB
Markdown
---
|
|
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 <!-- omit in toc -->
|
|
|
|
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.
|