Compare commits
104 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
44efd61418 | ||
|
4d3a39f6e4 | ||
|
8bc1ea85cc | ||
|
15d02fb757 | ||
|
6d4201300c | ||
|
f0fa254acb | ||
|
f749e15834 | ||
|
11b543c5bc | ||
|
d584267768 | ||
|
3fb9ef51c0 | ||
|
3c947c771e | ||
|
13eab9451c | ||
|
5923659439 | ||
|
10b29f586d | ||
|
7bc375a1ba | ||
|
3c0c956278 | ||
|
76fc5e98b8 | ||
|
e53b0c5f57 | ||
|
eb3c4076e1 | ||
|
77e881533e | ||
|
4a2e607b86 | ||
|
fea9965901 | ||
|
65188ee7bd | ||
|
1cd8463256 | ||
|
b3d2b64295 | ||
|
178e5046ec | ||
|
109aa282c8 | ||
|
91567c95e6 | ||
|
7292b14a63 | ||
|
7ca91f3c0b | ||
|
8fb0813f5f | ||
|
b66cca5574 | ||
|
59ca8e3e89 | ||
|
6760145412 | ||
|
f11bcafd86 | ||
|
c32f3d7f5d | ||
|
69308b5b72 | ||
|
90123c3a92 | ||
|
d49a20fa94 | ||
|
d91c3478b8 | ||
|
3b7e8b2a03 | ||
|
ad543a6074 | ||
|
f22ff15f91 | ||
|
a21769811d | ||
|
acf02b57e3 | ||
|
d8e0380f4d | ||
|
d3be4a7ca9 | ||
|
6057f8b331 | ||
|
9697e56b0e | ||
|
912c615016 | ||
|
d52ac03d5d | ||
|
06b5765234 | ||
|
6fbcef5b06 | ||
|
6667fa8c89 | ||
|
498669e55a | ||
|
a994ffc078 | ||
|
89dd970f19 | ||
|
75ddb0097f | ||
|
3f195de1bf | ||
|
c1299491f9 | ||
|
e2f2c32d1c | ||
|
a51ef8cb98 | ||
|
42ee2a3ad8 | ||
|
b70cc8329b | ||
|
dcdf4c3520 | ||
|
a3eb294b25 | ||
|
2b949d68f7 | ||
|
80319c51e5 | ||
|
cb1abbb7b4 | ||
|
797314ac03 | ||
|
014188e0ae | ||
|
02ab233deb | ||
|
670ab06a97 | ||
|
d7b830cc8c | ||
|
a77c588056 | ||
|
01f51293b4 | ||
|
b03eef9975 | ||
|
63b316e81c | ||
|
7016856ade | ||
|
6b593aba7a | ||
|
3fceb16011 | ||
|
ed62801f72 | ||
|
98e4b086b8 | ||
|
2c0083caa2 | ||
|
27c25e5dae | ||
|
dced156a02 | ||
|
db22b08d25 | ||
|
17299b1485 | ||
|
711a717de9 | ||
|
c5c2b37895 | ||
|
17e8d9d6d3 | ||
|
c917af7d7d | ||
|
535a6a301c | ||
|
e6c71abfbd | ||
|
23abf10cc9 | ||
|
709caa181a | ||
|
154b7c9b6b | ||
|
18a16f4a14 | ||
|
077b25da0d | ||
|
73b5a7a594 | ||
|
3974e9c7f6 | ||
|
995f048576 | ||
|
7d801918fa | ||
|
a8ac722de6 |
@ -10,6 +10,8 @@ plugins:
|
||||
- jekyll-redirect-from
|
||||
|
||||
collections:
|
||||
de:
|
||||
output: true
|
||||
en:
|
||||
output: true
|
||||
it:
|
||||
|
@ -4,6 +4,10 @@
|
||||
name: English (UK)
|
||||
cta: This book is available in English
|
||||
|
||||
- code: de
|
||||
name: Deutsch
|
||||
cta: Das Buch ist in Deutsch verfügbar
|
||||
|
||||
- code: it
|
||||
name: Italiano
|
||||
cta: Questo libro è disponibile in italiano
|
||||
|
204
_de/advmap/biomesdeco.md
Normal file
204
_de/advmap/biomesdeco.md
Normal file
@ -0,0 +1,204 @@
|
||||
---
|
||||
title: Biome und Dekorationen
|
||||
author: Shara
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 6.1
|
||||
description: Biome und Dekorationen erstellen lernen, um die Welt anzupassen.
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Die Möglichkeit, Biome und Dekorationen zu registrieren, ist von entscheidender Bedeutung, wenn es darum geht, eine
|
||||
interessante und abwechslungsreiche Umgebung im Spiel zu schaffen. In diesem Kapitel lernen Sie, Biome zu registrieren, die Verteilung der Biome zu steuern und Dekorationen in Biomen zu platzieren.
|
||||
|
||||
- [Was sind Biome?](#was-sind-biome)
|
||||
- [Biom-Platzierung](#biom-platzierung)
|
||||
- [Hitze und Feuchtigkeit](#hitze-und-feuchtigkeit)
|
||||
- [Visualisierung von Grenzen mithilfe von Voronoi-Diagrammen](#visualisierung-von-grenzen-mithilfe-von-voronoi-diagrammen)
|
||||
- [Erstellen eines Voronoi-Diagramms mithilfe von Geogebra](#erstellen-eines-voronoi-diagramms-mithilfe-von-geogebra)
|
||||
- [Ein Biom registrieren](#ein-biom-registrieren)
|
||||
- [Was sind Dekorationen?](#was-sind-dekorationen)
|
||||
- [Registrierung einer einfachen Dekoration](#registrierung-einer-einfachen-dekoration)
|
||||
- [Registrierung einer Schematischen Dekoration](#registrierung-einer-schematischen-dekoration)
|
||||
- [Kartenerstellungs-Aliase (Mapgen Aliases)](#kartenerstellungs-aliase-mapgen-aliases)
|
||||
|
||||
## Was sind Biome?
|
||||
|
||||
Ein Minetest-Biom ist eine bestimmte Umgebung im Spiel. Wenn Sie Biome registrieren, können Sie können Sie die Arten von Nodes bestimmen, die während der Kartenerstellung darin erscheinen. Einige der gebräuchlichsten Nodetypen, die von Biom zu Biom variieren können, sind:
|
||||
|
||||
* Oberer Node: Dies ist der Node, der am häufigsten auf der Oberfläche zu finden ist. Ein bekanntes Beispiel wäre "Dirt with Grass", also "Erde mit Gras" aus dem Minetest-Grundspiel (Minetest Game).
|
||||
* Füllender Node: Dies ist die Schicht unmittelbar unter dem obersten Node. In Biomen mit Gras ist dies oft Erde.
|
||||
* Steinnode: Dies ist der Node, den man am häufigsten unter der Erde sieht.
|
||||
* Wassernode: Dies ist normalerweise eine Flüssigkeit. Der Node erscheint dort,
|
||||
wo man Wasserflächen erwarten würde.
|
||||
|
||||
Andere Arten von Nodes können auch zwischen den Biomen variieren und bieten die Möglichkeit, innerhalb desselben Spiels sehr unterschiedliche Umgebungen zu schaffen.
|
||||
|
||||
## Biom-Platzierung
|
||||
|
||||
### Hitze und Feuchtigkeit
|
||||
|
||||
Es reicht nicht aus, ein Biom zu registrieren; Sie müssen auch entscheiden, wo es im Spiel vorkommen kann. Dies geschieht, indem Sie jedem Biom einen Wärme- und einen Feuchtigkeitswert zuweisen.
|
||||
|
||||
Sie sollten sich diese Werte gut überlegen, denn diese Werte bestimmen, welche Biome nebeneinander liegen können. Schlechte Entscheidungen könnten dazu führen, dass zum Beispiel eine heiße Wüste an einen Gletscher grenzt, und andere unwahrscheinliche
|
||||
Kombinationen, die Sie vielleicht lieber vermeiden möchten.
|
||||
|
||||
Im Spiel liegen die Wärme- und Feuchtigkeitswerte an jedem Punkt der Karte normalerweise zwischen 0 und 100. Die Werte ändern sich allmählich und steigen oder sinken, wenn Sie sich auf der Karte bewegen. Das Biom an einem bestimmten Punkt wird dadurch bestimmt, welches der registrierten Biome die Wärme- und Luftfeuchtigkeitswerte aufweisen, die den Werten an dieser Stelle der Karte am nächsten kommen.
|
||||
|
||||
Da sich die Wärme- und Luftfeuchtigkeitswerte allmählich ändern, ist es ratsam, den Biomen Wärme- und Luftfeuchtigkeitswerte auf der Grundlage vernünftiger Erwartungen über die Umgebung des Bioms zuzuordnen. Zum Beispiel:
|
||||
|
||||
* Eine Wüste könnte eine hohe Hitze und eine niedrige Luftfeuchtigkeit aufweisen.
|
||||
* Ein verschneiter Wald könnte eine geringe Wärme und eine mittlere Luftfeuchtigkeit aufweisen.
|
||||
* Ein Sumpfbiom würde im Allgemeinen eine hohe Luftfeuchtigkeit aufweisen.
|
||||
|
||||
In der Praxis bedeutet dies, dass die aneinander angrenzenden Biome eine logische Abfolge bilden, solange Sie eine Vielzahl von Biomen haben.
|
||||
|
||||
### Visualisierung von Grenzen mithilfe von Voronoi-Diagrammen
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}/static/biomes_voronoi.png" alt="Voronoi-Diagramm">
|
||||
<figcaption>
|
||||
Voronoi-Diagramm, das den nächstgelegenen Punkt zeigt.
|
||||
<span class="credit">Von <a href="https://en.wikipedia.org/wiki/Voronoi_diagram#/media/File:Euclidean_Voronoi_diagram.svg">Balu Ertl</a>, CC BY-SA 4.0.</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Die Feinabstimmung von Wärme- und Feuchtigkeitswerten für Biome
|
||||
ist einfacher, wenn Sie sich die Beziehung zwischen den von Ihnen verwendeten Biomen gut vorstellen können.
|
||||
Dies ist besonders wichtig, wenn Sie einen vollständigen Satz Ihrer eigenen Biome erstellen, aber
|
||||
kann auch hilfreich sein, wenn Sie ein Biom zu einem bestehenden Satz hinzufügen.
|
||||
|
||||
Am einfachsten lässt sich veranschaulichen, welche Biome möglicherweise gemeinsame Grenzen haben, indem Sie ein
|
||||
Voronoi-Diagramm erstellen, das zeigt, welchem Punkt in einem 2-dimensionalen
|
||||
Diagramm einer bestimmte Position am nächsten liegt.
|
||||
|
||||
Ein Voronoi-Diagramm kann aufzeigen, wo Biome, die aneinander grenzen sollten, dies nicht tun,
|
||||
und wo Biome, die nicht aneinander grenzen sollten, dies tun. Es kann auch einen Einblick in die Häufigkeit der Biome im Spiel geben, wobei größere und zentralere Biome häufiger vorkommen als kleinere Biome oder Biome, die
|
||||
am äußeren Rand des Diagramms liegen.
|
||||
|
||||
Dazu wird für jedes Biom ein Punkt auf der Grundlage von Wärme- und Feuchtigkeitswerten markiert,
|
||||
wobei die x-Achse für die Wärme und die y-Achse für die Feuchtigkeit steht. Das Diagramm wird dann
|
||||
in Bereiche unterteilt, so dass jede Position in einem bestimmten Bereich näher an dem
|
||||
Punkt innerhalb dieses Bereichs näher ist als zu jedem anderen Punkt des Diagramms.
|
||||
|
||||
Jedes Gebiet stellt ein Biom dar. Wenn zwei Bereiche eine gemeinsame Grenze haben, können die Biome im Spiel nebeneinander liegen. Die Länge der Grenze zwischen zwei Gebieten im Vergleich zu der Länge, die sie mit anderen Gebieten teilen, zeigt an, wie häufig zwei Biome nebeneinander zu finden sein werden.
|
||||
|
||||
### Erstellen eines Voronoi-Diagramms mithilfe von Geogebra
|
||||
|
||||
Sie können Voronoi-Diagramme nicht nur von Hand zeichnen, sondern auch mit
|
||||
Programmen wie [Geogebra](https://www.geogebra.org) erstellen.
|
||||
|
||||
1. Erstellen Sie Punkte, indem Sie das Punktwerkzeug in der Symbolleiste auswählen (Symbol ist ein Punkt mit 'A'),
|
||||
und dann auf das Diagramm klicken. Sie können Punkte verschieben oder ihre Position explizit in der linken Seitenleiste festlegen. Zur besseren Übersichtlichkeit sollten Sie außerdem jedem Punkt eine Bezeichnung geben.
|
||||
|
||||
2. Erstellen Sie nun das Voronoi-Diagramm, indem Sie die folgende Funktion in das
|
||||
Eingabefeld in der linken Seitenleiste eingeben:
|
||||
|
||||
```cpp
|
||||
Voronoi({ A, B, C, D, E })
|
||||
```
|
||||
|
||||
3. Fertig! Sie sollten nun ein Voronoi-Diagramm mit allen verschiebbaren Punkten haben.
|
||||
|
||||
|
||||
## Ein Biom registrieren
|
||||
|
||||
Mit dem folgenden Code wird ein einfaches Biom mit dem Namen "grasslands" (Grasland-Biom) registriert:
|
||||
|
||||
```lua
|
||||
minetest.register_biome({
|
||||
name = "grasslands",
|
||||
node_top = "default:dirt_with_grass",
|
||||
depth_top = 1,
|
||||
node_filler = "default:dirt",
|
||||
depth_filler = 3,
|
||||
y_max = 1000,
|
||||
y_min = -3,
|
||||
heat_point = 50,
|
||||
humidity_point = 50,
|
||||
})
|
||||
```
|
||||
|
||||
Dieses Biom hat eine Schicht aus Erde mit GrasNode an der Oberfläche und drei Schichten von Erde-Node darunter. Es wird kein SteinNode angegeben, so dass der Node, der in der Kartenerstellung-Alias-Registrierung (mapgen alias registration) für "mapgen_stone" definiert ist, unter der Erde vorhanden sein wird.
|
||||
|
||||
Es gibt viele Optionen bei der Registrierung eines Bioms. Diese sind dokumentiert in der [Minetest Lua API Referenz 🇬🇧](https://minetest.gitlab.io/minetest/definition-tables/#biome-definition).
|
||||
|
||||
Sie müssen nicht jede Option für jedes Biom, das Sie erstellen, definieren, aber in einigen Fällen kann das Auslassen einer bestimmte Option oder einem geeigneten Kartenerstellungs-Alias (mapgen alias) zu Fehlern bei der Kartenerstellung führen.
|
||||
|
||||
## Was sind Dekorationen?
|
||||
|
||||
Dekorationen sind entweder Nodes oder Schematics, die bei der Kartenerstellung (mapgen) auf der Karte platziert werden können.
|
||||
Einige gängige Beispiele sind Blumen, Sträucher und Bäume. Andere, kreativere Verwendungen sind z. B. hängende Eiszapfen oder Stalagmiten in Höhlen, unterirdische Kristallgebilde oder sogar die Platzierung von kleinen Gebäuden.
|
||||
|
||||
Dekorationen können auf bestimmte Biome, auf die Höhe oder auf die Nodepunkte beschränkt werden. Sie werden oft verwendet, um die Umgebung eines Bioms zu entwickeln,indem sie dafür sorgen, dass es bestimmte Pflanzen, Bäume oder andere Merkmale hat.
|
||||
|
||||
## Registrierung einer einfachen Dekoration
|
||||
|
||||
Einfache Dekorationen werden verwendet, um während der Kartenerstellung einzelne Nodedekorationen auf der Karte zu platzieren.
|
||||
Sie müssen den Node angeben, der als Dekoration platziert werden soll, Angaben dazu, wo er platziert werden kann und wie häufig er vorkommt.
|
||||
|
||||
Zum Beispiel:
|
||||
|
||||
```lua
|
||||
minetest.register_decoration({
|
||||
deco_type = "simple",
|
||||
place_on = {"base:dirt_with_grass"},
|
||||
sidelen = 16,
|
||||
fill_ratio = 0.1,
|
||||
biomes = {"grassy_plains"},
|
||||
y_max = 200,
|
||||
y_min = 1,
|
||||
decoration = "plants:grass",
|
||||
})
|
||||
```
|
||||
|
||||
In diesem Beispiel wird der Node mit dem Namen `plants:grass` im Biom mit dem Namen grassy_plains auf den Node `base:dirt_with_grass` platziert, zwischen den Höhen `y = 1` und `y = 200`.
|
||||
|
||||
Der Wert fill_ratio bestimmt, wie häufig die Dekoration erscheint, wobei höhere Werte bis 1 führen dazu, dass eine große Anzahl von Dekorationen platziert wird. Es ist möglich, stattdessen Noise-Parameter zu verwenden, um die Platzierung zu bestimmen.
|
||||
|
||||
## Registrierung einer Schematischen Dekoration
|
||||
|
||||
Schematic Dekorationen sind der einfachen Dekoration sehr ähnlich, beinhalten aber die Platzierung eines Schematics statt der Platzierung eines einzelnen Nodes, zum Beispiel:
|
||||
|
||||
```lua
|
||||
minetest.register_decoration({
|
||||
deco_type = "schematic",
|
||||
place_on = {"base:desert_sand"},
|
||||
sidelen = 16,
|
||||
fill_ratio = 0.0001,
|
||||
biomes = {"desert"},
|
||||
y_max = 200,
|
||||
y_min = 1,
|
||||
schematic = minetest.get_modpath("plants") .. "/schematics/cactus.mts",
|
||||
flags = "place_center_x, place_center_z",
|
||||
rotation = "random",
|
||||
})
|
||||
```
|
||||
|
||||
In diesem Beispiel wird das Schematic cactus.mts in Wüstenbiomen platziert. Sie müssen einen Pfad zu einem Schematic angeben, das in diesem Fall in einem speziellen Schematic-Verzeichnis innerhalb des Mods gespeichert ist.
|
||||
|
||||
In diesem Beispiel werden auch flags gesetzt, um die Platzierung des Schematics zu zentrieren, und die Rotation
|
||||
ist auf zufällig gesetzt. Die zufällige Drehung von Schemen, wenn sie als Dekoration platziert werden
|
||||
sorgt für mehr Abwechslung, wenn asymmetrische Schemen verwendet werden.
|
||||
|
||||
|
||||
## Kartenerstellungs-Aliase (Mapgen Aliases)
|
||||
|
||||
Vorhandene Spiele sollten bereits geeignete Kartenerstellungs-Aliase enthalten, so dass Sie nur die Registrierung eigener Kartenerstellungs-Aliase in Betracht ziehen, wenn Sie Ihr eigenes Spiel entwickeln.
|
||||
|
||||
Mapgen-Aliase liefern Informationen für das Kern-Mapgen und können so registriert werden:
|
||||
|
||||
```lua
|
||||
minetest.register_alias("mapgen_stone", "base:smoke_stone")
|
||||
```
|
||||
|
||||
Zumindest sollten Sie registrieren:
|
||||
|
||||
* mapgen_stone
|
||||
* mapgen_water_source
|
||||
* mapgen_river_water_source
|
||||
|
||||
Wenn Sie nicht für alle Biome HöhlenflüssigkeitsNode definieren, sollten Sie diese trotzdem ebenfalls registrieren:
|
||||
|
||||
* mapgen_lava_source
|
174
_de/advmap/lvm.md
Normal file
174
_de/advmap/lvm.md
Normal file
@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Lua Voxel Manipulator
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 6.2
|
||||
description: Erfahren Sie, wie Sie LVMs nutzen können, um Map-Operationen zu beschleunigen.
|
||||
redirect_from:
|
||||
- /de/chapters/lvm.html
|
||||
- /de/map/lvm.html
|
||||
mapgen_object:
|
||||
level: warning
|
||||
title: LVMs und Mapgen
|
||||
message: Verwenden Sie nicht `minetest.get_voxel_manip()` mit mapgen, da dies zu Störungen führen kann.
|
||||
Verwenden Sie stattdessen `minetest.get_mapgen_object("voxelmanip")`.
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Die im Kapitel [Grundlegende Kartenoperationen](environment.html) beschriebenen Funktionen sind bequem und einfach zu benutzen, aber für große Gebiete sind sie ineffizient. Jedes Mal, wenn Sie `set_node` oder `get_node` aufrufen, muss Ihr Mod mit der Engine kommunizieren. Dies führt zu ständigen einzelnen Kopiervorgängen zwischen der
|
||||
Engine und Ihrem Mod, was langsam ist und die Leistung des Spiels schnell verringert. Die Verwendung eines Lua Voxel Manipulators (LVM) kann eine gute Alternative sein.
|
||||
|
||||
- [Konzepte](#konzepte)
|
||||
- [Einlesen in den LVM](#einlesen-in-den-lvm)
|
||||
- [Nodes lesen](#nodes-lesen)
|
||||
- [Nodes schreiben](#nodes-schreiben)
|
||||
- [Beispiel](#beispiel)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
## Konzepte
|
||||
|
||||
Ein LVM ermöglicht es Ihnen, große Bereiche der Karte in den Speicher Ihres Mods zu laden. Sie können diese Daten dann ohne weitere Interaktion mit der Engine und ohne die Ausführung von Callbacks lesen und schreiben, was bedeutet, dass diese Operationen sehr schnell sind. Anschließend können Sie den Bereich wieder in die Engine zurückschreiben und Beleuchtungsberechnungen durchführen.
|
||||
|
||||
## Einlesen in den LVM
|
||||
|
||||
Sie können nur einen kubischen Bereich in einen LVM laden, also müssen Sie die minimalen und maximalen Positionen ausarbeiten, die Sie ändern möchten. Dann können Sie einen LVM erstellen und einlesen LVM einlesen. Zum Beispiel:
|
||||
|
||||
```lua
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
```
|
||||
|
||||
Aus Leistungsgründen wird ein LVM fast nie genau den Bereich lesen, den Sie ihm vorgeben. Stattdessen wird er wahrscheinlich einen größeren Bereich lesen. Der größere Bereich wird durch `emin` und `emax` angegeben, die für *emerged min pos* und *emerged max pos* stehen, auf Deutch: *Ermittelte Minimum-Position* bzw. *Ermittelte Maximum-Position*. Ein LVM lädt den Bereich, den er enthält, für Sie - sei es durch Laden aus dem Speicher, von der Festplatte oder Aufruf des Map-Generators.
|
||||
|
||||
{% include notice.html notice=page.mapgen_object %}
|
||||
|
||||
## Nodes lesen
|
||||
|
||||
Um die Typen von Node an bestimmten Positionen zu lesen, müssen Sie `get_data()` verwenden. Dies gibt ein flaches Array zurück, in dem jeder Eintrag den Typ eines bestimmten Nodes darstellt.
|
||||
|
||||
```lua
|
||||
local data = vm:get_data()
|
||||
```
|
||||
|
||||
Sie können param2 und Beleuchtungsdaten mit den Methoden `get_param2_data()` und `get_light_data()` erhalten.
|
||||
|
||||
Sie müssen `emin` und `emax` verwenden, um herauszufinden, wo ein Node in den flachen Arrays ist, die durch die oben genannten Methoden gegeben sind. Es gibt eine Hilfsklasse namens `VoxelArea`, die die die die Berechnung für Sie übernimmt.
|
||||
|
||||
```lua
|
||||
local a = VoxelArea:new{
|
||||
MinEdge = emin,
|
||||
MaxEdge = emax
|
||||
}
|
||||
|
||||
-- Index des Nodees abrufen
|
||||
local idx = a:index(x, y, z)
|
||||
|
||||
-- Node lesen
|
||||
print(data[idx])
|
||||
```
|
||||
|
||||
Wenn Sie den obigen Code ausführen, werden Sie feststellen, dass `data[vi]` eine Ganzzahl ist. Das ist so, weil die Engine aus Leistungsgründen keine Nodes in Form von Zeichenketten speichert. Stattdessen verwendet die Engine eine ganze Zahl, die sogenannte Inhalts-ID. Sie können die Inhalts-ID für einen bestimmten Nodetyp herausfinden mit `get_content_id()` herausfinden. Zum Beispiel:
|
||||
|
||||
```lua
|
||||
local c_stone = minetest.get_content_id("default:stone")
|
||||
```
|
||||
|
||||
Sie können dann überprüfen, ob der Node zum Beispiel Stein ist:
|
||||
|
||||
```lua
|
||||
local idx = a:index(x, y, z)
|
||||
if data[idx] == c_stone then
|
||||
print("ist Stein!")
|
||||
end
|
||||
```
|
||||
|
||||
Die Inhalts-IDs eines Nodetyps können sich während der Ladezeit ändern, daher wird es nicht empfohlen, diese während dieser Zeit abzurufen.
|
||||
|
||||
Node in einem LVM-Datenarray werden in umgekehrter Koordinatenreihenfolge gespeichert. Deshalb sollten Sie immer in der Reihenfolge `z, y, x` *iterieren* (umdrehen). Zum Beispiel:
|
||||
|
||||
```lua
|
||||
for z = min.z, max.z do
|
||||
for y = min.y, max.y do
|
||||
for x = min.x, max.x do
|
||||
-- vi, Voxel-Index, ist hier ein gängiger Variablenname
|
||||
local vi = a:index(x, y, z)
|
||||
if data[vi] == c_stone then
|
||||
print("ist Stein!")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Der Grund dafür liegt im Bereich der Computerarchitektur. Das Lesen aus dem RAM ist ziemlich kostspielig, daher verfügen CPUs über mehrere Caching-Ebenen. Wenn die Daten, die ein Prozess anfordert,
|
||||
im Cache sind, kann er sie sehr schnell abrufen. Wenn sich die Daten nicht im Cache befinden, kommt es zu einem Cache-Miss und der Prozess holt sich die benötigten Daten aus dem RAM. Alles,
|
||||
was die angeforderten Daten umgibt, wird ebenfalls geholt und ersetzt dann die Daten im Cache. Dies geschieht, weil es sehr wahrscheinlich ist, dass der Prozess erneut Daten in der Nähe dieser Stelle anfordert. Das bedeutet, dass es eine gute Optimierungsregel ist, so zu iterieren, dass die Daten nacheinander gelesen werden und *Chache Trashing* zu vermeiden.
|
||||
|
||||
## Nodes schreiben
|
||||
|
||||
Zunächst müssen Sie eine neue Inhalts-ID im Datenfeld festlegen:
|
||||
|
||||
```lua
|
||||
for z = min.z, max.z do
|
||||
for y = min.y, max.y do
|
||||
for x = min.x, max.x do
|
||||
local vi = a:index(x, y, z)
|
||||
if data[vi] == c_stone then
|
||||
data[vi] = c_air
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Wenn Sie die Einstellung der Node im LVM abgeschlossen haben, müssen Sie das Daten Array in die Engine hochladen:
|
||||
|
||||
```lua
|
||||
vm:set_data(data)
|
||||
vm:write_to_map(true)
|
||||
```
|
||||
|
||||
Zum Setzen von Beleuchtungs- und Param2-Daten verwenden Sie, wie schon erwähnt, die entsprechend benannten Methoden `set_light_data()` und `set_param2_data()`.
|
||||
|
||||
Die Methode `write_to_map()` nimmt einen booleschen Wert an, der `true` ist, wenn die Beleuchtung berechnet werden soll. Wenn Sie `false` übergeben, müssen Sie die Beleuchtung zu einem späteren Zeitpunkt mit `minetest.fix_light` neu berechnen lassen.
|
||||
|
||||
## Beispiel
|
||||
|
||||
```lua
|
||||
local function grass_to_dirt(pos1, pos2)
|
||||
local c_dirt = minetest.get_content_id("default:dirt")
|
||||
local c_grass = minetest.get_content_id("default:dirt_with_grass")
|
||||
|
||||
-- Daten in LVM einlesen
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
local a = VoxelArea:new{
|
||||
MinEdge = emin,
|
||||
MaxEdge = emax
|
||||
}
|
||||
local data = vm:get_data()
|
||||
|
||||
-- Daten ändern
|
||||
for z = pos1.z, pos2.z do
|
||||
for y = pos1.y, pos2.y do
|
||||
for x = pos1.x, pos2.x do
|
||||
local vi = a:index(x, y, z)
|
||||
if data[vi] == c_grass then
|
||||
data[vi] = c_dirt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Daten schreiben
|
||||
vm:set_data(data)
|
||||
vm:write_to_map(true)
|
||||
end
|
||||
```
|
||||
|
||||
## Sie sind dran
|
||||
|
||||
* Erstellen Sie `replace_in_area(from, to, pos1, pos2)`, das alle Instanzen von `von` durch `bis` in dem angegebenen Bereich ersetzt, wobei `von` und `bis` Nodenamen sind.
|
||||
* Programmieren Sie eine Funktion, die alle BrustNode um 90° dreht.
|
||||
* Erstellen Sie eine Funktion, die einen LVM benutzt, um zu bewirken, dass sich moosiges Kopfsteinpflaster auf nahegelegene Stein- und PflastersteinNode ausbreitet. Verursacht Ihre Implementierung, dass sich das Moospflaster auf mehr als einen Node ausbreitet? Wenn ja, wie können Sie dies verhindern?
|
148
_de/basics/getting_started.md
Normal file
148
_de/basics/getting_started.md
Normal file
@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Erste Schritte
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 1.1
|
||||
description: Lerne wie man einen Mod-Ordner mit init.lua, mod.conf und mehr anlegt.
|
||||
redirect_from:
|
||||
- /de/chapters/folders.html
|
||||
- /de/basics/folders.html
|
||||
---
|
||||
|
||||
## Einführung <!-- omit in toc -->
|
||||
|
||||
Es ist wesentlich, den Aufbau der grundlegenden Strukturen des Mod-Verzeichnisses zu verstehen, wenn man Mods erstellt.
|
||||
|
||||
- [Was sind Spiele und Mods?](#was-sind-spiele-und-mods)
|
||||
- [Wo werden die Mods gespeichert?](#wo-werden-die-mods-gespeichert)
|
||||
- [Ihren ersten Mod erstellen](#ihren-ersten-mod-erstellen)
|
||||
- [Mod-Verzeichnis](#mod-verzeichnis)
|
||||
- [mod.conf](#modconf)
|
||||
- [init.lua](#initlua)
|
||||
- [Zusammenfassung](#zusammenfassung)
|
||||
- [Abhängigkeiten](#abhängigkeiten)
|
||||
- [Mod-Pakete (Modpacks)](#mod-pakete-modpacks)
|
||||
|
||||
|
||||
## Was sind Spiele und Mods?
|
||||
|
||||
Die Stärke von Minetest ist die Fähigkeit, Spiele zu erstellen, ohne eigene Voxel-Grafik, Voxel Algorithmen und raffinierten Netzwerk-Code erstellen zu müssen.
|
||||
|
||||
In Minetest ist ein Spiel eine Sammlung von Modulen, welche miteinander arbeiten, um den Inhalt und das Verhalten des Spiels zur Verfügung zu stellen.
|
||||
Ein Modul, allgemein als Mod bezeichnet, ist eine Sammlung von Skripten und Ressourcen.
|
||||
Es ist möglich, ein Spiel mit nur einem Mod zu erstellen, aber das wird nur selten gemacht, weil es sonst nicht mehr so einfach ist, Teile des Spieles unabhängig von den Anderen anzupassen oder zu ersetzen.
|
||||
|
||||
Ebenfalls ist es möglich, Mods außerhalb eines Spieles zu verbreiten. In diesem Fall sind sie ebenfalls *Mods*, aber in einem traditionellerem Sinn: *Modifikationen*. Diese Mods verändern oder erweitern die Eigenschaften eines Spiels.
|
||||
|
||||
Sowohl die Mods die im Spiel enthalten sind, als auch die Mods von Dritten nutzen die selbe API (Programmierschnittstelle).
|
||||
|
||||
|
||||
## Wo werden die Mods gespeichert?
|
||||
<a name="mod-locations"></a>
|
||||
|
||||
Jede Mod hat ihr eigenes Verzeichnis, wo sich ihre Lua-Quelltexte, Texturen, Modelle und Tondateien befinden. Minetest überprüft verschiedene Orte auf Mods. Diese Orte werden allgemein *Mod-Lade-Verzeichnisse* genannt.
|
||||
|
||||
Für eine bestimmte Welt/ein bestimmtes Spiel werden drei Mod-Speicherorte überprüft. Diese sind, in dieser Reihenfolge:
|
||||
|
||||
1. Spiel-Mods. Dies sind die Mods, die das Spiel (die Welt) bilden.
|
||||
z.B.: `minetest/games/minetest_game/mods/`, `/usr/share/minetest/games/minetest/`
|
||||
2. Globale Mods. Der Ort, an dem Mods fast immer installiert werden.
|
||||
Im Zweifelsfall legen Sie Mods hier ab.
|
||||
z.B.: `minetest/mods/`
|
||||
3. Welt-Mods. Der Ort, an dem Mods gespeichert werden, die für eine bestimmte bestimmte Welt sind.
|
||||
z.B.: `minetest/worlds/world/worldmods/`
|
||||
|
||||
`minetest` ist das Verzeichnis für die Benutzerdaten. Sie können den Ort des Benutzerdaten-Verzeichnis finden, indem Sie Minetest öffnen und auf "Benutzerdatenverzeichnis öffnen" in der Registerkarte "Über" klicken.
|
||||
Falls Minetest mithilfe von Flathub installiert wurde, kann es sein, dass bei einem Klick nichts passiert. Normalerweise ist das `minetest`-Verzeichnis dann in `/home/USER/.var/app/net.minetest.Minetest/.minetest/`
|
||||
|
||||
Beim Laden von Mods prüft Minetest alle oben genannten Verzeichnisse der Reihe nach. Wenn es auf einen Mod stößt, der denselben Namen trägt wie ein zuvor gefundener, wird der spätere Mod anstelle des früheren Mods geladen. Das bedeutet, dass Sie die Spielmods überschreiben, indem Sie einen Mod mit demselben Namen in den globalen Mod-Speicherort einfügen.
|
||||
|
||||
|
||||
## Ihren ersten Mod erstellen
|
||||
|
||||
### Mod-Verzeichnis
|
||||
|
||||
Gehen Sie in das globale Mods-Verzeichnis (Über > Benutzerdatenverzeichnis öffnen > mods) und
|
||||
erstellen Sie einen neuen Ordner namens `mymod`. mymod ist der Name des Mods.
|
||||
|
||||
Jeder Mod sollte einen eindeutigen *Mod-Namen* haben, eine technische Kennung (id), die auf den Mod verweist. Mod-Namen können Buchstaben, Zahlen und Unterstriche enthalten. Ein guter Name sollte beschreiben, was die Mod tut. Das Verzeichnis, das die Komponenten einer Mod enthält, muss denselben Namen wie den Mod-Namen haben. Um herauszufinden, ob ein Mod-Name verfügbar ist, suchen Sie ihn auf
|
||||
[content.minetest.net](https://content.minetest.net).
|
||||
|
||||
mymod
|
||||
├── textures
|
||||
│ └── mymod_node.png Dateien
|
||||
├── init.lua
|
||||
└── mod.conf
|
||||
|
||||
Mods benötigen nur eine init.lua-Datei. Es wird jedoch empfohlen, die Datei mod.conf zu verwenden und je nach Funktionsumfang können weitere Komponenten erforderlich sein, abhängig von der Funktionalität der Mod.
|
||||
|
||||
### mod.conf
|
||||
|
||||
Erstellen Sie eine `mod.conf`-Datei mit folgendem Inhalt:
|
||||
|
||||
```
|
||||
name = mymod
|
||||
description = Fügt foo, bar und bo hinzu.
|
||||
depends = default
|
||||
```
|
||||
|
||||
Diese Datei wird für Mod-Metadaten verwendet, darunter der Name der Mod, die Beschreibung und andere
|
||||
Informationen.
|
||||
|
||||
### init.lua
|
||||
|
||||
Erstellen Sie eine `init.lua`-Datei mit folgendem Inhalt:
|
||||
|
||||
```lua
|
||||
print("Diese Datei wird zum Zeitpunkt des Ladens ausgeführt!")
|
||||
|
||||
minetest.register_node("mymod:node", {
|
||||
description = "Das ist ein Node",
|
||||
tiles = {"mymod_node.png"},
|
||||
groups = {cracky = 1}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mymod:node 3",
|
||||
recipe = { "default:dirt", "default:stone" },
|
||||
})
|
||||
```
|
||||
|
||||
Die Datei init.lua ist der Einstiegspunkt für einen Mod und wird ausgeführt, wenn der Mod geladen wird.
|
||||
|
||||
### Zusammenfassung
|
||||
|
||||
|
||||
Diese Mod hat den Namen "mymod". Er hat zwei Textdateien: init.lua und mod.conf. Das Skript gibt eine Nachricht aus und registriert dann einen Node und ein Handwerksrezept - diese
|
||||
werden später erklärt. Es gibt eine einzige Abhängigkeit, die
|
||||
[default mod](https://content.minetest.net/metapackages/default/), die
|
||||
normalerweise im Minetest-Grundspiel (Minetest Game) zu finden ist. Außerdem gibt es eine Textur in textures/ für den Node.
|
||||
|
||||
|
||||
## Abhängigkeiten
|
||||
|
||||
Eine Abhängigkeit entsteht, wenn eine Mod eine andere Mod benötigt, der vor ihr geladen werden muss. Ein Mod kann verlangen, dass der Code, die Gegenstände oder andere Ressourcen einer anderen Mod verfügbar sein müssen, damit sie verwendet werden können.
|
||||
|
||||
Es gibt zwei Arten von Abhängigkeiten: feste und optionale Abhängigkeiten. Beide erfordern, dass die Mod zuerst geladen wird. Wenn die Mod, von die die Abhängigkeit besteht, nicht verfügbar ist, führt eine feste Abhängigkeit dazu, dass die Mod nicht geladen wird, während eine optionale Abhängigkeit dazu führen kann, dass weniger Funktionen aktiviert werden.
|
||||
|
||||
Eine optionale Abhängigkeit ist nützlich, wenn Sie optional eine andere Mod unterstützen wollen; diese kann zusätzliche Inhalte aktivieren, wenn der Benutzer beide Mods gleichzeitig nutzen möchte.
|
||||
|
||||
Abhängigkeiten werden in einer kommagetrennten Liste in mod.conf angegeben.
|
||||
|
||||
depends = modeins, modzwei
|
||||
optional_depends = moddrei
|
||||
|
||||
## Mod-Pakete (Modpacks)
|
||||
|
||||
Mods können in Mod-Pakete gruppiert werden, die es ermöglichen, mehrere Mods zu verpacken und zusammen zu verschieben. Sie sind nützlich, wenn Sie einem Spieler mehrere Mods zur Verfügung stellen wollen, aber nicht wollen, dass er sie einzeln herunterladen muss.
|
||||
|
||||
modpack1
|
||||
├── modpack.conf (required) - signalisiert, dass es sich um ein Mod-Paket handelt
|
||||
├── mod1
|
||||
│ └── ... Mod-Dateien
|
||||
└── mymod (optional)
|
||||
└── ... Mod-Dateien
|
||||
|
||||
Bitte beachten Sie, dass ein Modpack kein *Spiel* ist.
|
||||
Spiele haben ihre eigene Organisationsstruktur, die im Kapitel “Spiele” erklärt wird.
|
283
_de/basics/lua.md
Normal file
283
_de/basics/lua.md
Normal file
@ -0,0 +1,283 @@
|
||||
---
|
||||
title: Lua-Skripting
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 1.2
|
||||
description: Eine grundlegende Einführung in Lua, einschließlich eines Leitfadens zum globalen/lokalen Geltungsbereich.
|
||||
redirect_from: /de/chapters/lua.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
In diesem Kapitel geht es um die Skripterstellung in Lua, die dazu benötigten Werkzeuge und einige Techniken, die Sie vielleicht nützlich finden.
|
||||
|
||||
- [Code-Editoren](#code-editoren)
|
||||
- [Programmieren in Lua](#programmieren-in-lua)
|
||||
- [Programmablauf](#programmablauf)
|
||||
- [Typen von Variablen](#typen-von-variablen)
|
||||
- [Arithmetische, also Mathematische, Operatoren](#arithmetische-also-mathematische-operatoren)
|
||||
- [Auswahl](#auswahl)
|
||||
- [Logische Operatoren](#logische-operatoren)
|
||||
- [Programmierung](#programmierung)
|
||||
- [Lokale und globale Reichweite](#lokale-und-globale-reichweite)
|
||||
- [Es sollte so viel wie möglich auf lokale Variablen zurückgegriffen werden.](#es-sollte-so-viel-wie-möglich-auf-lokale-variablen-zurückgegriffen-werden)
|
||||
- [Einbindung anderer Lua-Skripte](#einbindung-anderer-lua-skripte)
|
||||
|
||||
## Code-Editoren
|
||||
|
||||
Für das Schreiben von Skripten in Lua reicht ein Code-Editor mit Code-Hervorhebung aus.
|
||||
Die Codehervorhebung verwendet unterschiedliche Farben für Wörter und Zeichen je nachdem, wofür sie stehen. Auf diese Weise können Sie leicht Fehler und Ungereimtheiten erkennen.
|
||||
|
||||
Zum Beispiel:
|
||||
|
||||
```lua
|
||||
function ctf.post(team,msg)
|
||||
if not ctf.team(team) then
|
||||
return false
|
||||
end
|
||||
if not ctf.team(team).log then
|
||||
ctf.team(team).log = {}
|
||||
end
|
||||
|
||||
table.insert(ctf.team(team).log,1,msg)
|
||||
ctf.save()
|
||||
|
||||
return true
|
||||
end
|
||||
```
|
||||
|
||||
Die Schlüsselwörter in diesem Beispiel sind hervorgehoben, einschließlich `if`, `then`, `end` und `return`. Funktionen, die standardmäßig mit Lua ausgeliefert werden, wie z.B. `table.insert`, sind ebenfalls hervorgehoben.
|
||||
|
||||
Zu den häufig verwendeten Editoren, die sich gut für Lua eignen, gehören:
|
||||
|
||||
* [VSCode 🇬🇧](https://code.visualstudio.com/) - quelloffen (als Code-OSS oder VSCodium), populär, und hat [Plugins für Minetest Modding 🇬🇧](https://marketplace.visualstudio.com/items?itemName=GreenXenith.minetest-tools).
|
||||
* [Notepad++ 🇬🇧](http://notepad-plus-plus.org/) - nur für Windows
|
||||
|
||||
Andere geeignete Editoren sind ebenfalls verfügbar.
|
||||
|
||||
## Programmieren in Lua
|
||||
|
||||
### Programmablauf
|
||||
|
||||
Programme sind eine Reihe von Befehlen, die nacheinander ausgeführt werden. Wir nennen diese Befehle "Anweisungen". Der Programmablauf gibt an, wie diese Anweisungen ausgeführt werden. Verschiedene Arten des Ablaufs ermöglichen es Ihnen, Befehlsreihen zu überspringen.
|
||||
|
||||
Es gibt drei Haupttypen von Abläufen:
|
||||
|
||||
* Sequenz: führt eine Anweisung nach der anderen aus, ohne zu überspringen.
|
||||
* Auswahl: Überspringt Sequenzen in Abhängigkeit von Bedingungen.
|
||||
* Iteration: Wiederholung der gleichen Anweisungen, bis eine Bedingung erfüllt ist.
|
||||
|
||||
Wie sehen also die Anweisungen in Lua aus?
|
||||
|
||||
```lua
|
||||
local a = 2 -- Setzt 'a' auf 2
|
||||
local b = 2 -- Setzt 'b' auf 2
|
||||
local result = a + b -- Setzt 'result' (Deutsch: Ergebnis) auf a + b, was 4 ergibt
|
||||
a = a + 10
|
||||
print("Die Summe aus a und b ist "..result)
|
||||
```
|
||||
|
||||
In diesem Beispiel sind `a`, `b` und `result` *Variablen*. Lokale Variablen werden
|
||||
mit dem Schlüsselwort `local` deklariert und erhalten dann einen Anfangswert. `local`
|
||||
wird später besprochen, denn es ist Teil eines sehr wichtigen Konzepts namens
|
||||
*scope* (Deutsch: Geltungsbereich).
|
||||
|
||||
Das `=`-Zeichen bedeutet *assignment* (Deutsch: Zuweisung), also bedeutet `result = a + b`, dass der Wert von `result` auf den Wert von `a + b` gesetzt wird. Variablennamen können länger als ein Zeichen lang sein, wie bei der Variable `result` zu sehen ist. Es ist auch erwähnenswert, dass, wie wie die meisten Sprachen die Groß- und Kleinschreibung beachtet wird; `A` ist eine andere Variable als `a`.
|
||||
|
||||
|
||||
### Typen von Variablen
|
||||
|
||||
Eine Variable hat nur einen der folgenden Typen und kann nach einer Zuweisung den Typ wechseln. Es ist gute Praxis, sicherzustellen, dass eine Variable immer nur nil oder einen einzigen Nicht-Nil-Typ hat.
|
||||
|
||||
| Typ | Beschreibung | Beispiel |
|
||||
|----------|-----------------------------------------------------------------|----------------|
|
||||
| Nil | Nicht initialisiert. Die Variable ist leer, sie hat keinen Wert | `local A`, `D = nil` |
|
||||
| Number | Eine ganze oder dezimale Zahl. | `local A = 4` |
|
||||
| String | Ein Stück Text. | `local D = "one two three"` |
|
||||
| Boolean | True oder False. (Wahr oder Falsch) | `local is_true = false`, `local E = (1 == 1)` |
|
||||
| Table | Listen. | Unten erklärt. |
|
||||
| Function | Kann ausgeführt werden. Kann Eingaben erfordern und einen Wert zurückgeben. | `local result = func(1, 2, 3)` |
|
||||
|
||||
### Arithmetische, also Mathematische, Operatoren
|
||||
|
||||
Zu den Operatoren in Lua gehören:
|
||||
|
||||
| Symbol | Zweck | Beispiel |
|
||||
|--------|-------------------------------------------|---------------------------|
|
||||
| A + B | Addition | 2 + 2 = 4 |
|
||||
| A - B | Subtraktion | 2 - 10 = -8 |
|
||||
| A * B | Multiplikation | 2 * 2 = 4 |
|
||||
| A / B | Division | 100 / 50 = 2 |
|
||||
| A ^ B | Potenzieren | 2 ^ 2 = 2<sup>2</sup> = 4 |
|
||||
| A .. B | Konkatenation (Zeichenketten verbinden) | "foo" .. "bar" = "foobar" |
|
||||
|
||||
Bitte beachten Sie, dass diese Liste nicht vollständig ist; sie enthält nicht alle möglichen Operatoren.
|
||||
|
||||
### Auswahl
|
||||
|
||||
Die einfachste Methode der Auswahl ist die if-Anweisung. Zum Beispiel:
|
||||
|
||||
```lua
|
||||
local random_number = math.random(1, 100) -- Zwischen 1 und 100.
|
||||
if random_number > 50 then
|
||||
print("Woohoo!")
|
||||
else
|
||||
print("Oh nein!")
|
||||
end
|
||||
```
|
||||
|
||||
Dies erzeugt eine Zufallszahl zwischen 1 und 100. Es gibt dann "Woohoo!" aus, wenn diese Zahl größer als 50 ist, andernfalls wird "Oh nein!" ausgegeben.
|
||||
|
||||
|
||||
### Logische Operatoren
|
||||
|
||||
Zu den logischen Operatoren in Lua gehören:
|
||||
|
||||
| Symbol | Zweck | Beispiel |
|
||||
|---------|-----------------------------------------------------|-------------------------------------------------------------|
|
||||
| A == B | Gleich | 1 == 1 (true), 1 == 2 (false) |
|
||||
| A ~= B | nicht gleich | 1 ~= 1 (false), 1 ~= 2 (true) |
|
||||
| A > B | Größer als | 5 > 2 (true), 1 > 2 (false), 1 > 1 (false) |
|
||||
| A < B | Kleiner als | 1 < 3 (true), 3 < 1 (false), 1 < 1 (false) |
|
||||
| A >= B | Größer als oder gleich | 5 >= 5 (true), 5 >= 3 (true), 5 >= 6 (false) |
|
||||
| A <= B | Kleiner als oder gleich | 3 <= 6 (true), 3 <= 3 (true) |
|
||||
| A and B | Und (beides muss `true`, also wahr sein) | (2 > 1) and (1 == 1) (true), (2 > 3) and (1 == 1) (false) |
|
||||
| A or B | Entweder oder. Eines oder mehrere müssen wahr sein. | (2 > 1) or (1 == 2) (true), (2 > 4) or (1 == 3) (false) |
|
||||
| not A | Nicht `true`, also nicht wahr | not (1 == 2) (true), not (1 == 1) (false) |
|
||||
|
||||
Bitte beachten Sie, dass dies nicht alle möglichen Operatoren enthält.
|
||||
|
||||
Es ist auch möglich, Operatoren zu kombinieren. Zum Beispiel:
|
||||
|
||||
```lua
|
||||
if not A and B then
|
||||
print("Juhu!")
|
||||
end
|
||||
```
|
||||
|
||||
Dies gibt "Juhu!" aus, wenn A `False`, also falsch, und B `True`, also wahr, ist.
|
||||
|
||||
Logische und arithmetische Operatoren funktionieren auf die gleiche Weise; beide akzeptieren Eingaben und geben einen Wert zurück, der gespeichert werden kann. Zum Beispiel:
|
||||
|
||||
```lua
|
||||
local A = 5
|
||||
local ist_gleich = (A == 5)
|
||||
if ist_gleich then
|
||||
print("Ist gleich!")
|
||||
end
|
||||
```
|
||||
|
||||
## Programmierung
|
||||
|
||||
Beim Programmieren geht es darum, ein Problem, z. B. das Sortieren einer Liste
|
||||
und in Schritte umzuwandeln, die ein Computer verstehen kann.
|
||||
|
||||
Ihnen den logischen Prozess des Programmierens beizubringen, würde den Rahmen dieses Buches sprengen. Die folgenden Websites sind jedoch sehr nützlich, um dies zu entwickeln:
|
||||
|
||||
* [Codecademy 🇬🇧](http://www.codecademy.com/) ist eines der besten Quellen, um zu
|
||||
Lernen, wie man Code schreibt. Sie bietet ein interaktives Lernprogramm an.
|
||||
* [Scratch](https://scratch.mit.edu) ist eine gute Quelle, um mit den
|
||||
absoluten Grundlagen zu beginnen und die zum Programmieren erforderlichen Problemlösungstechniken zu erlernen.\\
|
||||
Scratch *wurde konzipiert, um Kindern das Programmieren beizubringen* und ist keine ernsthafte, sondern eine Block basierte Programmiersprache.
|
||||
|
||||
## Lokale und globale Reichweite
|
||||
|
||||
Ob eine Variable lokal oder global ist, bestimmt, wohin sie geschrieben oder gelesen werden kann. Auf eine lokale Variable kann nur von dort aus zugegriffen werden, wo sie definiert ist. Hier sind einige Beispiele:
|
||||
|
||||
```lua
|
||||
-- Von dieser Skriptdatei aus zugänglich
|
||||
local one = 1
|
||||
|
||||
function myfunc()
|
||||
-- Von dieser Funktion aus zugänglich
|
||||
local two = one + one
|
||||
|
||||
if two == one then
|
||||
-- Zugänglich aus dieser if-Anweisung
|
||||
local three = one + two
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Im Gegensatz dazu, kann auf globale Variablen von jeder Stelle in der Skriptdatei aus zugegriffen werden und von jeder anderen Mod aus.
|
||||
|
||||
```lua
|
||||
function one()
|
||||
foo = "bar"
|
||||
end
|
||||
|
||||
function two()
|
||||
print(dump(foo)) -- Gibt "bar" aus
|
||||
end
|
||||
|
||||
one()
|
||||
two()
|
||||
```
|
||||
|
||||
### Es sollte so viel wie möglich auf lokale Variablen zurückgegriffen werden.
|
||||
|
||||
Wann immer möglich, sollten lokale Variablen verwendet werden. Mods sollten nur eine globale variable mit dem gleichen Namen wie die Mod haben. Das Erstellen weiterer globaler Variablen ist unsaubere
|
||||
Kodierung, und Minetest wird davor warnen:
|
||||
|
||||
Assignment to undeclared global 'foo' inside function at init.lua:2
|
||||
|
||||
Auf Deutsch: Zuweisung an eine nicht deklarierte globale Variable 'foo' innerhalb der Funktion in init.lua:2
|
||||
|
||||
Um dies zu korrigieren, verwenden Sie "local":
|
||||
|
||||
```lua
|
||||
function one()
|
||||
local foo = "bar"
|
||||
end
|
||||
|
||||
function two()
|
||||
print(dump(foo)) -- Gibt "nil" aus
|
||||
end
|
||||
|
||||
one()
|
||||
two()
|
||||
```
|
||||
|
||||
Denken Sie daran, dass nil **nicht initialisiert** bedeutet. Die Variable wurde noch nicht mit einem Wert zugewiesen, existiert nicht oder wurde nicht initialisiert (d.h. auf null gesetzt).
|
||||
|
||||
Funktionen sind Variablen eines besonderen Typs sollten aber auch lokal gemacht werden, weil andere Mods Funktionen mit demselben Namen haben könnten.
|
||||
|
||||
```lua
|
||||
local function foo(bar)
|
||||
return bar * 2
|
||||
end
|
||||
```
|
||||
|
||||
Damit Mods Ihre Funktionen aufrufen können, sollten Sie eine Tabelle mit dem gleichen Namen wie die Mod erstellen und darin ihre Funktion einfügen. Diese Tabelle wird oft als "API table" (Deutsch: API-Tabelle) oder Namespace bezeichnet.
|
||||
|
||||
```lua
|
||||
mymod = {}
|
||||
|
||||
function mymod.foo(bar)
|
||||
return "foo" .. bar
|
||||
end
|
||||
|
||||
-- In einer anderen Mod oder einem anderen Skript:
|
||||
mymod.foo("foobar")
|
||||
```
|
||||
|
||||
## Einbindung anderer Lua-Skripte
|
||||
|
||||
Der empfohlene Weg, um andere Lua-Skripte in eine Mod einzubinden, ist die Verwendung von *dofile*.
|
||||
|
||||
```lua
|
||||
dofile(minetest.get_modpath("modname") .. "/script.lua")
|
||||
```
|
||||
|
||||
Ein Skript kann einen Wert zurückgeben, der für die gemeinsame Nutzung privater locals nützlich ist:
|
||||
|
||||
```lua
|
||||
-- script.lua
|
||||
return "Hallo Welt!"
|
||||
|
||||
-- init.lua
|
||||
local ret = dofile(minetest.get_modpath("modname") .. "/script.lua")
|
||||
print(ret) -- Hallo Welt!
|
||||
```
|
||||
|
||||
[In späteren Kapiteln](../quality/clean_arch.html) wird erklärt, wie man den Code einer Mod am besten aufteilt.
|
81
_de/games/games.md
Normal file
81
_de/games/games.md
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Spiele kreieren
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 7.1
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Die Stärke von Minetest ist die Möglichkeit, Spiele einfach zu entwickeln, ohne
|
||||
dass man eigene Voxel-Grafiken, Voxel-Algorithmen oder ausgefallenen
|
||||
Netzwerkcode erstellen muss.
|
||||
|
||||
- [Was ist ein Spiel?](#was-ist-ein-spiel)
|
||||
- [Spiel-Verzeichnis](#spiel-verzeichnis)
|
||||
- [Spielübergreifende Kompatibilität](#spielübergreifende-kompatibilität)
|
||||
- [API-Kompatibilität](#api-kompatibilität)
|
||||
- [Gruppen und Aliase](#gruppen-und-aliase)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
## Was ist ein Spiel?
|
||||
|
||||
Spiele sind eine Sammlung von Mods, die zusammen ein zusammenhängendes Spiel ergeben.
|
||||
Ein gutes Spiel hat zum Beispiel ein einheitliches Grundthema und eine Richtung,
|
||||
es könnte ein klassischer Craft-Miner mit harten Survival-Elementen sein oder
|
||||
ein Weltraum-Simulationsspiel mit einer Steampunk-Ästhetik.
|
||||
|
||||
Spieldesign ist ein komplexes Thema und eigentlich ein ganzes Fachgebiet.
|
||||
Es würde den Rahmen des Buches sprengen, es auch nur kurz zu besprechen.
|
||||
|
||||
## Spiel-Verzeichnis
|
||||
|
||||
Die Struktur und der Ort eines Spiels werden nach der Arbeit mit Mods ziemlich
|
||||
vertraut erscheinen.
|
||||
Spiele befinden sich in einem Spielverzeichnis, z. B. `minetest/games/foo_game`.
|
||||
|
||||
foo_game
|
||||
├── game.conf
|
||||
├── menu
|
||||
│ ├── header.png
|
||||
│ ├── background.png
|
||||
│ └── icon.png
|
||||
├── minetest.conf
|
||||
├── mods
|
||||
│ └── ... mods
|
||||
├── README.txt
|
||||
└── settingtypes.txt
|
||||
|
||||
Das einzige, was erforderlich ist, ist ein Mod-Ordner, aber `game.conf` und `menu/icon.png`
|
||||
werden empfohlen.
|
||||
|
||||
## Spielübergreifende Kompatibilität
|
||||
|
||||
### API-Kompatibilität
|
||||
|
||||
Es ist eine gute Idee, zu versuchen, so viel API-Kompatibilität mit dem Minetest-Grundspiel (Minetest Game) zu erhalten wie geeignet, da dies die Portierung von Mods in ein anderes Spiel
|
||||
sehr viel einfacher macht.
|
||||
|
||||
Der beste Weg, um die Kompatibilität mit einem anderen Spiel zu erhalten, ist die
|
||||
API-Kompatibilität mit allen Mods, die denselben Namen tragen. Das heißt, wenn ein Mod den gleichen Namen wie ein anderer Mod verwendet, auch wenn sie von einem Drittanbieter stammt, sollte sie eine kompatible API haben. Wenn ein Spiel zum Beispiel einen Mod mit dem Namen `doors` enthält, dann sollte dieser die gleiche API haben wie `doors` in Minetest Game.
|
||||
|
||||
Die API-Kompatibilität eines Mods ist die Summe der folgenden Punkte:
|
||||
|
||||
* Lua-API-Tabelle - Alle dokumentierten/angekündigten Funktionen in der globalen Tabelle, die den gleichen Namen haben. Zum Beispiel: `mobs.register_mob`.
|
||||
* Registrierte Nodes/Items - Das Vorhandensein von Items.
|
||||
|
||||
Kleine Fehler sind nicht so schlimm, wie z.B. das Fehlen eines zufälligen Nutzens
|
||||
Funktion zu haben, die eigentlich nur intern verwendet wurde, aber größere Brüche
|
||||
in Bezug auf Kernfunktionen sind sehr schlecht.
|
||||
|
||||
Es ist schwierig, die API-Kompatibilität mit einem Mega-God-Mod wie *default* in Minetest Game aufrechtzuerhalten. In diesem Fall sollte das Spiel keinen Mod namens default enthalten.
|
||||
|
||||
Die API-Kompatibilität gilt auch für andere Mods und Spiele von Drittanbietern, Stellen Sie also sicher, dass alle neuen Mods einen eindeutigen Mod-Namen haben. Um zu überprüfen, ob ein Mod-Name bereits vergeben ist, suchen Sie ihn z.B. auf [content.minetest.net](https://content.minetest.net/).
|
||||
|
||||
### Gruppen und Aliase
|
||||
|
||||
Gruppen und Aliase sind beides nützliche Werkzeuge, um die Kompatibilität zwischen Spielen zu gewährleisten, da sie es ermöglichen, dass die Namen von Gegenständen in verschiedenen Spielen unterschiedlich sein können. Gemeinsame Nodes wie Stein und Holz sollten Gruppen haben, um das Material zu kennzeichnen Es ist auch eine gute Idee, Aliasnamen von Standardnodes zu direkten Ersetzungen zu erstellen.
|
||||
|
||||
## Sie sind dran
|
||||
|
||||
* Erstellen Sie ein einfaches Spiel, bei dem der Spieler durch das Abbauen spezieller Nodes Punkte erhält.
|
37
_de/index.md
Normal file
37
_de/index.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: Titelseite
|
||||
layout: default
|
||||
homepage: true
|
||||
no_header: true
|
||||
root: ..
|
||||
idx: 0.1
|
||||
---
|
||||
|
||||
<header>
|
||||
<h1>Minetest Modding-Buch</h1>
|
||||
|
||||
<span>von <a href="https://rubenwardy.com" rel="author">rubenwardy</a></span>
|
||||
<span>Editiert von <a href="http://rc.minetest.tv/">Shara</a></span>
|
||||
<span>Übersetzt von von <a href="https://www.solars.de/">solars</a>, <a href="http://debiankaios.de/">debiankaios</a> und <a href="https://forum.minetest.net/memberlist.php?mode=viewprofile&u=41923">Tuxilio</a></span>
|
||||
<span>Korrekturgelessen von <a href="https://jojokorpi.ddns.net/">jjk1</a>, <a href="https://forum.minetest.net/memberlist.php?mode=viewprofile&u=41923">Tuxilio</a> und <a href="http://debiankaios.de/">debiankaios</a></span>
|
||||
</header>
|
||||
|
||||
## Einleitung
|
||||
|
||||
Minetest verwendet Lua-Skripte, um Modding-Unterstützung zu bieten.
|
||||
Dieses Buch soll Ihnen beibringen, wie Sie Ihre eigenen Mods erstellen können, beginnend mit den Grundlagen.
|
||||
Jedes Kapitel konzentriert sich auf einen bestimmten Teil der API und wird Sie bald dazu bringen
|
||||
eigene Mods zu erstellen.
|
||||
|
||||
Sowohl als [online lesbares Buch](https://rubenwardy.com/minetest_modding_book),
|
||||
oder [in HTML form downloadbar](https://gitlab.com/rubenwardy/minetest_modding_book/-/releases).
|
||||
|
||||
### Feedback und Beiträge
|
||||
|
||||
Haben Sie einen Fehler bemerkt oder möchten Sie ein Feedback geben? Teilen Sie mir das bitte mit.
|
||||
|
||||
* Erstelle eine [GitLab Issue](https://gitlab.com/rubenwardy/minetest_modding_book/-/issues).
|
||||
* Poste es im [Forum Topic](https://forum.minetest.net/viewtopic.php?f=14&t=10729).
|
||||
* [Kontaktiere mich](https://rubenwardy.com/contact/).
|
||||
* Lust Mitzumachen?
|
||||
[Read the README](https://gitlab.com/rubenwardy/minetest_modding_book/-/blob/master/README.md).
|
203
_de/items/callbacks.md
Normal file
203
_de/items/callbacks.md
Normal file
@ -0,0 +1,203 @@
|
||||
---
|
||||
title: Node und Item Callbacks
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 2.15
|
||||
description: Erfahren sie über callbacks, Aktionen, und Ereignissen, einschließlich on_use, on_punch, on_place, on_rightclick
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Minetest verwendet hauptsächlich ein Callback-basiertes Modding-Design. Ein Callback ist eine Funktion,
|
||||
die Sie an eine API übergeben und die aufgerufen wird, wenn ein Ereignis eintritt. Zum Beispiel können Sie
|
||||
eine Funktion `on_punch` in einer Node-Definition angeben, die aufgerufen wird, wenn ein Spieler
|
||||
einen Node anstößt. Es gibt auch globale Callbacks wie
|
||||
`minetest.register_on_punchnode`, um Ereignisse für alle Nodes zu empfangen.
|
||||
|
||||
- [Item Callbacks](#item-callbacks)
|
||||
- [on\_use](#on_use)
|
||||
- [on\_place und on\_secondary\_use](#on_place-und-on_secondary_use)
|
||||
- [on\_drop](#on_drop)
|
||||
- [after\_use](#after_use)
|
||||
- [item\_place vs place\_item](#item_place-vs-place_item)
|
||||
- [Node Callbacks](#node-callbacks)
|
||||
- [Rechtsklick und Platzieren eines Nodes](#rechtsklick-und-platzieren-eines-nodes)
|
||||
- [Schlagen und abbauen](#schlagen-und-abbauen)
|
||||
- [...und mehr!](#und-mehr)
|
||||
|
||||
|
||||
## Item Callbacks
|
||||
|
||||
Wenn ein Spieler einen Node, einen Handwerksgegenstand oder ein Werkzeug in seinem Inventar hat, kann er folgende Ereignisse auslösen
|
||||
bestimmte Ereignisse:
|
||||
|
||||
| Callback | Standard-Bindung | Standard Wert |
|
||||
|------------------|-------------------------------|----------------------------------------------|
|
||||
| on_use | links-click | nil |
|
||||
| on_place | rechts-click auf einen Node | `minetest.item_place` |
|
||||
| on_secondary_use | rechts-click auf keinen Node | `minetest.item_secondary_use` (does nothing) |
|
||||
| on_drop | Q | `minetest.item_drop` |
|
||||
| after_use | Abbauen eines Nodes | nil |
|
||||
|
||||
|
||||
### on_use
|
||||
|
||||
Mit einem on_use Callback wird verhindert, dass das Item zum abbauen von Blöcken verwendet wird. Eine häufige
|
||||
Verwendung des on_use Callback ist für Lebensmittel:
|
||||
|
||||
```lua
|
||||
minetest.register_craftitem("meinemod:matschekuchen", {
|
||||
description = "Alien Matschekuchen",
|
||||
inventory_image = "meinessen_matschekuchen.png",
|
||||
on_use = minetest.item_eat(20),
|
||||
})
|
||||
```
|
||||
|
||||
Die Zahl, die an die Funktion minetest.item_eat übergeben wird, ist die Anzahl der Hitpoints,
|
||||
Punkte, die durch den Verzehr dieser Nahrung geheilt werden. Jedes Herzsymbol, das der Spieler hat, ist
|
||||
zwei Hitpoints wert. Ein Spieler kann in der Regel bis zu 10 Herzen haben, was gleichbedeutend ist mit
|
||||
20 Hitpoints.
|
||||
|
||||
minetest.item_eat() ist eine Funktion, die eine Funktion zurückgibt und diese als on_use Callback. Das bedeutet, dass der obige Code dem hier entspricht:
|
||||
|
||||
```lua
|
||||
minetest.register_craftitem("meinemod:matschekuchen", {
|
||||
description = "Alien Matschekuchen",
|
||||
inventory_image = "meinessen_matschekuchen.png",
|
||||
on_use = function(...)
|
||||
return minetest.do_item_eat(20, nil, ...)
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
Wenn man versteht, wie item_eat funktioniert, indem es einfach eine Funktion zurückgibt, ist es möglich, die Funktion so zu ändern, dass sie ein komplexeres Verhalten wie das Abspielen eines benutzerdefinierten Sounds ermöglicht.
|
||||
|
||||
|
||||
### on_place und on_secondary_use
|
||||
|
||||
Der Unterschied zwischen `on_place` und `on_secondary_use` ist, dass `on_place` aufgerufen wird,
|
||||
wenn der Spieler auf einen Node zeigt und `on_secondary_use`, wenn der
|
||||
Spieler dies nicht tut.
|
||||
|
||||
Beide Callbacks werden für alle Arten von Items aufgerufen. `on_place` ist standardmäßig auf die
|
||||
Funktion `minetest.item_place`, die den Aufruf des `on_rightclick`
|
||||
Callback des angezeigten Nodes aufruft oder das gehaltene Item platziert, wenn es ein Node ist.
|
||||
|
||||
|
||||
### on_drop
|
||||
|
||||
on_drop wird aufgerufen, wenn der Spieler einen Gegenstand fallen lassen will, zum Beispiel mit
|
||||
der Abwurftaste (Q) oder durch herausziehen aus dem Inventar. Es wird standardmäßig die Funktion
|
||||
`minetest.item_drop` verwendet, die das Fallenlassen des Gegenstandes übernimmt.
|
||||
|
||||
|
||||
### after_use
|
||||
|
||||
`after_use` wird beim abbauen eines Nodes aufgerufen und ermöglicht es Ihnen, dass die Art der Abnutzung
|
||||
auf ein Werkzeug angewendet wird. Wenn after_use nicht existiert, dann ist es das gleiche wie:
|
||||
|
||||
```lua
|
||||
after_use = function(itemstack, user, node, digparams)
|
||||
itemstack:add_wear(digparams.wear)
|
||||
return itemstack
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
## item_place vs place_item
|
||||
|
||||
Die API von Minetest enthält viele verschiedene integrierte Callback-Implementierungen, die Sie verwenden können. Diese Callbacks werden mit dem Elementtyp zuerst benannt, zum Beispiel,
|
||||
`minetest.item_place` und `minetest.node_dig`. Einige Callback-Implementierungen werden
|
||||
direkt verwendet, während einige Funktionen sind, die den Rückruf zurückgeben:
|
||||
|
||||
```lua
|
||||
minetest.register_item("meinemod:beispiel", {
|
||||
on_place = minetest.item_place,
|
||||
on_use = minetest.item_eat(10),
|
||||
})
|
||||
```
|
||||
|
||||
Die API von Minetest enthält auch eingebaute Funktionen, die etwas _tun_. Diese sind
|
||||
oft verwirrend ähnlich benannt wie die eingebauten Callback-Implementierungen
|
||||
haben aber das Verb vorangestellt. Beispiele sind `minetest.place_item` und
|
||||
`minetest.dig_node` - diese Funktionen ermöglichen das abbauen und Platzieren von Blöcken mit einem
|
||||
ähnlichen Effekt wie Spieler.
|
||||
|
||||
|
||||
## Node Callbacks
|
||||
|
||||
Wenn sich ein Node in einem Inventar befindet, verwendet er, wie oben beschrieben, Item Callback. Wenn
|
||||
ein Node in der Welt platziert ist, verwendet er Node Callbacks. Es gibt eine ganze Menge von
|
||||
Node Callbacks, zu viele, um sie in diesem Buch zu behandeln. Allerdings werden einige von ihnen
|
||||
später in diesem Buch behandelt.
|
||||
|
||||
Einige der Callbacks beziehen sich auf Nodeoperationen wie das Platzieren und
|
||||
Entfernen aus der Welt. Es ist wichtig zu beachten, dass Nodeoperationen-Callbacks
|
||||
wie diese aus Leistungsgründen nicht von bulk changes - also solchen, die eine große Anzahl von
|
||||
Blöcken auf einmal setzen - aufgerufen werden. Daher kann man sich nicht darauf verlassen, dass diese
|
||||
Callbacks immer aufgerufen werden.
|
||||
|
||||
|
||||
### Rechtsklick und Platzieren eines Nodes
|
||||
|
||||
Wenn der Benutzer mit der rechten Maustaste auf ein Item klickt, während er auf einen Node zeigt, wird der Callback
|
||||
`on_place` des Items aufgerufen. Standardmäßig ist dieser auf `minetest.item_place` gesetzt.
|
||||
Wenn der gezeigte Node einen `on_rightclick` Callback hat und sneak (shift) gehalten wird,
|
||||
dann wird der `on_rightclick` Callback aufgerufen. Andernfalls wird `minetest.item_place`
|
||||
den Node platzieren.
|
||||
|
||||
Das Platzieren eines Nodes ruft sowohl `on_construct` als auch `after_place_node` auf.
|
||||
`on_construct` wird von jedem Node-Platzier-Ereignis aufgerufen, das nicht in Bulk war und es wird nur
|
||||
die Position und den Wert des Nodes gegeben. `after_place_node` wird nur von node
|
||||
place aufgerufen und hat daher mehr Informationen - wie den Placer und den Itemstack.
|
||||
|
||||
Es ist wichtig zu wissen, dass Spieler nicht die einzigen Objekte sind, die
|
||||
Blöcke platzieren können; es ist üblich, dass auch Mobs und Mods Blöcke platzieren können. `Placer` kann ein Spieler, eine Entity oder nil sein.
|
||||
|
||||
```lua
|
||||
minetest.register_node("mymod:mynode", {
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
if clicker:is_player() then
|
||||
minetest.chat_send_player(clicker:get_player_name(), "Hallo Welt!")
|
||||
end
|
||||
end,
|
||||
on_construct = function(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Mein Node!")
|
||||
end,
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
-- Überprüfen des Platzierers
|
||||
if placer and placer:is_player() then
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Schlagen und abbauen
|
||||
|
||||
Schlagen bedeutet, dass der Spieler kurz mit der linken Maustaste klickt. Wenn der benutze Gegenstand
|
||||
einen `on_use` Callback hat, wird dieser aufgerufen. Andernfalls wird der `on_punch`
|
||||
Callback auf dem angezeigten Node aufgerufen.
|
||||
|
||||
Wenn der Spieler versucht, einen Node abzubauen, wird der `on_dig` Callback auf dem Node aufgerufen.
|
||||
Dieser ist standardmäßig auf `minetest.node_dig` eingestellt, der den Gebietsschutz prüft, das
|
||||
das Werkzeug verschleißt, den Node entfernt und den `after_dig_node` Callback ausführt.
|
||||
|
||||
|
||||
```lua
|
||||
minetest.register_node("meinemod:meinnode", {
|
||||
on_punch = function(pos, node, puncher, pointed_thing)
|
||||
if puncher:is_player() then
|
||||
minetest.chat_send_player(puncher:get_player_name(), "Au!")
|
||||
end
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
### ...und mehr!
|
||||
|
||||
In der Lua-API-Referenz von Minetest finden Sie eine Liste aller Node-Callbacks und
|
||||
weitere Informationen zu den oben genannten Rückrufen.
|
96
_de/items/creating_textures.md
Normal file
96
_de/items/creating_textures.md
Normal file
@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Texturen erstellen
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 2.2
|
||||
description: Eine Einführung in die Erstellung von Texturen mit dem Editor Ihrer Wahl und eine Anleitung zu GIMP.
|
||||
redirect_from: /de/chapters/creating_textures.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Bei der Entwicklung für Minetest ist es sehr nützlich, Texturen zu erstellen und optimieren zu können.
|
||||
Es gibt viele Techniken, die für die Arbeit an Pixel-Art-Texturen relevant sind
|
||||
und das Verständnis dieser Techniken wird
|
||||
die Qualität der von Ihnen erstellten Texturen erheblich verbessern.
|
||||
|
||||
Detaillierte Ansätze zur Erstellung guter Pixel-Art liegen außerhalb des Rahmens
|
||||
dieses Buches, stattdessen werden nur die wichtigsten Grundtechniken
|
||||
behandelt werden.
|
||||
Es sind viele [gute Online-Tutorials](http://www.photonstorm.com/art/tutorials-art/16x16-pixel-art-tutorial)
|
||||
zur Verfügung, die die Pixel-Art viel detaillierter behandeln.
|
||||
|
||||
- [Techniken](#techniken)
|
||||
- [Nutzung des Stiftes](#nutzung-des-stiftes)
|
||||
- [Kacheln](#kacheln)
|
||||
- [Transparenz](#transparenz)
|
||||
- [Farbpaletten](#farbpaletten)
|
||||
- [Editore](#editore)
|
||||
- [MS Paint](#ms-paint)
|
||||
- [Aseprite / LibreSprite](#aseprite--libresprite)
|
||||
- [GIMP](#gimp)
|
||||
|
||||
## Techniken
|
||||
|
||||
### Nutzung des Stiftes
|
||||
|
||||
Das Stiftwerkzeug ist in den meisten Editoren verfügbar. Wenn es auf die kleinste Größe eingestellt ist,
|
||||
können Sie damit einen Pixel nach dem anderen bearbeiten, ohne andere Teile des Bildes zu
|
||||
ändern. Indem Sie die Pixel einzeln bearbeiten, schaffen Sie klare
|
||||
und scharfe Texturen ohne ungewollte Unschärfen. Außerdem bietet es Ihnen ein hohes
|
||||
Maß an Präzision und Kontrolle.
|
||||
|
||||
### Kacheln
|
||||
|
||||
Texturen, die für Nodes verwendet werden, sollten in der Regel kachelfähig sein. Das bedeutet
|
||||
wenn Sie mehrere Nodes mit der gleichen Textur zusammen platzieren, sehen die Kanten
|
||||
korrekt aus.
|
||||
|
||||
<!-- IMAGE NEEDED - cobblestone that tiles correctly -->
|
||||
|
||||
Wenn Sie die Kanten nicht richtig anpassen, ist das Ergebnis weit weniger ansprechend.
|
||||
|
||||
<!-- IMAGE NEEDED - node that doesn't tile correctly -->
|
||||
|
||||
### Transparenz
|
||||
|
||||
Transparenz ist wichtig bei der Erstellung von Texturen für fast alle Craftitems
|
||||
und einige Nodes, wie z. B. Glas.
|
||||
Nicht alle Editoren unterstützen Transparenz, also stellen Sie sicher, dass Sie einen
|
||||
Editor, der für die Texturen, die Sie erstellen möchten, geeignet ist.
|
||||
|
||||
### Farbpaletten
|
||||
|
||||
Die Verwendung einer einheitlichen Farbpalette ist ein einfacher Weg, um Ihre Texturen viel besser aussehen zu lassen.
|
||||
Es ist eine gute Idee, eine Palette mit einer begrenzten Anzahl von Farben zu verwenden, vielleicht höchstens 32.
|
||||
Vorgefertigte Paletten finden Sie unter
|
||||
[lospec.com](https://lospec.com/palette-list).
|
||||
|
||||
## Editore
|
||||
|
||||
### MS Paint
|
||||
|
||||
MS Paint ist ein einfacher Editor, der für einfaches Texturendesign nützlich sein kann;
|
||||
allerdings unterstützt er keine Transparenz.
|
||||
Dies spielt normalerweise keine Rolle, wenn Sie Texturen für die Seiten von Nodes erstellen,
|
||||
aber wenn Sie Transparenz in Ihren Texturen benötigen, sollten Sie einen
|
||||
anderen Editor wählen.
|
||||
|
||||
### Aseprite / LibreSprite
|
||||
|
||||
[Aseprite](https://www.aseprite.org/) ist ein proprietärer Pixel-Art-Editor.
|
||||
Er enthält standardmäßig eine Menge nützlicher Funktionen wie Farbpaletten und
|
||||
Animationswerkzeuge.
|
||||
|
||||
[LibreSprite](https://libresprite.github.io/) ist ein Open-Source-Fork von Aseprite
|
||||
bevor es proprietär wurde.
|
||||
|
||||
### GIMP
|
||||
|
||||
GIMP wird in der Minetest-Community häufig verwendet. Es hat eine ziemlich hohe
|
||||
Lernkurve, da viele seiner Funktionen nicht sofort
|
||||
offensichtlich sind.
|
||||
|
||||
Bei der Verwendung von GIMP kann das Bleistift-Werkzeug aus der Toolbox
|
||||
ausgewählt werden.s Es ist außerdem ratsam, das Kontrollkästchen "Harte Kanten"
|
||||
für das Radiergummi-Werkzeug zu aktivieren.
|
343
_de/items/inventories.md
Normal file
343
_de/items/inventories.md
Normal file
@ -0,0 +1,343 @@
|
||||
---
|
||||
title: ItemStacks und Inventare
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 2.4
|
||||
description: Manipulieren von InvRefs und ItemStacks
|
||||
redirect_from:
|
||||
- /de/chapters/inventories.html
|
||||
- /de/chapters/itemstacks.html
|
||||
- /de/inventories/inventories.html
|
||||
- /de/inventories/itemstacks.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
In diesem Kapitel werden Sie lernen, wie man Inventare verwendet und manipuliert, egal
|
||||
ob es sich um ein Spielerinventar, ein Nodeinventar oder ein freistehendes Inventar handelt.
|
||||
|
||||
- [Was sind ItemStacks und Inventare?](#was-sind-itemstacks-und-inventare)
|
||||
- [ItemStacks](#itemstacks)
|
||||
- [Inventarstandorte](#inventarstandorte)
|
||||
- [Listen](#listen)
|
||||
- [Größe und Breite](#größe-und-breite)
|
||||
- [Inhalt überprüfen](#inhalt-überprüfen)
|
||||
- [Ändern von Inventaren und ItemStacks](#ändern-von-inventaren-und-itemstacks)
|
||||
- [Zu einer Liste hinzufügen](#zu-einer-liste-hinzufügen)
|
||||
- [Items nehmen](#items-nehmen)
|
||||
- [Stacks manipulieren](#stacks-manipulieren)
|
||||
- [Abnutzung](#abnutzung)
|
||||
- [Lua Tabellen](#lua-tabellen)
|
||||
|
||||
## Was sind ItemStacks und Inventare?
|
||||
|
||||
Ein ItemStack sind die Daten hinter einer einzelnen Zelle in einem Inventar.
|
||||
|
||||
Ein *Inventar* ist eine Sammlung von *Inventarlisten*, von denen jede
|
||||
ein 2D-Gitter aus ItemStacks ist.
|
||||
Inventarlisten werden im Kontext von Inventaren einfach *Listen* genannt.
|
||||
Der Sinn eines Inventars ist es, mehrere Raster zu ermöglichen, wenn Spieler
|
||||
und Nodes nur maximal ein Inventar haben.
|
||||
|
||||
## ItemStacks
|
||||
|
||||
ItemStacks haben vier Komponenten: name, count, wear und metadata.
|
||||
|
||||
Der Itemname kann der Itemname eines registrierten Items, ein Alias oder ein
|
||||
unbekannter Itemname sein.
|
||||
Unbekannte Items treten häufig auf, wenn Benutzer Mods deinstallieren oder wenn Mods
|
||||
Items ohne Vorsichtsmaßnahmen, z. B. durch die Registrierung von Aliasen, entfernen.
|
||||
|
||||
```lua
|
||||
print(stack:get_name())
|
||||
stack:set_name("default:dirt")
|
||||
|
||||
if not stack:is_known() then
|
||||
print("Ist ein unbekanntes Item!")
|
||||
end
|
||||
```
|
||||
|
||||
Die Anzahl wird immer 0 oder größer sein.
|
||||
Bei normalem Spielverlauf sollte die Anzahl nicht größer sein als die maximale
|
||||
Stackgröße des Gegenstands - `stack_max`.
|
||||
Allerdings können Admin-Befehle und fehlerhafte Mods dazu führen, dass Stacks die maximale Größe überschreiten.
|
||||
|
||||
```lua
|
||||
print(stack:get_stack_max())
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
Ein ItemStack kann leer sein, in diesem Fall ist die Anzahl 0.
|
||||
|
||||
```lua
|
||||
print(stack:get_count())
|
||||
stack:set_count(10)
|
||||
```
|
||||
|
||||
ItemStacks können auf verschiedene Weise mit der Funktion ItemStack konstruiert werden.
|
||||
|
||||
|
||||
```lua
|
||||
ItemStack() -- name="", count=0
|
||||
ItemStack("default:pick_stone") -- count=1
|
||||
ItemStack("default:stone 30")
|
||||
ItemStack({ name = "default:wood", count = 10 })
|
||||
```
|
||||
|
||||
Item-Metadaten sind ein unbegrenzter Schlüssel-Wert-Speicher für Daten über das Item.
|
||||
Schlüssel-Wert bedeutet, dass Sie einen Namen (den sogenannten key) verwenden, um auf die Daten (den sogenannten value) zuzugreifen.
|
||||
Einige Keys haben eine besondere Bedeutung, wie z. B. `description`, welches für eine per-Item-Beschreibung verwendet wird.
|
||||
Dies wird im Kapitel über Metadaten und Speicherung ausführlicher behandelt.
|
||||
|
||||
## Inventarstandorte
|
||||
|
||||
Ein Inventarstandort ist der Ort und die Art und Weise, wie das Inventar gespeichert wird.
|
||||
Es gibt drei Arten von Inventarstandorten: Spieler, Nodes und freistehend.
|
||||
Ein Inventar ist direkt an einen und nur einen Ort gebunden - eine Aktualisierung des
|
||||
Inventars wird es sofort aktualisieren.
|
||||
|
||||
Nodeinventare beziehen sich auf die Position eines bestimmten Nodes, z. B. einer Truhe.
|
||||
Der Node muss geladen werden, da er in den [Node-Metadaten](../map/storage.html#metadata) gespeichert ist.
|
||||
|
||||
```lua
|
||||
local inv = minetest.get_inventory({ type="node", pos={x=1, y=2, z=3} })
|
||||
```
|
||||
|
||||
Auf diese Weise erhält man eine *Inventar-Referenz*, auch als *InvRef* bekannt.
|
||||
Inventar-Referenzen werden verwendet, um ein Inventar zu manipulieren.
|
||||
*Referenz* bedeutet, dass die Daten nicht tatsächlich in diesem Objekt gespeichert sind,
|
||||
sondern das Objekt aktualisiert die Daten direkt an Ort und Stelle.
|
||||
|
||||
Der Standort eines Inventar-Referenz kann wie folgt ermittelt werden:
|
||||
|
||||
```lua
|
||||
local location = inv:get_location()
|
||||
```
|
||||
|
||||
Spielerinventare können auf ähnliche Weise oder über eine Spielerreferenz abgerufen werden.
|
||||
Der Spieler muss online sein, um auf sein Inventar zugreifen zu können.
|
||||
|
||||
```lua
|
||||
local inv = minetest.get_inventory({ type="player", name="spieler1" })
|
||||
-- oder
|
||||
local inv = player:get_inventory()
|
||||
```
|
||||
|
||||
Ein freistehendes Inventar ist ein Inventar, das unabhängig von Spielern oder Nodes ist.
|
||||
Freistehende Inventare werden auch nicht bei einem Neustart gespeichert.
|
||||
|
||||
```lua
|
||||
local inv = minetest.get_inventory({
|
||||
type="detached", name="inventar_name" })
|
||||
```
|
||||
|
||||
Im Gegensatz zu den anderen Inventararten müssen Sie zunächst ein freistehendes Inventar erstellen
|
||||
erstellen, bevor Sie darauf zugreifen:
|
||||
```lua
|
||||
minetest.create_detached_inventory("inventar_name")
|
||||
```
|
||||
|
||||
Die Funktion create_detached_inventory akzeptiert 3 Argumente, wobei nur das erste -
|
||||
der Inventarname - erforderlich ist.
|
||||
Das zweite Argument nimmt eine Tabelle von Callbacks entgegen, die verwendet werden können,
|
||||
um zu steuern, wie Spieler mit dem Inventar interagieren:
|
||||
|
||||
```lua
|
||||
-- Nur-Eingabefähiges freistehendes Inventar
|
||||
minetest.create_detached_inventory("inventory_name", {
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
return count -- erlaubt bewegen
|
||||
end,
|
||||
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
return stack:get_count() -- erlaubt ablegen
|
||||
end,
|
||||
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
return 0 -- erlaubt Nehmen nicht
|
||||
end,
|
||||
|
||||
on_put = function(inv, listname, index, stack, player)
|
||||
minetest.chat_send_all(player:get_player_name() ..
|
||||
" gab " .. stack:to_string() ..
|
||||
" In die Spendenkiste von " .. minetest.pos_to_string(player:get_pos()))
|
||||
end,
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
Berechtigungsaufrufe - d.h. diejenigen, die mit `allow_` beginnen - geben die Anzahl
|
||||
der zu übertragenden Items zurück, wobei 0 verwendet wird, um die Übertragung vollständig zu verhindern.
|
||||
|
||||
Im Gegensatz dazu haben Aktionsrückrufe - beginnend mit `on_` - keinen Rückgabewert.
|
||||
|
||||
## Listen
|
||||
|
||||
Inventarlisten sind ein Konzept, das es ermöglicht, mehrere Raster an einem einzigen Ort zu speichern.
|
||||
Dies ist besonders nützlich für den Spieler, da es eine Reihe von allgemeinen Listen
|
||||
gibt, die alle Spiele haben, wie das *Hauptinventar* und die *Craftingplätze*.
|
||||
|
||||
### Größe und Breite
|
||||
|
||||
Listen haben eine Größe, die die Gesamtzahl der Zellen im Raster angibt, und eine
|
||||
Breite, die nur innerhalb der Engine verwendet wird.
|
||||
Die Breite der Liste wird beim Zeichnen des Inventars in einem Fenster nicht verwendet, denn der Code hinter dem Fenster bestimmt die zu verwendende Breite.
|
||||
|
||||
```lua
|
||||
if inv:set_size("main", 32) then
|
||||
inv:set_width("main", 8)
|
||||
print("Größe: " .. inv:get_size("main"))
|
||||
print("Breite: " .. inv:get_width("main"))
|
||||
else
|
||||
print("Fehler! Ungültiger itemname oder ungültige Größe für set_size()")
|
||||
end
|
||||
```
|
||||
|
||||
`set_size` wird fehlschlagen und false zurückgeben, wenn der Listenname oder die Größe ungültig ist.
|
||||
Zum Beispiel kann die neue Größe zu klein sein, damit alle aktuellen Gegenstände
|
||||
im Inventar zu passen.
|
||||
|
||||
### Inhalt überprüfen
|
||||
|
||||
`is_empty` kann benutzt werden, um zu sehen, ob eine Liste Items enthält:
|
||||
|
||||
```lua
|
||||
if inv:is_empty("main") then
|
||||
print("Die Liste ist leer!")
|
||||
end
|
||||
```
|
||||
|
||||
`contains_item` kann verwendet werden, um festzustellen, ob eine Liste ein bestimmtes Item enthält:
|
||||
|
||||
```lua
|
||||
if inv:contains_item("main", "default:stone") then
|
||||
print("Ich habe ein paar Steine gefunden!")
|
||||
end
|
||||
```
|
||||
|
||||
## Ändern von Inventaren und ItemStacks
|
||||
|
||||
### Zu einer Liste hinzufügen
|
||||
|
||||
`add_item` fügt Items zu einer Liste hinzu (in diesem Fall `"main"`). Im folgenden
|
||||
Beispiel wird auch die maximale Stackgröße beachtet:
|
||||
|
||||
```lua
|
||||
local stack = ItemStack("default:stone 99")
|
||||
local reste = inv:add_item("main", stack)
|
||||
if reste:get_count() > 0 then
|
||||
print("Inventar ist voll! " ..
|
||||
reste:get_count() .. " Items wurden nicht hinzugefügt")
|
||||
end
|
||||
```
|
||||
|
||||
### Items nehmen
|
||||
|
||||
Um Items von einer Liste zu löschen:
|
||||
|
||||
```lua
|
||||
local genommen = inv:remove_item("main", stack)
|
||||
print("Nahm " .. genommen:get_count())
|
||||
```
|
||||
|
||||
### Stacks manipulieren
|
||||
|
||||
Sie können einzelne Stacks ändern, indem Sie sie zuerst auswählen:
|
||||
|
||||
```lua
|
||||
local stack = inv:get_stack(listenname, 0)
|
||||
```
|
||||
|
||||
Dann ändern Sie sie durch das Setzen von Eigenschaften oder durch die Verwendung von
|
||||
Methoden, welche die `stack_size` respektieren:
|
||||
|
||||
|
||||
```lua
|
||||
local stack = ItemStack("default:stone 50")
|
||||
local zum_hinzufuegen = ItemStack("default:stone 100")
|
||||
local reste = stack:add_item(to_add)
|
||||
local genommen = stack:take_item(19)
|
||||
|
||||
print("Konnte nicht" .. reste:get_count() .. " der items hinzufügen.")
|
||||
-- ^ wird 51 seind
|
||||
|
||||
print("Habe " .. stack:get_count() .. " items")
|
||||
-- ^ wird 80 sein
|
||||
-- min(50+100, stack_max) - 19 = 80
|
||||
-- wobei stack_max = 99
|
||||
```
|
||||
|
||||
`add_item` fügt Items zu einem ItemStack hinzu und gibt alle zurück, die nicht hinzugefügt werden konnten.
|
||||
`take_item` nimmt bis zu der Anzahl der Items, kann aber auch weniger nehmen, und gibt den genommenen Stack zurück.
|
||||
|
||||
Legen Sie schließlich den ItemStack fest:
|
||||
|
||||
```lua
|
||||
inv:set_stack(listenname, 0, stack)
|
||||
```
|
||||
|
||||
## Abnutzung
|
||||
|
||||
Werkzeuge können abgenutzt sein; die Abnutzung wird in einem Fortschrittsbalken angezeigt und führt zum Abbruch des Werkzeugs, wenn es vollständig abgenutzt ist.
|
||||
Die Abnutzung ist eine Zahl bis 65535; je höher sie ist, desto mehr ist das Werkzeug abgenutzt.
|
||||
|
||||
Die Abnutzung kann mit `add_wear()`, `get_wear()` und `set_wear(wear)` beeinflusst werden.
|
||||
|
||||
```lua
|
||||
local stack = ItemStack("default:pick_mese")
|
||||
local max_nutzungen = 10
|
||||
|
||||
-- Dies geschieht automatisch, wenn Sie ein Werkzeug verwenden, das Dinge abbaut
|
||||
-- Sie erhöht die Abnutzung eines Gegenstandes um einen Einsatz.
|
||||
stack:add_wear(65535 / (max_nutzungen - 1))
|
||||
```
|
||||
|
||||
Beim abbauen eines Nodes kann die Abnutzung des Werkzeugs von dem Node abhängen,
|
||||
der abgebaut wird. Daher variiert max_nutzungen je nachdem, was abgebaut wird.
|
||||
|
||||
## Lua Tabellen
|
||||
|
||||
ItemStacks und Inventare können in und aus Tabellen konvertiert werden.
|
||||
Dies ist nützlich für Kopier- und Bulk-Operationen.
|
||||
|
||||
```lua
|
||||
-- Gesamtes Inventar
|
||||
local daten = inv1:get_lists()
|
||||
inv2:set_lists(daten)
|
||||
|
||||
-- Eine Liste
|
||||
local listendaten = inv1:get_list("main")
|
||||
inv2:set_list("main", listendaten)
|
||||
```
|
||||
|
||||
Die Tabelle der Listen, die von `get_lists()` zurückgegeben wird, wird in dieser Form vorliegen:
|
||||
|
||||
```lua
|
||||
{
|
||||
liste_eins = {
|
||||
ItemStack,
|
||||
ItemStack,
|
||||
ItemStack,
|
||||
ItemStack,
|
||||
-- inv:get_size("liste_eins") Elemente
|
||||
},
|
||||
liste_zwei = {
|
||||
ItemStack,
|
||||
ItemStack,
|
||||
ItemStack,
|
||||
ItemStack,
|
||||
-- inv:get_size("liste_zwei") Elemente
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`get_list()` gibt eine einzelne Liste zurück, die lediglich eine Liste von ItemStacks ist.
|
||||
|
||||
Ein wichtiger Punkt ist, dass die obigen Set-Methoden die Größe der Listen nicht verändern.
|
||||
Das bedeutet, dass Sie eine Liste löschen können, indem Sie sie auf eine leere Tabelle setzen, ohne dass sie verkleinert wird:
|
||||
|
||||
```lua
|
||||
inv:set_list("main", {})
|
||||
```
|
442
_de/items/node_drawtypes.md
Normal file
442
_de/items/node_drawtypes.md
Normal file
@ -0,0 +1,442 @@
|
||||
---
|
||||
title: Node Zeichnungstypen
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 2.3
|
||||
description: Ratgeber für alle Zeichnungstypen, einschließlich node boxen/Nodeboxen und mesh nodes.
|
||||
redirect_from: /de/chapters/node_drawtypes.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Die Methode, mit der ein Node gezeichnet wird, wird als *Zeichnungstyp* bezeichnet. Es gibt viele
|
||||
verfügbare Zeichnungstypen. Das Verhalten eines Zeichnungstyps kann
|
||||
durch Angabe von Eigenschaften in der Nodetypdefinition gesteuert werden. Diese Eigenschaften
|
||||
sind für alle Instanzen dieses Nodes festgelegt. Es ist möglich, einige Eigenschaften
|
||||
pro Node zu steuern, indem man `param2` verwendet.
|
||||
|
||||
Im vorigen Kapitel wurde das Konzept der Nodes und Items eingeführt, aber eine
|
||||
vollständige Definition eines Nodes wurde nicht gegeben. Die Minetest-Welt ist ein 3D-Gitter aus
|
||||
Positionen. Jede Position wird als Node bezeichnet und besteht aus dem Nodetyp
|
||||
(Name) und zwei Parametern (param1 und param2). Die Funktion
|
||||
`minetest.register_node` (übersetzt `minetest.registriere_node`)ist ein wenig irreführend, da sie nicht wirklich
|
||||
einen Node registriert - sie registriert einen neuen *Typ* von Node.
|
||||
|
||||
Die Nodeparameter werden zum steuern, wie ein Nde individuell gerendert wird, verwendet.
|
||||
`param1` wird verwendet, um die Beleuchtung eines Nodes zu speichern, und die Bedeutung von
|
||||
`param2` hängt von der Eigenschaft `paramtype2` der Nodetypdefinition ab.
|
||||
|
||||
- [Würfelförmiger Node: Normale und allfaces](#würfelförmiger-node-normale-und-allfaces)
|
||||
- [Glasartige Nodes](#glasartige-nodes)
|
||||
- [Glasartig gerahmt](#glasartig-gerahmt)
|
||||
- [Luftartige Nodes](#luftartige-nodes)
|
||||
- [Beleuchtung und Sonnenlichtausbreitung](#beleuchtung-und-sonnenlichtausbreitung)
|
||||
- [Flüssige Nodes](#flüssige-nodes)
|
||||
- [Nodeboxen](#nodeboxen)
|
||||
- [Wandgehaltene Nodeboxen](#wandgehaltene-nodeboxen)
|
||||
- [Mesh Nodes](#mesh-nodes)
|
||||
- [Signlike Nodes](#signlike-nodes)
|
||||
- [Plantlike Nodes](#plantlike-nodes)
|
||||
- [Firelike Nodes](#firelike-nodes)
|
||||
- [Mehr Zeichnungstypen](#mehr-zeichnungstypen)
|
||||
|
||||
|
||||
## Würfelförmiger Node: Normale und allfaces
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}//static/drawtype_normal.png" alt="Normaler Zeichnungstyp">
|
||||
<figcaption>
|
||||
Normaler Zeichnungstyp
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Der Zeichnungstyp normal wird typischerweise zum Rendern eines Würfelförmigen Nodes verwendet.
|
||||
Wenn die Seite eines normalen Nodes an einer festen Seite anliegt, wird diese Seite nicht gerendert,
|
||||
was zu einem großen Leistungsgewinn führt.
|
||||
|
||||
Im Gegensatz dazu wird der Zeichnungstyp allfaces immer noch die Innenseite darstellen, wenn er auf einen
|
||||
soliden Node ist. Dies ist gut für Nodes mit teilweise transparenten Seiten, wie z.B. den
|
||||
Blatt-Node. Sie können den allfaces_optional drawtype verwenden, um den Benutzern die Möglichkeit zu geben, die
|
||||
langsamere Zeichnen auszuschalten, in diesem Fall verhält er sich wie ein normaler Node.
|
||||
|
||||
```lua
|
||||
minetest.register_node("meinemod:diamant", {
|
||||
description = "Alien Diamant",
|
||||
tiles = {"meinemod_diamant.png"},
|
||||
groups = {cracky = 3},
|
||||
})
|
||||
|
||||
minetest.register_node("default:leaves", {
|
||||
description = "Blätter",
|
||||
drawtype = "allfaces_optional",
|
||||
tiles = {"default_leaves.png"}
|
||||
})
|
||||
```
|
||||
|
||||
Hinweis: Der normale Zeichentyp ist der Standard-Zeichentyp, Sie müssen ihn also nicht explizit
|
||||
angeben.
|
||||
|
||||
|
||||
## Glasartige Nodes
|
||||
|
||||
Der Unterschied zwischen glasartigen(glasslike) und normalen Nodes besteht darin, dass die Platzierung eines glasartigen Nodes
|
||||
neben einem normalen Node, nicht dazu führt, dass die Seite des normalen Nodes ausgeblendet wird.
|
||||
Dies ist nützlich, weil glasartige Nodes in der Regel transparent sind und daher die Verwendung eines normalen
|
||||
Drawtype dazu führen würde, dass man durch die Welt hindurchsehen kann.
|
||||
|
||||
<figure>
|
||||
<img src="{{ page.root }}//static/drawtype_glasslike_edges.png" alt="Glasartige Ränder">
|
||||
<figcaption>
|
||||
Glasartige Ränder
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
minetest.register_node("default:obsidian_glass", {
|
||||
description = "Obsidian Glas",
|
||||
drawtype = "glasslike",
|
||||
tiles = {"default_obsidian_glass.png"},
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
sunlight_propagates = true,
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
groups = {cracky=3,oddly_breakable_by_hand=3},
|
||||
})
|
||||
```
|
||||
|
||||
### Glasartig gerahmt
|
||||
|
||||
Dies führt dazu, dass die Kante des Nodes mit einem 3D-Effekt um das ganze Ding herumgeht und nicht
|
||||
um einen einzelnen Node, wie im Folgenden dargestellt:
|
||||
|
||||
<figure>
|
||||
<img src="{{ page.root }}//static/drawtype_glasslike_framed.png" alt="Glasartig gerahmt Ränder">
|
||||
<figcaption>
|
||||
Glasartig gerahmt Ränder
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Sie können den Zeichnungstyp `glasslike_framed_optional` verwenden, um dem Benutzer die Möglichkeit zu geben,
|
||||
das gerahmte Erscheinungsbild manuell zu aktivieren.
|
||||
|
||||
```lua
|
||||
minetest.register_node("default:glass", {
|
||||
description = "Glas",
|
||||
drawtype = "glasslike_framed",
|
||||
tiles = {"default_glass.png", "default_glass_detail.png"},
|
||||
inventory_image = minetest.inventorycube("default_glass.png"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true, -- Sonnenlicht kann durch den Node scheinen
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 3},
|
||||
sounds = default.node_sound_glass_defaults()
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## Luftartige Nodes
|
||||
|
||||
Diese Nodes werden nicht gerendert und haben daher keine Texturen.
|
||||
|
||||
```lua
|
||||
minetest.register_node("meineluft:luft", {
|
||||
description = "MeineLuft (Du hacker du!)",
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
|
||||
walkable = false, -- Würde den Spieler mit dem Luftnode kollidieren lassen
|
||||
pointable = false, -- Sie können den Node nicht auswählen
|
||||
diggable = false, -- Sie können den Node nicht abbauen
|
||||
buildable_to = true, -- Nodes können diesen Node ersetzen
|
||||
-- (Sie können einen Node platzieren und den Luftnode
|
||||
-- entfernen die früher einmal da waren)
|
||||
|
||||
air_equivalent = true,
|
||||
drop = "",
|
||||
groups = {not_in_creative_inventory=1}
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## Beleuchtung und Sonnenlichtausbreitung
|
||||
|
||||
Die Beleuchtung eines Nodes wird in param1 gespeichert. Um herauszufinden, wie man die
|
||||
Seite eines Nodes zu schattieren hat, wird der Lichtwert des benachbarten Nodes verwendet.
|
||||
Aus diesem Grund haben solide Nodes keine Lichtwerte, da sie das Licht blockieren.
|
||||
|
||||
Standardmäßig lässt ein Nodetyp nicht zu, dass Licht in Nodeinstanzen gespeichert wird.
|
||||
Es ist normalerweise wünschenswert, dass einige Nodes wie Glas und Luft in der Lage sind,
|
||||
Licht durchzulassen. Zu diesem Zweck müssen zwei Eigenschaften definiert werden:
|
||||
|
||||
```lua
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
```
|
||||
|
||||
Die erste Zeile bedeutet, dass param1 in der Tat den Lichtwert speichert.
|
||||
Die zweite Zeile bedeutet, dass das Sonnenlicht diesen Node durchlaufen sollte, ohne an Wert zu verlieren.
|
||||
|
||||
## Flüssige Nodes
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}//static/drawtype_liquid.png" alt="Flüssigkeiten Zeichnungstyp">
|
||||
<figcaption>
|
||||
Flüssigkeiten Zeichnungstyp
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Jede Art von Flüssigkeit erfordert zwei Nodedefinitionen - eine für die Flüssigkeitsquelle und
|
||||
eine weitere für die fließende Flüssigkeit.
|
||||
|
||||
```lua
|
||||
-- Einige Eigenschaften wurden entfernt, da sie nicht mehr
|
||||
-- den Anwendungsbereich dieses Kapitels entsprechen.
|
||||
minetest.register_node("default:water_source", {
|
||||
drawtype = "liquid",
|
||||
paramtype = "light",
|
||||
|
||||
inventory_image = minetest.inventorycube("default_water.png"),
|
||||
-- ^ dies ist erforderlich, damit das Inventarbild nicht auch animiert wird
|
||||
|
||||
tiles = {
|
||||
{
|
||||
name = "default_water_source_animated.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 2.0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
special_tiles = {
|
||||
-- Wasserquellenmaterial neuen Typs (größtenteils unbenutzt)
|
||||
{
|
||||
name = "default_water_source_animated.png",
|
||||
animation = {type = "vertical_frames", aspect_w = 16,
|
||||
aspect_h = 16, length = 2.0},
|
||||
backface_culling = false,
|
||||
}
|
||||
},
|
||||
|
||||
--
|
||||
-- Verhalten
|
||||
--
|
||||
walkable = false, -- Der Spieler fällt durch
|
||||
pointable = false, -- Der Spieler kann es nicht auswählen
|
||||
diggable = false, -- Der Spieler kann es nicht abbauen
|
||||
buildable_to = true, -- Nodes können diesen Node ersetzen
|
||||
|
||||
alpha = 160,
|
||||
|
||||
--
|
||||
-- Flüssigkeits Eigenschaften
|
||||
--
|
||||
drowning = 1,
|
||||
liquidtype = "source",
|
||||
|
||||
liquid_alternative_flowing = "default:water_flowing",
|
||||
-- ^ wenn die Flüssigkeit fließt
|
||||
|
||||
liquid_alternative_source = "default:water_source",
|
||||
-- ^ wenn die Flüssigkeit eine Quelle ist
|
||||
|
||||
liquid_viscosity = WATER_VISC,
|
||||
-- ^ wie schnell
|
||||
|
||||
liquid_range = 8,
|
||||
-- ^ wie weit
|
||||
|
||||
post_effect_color = {a=64, r=100, g=100, b=200},
|
||||
-- ^ Farbe des Bildschirms, wenn der Spieler untergetaucht ist
|
||||
})
|
||||
```
|
||||
|
||||
Fließende Nodes haben eine ähnliche Definition, allerdings mit einem anderen Namen und einer anderen Animation.
|
||||
Siehe `default:water_flowing` in der Standard-Mod in `minetest_game` für ein vollständiges Beispiel.
|
||||
|
||||
## Nodeboxen
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}//static/drawtype_nodebox.gif" alt="Nodebox Zeichnungstyp">
|
||||
<figcaption>
|
||||
Nodebox Zeichnungstyp
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Mit Nodeboxen können Sie einen Node erstellen, der nicht würfelförmig ist, sondern
|
||||
aus beliebig vielen Quadern besteht.
|
||||
|
||||
```lua
|
||||
minetest.register_node("stairs:stair_stone", {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
{-0.5, 0, 0, 0.5, 0.5, 0.5},
|
||||
},
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Der wichtigste Teil ist die Nodebox-Tabelle:
|
||||
|
||||
```lua
|
||||
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
{-0.5, 0, 0, 0.5, 0.5, 0.5}
|
||||
```
|
||||
|
||||
Jede Zeile ist ein Quader, der zu einem einzigen Nodepunkt verbunden ist.
|
||||
Die ersten drei Zahlen sind die Koordinaten (von -0,5 bis einschließlich 0,5) der
|
||||
der unteren, vorderen, linken Ecke, die letzten drei Zahlen sind die gegenüberliegende Ecke.
|
||||
Sie haben die Form X, Y, Z, wobei Y für oben steht.
|
||||
|
||||
Sie können den [NodeBoxEditor](https://forum.minetest.net/viewtopic.php?f=14&t=2840) benutzen um
|
||||
Nodeboxen durch Ziehen der Kanten zu erstellen. Das ist anschaulicher als die Arbeit von Hand.
|
||||
|
||||
|
||||
### Wandgehaltene Nodeboxen
|
||||
|
||||
Manchmal möchte man verschiedene Nodeboxen für die Platzierung auf dem Boden, an der Wand oder an der Decke wie bei Fackeln.
|
||||
|
||||
```lua
|
||||
minetest.register_node("default:sign_wall", {
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "wallmounted",
|
||||
|
||||
-- Decke
|
||||
wall_top = {
|
||||
{-0.4375, 0.4375, -0.3125, 0.4375, 0.5, 0.3125}
|
||||
},
|
||||
|
||||
-- Boden
|
||||
wall_bottom = {
|
||||
{-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125}
|
||||
},
|
||||
|
||||
-- Wand
|
||||
wall_side = {
|
||||
{-0.5, -0.3125, -0.4375, -0.4375, 0.3125, 0.4375}
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Mesh Nodes
|
||||
|
||||
Nodeboxen sind zwar im Allgemeinen einfacher zu erstellen, doch sind sie insofern eingeschränkt, als dass
|
||||
sie nur aus Quadern bestehen können. Nodeboxen sind außerdem nicht optimiert;
|
||||
Innenflächen werden auch dann noch gerendert, wenn sie vollständig ausgeblendet sind.
|
||||
|
||||
Eine Fläche ist eine ebene Oberfläche in einem Mesh(Netz). Eine innere Fläche entsteht, wenn sich die Flächen von zwei
|
||||
verschiedenen Nodeboxen überlappen, wodurch Teile des Nodebox-Modells
|
||||
unsichtbar sind, aber dennoch gerendert werden.
|
||||
|
||||
So können Sie ein Mesh Node registrieren:
|
||||
|
||||
```lua
|
||||
minetest.register_node("mymod:meshy", {
|
||||
drawtype = "mesh",
|
||||
|
||||
-- Enthält die Textur für jedes "material"
|
||||
tiles = {
|
||||
"mymod_meshy.png"
|
||||
},
|
||||
|
||||
-- Verzeichnis zum Mesh
|
||||
mesh = "mymod_meshy.b3d",
|
||||
})
|
||||
```
|
||||
|
||||
Vergewissern Sie sich, dass das Mesh in einem `models`-Verzeichnis verfügbar ist.
|
||||
Meistens sollte sich das Mesh im eigenen Mod-Ordner befinden, aber es ist auch in Ordnung, wenn
|
||||
ein Mesh zu verwenden, das von einer anderen Mod bereitgestellt wird, von dem sie abhängig ist. Zum Beispiel kann eine Mod, die
|
||||
weitere Möbeltypen hinzufügt, das Modell einer grundlegenden
|
||||
Möbel-Mod sein.
|
||||
|
||||
|
||||
## Signlike Nodes
|
||||
|
||||
Signlike Nodes sind flache Nodes, die an den Seiten anderer Nodes angebracht werden können.
|
||||
Trotz des Namens dieses Zeichentyps verwenden Schilder in der Regel nicht signlike,
|
||||
sondern verwenden stattdessen den Zeichnungstyp `Nodebox`, um einen 3D-Effekt zu erzielen. Der Zeichentyp `signlike`
|
||||
wird jedoch häufig von Leitern verwendet.
|
||||
|
||||
```lua
|
||||
minetest.register_node("default:ladder_wood", {
|
||||
drawtype = "signlike",
|
||||
|
||||
tiles = {"default_ladder_wood.png"},
|
||||
|
||||
-- Erforderlich: Speichern der Drehung in param2
|
||||
paramtype2 = "wallmounted",
|
||||
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
## Plantlike Nodes
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}//static/drawtype_plantlike.png" alt="Plantlike Zeichnungstyp">
|
||||
<figcaption>
|
||||
Plantlike Zeichnungstyp
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Plantlike Nodes zeichnen ihre Kacheln in einem X-ähnlichen Muster.
|
||||
|
||||
```lua
|
||||
minetest.register_node("default:papyrus", {
|
||||
drawtype = "plantlike",
|
||||
|
||||
-- Nur eine Textur in Verwendung
|
||||
tiles = {"default_papyrus.png"},
|
||||
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-6 / 16, -0.5, -6 / 16, 6 / 16, 0.5, 6 / 16},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Firelike Nodes
|
||||
|
||||
Firelike ist ähnlich zu plantlike, mit der Ausnahme, dass es so konmzipiert ist, dass es auch an Wänden und Decken haften kann.
|
||||
|
||||
<figure>
|
||||
<img src="{{ page.root }}//static/drawtype_firelike.png" alt="Firelike nodes">
|
||||
<figcaption>
|
||||
Firelike nodes
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
minetest.register_node("meinemod:klinger", {
|
||||
drawtype = "firelike",
|
||||
|
||||
-- Nur eine Textur in Verwendung
|
||||
tiles = { "mymod:klinger" },
|
||||
})
|
||||
```
|
||||
|
||||
## Mehr Zeichnungstypen
|
||||
|
||||
Dies ist keine vollständige Liste, es gibt noch weitere Arten:
|
||||
|
||||
* Fencelike
|
||||
* Plantlike rooted - für Unterwasser-Pflanzen
|
||||
* Raillike - für Schienen
|
||||
* Torchlike - für 2D Wand/Boden/Decken-Nodes.
|
||||
Die Fackeln in Minetest Game verwenden zwei verschiedene Node-Definitionen von
|
||||
mesh nodes (default:torch und default:torch_wall).
|
||||
|
||||
Und lese immer [Lua API dokumentation](https://minetest.gitlab.io/minetest/nodes/#node-drawtypes)
|
||||
für eine komplette Liste.
|
309
_de/items/nodes_items_crafting.md
Normal file
309
_de/items/nodes_items_crafting.md
Normal file
@ -0,0 +1,309 @@
|
||||
---
|
||||
title: Nodes, Items und Craften
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 2.1
|
||||
description: Benutze register_node, register_item und register_craft um zu lernen, wie man Nodes, Items und Rezepte erstellt.
|
||||
redirect_from: /de/chapters/nodes_items_crafting.html
|
||||
---
|
||||
|
||||
## Einführung <!-- omit in toc -->
|
||||
|
||||
Neue Nodes, Craftitems und Rezepte zu erstellen
|
||||
sind Grundlagen von vielen Mods
|
||||
|
||||
- [Was sind Nodes und Craftitems?](#was-sind-nodes-und-craftitems)
|
||||
- [Items erstellen](#items-erstellen)
|
||||
- [Itemnamen](#itemnamen)
|
||||
- [Itemaliase](#itemaliase)
|
||||
- [Texturen](#texturen)
|
||||
- [Erstellen von Basis-Nodes](#erstellen-von-basis-nodes)
|
||||
- [Crafting](#crafting)
|
||||
- [Shaped](#shaped)
|
||||
- [Shapeless](#shapeless)
|
||||
- [Cooking und Fuel](#cooking-und-fuel)
|
||||
- [Gruppen](#gruppen)
|
||||
- [Werkzeuge, Fähigkeiten und Grabungstypen](#werkzeuge-fähigkeiten-und-grabungstypen)
|
||||
|
||||
## Was sind Nodes und Craftitems?
|
||||
|
||||
Nodes, Craftitems und Tools sind alles Items. Ein Item ist etwas, das im Inventar gefunden werden kann - sogar wenn es im normalen Gameplay nicht möglich ist.
|
||||
|
||||
Ein Node ist ein Item, das platziert oder in der Welt gefunden werden kann. Jede Position muss belegt werden mit einem und nur einem Node - scheinbar leere Position sind normalerweise Luftnodes.
|
||||
|
||||
Ein Craftitem kann nicht platziert werden. Man kann es nur im Inventar finden oder als gedropptes Item in der Welt.
|
||||
|
||||
Ein Werkzeug ist wie ein Item, hat aber die Fähigkeit zu verschleißen. Wenn das Werkzeug benutzt wird, geht der Abnutzungsbalken nach unten, bis das Werkzeug zerbricht. Werkzeuge können auch nicht gestapelt werden. In Zukunft werden Craftitems und Werkzeuge wahrscheinlich verschmelzen, weil die Unterscheidung zwischen ihnen eher ausgedacht ist.
|
||||
|
||||
## Items erstellen
|
||||
|
||||
Itemdefinitionen bestehen aus einen *Itemnamen* und einer *Definitionstabelle*. Die Definitionstabellen beinhalten Attribute, welche das Verhalten eines Items beeinflussen.
|
||||
|
||||
```lua
|
||||
minetest.register_craftitem("modname:itemname", {
|
||||
description = "Mein spezielles Item",
|
||||
inventory_image = "modname_itemname.png"
|
||||
})
|
||||
```
|
||||
|
||||
### Itemnamen
|
||||
|
||||
jedes Item hat einen Itemnamen, welches auf sich verweist. Er sollte folgendes Format haben:
|
||||
|
||||
modname:itemname
|
||||
|
||||
### Itemaliase
|
||||
Items können auch *Aliase* haben, die auf ihren Namen zeigen. Ein *Alias* ist ein nachgemachter Itemname, der dazu führt, dass die Engine alle Aliase so behandelt als wären es Itemnamen. Es gibt hierfür zwei verbreitete Anwendungsmöglichkeiten:
|
||||
|
||||
* Umbenennen von entfernten Items. Es kann unbekannte Items in der Welt oder im Inventar geben, wenn ein Gegenstand ohne Korrektur aus einen Mod entfernt wird.
|
||||
* Hinzufügen von Abkürzungen. `/giveme dirt` ist einfacher als `/giveme default:dirt`.
|
||||
|
||||
Ein Itemalias zu erstellen ist richtig einfach. Ein guter Weg um sich die Reinfolge von der Argumenten zu merken ist `von → zu` wo *von* der alias ist und *zu* das Orginal.
|
||||
|
||||
```lua
|
||||
minetest.register_alias("dirt", "default:dirt")
|
||||
```
|
||||
|
||||
Mods müssen sicher gehen, dass Alias aufgelöst werden, bevor sie sich direkt mit Itemnamen befassen, da die Engine dies nicht tut. Das ist allerdings ziemlich einfach:
|
||||
|
||||
```lua
|
||||
itemname = minetest.registered_aliases[itemname] or itemname
|
||||
```
|
||||
|
||||
|
||||
### Texturen
|
||||
|
||||
Texturen solten in den textures/ Pfad mit Namen im Format
|
||||
`modname_itemname.png`.\\
|
||||
platziert werden. JPEG Texturen werden unterstüzt, aber sie unterstützen keine Transparenz und haben generell
|
||||
schlechte Qualität und eine niedrige Auflösung.
|
||||
Es ist oft besser das PNG Format zu benutzen.
|
||||
|
||||
Texturen in Minetest sind in der Regel 16 mal 16 Pixel.
|
||||
Sie können in jeder Auflösung sein, es wird jedoch empfohlen, dass sie in der Größenordnung von 2 liegen, beispielsweise 16, 32, 64 oder 128.
|
||||
Das liegt daran, dass andere Auflösungen auf älteren Geräten möglicherweise nicht korrekt unterstützt werden, was zu einer geringeren Leistung führt.
|
||||
|
||||
## Erstellen von Basis-Nodes
|
||||
```lua
|
||||
minetest.register_node("meinemod:diamant", {
|
||||
description = "Alien Diamanten",
|
||||
tiles = {"meinemod_diamant.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, stone=1}
|
||||
})
|
||||
```
|
||||
Die Eigenschaft `tiles` ist eine Tabelle mit Texturnamen, die der Node verwenden wird.
|
||||
Wenn es nur eine Textur gibt, wird diese Textur auf jeder Seite verwendet.
|
||||
Um eine andere Textur pro Seite zu erhalten, geben Sie die Namen von 6 Texturen in dieser Reihenfolge an:
|
||||
|
||||
oben (+Y), unten (-Y), rechts (+X), links (-X), hinten (+Z), vorne (-Z).
|
||||
(+Y, -Y, +X, -X, +Z, -Z)
|
||||
|
||||
Denken Sie daran, dass +Y in Minetest nach oben zeigt, wie es in den meisten 3D-Computerspielen der Fall ist.
|
||||
|
||||
```lua
|
||||
minetest.register_node("meinemod:diamant", {
|
||||
description = "Alien Diamanten",
|
||||
tiles = {
|
||||
"meinemod_diamant_oben.png", -- y+
|
||||
"meinemod_diamant_unten.png", -- y-
|
||||
"meinemod_diamant_rechts.png", -- x+
|
||||
"meinemod_diamant_links.png", -- x-
|
||||
"meinemod_diamant_hinten.png", -- z+
|
||||
"meinemod_diamant_vorne.png", -- z-
|
||||
},
|
||||
is_ground_content = true,
|
||||
groups = {cracky = 3},
|
||||
drop = "meinemod:diamant_fragment"
|
||||
-- ^ Anstatt diamanten dropen zu lassen, lassen Sie meinemod:diamant_fragment dropen
|
||||
})
|
||||
```
|
||||
|
||||
Mit dem Attribut `is_ground_content` können Höhlen über dem Stein erzeugt werden. Dies ist wichtig für jeden Node, der während der Kartenerstellung unterirdisch platziert werden kann. Höhlen werden aus der Welt herausgeschnitten, nachdem alle anderen Nodes in einem Gebiet generiert wurden.
|
||||
|
||||
## Crafting
|
||||
|
||||
Es gibt verschiedene Arten von Craftingrezepten, die durch die Eigenschaft `type` angezeigt werden.
|
||||
|
||||
* shaped - Die Zutaten müssen sich an der richtigen Stelle befinden.
|
||||
* shapeless - Es spielt keine Rolle, wo sich die Zutaten befinden,
|
||||
es muss nur die richtige Menge vorhanden sein.
|
||||
* cooking - Rezepte, die der Ofen verwenden soll.
|
||||
* fuel - Definiert Gegenstände, die in Öfen verbrannt werden können.
|
||||
* tool_repair - Definiert Gegenstände, die mit Werkzeugen repariert werden können.
|
||||
|
||||
Craftingrezepte sind keine Gegenstände, daher verwenden sie keine eindeutigen Itemnamen um sie zu identifizieren.
|
||||
|
||||
### Shaped
|
||||
|
||||
Shaped Rezepte sind Rezepte, bei denen die Zutaten die richtige Form oder das richtige
|
||||
Muster haben müssen, um zu funktionieren. In dem folgenden Beispiel müssen die Fragmente in einem
|
||||
stuhlähnlichen Muster liegen, damit das Crafting funktioniert.
|
||||
|
||||
```lua
|
||||
minetest.register_craft({
|
||||
type = "shaped",
|
||||
output = "meinemod:diamant_stuhl 99",
|
||||
recipe = {
|
||||
{"meinemod:diamant_fragment", "", ""},
|
||||
{"meinemod:diamant_fragment", "meinemod:diamant_fragment", ""},
|
||||
{"meinemod:diamant_fragment", "meinemod:diamant_fragment", ""}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Zu beachten ist auch die leere Spalte auf der rechten Seite.
|
||||
Das bedeutet, dass rechts von der Form eine leere Spalte vorhanden sein *muss*, sonst
|
||||
wird dies nicht funktionieren.
|
||||
Wenn diese leere Spalte nicht erforderlich sein sollte, können die leeren Strings
|
||||
folgendermaßen weggelassen werden:
|
||||
|
||||
```lua
|
||||
minetest.register_craft({
|
||||
type = "shaped",
|
||||
output = "meinemod:diamant_stuhl 99",
|
||||
recipe = {
|
||||
{"meinemod:diamant_fragment", "" },
|
||||
{"meinemod:diamant_fragment", "meinemod:diamant_fragment"},
|
||||
{"meinemod:diamant_fragment", "meinemod:diamant_fragment"}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Das Feld "type" wird für Shaped Rezepte nicht benötigt, da "shaped" der
|
||||
Standard-Craftingtyp ist.
|
||||
|
||||
### Shapeless
|
||||
|
||||
Shapeless Rezepte sind eine Art von Rezepten, bei denen es nicht darauf ankommt
|
||||
wo die Zutaten platziert werden, sondern nur, dass sie da sind.
|
||||
|
||||
```lua
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "meinemod:diamant 3",
|
||||
recipe = {
|
||||
"meinemod:diamant_fragment",
|
||||
"meinemod:diamant_fragment",
|
||||
"meinemod:diamant_fragment",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Cooking und Fuel
|
||||
|
||||
Rezepte des Typs "cooking" werden nicht im crafting grid hergestellt,
|
||||
sondern im Ofen oder anderen Kochwerkzeugen, welche womöglich in Mods gefunden werden können, gekocht
|
||||
|
||||
|
||||
```lua
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "meinemod:diamant_fragment",
|
||||
recipe = "default:coalblock",
|
||||
cooktime = 10,
|
||||
})
|
||||
```
|
||||
|
||||
Der einzige wirklich Unterschied im Code ist, dass das Rezept nur ein Item
|
||||
im Vergleich zu einer Tabelle (zwischen geschweiften Klammern) beinhaltet.
|
||||
Sie haben optional auch einen "cooktime" Parameter, welcher
|
||||
definiert wie lange die Items zu kochen brauchen.
|
||||
Wenn dies nicht gesetzt ist, ist der Standard 3.
|
||||
|
||||
Das Rezept oben funktioniert, wenn der Kohlenode im Input-Slot ist,
|
||||
mit irgendeiner Form von Brennstoff darunter.
|
||||
Es stellt nach 10 Sekunden ein Diamant-Fragment her!
|
||||
|
||||
Dieser Typ ist eine Ergänzung zum Kochtyp, da er definiert,
|
||||
was in Öfen und anderen Kochgeräten aus Mods verbrannt werden kann.
|
||||
|
||||
```lua
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "meinemod:diamant",
|
||||
burntime = 300,
|
||||
})
|
||||
```
|
||||
|
||||
Sie haben keinen Ausgabe wie andere Rezepte, aber sie haben eine Brenndauer
|
||||
die angibt, wie lange sie in Sekunden als Brennstoff reichen.
|
||||
Der Diamant ist also 300 Sekunden lang als Brennstoff verwendbar!
|
||||
|
||||
## Gruppen
|
||||
|
||||
Items können Mitglieder von vielen Gruppen sein und Gruppen können viele Mitglieder haben.
|
||||
Gruppen werden durch die Verwendung der `groups`-Eigenschaft in der Definitions-Tabelle definiert
|
||||
und haben einen dazugehörigen Wert.
|
||||
|
||||
```lua
|
||||
groups = {cracky = 3, wood = 1}
|
||||
```
|
||||
|
||||
Es gibt mehrere Gründe für die Verwendung von Gruppen.
|
||||
Erstens werden Gruppen verwendet, um Eigenschaften wie Abbautyp und Entflammbarkeit zu beschreiben.
|
||||
Zweitens können Gruppen in einem Handwerksrezept anstelle eines Itemnamens verwendet werden, damit
|
||||
ein beliebiges Item aus der Gruppe verwendet werden kann.
|
||||
|
||||
```lua
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "meinemod:diamant_ding 3",
|
||||
recipe = {"group:holz", "mymod:diamant"}
|
||||
})
|
||||
```
|
||||
|
||||
## Werkzeuge, Fähigkeiten und Grabungstypen
|
||||
|
||||
Grabungstypen sind Gruppen, die dazu benutzt werden, um zu definieren wie stark ein Node ist, wenn er
|
||||
von verschiedenen Werkzeugen abgebaut wird.
|
||||
Eine Grabtypgruppe mit einem höheren Wert bedeutet, dass der Node leichter
|
||||
und schneller abzubauen ist.
|
||||
Es ist möglich, mehrere Grabtypen zu kombinieren, um eine effizientere Nutzung
|
||||
von mehreren Werkzeugtypen zu erzielen.
|
||||
Ein Node ohne Grabtypen kann mit keinem Werkzeug abgebaut werden.
|
||||
|
||||
|
||||
| Gruppe | Bestes Werkzeug | Beschreibung |
|
||||
|---------|-----------------|-------------|
|
||||
| crumbly | Schaufel | Erde, Sand |
|
||||
| cracky | Spitzhacke | Zähes (aber brüchiges) Material wie Stein |
|
||||
| snappy | *irgendeins* | Kann mit feinen Werkzeugen geschnitten werden;<br>z.B. Blätter, kleine Pflanzen, Draht, Metallbleche |
|
||||
| choppy | Axt | Kann mit scharfer Gewalt geschnitten werden, z. B. Bäume, Holzbretter |
|
||||
| fleshy | Schwert | Lebende Dinge wie Tiere und der Spieler <br>Das könnte einige Bluteffekte beim Schlagen mit sich bringen. |
|
||||
| explody | ? | Besonders anfällig für Explosionen |
|
||||
| oddly_breakable_by_hand | *irgendeins* | Fackeln und dergleichen - sehr schnell zu graben |
|
||||
|
||||
|
||||
Jedes Werkzeug hat eine Werkzeugfähigkeit.
|
||||
Eine Fähigkeit umfasst eine Liste der unterstützten Grabtypen und die zugehörigen Eigenschaften
|
||||
für jeden Typ, wie z. B. die Grabungszeiten und der Grad der Abnutzung.
|
||||
Werkzeuge können auch eine maximal unterstützte Härte für jeden Typ haben, was es ermöglicht
|
||||
schwächere Werkzeuge daran zu hindern, härtere Nodes zu graben.
|
||||
Es ist sehr üblich, dass Werkzeuge alle Grabtypen in ihren Fähigkeiten enthalten.
|
||||
Die weniger geeigneten haben dabei sehr ineffiziente Eigenschaften.
|
||||
Wenn der Gegenstand, den ein Spieler gerade trägt, keine explizite Werkzeugfähigkeit
|
||||
hat, dann wird stattdessen die Fähigkeit der aktuellen Hand verwendet.
|
||||
|
||||
```lua
|
||||
minetest.register_tool("meinemod:werkzeug", {
|
||||
description = "Mein Werkzeug",
|
||||
inventory_image = "meinemod_werkzeug.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.5,
|
||||
max_drop_level = 1,
|
||||
groupcaps = {
|
||||
crumbly = {
|
||||
maxlevel = 2,
|
||||
uses = 20,
|
||||
times = { [1]=1.60, [2]=1.20, [3]=0.80 }
|
||||
},
|
||||
},
|
||||
damage_groups = {fleshy=2},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
Groupcaps ist die Liste der unterstützten Grabungstypen für Grabungsnodes.
|
||||
Schadensgruppen (Damage Groups) dienen zur Steuerung der Art und Weise, wie Werkzeuge Objekte beschädigen; dies wird
|
||||
später im Kapitel Objekte, Spieler und Entities behandelt werden.
|
232
_de/map/environment.md
Normal file
232
_de/map/environment.md
Normal file
@ -0,0 +1,232 @@
|
||||
---
|
||||
title: Grundlegende Kartenoperationen
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 3.1
|
||||
description: Grundlegende Operationen wie set_node und get_node
|
||||
redirect_from: /de/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)
|
||||
- [Nodes lesen](#nodes-lesen)
|
||||
- [Nodes finden](#nodes-finden)
|
||||
- [Schreiben](#schreiben)
|
||||
- [Nodes schreiben](#nodes-schreiben)
|
||||
- [Nodes löschen](#nodes-löschen)
|
||||
- [Mapblöcke laden](#mapblöcke-laden)
|
||||
- [Nodes löschen](#nodes-löschen-1)
|
||||
|
||||
## Karten-Struktur
|
||||
|
||||
Die Minetest-Karte ist in Map-Blöcke(nicht zu verwechseln mit Nodes im 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*-Nodes, einem unpassierbaren, nicht auswählbaren Platzhalternode. Leerer Raum ist
|
||||
voll von *Luft*-Blöcken, einem unsichtbaren Node, durch den man hindurchgehen kann.
|
||||
|
||||
Ein aktiver Map-Block ist ein Node, der geladen ist und für den Aktualisierungen durchgeführt werden.
|
||||
|
||||
Geladene Map-Blöcke werden oft als *aktive Nodes* bezeichnet. Aktive Nodes 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
|
||||
|
||||
### Nodes 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 Node der Map-Block gemeint. Daher emphielt sich für die Variabelnamen node(Node) zu verwenden
|
||||
print(dump(node)) --> { name=.., param1=.., param2=.. }
|
||||
```
|
||||
|
||||
Handelt es sich bei der Position um eine Dezimalzahl, so wird sie auf den enthaltenen Node gerundet.
|
||||
Die Funktion gibt immer eine Tabelle mit den Nodeinformationen zurück:
|
||||
|
||||
* `name` - Der Nodename, der beim Entladen des Bereichs *ignoriert* wird.
|
||||
* `param1` - Siehe Node-Definition. Dieser ist in der Regel light.
|
||||
* `param2` - Siehe Node-Definition.
|
||||
|
||||
Es ist erwähnenswert, dass die Funktion den enthaltenen Node nicht lädt, wenn der Node
|
||||
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 Node dann immer noch nicht geladen.
|
||||
Dies kann immer noch `ignore` zurückgeben, wenn ein Node 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.
|
||||
|
||||
### Nodes finden
|
||||
|
||||
Minetest bietet eine Reihe von Hilfsfunktionen, um gängige Map-Aktionen zu beschleunigen.
|
||||
Die am häufigsten verwendeten Funktionen dienen dem Auffinden von Nodesn.
|
||||
|
||||
Angenommen, wir wollen eine bestimmte Pflanzenart herstellen, die besser in der Nähe von Mese wächst;
|
||||
müssten Sie nach allen Mese-Blöcke in der Nähe suchen,
|
||||
und die Wachstumsrate entsprechend anpassen.
|
||||
|
||||
`minetest.find_node_near` liefert den ersten gefundenen Node in einem bestimmten Radius
|
||||
der mit den angegebenen Nodenamen oder Gruppen übereinstimmt. Im folgenden Beispiel,
|
||||
suchen wir nach einem Mese-Node innerhalb von 5 Node von der Position:
|
||||
```lua
|
||||
local wachstums_geschwindigkeit = 1
|
||||
local node_pos = minetest.find_node_near(pos, 5, { "default:mese" })
|
||||
if node_pos then
|
||||
minetest.chat_send_all("Bei " .. dump(node_pos) .. " Node gefunden")
|
||||
wachstums_geschwindigkeit = 2
|
||||
end
|
||||
```
|
||||
|
||||
Nehmen wir zum Beispiel an, dass die Wachstumsrate steigt, je mehr Mese in der Nähe ist.
|
||||
Dann sollten Sie eine Funktion verwenden, die mehrere Nodes in dem Gebiet finden kann:
|
||||
|
||||
```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 wachstums_geschwindigkeit = 1 + #pos_list
|
||||
```
|
||||
|
||||
Der obige Code ermittelt die Anzahl der Node in einem *kubischen Volumen*. Dies ist anders
|
||||
zu `find_node_near`, das den Abstand zur Position (d.h. einer *Kugel*) verwendet.
|
||||
Um dies zu beheben, müssen wir den Bereich manuell selbst überprüfen:
|
||||
|
||||
```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
|
||||
wachstums_geschwindigkeit = wachstums_geschwindigkeit + 1
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Jetzt erhöht der Code korrekt die `wachstums_geschwindigkeit` basierend auf der Anzahl der Node in Reichweite.
|
||||
|
||||
Beachten Sie, dass wir den quadrierten Abstand von der Position verglichen haben, anstatt
|
||||
ihn zu quadrierenum die tatsächliche Entfernung zu erhalten. Dies liegt daran, dass Quadratwurzeln
|
||||
für den Computer sehr rechenintensiv sind und daher möglichst vermieden werden sollten.
|
||||
|
||||
Es gibt weitere Variationen der beiden oben genannten Funktionen, wie z.B.
|
||||
`find_nodes_with_meta` und `find_nodes_in_area_under_air`, die ähnlich funktionieren
|
||||
und unter anderen Umständen nützlich sind.
|
||||
|
||||
## Schreiben
|
||||
|
||||
### Nodes schreiben
|
||||
|
||||
Sie können `set_node` verwenden, um in die Karte zu schreiben. Jeder Aufruf von set_node wird dazu führen, dass
|
||||
die Beleuchtung neu berechnet und Node-Callbacks ausgeführt werden, was bedeutet, dass set_node
|
||||
bei einer großen Anzahl von Nodesn ziemlich langsam ist.
|
||||
|
||||
```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 entfernt alle zugehörigen Metadaten oder Bestände von dieser Position.
|
||||
Dies ist nicht unter allen Umständen wünschenswert, insbesondere wenn Sie mehrere
|
||||
Node-Definitionen verwenden, um einen konzeptionellen Nodes zu repräsentieren. Ein Beispiel hierfür ist der
|
||||
Ofennode - während man ihn konzeptionell als einen Node betrachtet, sind es eigentlich
|
||||
zwei.
|
||||
|
||||
Sie können einen Node setzen, ohne die Metadaten oder das Inventar zu löschen, wie folgt:
|
||||
|
||||
```lua
|
||||
minetest.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
|
||||
```
|
||||
|
||||
### Nodes löschen
|
||||
|
||||
Ein Node muss immer vorhanden sein. Um einen Node zu entfernen, setzen Sie die Position auf "Luft".
|
||||
|
||||
Die folgenden beiden Zeilen entfernen beide einen Node und sind identisch:
|
||||
|
||||
```lua
|
||||
minetest.remove_node(pos)
|
||||
minetest.set_node(pos, { name = "air" })
|
||||
```
|
||||
|
||||
Tatsächlich ist remove_node nur eine Hilfsfunktion, die set_node mit `"air"` aufruft.
|
||||
|
||||
## Mapblöcke laden
|
||||
|
||||
Sie können `minetest.emerge_area` verwenden, um Map-Blöcke zu laden. Emerge area ist asynchron,
|
||||
das heißt, die Mapblöcke werden nicht sofort geladen. Stattdessen werden sie
|
||||
in der Zukunft geladen und der Callback wird jedes Mal aufgerufen.
|
||||
|
||||
```lua
|
||||
-- Lädt einen 20x20x20-Bereich
|
||||
local halbegroesse = { x = 10, y = 10, z = 10 } --ss = ß
|
||||
local pos1 = vector.subtract(pos, halbegroesse)
|
||||
local pos2 = vector.add (pos, halbegroesse)
|
||||
|
||||
local kontext = {} -- Daten zwischen Callback-Aufrufen aufrechterhalten
|
||||
minetest.emerge_area(pos1, pos2, emerge_callback, kontext)
|
||||
```
|
||||
|
||||
Minetest ruft `emerge_callback` immer dann auf, wenn er einen Map-Block, mit einigen
|
||||
Fortschrittsinformationen, lädt.
|
||||
|
||||
```lua
|
||||
local function emerge_callback(pos, aktion,
|
||||
verbleibende_calls, kontext)
|
||||
-- Beim ersten Aufruf, Anzahl der Mapblöcke erfassen
|
||||
if not kontext.bloecke_insgesamt then
|
||||
kontext.bloecke_insgesamt = verbleibende_calls + 1
|
||||
kontext.geladene_bloecke = 0
|
||||
end
|
||||
|
||||
-- Erhöhung der Anzahl der geladenen Nodes
|
||||
kontext.bloecke_insgesamt = kontext.geladene_bloecke + 1
|
||||
|
||||
-- Fortschrittsmeldung senden
|
||||
if kontext.bloecke_insgesamt == kontext.geladene_bloecke then
|
||||
minetest.chat_send_all("Blöcke laden abgeschlossen!")
|
||||
end
|
||||
local perc = 100 * kontext.geladene_bloecke / kontext.bloecke_insgesamt
|
||||
local msg = string.format("Geladene Nodes %d/%d (%.2f%%)",
|
||||
kontext.geladene_bloecke, kontext.bloecke_insgesamt, perc)
|
||||
minetest.chat_send_all(msg)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Dies ist nicht die einzige Möglichkeit, Mapblöcke zu laden; die Verwendung eines
|
||||
[Lua Voxel Manipulator (LVM)](../advmap/lvm.html) bewirkt ebenfalls, dass die
|
||||
umschlossenen Mapblöcke synchron geladen werden.
|
||||
|
||||
## Nodes löschen
|
||||
|
||||
Sie können delete_blocks verwenden, um einen Bereich von Map-Blöcken zu löschen:
|
||||
|
||||
```lua
|
||||
-- Löscht einen 20x20x20-Bereich
|
||||
local halbegroesse = { x = 10, y = 10, z = 10 }
|
||||
local pos1 = vector.subtract(pos, halbegroesse)
|
||||
local pos2 = vector.add (pos, halbegroesse)
|
||||
|
||||
minetest.delete_area(pos1, pos2)
|
||||
```
|
||||
|
||||
Dadurch werden alle Map-Blöcke in diesem Bereich *inklusive* gelöscht. Das bedeutet, dass einige
|
||||
Blöcke außerhalb des Bereichs gelöscht werden, da sie sich auf einem Map-Block befinden, der sich mit den
|
||||
die Bereichsgrenzen überlappen.
|
363
_de/map/objects.md
Normal file
363
_de/map/objects.md
Normal file
@ -0,0 +1,363 @@
|
||||
---
|
||||
title: Objekte, Spieler und Entities
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 3.4
|
||||
description: Nutzung eines ObjectRef
|
||||
degrad:
|
||||
level: warning
|
||||
title: Grad and Radiant
|
||||
message: >-
|
||||
Die Drehung von Anbauteilen wird in Grad angegeben, während die Drehung
|
||||
von Objekten in Radiant angegeben wird. Stellen Sie sicher, dass Sie das
|
||||
richtige Winkelmaß verwenden.
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
In diesem Kapitel lernen Sie, wie man Objekte manipuliert und eigene Objekte
|
||||
definiert.
|
||||
|
||||
- [Was sind Objekte, Spieler, und Entities?](#was-sind-objekte-spieler-und-entities)
|
||||
- [Position und Geschwindigkeit](#position-und-geschwindigkeit)
|
||||
- [Objekt-Eigenschaften](#objekt-eigenschaften)
|
||||
- [Entities](#entities)
|
||||
- [Leben und Schaden](#leben-und-schaden)
|
||||
- [Lebenspunkte (HP)](#lebenspunkte-hp)
|
||||
- [Schlagen, Damage Groups, und Armor Groups](#schlagen-damage-groups-und-armor-groups)
|
||||
- [Beispiel für die Schadensberechnung](#beispiel-für-die-schadensberechnung)
|
||||
- [Anhänge](#anhänge)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
## Was sind Objekte, Spieler, und Entities?
|
||||
|
||||
Spieler und Entities sind beide Arten von Objekten. Ein Objekt ist etwas, das sich unabhängig
|
||||
vom Node-Raster bewegen kann und Eigenschaften wie Geschwindigkeit und Skalierung besitzt.
|
||||
Objekte sind keine Gegenstände, und sie haben ihr eigenes Registrierungssystem.
|
||||
|
||||
Es gibt ein paar Unterschiede zwischen Spielern und Entities.
|
||||
Der größte ist, dass Spieler von Spielern gesteuert werden, während Entities von Mods gesteuert werden.
|
||||
Das bedeutet, dass die Geschwindigkeit eines Spielers nicht von Mods eingestellt werden kann -
|
||||
Spieler sind client-seitig, und Entities sind serverseitig.
|
||||
Ein weiterer Unterschied ist, dass Spieler das Laden von Kartenblöcken verursachen, während Entities
|
||||
nur gespeichert werden und inaktiv werden.
|
||||
|
||||
Diese Unterscheidung wird durch die Tatsache erschwert, dass Entities über eine Tabelle
|
||||
gesteuert werden, die als Lua entity bezeichnet wird, wie später erläutert wird.
|
||||
|
||||
## Position und Geschwindigkeit
|
||||
|
||||
`get_pos` und `set_pos` existieren, um die Position eines Entitys zu ermitteln und zu setzen.
|
||||
|
||||
```lua
|
||||
local objekt = minetest.get_player_by_name("bob")
|
||||
local pos = objekt:get_pos()
|
||||
objekt:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z })
|
||||
```
|
||||
|
||||
`set_pos` setzt die Position sofort und ohne Animation. Wenn Sie ein Objekt
|
||||
sanft an die neue Position animieren möchten, sollte man `move_to` verwenden.
|
||||
Dies funktioniert leider nur für Entities.
|
||||
|
||||
```lua
|
||||
objekt:move_to({ x = pos.x, y = pos.y + 1, z = pos.z })
|
||||
```
|
||||
|
||||
Ein wichtiger Punkt beim Umgang mit Entities ist die Latenzzeit im Netz.
|
||||
In einer idealen Welt würden Nachrichten über Entitybewegungen sofort ankommen,
|
||||
in der richtigen Reihenfolge und in einem ähnlichen Intervall ankommen, wie Sie sie gesendet haben.
|
||||
Solange man sich jedoch nicht im Einzelspielermodus befindet, ist dies keine ideale Welt.
|
||||
Nachrichten brauchen eine Weile, bis sie ankommen. Positionsnachrichten können in der falschen Reihenfolge ankommen,
|
||||
was dazu führt, dass einige `set_pos`-Aufrufe übersprungen werden, da es keinen Sinn macht, zu einer
|
||||
Position zu gehen, die älter ist als die aktuell bekannte Position.
|
||||
Bewegungen können nicht in ähnlichen Abständen erfolgen, was es schwierig macht, sie für Animationen zu verwenden.
|
||||
All dies führt dazu, dass der Client andere Dinge sieht als der Server, und das ist etwas,
|
||||
das Sie beachten müssen.
|
||||
|
||||
## Objekt-Eigenschaften
|
||||
|
||||
Objekt-Eigenschaften werden verwendet, um dem Client mitzuteilen, wie ein Objekt zu rendern und zu behandeln ist.
|
||||
Es ist nicht möglich, benutzerdefinierte Eigenschaften zu definieren, denn die Eigenschaften sind
|
||||
per Definition von der Engine zu verwenden.
|
||||
|
||||
Im Gegensatz zu Blöcken haben Objekte ein dynamisches und kein festes Aussehen.
|
||||
Sie können unter anderem das Aussehen eines Objekts jederzeit ändern, indem Sie
|
||||
seine Eigenschaften ändern.
|
||||
|
||||
```lua
|
||||
object:set_properties({
|
||||
visual = "mesh",
|
||||
mesh = "character.b3d",
|
||||
textures = {"character_texture.png"},
|
||||
visual_size = {x=1, y=1},
|
||||
})
|
||||
```
|
||||
|
||||
Die aktualisierten Eigenschaften werden an alle Spieler in Reichweite gesendet.
|
||||
Dies ist sehr nützlich, um eine große Menge an Vielfalt sehr einfach zu bekommen, wie zum Beispiel
|
||||
verschiedene Skins pro Spieler.
|
||||
|
||||
Wie im nächsten Abschnitt gezeigt wird, können Entities Erst-Eigenschaften haben,
|
||||
die in ihrer Definition angegeben werden.
|
||||
Die Standardeigenschaften des Spielers sind jedoch in der Engine definiert, so dass man
|
||||
`on_joinplayer` die Funktion `set_properties()` verwenden kann, um die Eigenschaften für neue
|
||||
Spieler zu setzen.
|
||||
|
||||
## Entities
|
||||
|
||||
Ein Entity hat eine Definitionstabelle, die einer Objektdefinitionstabelle ähnelt.
|
||||
Diese Tabelle kann Callback-Methoden, anfängliche Objekteigenschaften und benutzerdefinierte
|
||||
Mitglieder enthalten.
|
||||
|
||||
```lua
|
||||
local MeinEntity = {
|
||||
initial_properties = {
|
||||
hp_max = 1,
|
||||
physical = true,
|
||||
collide_with_objects = false,
|
||||
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
|
||||
visual = "wielditem",
|
||||
visual_size = {x = 0.4, y = 0.4},
|
||||
textures = {""},
|
||||
spritediv = {x = 1, y = 1},
|
||||
initial_sprite_basepos = {x = 0, y = 0},
|
||||
},
|
||||
|
||||
message = "Default message",
|
||||
}
|
||||
|
||||
function MeinEntity:set_message(msg)
|
||||
self.message = msg
|
||||
end
|
||||
```
|
||||
|
||||
Entity-Definitionen unterscheiden sich in einem sehr wichtigen Punkt von Item-Definitionen.
|
||||
Wenn ein Entity auftaucht (d.h.: geladen oder erstellt wird), wird eine neue Tabelle für
|
||||
dieses Entity erstellt, die es von der Definitionstabelle *erbt*.
|
||||
|
||||
<!--
|
||||
Diese Vererbung wird mit Hilfe von Metatabellen durchgeführt.
|
||||
Metatabellen sind eine wichtige Lua-Funktion, die Sie kennen müssen, da sie
|
||||
ist ein wesentlicher Bestandteil der Lua-Sprache. Laienhaft ausgedrückt, erlaubt eine Metatabelle
|
||||
zu steuern, wie sich die Tabelle bei der Verwendung bestimmter Lua-Syntaxen verhält. Die häufigste
|
||||
Verwendung von Metatabellen ist die Möglichkeit, eine andere Tabelle als Prototyp zu verwenden,
|
||||
Eigenschaften und Methoden der anderen Tabelle zu verwenden,
|
||||
wenn sie in der aktuellen Tabelle nicht vorhanden sind.
|
||||
Angenommen, Sie wollen auf `a.x` zugreifen. Wenn die Tabelle `a` dieses Element hat, dann wird es
|
||||
normal zurückgegeben. Wenn die Tabelle jedoch nicht über dieses Element verfügt und die
|
||||
metatable eine Tabelle `b` als Prototyp aufführt, wird die Tabelle `b` daraufhin überprüft
|
||||
um zu sehen, ob sie dieses Mitglied hat.
|
||||
-->
|
||||
|
||||
Sowohl ein ObjectRef als auch eine Entity-Tabelle bieten Möglichkeiten, das Gegenstück zu erhalten:
|
||||
|
||||
```lua
|
||||
local entity = object:get_luaentity()
|
||||
local objekt = entity.objekt
|
||||
print("Entity ist bei " .. minetest.pos_to_string(objekt:get_pos()))
|
||||
```
|
||||
|
||||
Es gibt eine Reihe von Callbacks für die Verwendung mit Entities.
|
||||
Eine vollständige Liste findet sich in [lua_api.txt 🇬🇧](https://minetest.gitlab.io/minetest/minetest-namespace-reference/#registered-definition-tables).
|
||||
|
||||
```lua
|
||||
function MeinEntity:on_step(dtime)
|
||||
local pos = self.object:get_pos()
|
||||
local pos_drunter = vector.subtract(pos, vector.new(0, 1, 0))
|
||||
|
||||
local delta
|
||||
if minetest.get_node(pos_drunter).name == "air" then
|
||||
delta = vector.new(0, -1, 0)
|
||||
elseif minetest.get_node(pos).name == "air" then
|
||||
delta = vector.new(0, 0, 1)
|
||||
else
|
||||
delta = vector.new(0, 1, 0)
|
||||
end
|
||||
|
||||
delta = vector.multiply(delta, dtime)
|
||||
|
||||
self.object:move_to(vector.add(pos, delta))
|
||||
end
|
||||
|
||||
function MeinEntity:on_punch(hitter)
|
||||
minetest.chat_send_player(hitter:get_player_name(), self.message)
|
||||
end
|
||||
```
|
||||
|
||||
Wenn Sie nun diese Entity spawnen und verwenden würden, würden Sie feststellen, dass die Nachricht
|
||||
vergessen wird, wenn die Entity inaktiv und dann wieder aktiv wird.
|
||||
Das liegt daran, dass die Nachricht nicht gespeichert wird.
|
||||
Anstatt alles in der Entity-Tabelle zu speichern, gibt Minetest Ihnen die Kontrolle darüber
|
||||
wie die Dinge gespeichert werden sollen.
|
||||
Staticdata ist ein String, der alle benutzerdefinierten Informationen enthält, die
|
||||
gespeichert werden müssen.
|
||||
|
||||
```lua
|
||||
function MeinEntity:get_staticdata()
|
||||
return minetest.write_json({
|
||||
message = self.message,
|
||||
})
|
||||
end
|
||||
|
||||
function MeinEntity:on_activate(staticdata, dtime_s)
|
||||
if staticdata ~= "" and staticdata ~= nil then
|
||||
local data = minetest.parse_json(staticdata) or {}
|
||||
self:set_message(data.message)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Minetest kann `get_staticdata()` so oft wie gewünscht und zu jeder Zeit aufrufen.
|
||||
Der Grund dafür ist, dass Minetest nicht darauf wartet, dass ein Map-Block inaktiv wird,
|
||||
um ihn zu speichern, da dies zu Datenverlusten führen würde. Map-Blöcke werden ungefähr alle 18
|
||||
Sekunden gespeichert, also sollten Sie ein ähnliches Intervall für den Aufruf von `get_staticdata()` feststellen.
|
||||
|
||||
`on_activate()` wird dagegen nur aufgerufen, wenn eine Entity
|
||||
aktiv wird, entweder wenn der Map-Block aktiv wird oder wenn die Entity spawnen wird.
|
||||
Dies bedeutet, dass staticdata leer sein könnte.
|
||||
|
||||
Schließlich müssen Sie die Typentabelle mit der treffenden Bezeichnung `register_entity` registrieren.
|
||||
|
||||
```lua
|
||||
minetest.register_entity("meinemod:entity", MeinEntity)
|
||||
```
|
||||
|
||||
Die Entity kann von einem Mod wie folgt erzeugt werden:
|
||||
|
||||
```lua
|
||||
local pos = { x = 1, y = 2, z = 3 }
|
||||
local obj = minetest.add_entity(pos, "meinemod:entity", nil)
|
||||
```
|
||||
|
||||
Der dritte Parameter sind die anfänglichen statischen Daten.
|
||||
Um die Nachricht einzustellen, können Sie die Methode der Entity-Tabelle verwenden:
|
||||
|
||||
```lua
|
||||
obj:get_luaentity():set_message("hello!")
|
||||
```
|
||||
|
||||
Spieler mit dem *give* [Privileg](../players/privileges.html) können
|
||||
einen [Chat command](../players/chat.html) zum spawnen von entities benutzen:
|
||||
|
||||
/spawnentity mymod:entity
|
||||
|
||||
|
||||
## Leben und Schaden
|
||||
|
||||
### Lebenspunkte (HP)
|
||||
|
||||
Jedes Objekt hat eine Anzahl von Lebenspunkten (HP), die die aktuelle Gesundheit darstellt.
|
||||
Spieler haben eine maximale Lebenspunktzahl, die mit der Objekteigenschaft `hp_max` festgelegt wird.
|
||||
Ein Objekt stirbt, wenn seine HP 0 erreichen.
|
||||
|
||||
```lua
|
||||
local hp = object:get_hp()
|
||||
object:set_hp(hp + 3)
|
||||
```
|
||||
|
||||
### Schlagen, Damage Groups, und Armor Groups
|
||||
|
||||
Schaden ist die Verringerung der HP eines Objekts. Ein Objekt kann ein anderes Objekt *schlagen*, um
|
||||
Schaden zuzufügen. Ein Schlag muss nicht unbedingt ein echter Schlag sein - es kann auch eine
|
||||
Explosion, ein Schwerthieb oder etwas anderes sein.
|
||||
|
||||
Der Gesamtschaden wird durch Multiplikation der Schadensgruppen des Schlags mit den
|
||||
Verwundbarkeiten des Ziels berechnet. Dies wird dann begrenzt, je nachdem, wie lange der letzte
|
||||
Schlag her war. Wir werden gleich ein Beispiel für diese Berechnung erläutern.
|
||||
|
||||
Genau wie [Node Grabungsgruppen](../items/nodes_items_crafting.html#tools-capabilities-and-dig-types),
|
||||
können diese Gruppen jeden Namen annehmen und müssen nicht registriert werden. Es ist jedoch
|
||||
üblich, dieselben Gruppennamen wie bei Node digging zu verwenden.
|
||||
|
||||
Wie anfällig ein Objekt für bestimmte Arten von Schaden ist, hängt von seiner
|
||||
`armor_groups` [Objekteigenschaft](#objekt-eigenschaften) ab. Trotz seines irreführenden
|
||||
Namen gibt `armor_groups` den prozentualen Schaden von bestimmten
|
||||
Schadensgruppen an und nicht den Widerstand. Wenn eine Schadensgruppe nicht in den Armor Groups eines Objekts aufgeführt ist,
|
||||
ist das Objekt völlig unverwundbar.
|
||||
|
||||
```lua
|
||||
target:set_armor_groups({
|
||||
fleshy = 90,
|
||||
crumbly = 50,
|
||||
})
|
||||
```
|
||||
|
||||
Im obigen Beispiel erleidet das Objekt 90 % `fleshy` Schadens und 50 % des
|
||||
`crumbly`-Schaden.
|
||||
|
||||
Wenn ein Spieler ein Objekt schlägt, stammen die Schadensgruppen von dem Gegenstand, das er
|
||||
gerade trägt. In anderen Fällen entscheiden die Mods, welche Schadensgruppen verwendet werden.
|
||||
|
||||
### Beispiel für die Schadensberechnung
|
||||
|
||||
Schlagen wir das Objekt `target`:
|
||||
|
||||
```lua
|
||||
local werkzeug_faehigkeiten = {
|
||||
full_punch_interval = 0.8,
|
||||
damage_groups = { fleshy = 5, choppy = 10 },
|
||||
|
||||
-- Dies wird nur für das abbauen von Nodes verwendet, ist aber dennoch erforderlich
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=2.5, [2]=1.20, [3]=0.35}, uses=30, maxlevel=2},
|
||||
},
|
||||
}
|
||||
|
||||
local zeit_seit_letzten_schlag = werkzeug_faehigkeiten.full_punch_interval
|
||||
target:punch(object, zeit_seit_letzten_schlag, werkzeug_faehigkeiten)
|
||||
```
|
||||
|
||||
Berechnen wir nun, wie hoch der Schaden sein wird. Die Schadensgruppen des Schlags sind
|
||||
`fleshy=5` und `choppy=10`, und die Zielperson erleidet 90% Schaden durch fleshy und 0%
|
||||
von choppy.
|
||||
|
||||
Zuerst multiplizieren wir die Schadensgruppen mit der Verwundbarkeit und addieren das Ergebnis.
|
||||
Dann multiplizieren wir mit einer Zahl zwischen 0 oder 1, abhängig von der `zeit_seit_letzten_schlag`.
|
||||
|
||||
```lua
|
||||
= (5*90/100 + 10*0/100) * limit(zeit_seit_letzten_schlag / full_punch_interval, 0, 1)
|
||||
= (5*90/100 + 10*0/100) * 1
|
||||
= 4.5
|
||||
```
|
||||
|
||||
Da die HP eine ganze Zahl sind, wird der Schaden auf 5 Lebenspunkte gerundet.
|
||||
|
||||
|
||||
|
||||
## Anhänge
|
||||
|
||||
Angehängte Objekte bewegen sich, wenn das übergeordnete Objekt - also das Objekt, an das sie angehängt sind -
|
||||
bewegt wird. Ein angefügtes Objekt ist ein Kind des übergeordneten Objekts.
|
||||
Ein Objekt kann eine unbegrenzte Anzahl von Kindern haben, aber höchstens ein Elternteil.
|
||||
|
||||
```lua
|
||||
child:set_attach(parrent, knochen, position, drehung)
|
||||
```
|
||||
|
||||
Die Funktion `get_pos()` eines Objekts gibt immer die globale Position des Objekts zurück,
|
||||
unabhängig davon, ob es angehängt ist oder nicht.
|
||||
`set_attach` nimmt eine relative Position, aber nicht so, wie man es erwarten würde.
|
||||
Die Anhängeposition ist relativ zum Ursprung des Elternobjekts und wird um das 10-fache vergrößert.
|
||||
Also wäre "0,5,0" ein halber Node über dem Ursprung des Elternobjekts.
|
||||
|
||||
{% include notice.html notice=page.degrad %}
|
||||
|
||||
Bei 3D-Modellen mit Animationen wird das Argument knochen verwendet, um das Entity
|
||||
an einen Knochen zu binden.
|
||||
3D-Animationen basieren auf Skeletten - einem Netzwerk von Knochen im Modell, bei dem
|
||||
jedem Knochen eine Position und Drehung zugewiesen werden kann, um das Modell zu verändern, z. B,
|
||||
um den Arm zu bewegen.
|
||||
Das Anhängen an einen Knochen ist nützlich, wenn Sie eine Figur etwas halten lassen wollen:
|
||||
|
||||
```lua
|
||||
obj:set_attach(spieler,
|
||||
"Arm_Right", -- normaler Knochen
|
||||
{x=0.2, y=6.5, z=3}, -- normale Position
|
||||
{x=-100, y=225, z=90}) -- normale Drehung
|
||||
```
|
||||
|
||||
## Sie sind dran
|
||||
|
||||
* Erstelle eine Windmühle, indem du Nodes und eine Entity kombinierst.
|
||||
* Erstelle einen Mob deiner Wahl (nur mit der Entity-API und ohne andere Mods zu verwenden).
|
247
_de/map/storage.md
Normal file
247
_de/map/storage.md
Normal file
@ -0,0 +1,247 @@
|
||||
---
|
||||
title: Storage und Metadaten
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 3.3
|
||||
description: Mod Storage, NodeMetaRef (get_meta).
|
||||
redirect_from:
|
||||
- /de/chapters/node_metadata.html
|
||||
- /de/map/node_metadata.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
In diesem Kapitel erfahren Sie, wie Sie Daten speichern können.
|
||||
|
||||
- [Metadaten](#metadaten)
|
||||
- [Was sind Metadaten?](#was-sind-metadaten)
|
||||
- [Abrufen eines Metadatenobjekts](#abrufen-eines-metadatenobjekts)
|
||||
- [Lesen und Schreiben](#lesen-und-schreiben)
|
||||
- [Besondere Schlüssel](#besondere-schlüssel)
|
||||
- [Speichern von Tabellen](#speichern-von-tabellen)
|
||||
- [Private Metadaten](#private-metadaten)
|
||||
- [Lua Tabellen](#lua-tabellen)
|
||||
- [Mod Storage](#mod-storage)
|
||||
- [Datenbanken](#datenbanken)
|
||||
- [Entscheidung, was man benutzt](#entscheidung-was-man-benutzt)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
## Metadaten
|
||||
|
||||
### Was sind Metadaten?
|
||||
|
||||
In Minetest sind Metadaten ein Key-Value-Speicher, der verwendet wird, um benutzerdefinierte Daten an etwas anzuhängen.
|
||||
Sie können Metadaten verwenden, um Informationen zu einem Node, Spieler oder ItemStack zu speichern.
|
||||
|
||||
Jede Art von Metadaten verwendet genau dieselbe API.
|
||||
Metadaten speichern Werte als Strings, aber es gibt eine Reihe von Methoden um
|
||||
andere primitive Typen zu konvertieren und zu speichern.
|
||||
|
||||
Einige Schlüssel in Metadaten können eine besondere Bedeutung haben.
|
||||
Zum Beispiel wird `infotext` in den Node-Metadaten verwendet, um den Tooltip zu speichern,
|
||||
der angezeigt wird, wenn man mit dem Fadenkreuz über den Node fährt.
|
||||
Um Konflikte mit anderen Mods zu vermeiden, sollten Sie die Standard-Namensraum
|
||||
Konvention für Schlüssel verwenden: `Modname:Schlüsselname`.
|
||||
Die Ausnahme sind konventionelle Daten wie der Name des Besitzers, der als
|
||||
`owner` abgespeichert wird.
|
||||
|
||||
Metadaten sind Daten über Daten.
|
||||
Die Daten selbst, wie der Typ eines Nodes oder die Anzahl eines Stapels, sind keine Metadaten.
|
||||
|
||||
### Abrufen eines Metadatenobjekts
|
||||
|
||||
Wenn Sie die Position eines Nodes kennen, können Sie seine Metadaten abrufen:
|
||||
|
||||
```lua
|
||||
local meta = minetest.get_meta({ x = 1, y = 2, z = 3 })
|
||||
```
|
||||
|
||||
Spieler- und ItemStack-Metadaten werden mit `get_meta()` ermittelt.:
|
||||
|
||||
```lua
|
||||
local pmeta = player:get_meta()
|
||||
local imeta = stack:get_meta()
|
||||
```
|
||||
|
||||
### Lesen und Schreiben
|
||||
|
||||
In den meisten Fällen werden die Methoden `get_<type>()` und `set_<type>()` zum Lesen
|
||||
und zum schreiben in Metadaten verwendet .
|
||||
Metadaten speichern Strings, so dass die String-Methoden direkt den Wert setzen und holen.
|
||||
|
||||
```lua
|
||||
print(meta:get_string("foo")) --> ""
|
||||
meta:set_string("foo", "bar")
|
||||
print(meta:get_string("foo")) --> "bar"
|
||||
```
|
||||
|
||||
Alle typisierten Getter geben einen neutralen Standardwert zurück, wenn der Schlüssel nicht
|
||||
nicht existiert, wie zum Beispiel `""` oder `0`.
|
||||
Sie können `get()` verwenden, um einen String oder nil zurückzugeben.
|
||||
|
||||
Da es sich bei Metadaten um eine Referenz handelt, werden alle Änderungen automatisch in der Quelle aktualisiert.
|
||||
ItemStacks sind jedoch keine Referenzen, daher müssen Sie den Itemstack im
|
||||
Inventar aktualisieren.
|
||||
|
||||
Die nicht typisierten Getter und Setter werden in und aus Strings konvertiert:
|
||||
|
||||
```lua
|
||||
print(meta:get_int("count")) --> 0
|
||||
meta:set_int("count", 3)
|
||||
print(meta:get_int("count")) --> 3
|
||||
print(meta:get_string("count")) --> "3"
|
||||
```
|
||||
|
||||
### Besondere Schlüssel
|
||||
|
||||
`infotext` wird in Node-Metadaten verwendet, um einen Tooltip anzuzeigen, wenn das Fadenkreuz über einem Node schwebt.
|
||||
Dies ist nützlich, um die Eigentümerschaft oder den Status eines Nodes anzuzeigen.
|
||||
|
||||
`description` wird in ItemStack-Metadaten verwendet, um die Beschreibung zu überschreiben,
|
||||
wenn der Mauszeiger über den Stack in einem Inventar bewegt wird.
|
||||
Sie können Farben verwenden, indem Sie sie mit `minetest.colorize()` kodieren.
|
||||
|
||||
`owner` ist ein allgemeiner Schlüssel, der verwendet wird, um den Benutzernamen des Spielers zu speichern,
|
||||
der Eigentümer des Elements oder Node ist.
|
||||
|
||||
### Speichern von Tabellen
|
||||
|
||||
Tabellen müssen in Strings umgewandelt werden, bevor sie gespeichert werden können.
|
||||
Minetest bietet dafür zwei Formate an: Lua und JSON.
|
||||
|
||||
Die Lua-Methode ist in der Regel viel schneller und entspricht dem Format, das Lua
|
||||
für Tabellen verwendet, während JSON ein standardisierteres Format ist, das besser
|
||||
strukturiert und sich gut für den Austausch von Informationen mit
|
||||
einem anderen Programm eignet.
|
||||
|
||||
```lua
|
||||
local daten = { benutzername = "spieler1", punktestand = 1234 }
|
||||
meta:set_string("foo", minetest.serialize(daten))
|
||||
|
||||
daten = minetest.deserialize(minetest:get_string("foo"))
|
||||
```
|
||||
|
||||
### Private Metadaten
|
||||
|
||||
Standardmäßig werden alle Node-Metadaten an den Client gesendet.
|
||||
Sie können Schlüssel als privat markieren, um dies zu verhindern.
|
||||
|
||||
```lua
|
||||
meta:set_string("geheim", "asd34dn")
|
||||
meta:mark_as_private("geheim")
|
||||
```
|
||||
|
||||
### Lua Tabellen
|
||||
|
||||
Sie können mit `to_table` und `from_table` in und aus Lua-Tabellen konvertieren:
|
||||
|
||||
```lua
|
||||
local tmp = meta:to_table()
|
||||
tmp.foo = "bar"
|
||||
meta:from_table(tmp)
|
||||
```
|
||||
|
||||
## Mod Storage
|
||||
|
||||
Mod-Storage verwendet genau dieselbe API wie Metadaten, obwohl sie technisch gesehen keine
|
||||
Metadaten sind.
|
||||
Mod-Speicher ist pro Mod und kann nur während der Ladezeit abgefragt werden, um zu wissen,
|
||||
welche Mod sie anfordert.
|
||||
|
||||
```lua
|
||||
local storage = minetest.get_mod_storage()
|
||||
```
|
||||
|
||||
Sie können den Speicher nun genau wie Metadaten manipulieren:
|
||||
|
||||
```lua
|
||||
storage:set_string("foo", "bar")
|
||||
```
|
||||
|
||||
## Datenbanken
|
||||
|
||||
Wenn der Mod wahrscheinlich auf einem Server verwendet wird und viele Daten speichert,
|
||||
ist es eine gute Idee, eine Datenbank-Speichermethode anzubieten.
|
||||
Sie sollten dies optional machen, indem Sie trennen, wie die Daten gespeichert werden und wo
|
||||
sie verwendet werden.
|
||||
|
||||
```lua
|
||||
local backend
|
||||
if verwende_datenbank then
|
||||
backend =
|
||||
dofile(minetest.get_modpath("meinemod") .. "/backend_sqlite.lua")
|
||||
else
|
||||
backend =
|
||||
dofile(minetest.get_modpath("meinemod") .. "/backend_storage.lua")
|
||||
end
|
||||
|
||||
backend.get_foo("a")
|
||||
backend.set_foo("a", { score = 3 })
|
||||
```
|
||||
|
||||
Die Datei backend_storage.lua sollte eine Mod-Storage-Implementierung enthalten:
|
||||
|
||||
```lua
|
||||
local storage = minetest.get_mod_storage()
|
||||
local backend = {}
|
||||
|
||||
function backend.set_foo(schluessel, wert)
|
||||
storage:set_string(schluessel, minetest.serialize(wert))
|
||||
end
|
||||
|
||||
function backend.get_foo(schluessel)
|
||||
return minetest.deserialize(storage:get_string(schluessel))
|
||||
end
|
||||
|
||||
return backend
|
||||
```
|
||||
|
||||
Backend_sqlite würde eine ähnliche Funktion erfüllen, aber Sie sollten die Lua-Bibliothek *lsqlite3* verwenden
|
||||
anstelle des Mod-Speichers.
|
||||
|
||||
Die Verwendung einer Datenbank wie SQLite erfordert die Verwendung einer unsicheren Umgebung.
|
||||
Eine unsichere Umgebung ist eine Tabelle, die nur für Mods verfügbar ist,
|
||||
die vom Benutzer explizit auf eine Whitelist gesetzt wurden, und sie enthält eine weniger eingeschränkte
|
||||
Kopie der Lua-API, die missbraucht werden könnte, wenn sie böswilligen Mods zur Verfügung stünde.
|
||||
Unsichere Umgebungen werden im Detail in dem
|
||||
Kapitel [Sicherheit](../quality/security.html) behandelt.
|
||||
|
||||
```lua
|
||||
local uu = minetest.request_insecure_environment()
|
||||
assert(uu, "Bitte fügen Sie meinemod zu secure.trusted_mods in den Einstellungen hinzu")
|
||||
|
||||
local _sql = uu.require("lsqlite3")
|
||||
-- Andere Mods daran hindern, die globale sqlite3-Bibliothek zu verwenden
|
||||
if sqlite3 then
|
||||
sqlite3 = nil
|
||||
end
|
||||
```
|
||||
|
||||
Die Vermittlung von Wissen über SQL oder die Verwendung der lsqlite3-Bibliothek ist nicht Gegenstand dieses Buches.
|
||||
|
||||
## Entscheidung, was man benutzt
|
||||
|
||||
Welche Methode Sie verwenden, hängt davon ab, worum es sich bei den Daten handelt,
|
||||
wie sie formatiert sind und wie groß sie sind.
|
||||
Als Richtlinie gilt, dass kleine Daten bis zu 10K, mittlere Daten bis zu 10MB und große
|
||||
Daten jede Größe darüber sind.
|
||||
|
||||
Node-Metadaten sind eine gute Wahl, wenn Sie nodebezogene Daten speichern müssen.
|
||||
Die Speicherung mittlerer Daten ist recht effizient, wenn Sie sie privat machen.
|
||||
|
||||
Item-Metadaten sollten nur zum Speichern kleiner Datenmengen verwendet werden, da es nicht möglich ist,
|
||||
sie an den Client zu senden.
|
||||
Die Daten werden auch jedes Mal kopiert, wenn der Stack verschoben oder von Lua aus aufgerufen wird.
|
||||
|
||||
Mod-Storage ist gut für mittlere Daten, aber das Schreiben großer Daten kann ineffizient sein.
|
||||
Es ist besser, eine Datenbank für große Daten zu verwenden, um zu vermeiden,
|
||||
dass alle Daten bei jedem Speichern geschrieben werden müssen.
|
||||
|
||||
Datenbanken kommen nur für Server in Frage, da
|
||||
der Mod auf eine Whitelist gesetzt werden muss, um auf eine unsichere Umgebung zugreifen zu können.
|
||||
Sie sind gut geeignet für große Datenmengen.
|
||||
|
||||
## Sie sind dran
|
||||
|
||||
* Erstellen Sie einen Node, der verschwindet, nachdem er fünfmal geschlagen wurde.
|
||||
(Verwenden Sie `on_punch` in der Definition des Nodes und `minetest.set_node`.)
|
109
_de/map/timers.md
Normal file
109
_de/map/timers.md
Normal file
@ -0,0 +1,109 @@
|
||||
---
|
||||
title: Node Timer und ABMs
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 3.2
|
||||
description: Lernen Sie, wie man ABMs zum Ändern von Mapblöcken erstellt.
|
||||
redirect_from:
|
||||
- /de/chapters/abms.html
|
||||
- /de/map/abms.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Die periodische Ausführung einer Funktion auf bestimmten Node ist eine häufige Aufgabe.
|
||||
Minetest bietet dafür zwei Methoden: Aktive Map-Block Modifikatoren (ABMs) und Node-Timer(node timers).
|
||||
|
||||
ABMs scannen alle geladenen Map-Blöcke auf der Suche nach Blöcken, die einem Kriterium entsprechen.
|
||||
Sie eignen sich am besten für Blöcken, die in der Welt häufig vorkommen,
|
||||
wie zum Beispiel Gras.
|
||||
Sie haben einen hohen CPU-Overhead, aber einen geringen Speicher- und Storage-Overhead.
|
||||
|
||||
Für Blöcke, die ungewöhnlich sind oder bereits Metadaten verwenden, wie Öfen
|
||||
und Maschinen, sollten stattdessen Nodetimer verwendet werden.
|
||||
Nodetimer funktionieren, indem sie die ausstehenden Timer in jedem Map-Block verfolgen und dann ausführen
|
||||
wenn sie ablaufen.
|
||||
Dies bedeutet, dass die Zeitgeber nicht alle geladenen Blöcke durchsuchen müssen, um Übereinstimmungen zu finden,
|
||||
sondern stattdessen etwas mehr Speicherplatz für die Verfolgung
|
||||
der ausstehenden Timer benötigen.
|
||||
|
||||
- [Nodetimer](#nodetimer)
|
||||
- [Aktive Mapblock Modifikatoren](#aktive-mapblock-modifikatoren)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
## Nodetimer
|
||||
|
||||
Nodetimer sind direkt an einen einzelnen Node gebunden.
|
||||
Sie können Nodetimer verwalten, indem Sie ein NodeTimerRef-Objekt erhalten.
|
||||
|
||||
```lua
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(10.5) -- in Sekunden
|
||||
```
|
||||
|
||||
Wenn der Zeitgeber eines Nodes abgelaufen ist, wird die Methode `on_timer` in der Definitionstabelle des Nodes
|
||||
aufgerufen. Die Methode benötigt nur einen einzigen Parameter, die Position des Nodes:
|
||||
|
||||
```lua
|
||||
minetest.register_node("autotuer:offene_tuer", {
|
||||
on_timer = function(pos)
|
||||
minetest.set_node(pos, { name = "autotuer:offene_tuer" })
|
||||
return false
|
||||
end
|
||||
})
|
||||
```
|
||||
|
||||
Die Rückgabe von true in `on_timer` bewirkt, dass der Timer wieder für das gleiche Intervall läuft.
|
||||
Es ist auch möglich, `get_node_timer(pos)` innerhalb von `on_timer` zu verwenden, stellen Sie nur sicher
|
||||
dass Sie false zurückgeben, um Konflikte zu vermeiden.
|
||||
|
||||
Sie haben vielleicht eine Einschränkung bei den Zeitgebern bemerkt: Aus Optimierungsgründen ist
|
||||
nur eine Art von Timern pro Nodetyp und nur ein Timer pro Node möglich.
|
||||
|
||||
|
||||
## Aktive Mapblock Modifikatoren
|
||||
|
||||
Für die Zwecke dieses Kapitels ist Aliengras eine Grasart, die
|
||||
in der Nähe von Wasser vorkommen kann.
|
||||
|
||||
```lua
|
||||
minetest.register_node("aliens:gras", {
|
||||
description = "Aliengras",
|
||||
light_source = 3, -- Der Node strahlt Licht aus. Min 0, max 14
|
||||
tiles = {"aliens_grass.png"},
|
||||
groups = {choppy=1},
|
||||
on_use = minetest.item_eat(20)
|
||||
})
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"default:dirt_with_grass"},
|
||||
neighbors = {"default:water_source", "default:water_flowing"},
|
||||
interval = 10.0, -- Wird alle 10 Sekunden ausgeführt
|
||||
chance = 50, -- Jeder Node hat eine Chance von 1 zu 50 ausgewählt zu werden
|
||||
action = function(pos, node, active_object_count,
|
||||
active_object_count_wider)
|
||||
local pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
minetest.set_node(pos, {name = "aliens:gras"})
|
||||
end
|
||||
})
|
||||
```
|
||||
|
||||
Dieser ABM läuft alle zehn Sekunden, und für jeden passenden Node besteht eine
|
||||
Chance von 1 zu 50, dass es ausgeführt wird.
|
||||
Wenn die ABM auf einem Node ausgeführt wird, wird ein fremder Grasnode über ihm platziert.
|
||||
Bitte seien Sie gewarnt, dies löscht alle Blöcke, die sich zuvor an dieser Position befanden.
|
||||
Um dies zu verhindern, sollten Sie eine Überprüfung mit minetest.get_node einbauen, um sicherzustellen, dass Platz für das Gras vorhanden ist.
|
||||
|
||||
Die Angabe eines Nachbarn(neighbor) ist optional.
|
||||
Wenn Sie mehrere Nachbarn angeben, muss nur einer von ihnen vorhanden sein
|
||||
vorhanden sein, um die Anforderungen zu erfüllen.
|
||||
|
||||
Die Angabe der Chance ist ebenfalls optional.
|
||||
Wenn Sie die Chance nicht angeben, wird der ABM immer ausgeführt, wenn die anderen Bedingungen erfüllt sind.
|
||||
|
||||
## Sie sind dran
|
||||
|
||||
* Midas Berührung: Verwandelt Wasser alle 5 Sekunden mit einer Wahrscheinlichkeit von 1 zu 100 in Goldblöcke.
|
||||
* Fäulnis: Verwandelt Holz in Dreck um, wenn Wasser der Nachbar ist.
|
||||
* Brennen: Bringt jeden LuftNode in Brand. (Tipp: "air" und "fire:basic_flame").
|
||||
Warnung: Rechnen Sie damit, dass das Spiel abstürzt.
|
166
_de/players/chat.md
Normal file
166
_de/players/chat.md
Normal file
@ -0,0 +1,166 @@
|
||||
---
|
||||
title: Chat und Befehle
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 4.2
|
||||
description: Registrierung eines Chatbefehls und Behandlung von Chatnachrichten mit register_on_chat_message
|
||||
redirect_from: /de/chapters/chat.html
|
||||
cmd_online:
|
||||
level: warning
|
||||
title: Offline-Spieler können Befehle ausführen
|
||||
message: <p>Ein Spielername wird anstelle eines Spielerobjekts übergeben, da Mods
|
||||
Befehle im Namen von Offline-Spielern ausführen können. Zum Beispiel erlaubt die IRC-
|
||||
Brücke den Spielern, Befehle auszuführen, ohne dem Spiel beizutreten.</p>
|
||||
|
||||
<p>Stellen Sie also sicher, dass Sie nicht davon ausgehen, dass der Spieler online ist.
|
||||
Sie können überprüfen, ob <pre>minetest.get_player_by_name</pre> einen Spieler liefert.</p>
|
||||
|
||||
cb_cmdsprivs:
|
||||
level: warning
|
||||
title: Privilegien und Chat-Befehle
|
||||
message: Das "Shout"-Privileg ist für einen Spieler nicht erforderlich, um diesen Callback auszulösen.
|
||||
Das liegt daran, dass Chat-Befehle in Lua implementiert sind, und nur
|
||||
Chat-Nachrichten sind, die mit einem / beginnen.
|
||||
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Mods können mit dem Spielerchat interagieren, einschließlich
|
||||
Senden von Nachrichten, Abfangen von Nachrichten und Registrieren von Chat-Befehlen.
|
||||
|
||||
- [Senden von Nachrichten an alle Spieler](#senden-von-nachrichten-an-alle-spieler)
|
||||
- [Nachrichten an bestimmte Spieler senden](#nachrichten-an-bestimmte-spieler-senden)
|
||||
- [Chat-Befehle](#chat-befehle)
|
||||
- [Komplexe Unterbefehle](#komplexe-unterbefehle)
|
||||
- [Abfangen von Nachrichten](#abfangen-von-nachrichten)
|
||||
|
||||
## Senden von Nachrichten an alle Spieler
|
||||
|
||||
Um eine Nachricht an alle Spieler im Spiel zu senden, rufen Sie die Funktion chat_send_all auf.
|
||||
|
||||
```lua
|
||||
minetest.chat_send_all("Dies ist eine Chat-Nachricht an alle Spieler")
|
||||
```
|
||||
|
||||
Hier ist ein Beispiel dafür, wie dies im Spiel aussieht:
|
||||
|
||||
<player1> Sehen Sie sich diesen Eingang an
|
||||
Dies ist eine Chat-Nachricht an alle Spieler
|
||||
<player2> Was ist damit?
|
||||
|
||||
Die Nachricht erscheint in einer separaten Zeile, um sie vom Spieler-Chat im Spiel zu unterscheiden.
|
||||
|
||||
## Nachrichten an bestimmte Spieler senden
|
||||
|
||||
Um eine Nachricht an einen bestimmten Spieler zu senden, rufen Sie die Funktion chat_send_player auf:
|
||||
|
||||
```lua
|
||||
minetest.chat_send_player("Spieler1", "Dies ist eine Chat-Nachricht für Spieler1")
|
||||
```
|
||||
|
||||
Diese Nachricht wird auf dieselbe Weise angezeigt wie die Nachrichten an alle Spieler, ist aber
|
||||
nur für den benannten Spieler sichtbar, in diesem Fall für Spieler1.
|
||||
|
||||
## Chat-Befehle
|
||||
|
||||
Um einen Chat-Befehl zu registrieren, zum Beispiel `/foo`, verwenden Sie `register_chatcommand`:
|
||||
|
||||
```lua
|
||||
minetest.register_chatcommand("foo", {
|
||||
privs = {
|
||||
interact = true,
|
||||
},
|
||||
func = function(name, param)
|
||||
return true, "Sie sagten " .. param .. "!"
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
Im obigen Ausschnitt ist "interact" als erforderliches
|
||||
[Privileg](privileges.html) aufgeführt, was bedeutet, dass nur Spieler mit dem Privileg "interact" den Befehl ausführen können.
|
||||
|
||||
Chat-Befehle können bis zu zwei Werte zurückgeben,
|
||||
Der erste ist ein boolescher Wert, der den Erfolg anzeigt, und der zweite ist eine
|
||||
Nachricht, die an den Benutzer gesendet wird.
|
||||
|
||||
{% include notice.html notice=page.cmd_online %}
|
||||
|
||||
## Komplexe Unterbefehle
|
||||
|
||||
Es wird oft benötigt, komplexe Chat-Befehle zu bereitzustellen, wie z.B.:
|
||||
|
||||
* `/msg <zu> <Nachricht>`
|
||||
* `/team join <Teamname>`
|
||||
* `/team leave <Teamname>`
|
||||
* `/team list`
|
||||
|
||||
Dies geschieht normalerweise mit [Lua-Mustern] (https://www.lua.org/pil/20.2.html).
|
||||
Patterns sind eine Methode, um anhand von Regeln Dinge aus Text zu extrahieren.
|
||||
|
||||
```lua
|
||||
local to, msg = param:match("^([%a%d_-]+) (.+)$")
|
||||
```
|
||||
|
||||
Der obige Code implementiert `/msg <zu> <Nachricht>`. Lassen Sie uns von links nach rechts vorgehen:
|
||||
|
||||
* `^` bedeutet, dass der Anfang der Zeichenkette übereinstimmt.
|
||||
* `()` ist eine übereinstimmende Gruppe - alles, was hier drin steht, wird
|
||||
von string.match zurückgegeben.
|
||||
* `[]` bedeutet, dass die Zeichen in dieser Liste akzeptiert werden.
|
||||
* `%a` bedeutet, jeden Buchstaben zu akzeptieren und `%d` bedeutet, eine beliebige Ziffer zu akzeptieren.
|
||||
* `[%a%d_-]` bedeutet, einen beliebigen Buchstaben, eine beliebige Ziffer, `_` oder `-` zu akzeptieren.
|
||||
* `+` bedeutet, dass die Sache ein oder mehrere Male übereinstimmt.
|
||||
* `.` bedeutet, dass ein beliebiges Zeichen in diesem Zusammenhang übereinstimmt.
|
||||
* `$` bedeutet, das Ende der Zeichenkette zu finden.
|
||||
|
||||
Einfach ausgedrückt: Das Muster entspricht dem Namen (ein Wort mit nur Buchstaben/Zahlen/-/_),
|
||||
dann ein Leerzeichen, dann die Nachricht (ein oder mehrere beliebige Zeichen). Der Name und die
|
||||
werden zurückgegeben, da sie von Klammern umgeben sind.
|
||||
|
||||
Das ist die Art und Weise, wie die meisten Mods komplexe Chat-Befehle implementieren. Eine bessere Anleitung für Lua
|
||||
Patterns wäre wahrscheinlich das
|
||||
[lua-users.org tutorial](http://lua-users.org/wiki/PatternsTutorial)
|
||||
oder die [PIL-Dokumentation](https://www.lua.org/pil/20.2.html).
|
||||
|
||||
<p class="book_hide">
|
||||
Es gibt auch eine vom Autor dieses Buches geschriebene Bibliothek, die man dazu benutzen
|
||||
kann um komplexe Chat-Befehle ohne Muster zu erstellen, den
|
||||
<a href="https://gitlab.com/rubenwardy/ChatCmdBuilder">Chat Command Builder</a>.
|
||||
</p>
|
||||
|
||||
|
||||
## Abfangen von Nachrichten
|
||||
|
||||
Um eine Nachricht abzufangen, verwenden Sie register_on_chat_message:
|
||||
|
||||
```lua
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
print(name .. " sagte " .. message)
|
||||
return false
|
||||
end)
|
||||
```
|
||||
|
||||
Wenn Sie false zurückgeben, erlauben Sie, dass die Chat-Nachricht vom Standard
|
||||
Handler gesendet wird. Sie können die Zeile `return false` sogar entfernen und es würde immer noch
|
||||
funktionieren, da `nil` implizit zurückgegeben wird und wie false behandelt wird.
|
||||
|
||||
{% include notice.html notice=page.cb_cmdsprivs %}
|
||||
|
||||
Sie sollten berücksichtigen, dass es sich um einen Chat-Befehl handeln könnte,
|
||||
oder der Benutzer vielleicht kein `shout` hat.
|
||||
|
||||
```lua
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
if message:sub(1, 1) == "/" then
|
||||
print(name .. " hat einen Chat-Befehl ausgeführt")
|
||||
elseif minetest.check_player_privs(name, { shout = true }) then
|
||||
print(name .. " sagte " .. message)
|
||||
else
|
||||
print(name .. " versucht zu sagen " .. message ..
|
||||
" hat aber kein shout")
|
||||
end
|
||||
|
||||
return false
|
||||
end)
|
||||
```
|
382
_de/players/formspecs.md
Normal file
382
_de/players/formspecs.md
Normal file
@ -0,0 +1,382 @@
|
||||
---
|
||||
title: GUIs (Formspecs)
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 4.5
|
||||
description: Lerne, wie man GUIs mit formspecs anzeigt
|
||||
redirect_from: /de/chapters/formspecs.html
|
||||
submit_vuln:
|
||||
level: warning
|
||||
title: Malicious clients can submit anything at anytime
|
||||
message: Sie sollten niemals einer formspec-Übermittlung vertrauen. Ein böswilliger Client
|
||||
kann jederzeit alles übermitteln, was er will - auch wenn Sie ihm nie die
|
||||
den formspec gezeigt haben. Das bedeutet, dass Sie die Berechtigungen prüfen sollten
|
||||
und sicherstellen, dass sie die Aktion durchführen dürfen.
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}//static/formspec_example.png" alt="Ofen-Inventar">
|
||||
<figcaption>
|
||||
Screenshot eines formspec für Öfen, beschriftet.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
In diesem Kapitel werden wir lernen, wie man einen Formspec erstellt und ihn dem Benutzer anzeigt.
|
||||
Ein Formspec ist der Spezifikationscode für ein Form.
|
||||
In Minetest sind Forms Fenster wie das Spielerinventar und können eine
|
||||
eine Vielzahl von Elementen wie Beschriftungen, Schaltflächen und Felder enthalten.
|
||||
|
||||
Beachten Sie, dass Sie, wenn Sie keine Benutzereingaben benötigen, zum Beispiel wenn Sie nur
|
||||
Informationen für den Spieler bereitstellen möchten, Sie die Verwendung von
|
||||
[Heads Up Display (HUD)](hud.html)-Elementen anstelle von Formspecs verwenden sollten, da
|
||||
unerwartete Fenster das Spielgeschehen stören können.
|
||||
|
||||
- [Reale oder Legacy-Koordinaten](#reale-oder-legacy-koordinaten)
|
||||
- [Anatomie eines formspecs](#anatomie-eines-formspecs)
|
||||
- [Elemente](#elemente)
|
||||
- [Header](#header)
|
||||
- [Ratespiel](#ratespiel)
|
||||
- [Padding und Abstände](#padding-und-abstände)
|
||||
- [Empfang von Formspec-Übermittlungen](#empfang-von-formspec-übermittlungen)
|
||||
- [Contexts](#contexts)
|
||||
- [Formspec-Quellen](#formspec-quellen)
|
||||
- [Node Meta Formspecs](#node-meta-formspecs)
|
||||
- [Spieler Inventar Formspecs](#spieler-inventar-formspecs)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
|
||||
## Reale oder Legacy-Koordinaten
|
||||
|
||||
In älteren Versionen von Minetest waren die Formspecs inkonsistent. Wegen der Art und Weise, wie verschiedene
|
||||
Elemente auf unerwartete Art und Weise positioniert wurden war es schwierig, die
|
||||
Platzierung der Elemente vorherzusagen und auszurichten. Minetest 5.1.0 enthält eine Funktion namens
|
||||
Koordinaten, die dieses Problem durch die Einführung eines konsistenten
|
||||
Koordinatensystem beheben. Die Verwendung von realen Koordinaten wird dringend empfohlen, und deshalb
|
||||
dieses Kapitel ausschließlich diese verwenden.
|
||||
|
||||
Die Verwendung einer formspec_version von 2 oder höher aktiviert reale Koordinaten.
|
||||
|
||||
## Anatomie eines formspecs
|
||||
|
||||
### Elemente
|
||||
|
||||
Formspec ist eine domänenspezifische Sprache mit einem ungewöhnlichen Format.
|
||||
Sie besteht aus einer Reihe von Elementen mit der folgenden Form:
|
||||
|
||||
type[param1;param2]
|
||||
|
||||
Der Elementtyp wird deklariert und dann werden alle Parameter
|
||||
in eckigen Klammern angegeben. Mehrere Elemente können miteinander verbunden werden, oder
|
||||
auf mehrere Zeilen verteilt werden, etwa so:
|
||||
|
||||
foo[param1]bar[param1]
|
||||
bo[param1]
|
||||
|
||||
|
||||
Elemente sind Elemente wie Textfelder oder Schaltflächen oder können Metadaten wie Größe
|
||||
oder Hintergrund sein. Sie sollten nachschlagen in der
|
||||
[lua_api.txt 🇬🇧](https://minetest.gitlab.io/minetest/formspec/)
|
||||
für eine Liste aller möglichen Elemente.
|
||||
|
||||
|
||||
### Header
|
||||
|
||||
Der Header eines Formspec enthält Informationen, die zuerst erscheinen müssen. Diese
|
||||
umfasst die Größe des Formspec, die Position, den Anker und ob das
|
||||
spielweite Thema angewendet werden soll.
|
||||
|
||||
Die Elemente im Header müssen in einer bestimmten Reihenfolge definiert werden, sonst
|
||||
wird ein Fehler angezeigt. Diese Reihenfolge ist im obigen Absatz angegeben und - wie immer -
|
||||
in der Lua-API-Referenz dokumentiert.
|
||||
|
||||
Die Größe wird in Formspec-Slots angegeben - eine Maßeinheit, die etwa
|
||||
64 Pixeln entspricht, jedoch abhängig von der Bildschirmdichte und den Skalierungs
|
||||
Einstellungen des Clients ist. Hier ist ein formspec mit der Größe "2,2":
|
||||
|
||||
formspec_version[4]
|
||||
size[2,2]
|
||||
|
||||
Beachten Sie, dass wir die Formspec-Sprachversion ausdrücklich definiert haben müssen.
|
||||
Ohne dies wird stattdessen das Altsystem verwendet - was
|
||||
die Verwendung der konsistenten Elementpositionierung und anderer neuer Funktionen verhindert.
|
||||
|
||||
Die Elemente position und anchor werden verwendet, um das Formspec auf dem Bildschirm zu platzieren.
|
||||
Die Position legt fest, wo auf dem Bildschirm das formspec sein wird, und ist standardmäßig auf
|
||||
die Mitte (`0,5,0,5`). Der Anker legt fest, wo auf dem formspec die Position ist,
|
||||
so dass Sie das Formspec mit dem Rand des Bildschirms ausrichten können. Das Formspec
|
||||
kann auf diese Weise links vom Bildschirm platziert werden:
|
||||
|
||||
formspec_version[4]
|
||||
size[2,2]
|
||||
position[0,0.5]
|
||||
anchor[0,0.5]
|
||||
|
||||
Dadurch wird der Anker an den linken mittleren Rand des Formspec-Feldes gesetzt, und die
|
||||
Position dieses Ankers auf der linken Seite des Bildschirms.
|
||||
|
||||
|
||||
## Ratespiel
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}/static/formspec_guessing.png" alt="Rate-Formspec">
|
||||
<figcaption>
|
||||
Das Ratespiel formspec.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Der beste Weg, etwas zu lernen, ist, etwas zu machen, also lasst uns ein Ratespiel machen.
|
||||
Das Prinzip ist einfach: Die Mod entscheidet sich für eine Zahl, und der Spieler
|
||||
errät die Zahl. Die Mod sagt dann, ob die erratene Zahl höher oder niedriger ist als
|
||||
die tatsächliche Zahl.
|
||||
|
||||
Zunächst erstellen wir eine Funktion, die den Formspec-Code erzeugt. Es ist gute Praxis, dies
|
||||
zu tun, da es die Wiederverwendung an anderer Stelle erleichtert.
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
|
||||
```lua
|
||||
guessing = {}
|
||||
|
||||
function guessing.get_formspec(name)
|
||||
-- TODO: Anzeige, ob die letzte Schätzung höher oder niedriger war
|
||||
local text = "Ich denke an eine Zahl... Raten Sie mal!"
|
||||
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
"size[6,3.476]",
|
||||
"label[0.375,0.5;", minetest.formspec_escape(text), "]",
|
||||
"field[0.375,1.25;5.25,0.8;nummer;Nummer;]",
|
||||
"button[1.5,2.3;3,0.8;raten;Raten]"
|
||||
}
|
||||
|
||||
-- table.concat ist schneller als String-Verkettung - `..`
|
||||
return table.concat(formspec, "")
|
||||
end
|
||||
```
|
||||
|
||||
Im obigen Code platzieren wir ein Feld, eine Beschriftung und eine Schaltfläche.
|
||||
Ein Feld erlaubt die Eingabe von Text und eine Schaltfläche dient zum
|
||||
Absenden des Forms. Sie werden feststellen, dass die Elemente
|
||||
sorgfältig positioniert sind, um Padding und Abstände hinzuzufügen, was später erklärt wird.
|
||||
|
||||
Als Nächstes wollen wir dem Spieler erlauben, den Formspec anzuzeigen. Der beste Weg, dies zu tun
|
||||
ist die Verwendung von `show_formspec`:
|
||||
|
||||
```lua
|
||||
function guessing.show_to(name)
|
||||
minetest.show_formspec(name, "guessing:game", guessing.get_formspec(name))
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("game", {
|
||||
func = function(name)
|
||||
guessing.show_to(name)
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
Die Funktion `show_formspec` akzeptiert einen Spielernamen, den Namen der Formspec und den
|
||||
Formspec selbst. Der Formspec-Name sollte ein gültiger Itemname sein, d.h. im Format
|
||||
`Modname:Gegenstandsname`.
|
||||
|
||||
|
||||
### Padding und Abstände
|
||||
|
||||
<figure class="right_image">
|
||||
<img src="{{ page.root }}/static/formspec_padding_spacing.png" alt="Padding und Abstände">
|
||||
<figcaption>
|
||||
The guessing game formspec.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Padding ist der Abstand zwischen dem Rand des Formspec und seinem Inhalt oder zwischen nicht verwandten Elementen,
|
||||
dargestellt in Rot. Abstand ist der Abstand zwischen zusammenhängenden Elementen, der blau dargestellt wird.
|
||||
|
||||
|
||||
Ein Padding von `0,375` und ein Abstand von `0,25` sind üblich.
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
|
||||
|
||||
### Empfang von Formspec-Übermittlungen
|
||||
|
||||
Wenn `show_formspec` aufgerufen wird, wird der formspec an den Client gesendet, um angezeigt zu werden.
|
||||
Damit Formspecs nützlich sind, müssen Informationen vom Client zum Server zurückgeschickt werden.
|
||||
Die Methode dafür heißt formspec field submission, und für `show_formspec` wird diese
|
||||
Übermittlung über einen globalen Callback empfangen:
|
||||
|
||||
```lua
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "guessing:game" then
|
||||
return
|
||||
end
|
||||
|
||||
if fields.guess then
|
||||
local pname = player:get_player_name()
|
||||
minetest.chat_send_all(pname .. " riet " .. fields.number)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
Die in `minetest.register_on_player_receive_fields` angegebene Funktion wird
|
||||
jedes Mal aufgerufen, wenn ein Benutzer ein Formular absendet. Die meisten Callbacks müssen den
|
||||
an die Funktion übergebenen Formularnamen überprüfen und beenden, wenn es sich nicht um das richtige Form handelt; einige
|
||||
müssen jedoch möglicherweise für mehrere Formulare oder für alle Formulare funktionieren.
|
||||
|
||||
Der Parameter `fields` der Funktion ist eine Tabelle mit den vom Benutzer übermittelten Werten
|
||||
Benutzer übermittelten Werte, die durch Zeichenketten indiziert sind. Benannte Elemente erscheinen in dem Feld unter ihrem eigenen
|
||||
Namen, aber nur, wenn sie für das Ereignis, das die Übermittlung verursacht hat, relevant sind.
|
||||
Ein Schaltflächenelement erscheint beispielsweise nur dann in Feldern, wenn die betreffende Schaltfläche
|
||||
gedrückt wurde.
|
||||
|
||||
{% include notice.html notice=page.submit_vuln %}
|
||||
|
||||
Der formspec wird also an den Client gesendet, und der Client sendet Informationen zurück.
|
||||
Der nächste Schritt besteht darin, den Zielwert irgendwie zu generieren und zu speichern, und die
|
||||
die formspec auf der Grundlage von Schätzungen zu aktualisieren. Dies geschieht mit Hilfe eines Konzepts namens
|
||||
"contexts".
|
||||
|
||||
|
||||
### Contexts
|
||||
|
||||
In vielen Fällen möchten Sie, dass minetest.show_formspec Informationen
|
||||
an den Callback weitergeben, die nicht an den Client gesendet werden sollen.
|
||||
Dies könnte beinhalten dass ein Chat-Befehl aufgerufen wurde, oder worum es
|
||||
in dem Dialog geht. In diesem Fall, der Zielwert, der gespeichert werden muss.
|
||||
|
||||
Ein Context ist eine pro-Spieler-Tabelle zum Speichern von Informationen, und die Contexts für alle
|
||||
Online-Spieler werden in einer dateilokalen Variablen gespeichert:
|
||||
|
||||
```lua
|
||||
local _contexts = {}
|
||||
local function get_context(name)
|
||||
local context = _contexts[name] or {}
|
||||
_contexts[name] = context
|
||||
return context
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(spieler)
|
||||
_contexts[spieler:get_player_name()] = nil
|
||||
end)
|
||||
```
|
||||
|
||||
Als nächstes müssen wir den Show-Code ändern, um den Context zu aktualisieren
|
||||
zu aktualisieren, bevor der Formspec angezeigt wird:
|
||||
|
||||
```lua
|
||||
function guessing.show_to(name)
|
||||
local context = get_context(name)
|
||||
context.target = context.target or math.random(1, 10)
|
||||
|
||||
local fs = guessing.get_formspec(name, context)
|
||||
minetest.show_formspec(name, "guessing:game", fs)
|
||||
end
|
||||
```
|
||||
|
||||
Wir müssen auch den Code für die Generierung von Formularen ändern, um den Context zu verwenden:
|
||||
|
||||
```lua
|
||||
function guessing.get_formspec(name, context)
|
||||
local text
|
||||
if not context.guess then
|
||||
text = "I'm thinking of a number... Make a guess!"
|
||||
elseif context.guess == context.target then
|
||||
text = "Hurray, you got it!"
|
||||
elseif context.guess > context.target then
|
||||
text = "Too high!"
|
||||
else
|
||||
text = "Too low!"
|
||||
end
|
||||
```
|
||||
|
||||
Beachten Sie, dass es gute Praxis ist, wenn `get_formspec` den Context nur liest und
|
||||
überhaupt nicht zu aktualisiert. Dies kann die Funktion einfacher machen, und auch leichter zu testen.
|
||||
|
||||
Und schließlich müssen wir den Handler aktualisieren, um den Context mit der Vermutung zu aktualisieren:
|
||||
|
||||
```lua
|
||||
if fields.guess then
|
||||
local name = spieler:get_player_name()
|
||||
local context = get_context(name)
|
||||
context.guess = tonumber(fields.number)
|
||||
guessing.show_to(name)
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
## Formspec-Quellen
|
||||
|
||||
Es gibt drei verschiedene Möglichkeiten, wie ein Formspec an den Client übermittelt werden kann:
|
||||
|
||||
* [show_formspec](#ratespiel): Bei der oben beschriebenen Methode werden die Felder durch
|
||||
`register_on_player_receive_fields` empfangen.
|
||||
* [Node Meta Formspecs](#node-meta-formspecs): der Node enthält in seinen Metadaten eine Formularvorgabe,
|
||||
und der Client zeigt es *sofort* an, wenn der Spieler mit der rechten Maustaste klickt. Felder werden
|
||||
durch eine Methode in der Node-Definition namens `on_receive_fields` empfangen.
|
||||
* [Player Inventory Formspecs](#spieler-inventar-formspecs): der Formspec wird irgendwann an den Client gesendet und dann
|
||||
sofort angezeigt, wenn der Spieler auf "i" drückt. Felder werden durch
|
||||
`register_on_player_receive_fields` empfangen.
|
||||
|
||||
### Node Meta Formspecs
|
||||
|
||||
`minetest.show_formspec` ist nicht die einzige Möglichkeit, einen Formspec anzuzeigen; Sie können auch
|
||||
Formspecs zu den Metadaten eines [Nodes](node_metadata.html) hinzufügen. Zum Beispiel,
|
||||
wird dieses bei Truhen verwendet, um ein schnelleres Öffnen zu ermöglichen -
|
||||
man muss nicht darauf warten, dass der Server dem Spieler den Formspec für die Truhe schickt.
|
||||
|
||||
```lua
|
||||
minetest.register_node("meinemod:rechtsclick", {
|
||||
description = "Rechtsclicke me!",
|
||||
tiles = {"mymod_rechtsclick.png"},
|
||||
groups = {cracky = 1},
|
||||
after_place_node = function(pos, placer)
|
||||
-- Diese Funktion wird ausgeführt, wenn das Kisten-Node platziert wird.
|
||||
-- Der folgende Code setzt den formspec für Kiste.
|
||||
-- Meta ist eine Möglichkeit, Daten in einem Node zu speichern.
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"formspec_version[4]" ..
|
||||
"size[5,5]" ..
|
||||
"label[1,1;Dies wird beim Rechtsklick angezeigt]" ..
|
||||
"field[1,2;2,1;x;x;]")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, spieler)
|
||||
if fields.quit then
|
||||
return
|
||||
end
|
||||
|
||||
print(fields.x)
|
||||
end
|
||||
})
|
||||
```
|
||||
|
||||
Auf diese Weise eingestellte Formspecs lösen nicht denselben Callback aus. Um
|
||||
Formulareingaben für meta formspecs zu erhalten, müssen Sie einen
|
||||
`on_receive_fields`-Eintrag bei der Registrierung des Nodes enthalten.
|
||||
|
||||
Diese Art von Callback wird ausgelöst, wenn Sie die Eingabetaste
|
||||
in einem Feld drücken, was mit `minetest.show_formspec` unmöglich ist;
|
||||
diese Art von Formular kann jedoch nur durch Rechtsklick auf einen Node angezeigt werden. Sie kann nicht programmatisch ausgelöst werden.
|
||||
|
||||
### Spieler Inventar Formspecs
|
||||
|
||||
Der Formspec für das Spielerinventar wird angezeigt, wenn der Spieler auf i drückt.
|
||||
Der globale Callback wird verwendet, um Ereignisse von diesem Formspec zu empfangen, und der
|
||||
formname ist `""`.
|
||||
|
||||
Es gibt eine Reihe von verschiedenen Mods, die es ermöglichen, das
|
||||
das Spielerinventar anzupassen. Die offiziell empfohlene Mod ist
|
||||
[Simple Fast Inventory (sfinv)](https://github.com/rubenwardy/sfinv/blob/master/Tutorial.md),
|
||||
und ist in Minetest Game enthalten. Ich als Übersetzer empfehle jedoch
|
||||
eher [i3](https://github.com/minetest-mods/i3) oder [unified inventory](https://github.com/minetest-mods/unified_inventory)
|
||||
|
||||
|
||||
### Sie sind dran
|
||||
|
||||
* Erweitern Sie das Ratespiel, um die höchste Punktzahl jedes Spielers zu ermitteln, wobei
|
||||
die höchste Punktzahl angibt, wie viele Ratschläge nötig waren.
|
||||
* Erstellen Sie einen Node namens "Inbox", in dem Benutzer ein Formspec öffnen und Nachrichten hinterlassen können.
|
||||
Dieser Node sollte den Namen des Placers als `owner` in der Meta speichern, und sollte
|
||||
`show_formspec` verwenden, um verschiedene Formspecs für verschiedene Spieler anzuzeigen.
|
293
_de/players/hud.md
Normal file
293
_de/players/hud.md
Normal file
@ -0,0 +1,293 @@
|
||||
---
|
||||
title: HUD
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 4.6
|
||||
description: Lernen Sie, wie man HUD-Elemente anzeigt
|
||||
redirect_from: /de/chapters/hud.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Heads Up Display (HUD) Elemente ermöglichen es Ihnen, Texte, Bilder und andere grafische Elemente anzuzeigen.
|
||||
|
||||
Das HUD akzeptiert keine Benutzereingaben; dafür sollten Sie eine [formspec](formspecs.html) verwenden.
|
||||
|
||||
- [Positionierung](#positionierung)
|
||||
- [Position und Versatz](#position-und-versatz)
|
||||
- [Ausrichtung](#ausrichtung)
|
||||
- [Anzeigetafel](#anzeigetafel)
|
||||
- [Textelemente](#textelemente)
|
||||
- [Parameter](#parameter)
|
||||
- [Unser Beispiel](#unser-beispiel)
|
||||
- [Bild-Elemente](#bild-elemente)
|
||||
- [Parameter](#parameter-1)
|
||||
- [Scale](#scale)
|
||||
- [Ein Element verändern](#ein-element-verändern)
|
||||
- [IDs speichern](#ids-speichern)
|
||||
- [Andere Elemente](#andere-elemente)
|
||||
|
||||
## Positionierung
|
||||
|
||||
### Position und Versatz
|
||||
|
||||
<figure class="right_image">
|
||||
<img
|
||||
width="300"
|
||||
src="{{ page.root }}//static/hud_diagram_center.svg"
|
||||
alt="Diagramm mit einem zentrierten Textelement">
|
||||
</figure>
|
||||
|
||||
Bildschirme gibt es in verschiedenen Größen und Auflösungen. Ein HUD muss auf allen
|
||||
Bildschirmtypen gut funktionieren.
|
||||
|
||||
Die Lösung von Minetest besteht darin, die Position eines Elements sowohl durch
|
||||
einer prozentualen Position und einem Versatz festzulegen.
|
||||
Die prozentuale Position bezieht sich auf die Bildschirmgröße, d. h. um ein Element
|
||||
in der Mitte des Bildschirms zu platzieren, müssen Sie eine prozentuale Position von der Hälfte
|
||||
des Bildschirms, z. B. (50%, 50%), und einen Versatz von (0, 0) angeben.
|
||||
|
||||
Der Versatz wird dann verwendet, um ein Element relativ zur Prozentposition zu verschieben.
|
||||
|
||||
<div style="clear:both;"></div>
|
||||
|
||||
### Ausrichtung
|
||||
|
||||
Die Ausrichtung gibt an, wo das Ergebnis von Position und Versatz auf dem Element liegt -
|
||||
zum Beispiel `{x = -1.0, y = 0.0}` lässt das Ergebnis von Position und Versatz
|
||||
auf die linke Seite der Elementbegrenzung liegen. Dies ist besonders nützlich, wenn Sie
|
||||
ein Textelement links-, mittel- oder rechtsbündig ausrichten wollen.
|
||||
|
||||
<figure>
|
||||
<img
|
||||
width="500"
|
||||
src="{{ page.root }}//static/hud_diagram_alignment.svg"
|
||||
alt="Diagramm zur Ausrichtung">
|
||||
</figure>
|
||||
|
||||
Das obige Diagramm zeigt 3 Fenster (blau), jedes mit einem einzelnen HUD-Element (gelb)
|
||||
und jeweils einer anderen Ausrichtung. Der Pfeil ist das Ergebnis der Berechnung von Position
|
||||
und Versatzberechnung.
|
||||
|
||||
### Anzeigetafel
|
||||
|
||||
In diesem Kapitel werden Sie lernen, wie Sie eine Anzeigetafel
|
||||
positionieren und aktualisieren können:
|
||||
|
||||
<figure>
|
||||
<img
|
||||
src="{{ page.root }}//static/hud_final.png"
|
||||
alt="Screenshot vom uns angestrebten HUD">
|
||||
</figure>
|
||||
|
||||
Im obigen Screenshot haben alle Elemente die gleiche prozentuale Position
|
||||
(100%, 50%) - aber unterschiedliche Versätze. Dadurch wird das Ganze am rechten Rand des Fensters verankert, kann aber in der Größe verändert werden, ohne einen Bruch zu benutzen.
|
||||
|
||||
## Textelemente
|
||||
|
||||
Sie können ein HUD-Element erstellen, sobald Sie eine Kopie des Spieler-Objekts haben:
|
||||
|
||||
```lua
|
||||
local player = minetest.get_player_by_name("benutzername")
|
||||
local idx = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
offset = {x = 0, y = 0},
|
||||
text = "Hallo Welt!",
|
||||
alignment = {x = 0, y = 0}, -- mittig Ausrichtung
|
||||
scale = {x = 100, y = 100}, -- Später behandelt
|
||||
})
|
||||
```
|
||||
|
||||
Die Funktion `hud_add` gibt eine Element-ID zurück - diese kann später verwendet werden, um ein
|
||||
HUD-Element zu modifizieren oder zu entfernen.
|
||||
|
||||
### Parameter
|
||||
|
||||
Der Typ des Elements wird mit der Eigenschaft `hud_elem_type` in der Definitionstabelle angegeben
|
||||
Tabelle angegeben. Die Bedeutung der anderen Eigenschaften hängt von diesem Typ ab.
|
||||
|
||||
`scale` ist die maximale Begrenzung des Textes; Text außerhalb dieser Begrenzung wird abgeschnitten, z. B.: `{x=100, y=100}`.
|
||||
|
||||
`number` ist die Farbe des Textes in [hexadezimaler Form] (http://www.colorpicker.com/), z. B.: `0xFF0000`.
|
||||
|
||||
|
||||
### Unser Beispiel
|
||||
|
||||
Lassen Sie uns fortfahren und den gesamten Text in unserer Punkte-Tafel platzieren:
|
||||
|
||||
```lua
|
||||
-- Holt sich die Anzahl der Abbauungen und Platzierungen aus dem Speicher, oder setzt den Wert auf standardmäßig 0
|
||||
local meta = player:get_meta()
|
||||
local abbau_text = "Abgebaut: " .. meta:get_int("score:digs")
|
||||
local platzier_text = "Platziert: " .. meta:get_int("score:places")
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -120, y = -25},
|
||||
text = "Statistiken",
|
||||
alignment = 0,
|
||||
scale = { x = 100, y = 30},
|
||||
number = 0xFFFFFF,
|
||||
})
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -180, y = 0},
|
||||
text = abbau_text,
|
||||
alignment = -1,
|
||||
scale = { x = 50, y = 10},
|
||||
number = 0xFFFFFF,
|
||||
})
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -70, y = 0},
|
||||
text = platzier_text,
|
||||
alignment = -1,
|
||||
scale = { x = 50, y = 10},
|
||||
number = 0xFFFFFF,
|
||||
})
|
||||
```
|
||||
|
||||
Daraus ergibt sich das Folgende:
|
||||
|
||||
<figure>
|
||||
<img
|
||||
src="{{ page.root }}//static/hud_text.png"
|
||||
alt="Screenshot des HUD, den wir anstreben">
|
||||
</figure>
|
||||
|
||||
|
||||
## Bild-Elemente
|
||||
|
||||
Bild-Elemente werden auf sehr ähnliche Weise wie Text-Elemente erstellt:
|
||||
|
||||
```lua
|
||||
player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -220, y = 0},
|
||||
text = "punkte_hintergrund.png",
|
||||
scale = { x = 1, y = 1},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
```
|
||||
|
||||
Sie werden jetzt dies haben:
|
||||
|
||||
<figure>
|
||||
<img
|
||||
src="{{ page.root }}//static/hud_background_img.png"
|
||||
alt="Screenshot des bisherigen HUDs">
|
||||
</figure>
|
||||
|
||||
### Parameter
|
||||
|
||||
Das Feld `text` wird für die Angabe des Bildnamens verwendet.
|
||||
|
||||
Wenn eine Koordinate positiv ist, handelt es sich um einen Skalierungsfaktor, wobei 1
|
||||
die ursprüngliche Bildgröße, 2 die doppelte Größe usw. ist.
|
||||
Ist eine Koordinate jedoch negativ, handelt es sich um einen Prozentsatz der Bildschirmgröße.
|
||||
Zum Beispiel ist `x=-100` 100% der Breite.
|
||||
|
||||
### Scale
|
||||
|
||||
Lassen Sie uns den Fortschrittsbalken für unser Score-Panel als Beispiel für die Skala erstellen:
|
||||
|
||||
```lua
|
||||
local percent = tonumber(meta:get("score:score") or 0.2)
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -215, y = 23},
|
||||
text = "punkte_balken_leer.png",
|
||||
scale = { x = 1, y = 1},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
|
||||
player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -215, y = 23},
|
||||
text = "punkte_balken_voll.png",
|
||||
scale = { x = percent, y = 1},
|
||||
alignment = { x = 1, y = 0 },
|
||||
})
|
||||
```
|
||||
|
||||
Wir haben jetzt ein HUD, der wie der im ersten Abschnitt aussieht!
|
||||
Es gibt jedoch ein Problem: Es wird nicht aktualisiert, wenn sich die Statistiken ändern.
|
||||
|
||||
## Ein Element verändern
|
||||
|
||||
Sie können die von der Methode `hud_add` zurückgegebene ID verwenden, um es später zu aktualisieren oder zu entfernen.
|
||||
|
||||
```lua
|
||||
local idx = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
text = "Hallo Welt!",
|
||||
-- Parameter der Kürze halber entfernt
|
||||
})
|
||||
|
||||
player:hud_change(idx, "text", "Neuer Text")
|
||||
player:hud_remove(idx)
|
||||
```
|
||||
|
||||
Die Methode `hud_change` nimmt die Element-ID, die zu ändernde Eigenschaft und den
|
||||
neuen Wert. Der obige Aufruf ändert die Eigenschaft `text` von "Hallo Welt!" in "Neuer Text".
|
||||
|
||||
Das bedeutet, dass die Ausführung von `hud_change` unmittelbar nach `hud_add` funktionell
|
||||
äquivalent zu folgendem ist, und zwar auf eine ziemlich ineffiziente Weise:
|
||||
|
||||
```lua
|
||||
local idx = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
text = "Neuer Text",
|
||||
})
|
||||
```
|
||||
|
||||
## IDs speichern
|
||||
|
||||
```lua
|
||||
punkte = {}
|
||||
local gespeicherte_huds = {}
|
||||
|
||||
function punkte.update_hud(player)
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
-- Holt die Anzahl der Abbauungen und Orte aus dem Speicher, oder setzt ihn auf standardmäßig 0
|
||||
local meta = player:get_meta()
|
||||
local abbau_text = "Abbauungen: " .. meta:get_int("score:digs")
|
||||
local platier_text = "Platzierungen: " .. meta:get_int("score:places")
|
||||
local prozent = tonumber(meta:get("score:score") or 0.2)
|
||||
|
||||
local ids = gespeicherte_huds[player_name]
|
||||
if ids then
|
||||
player:hud_change(ids["platzierungen"], "text", places_text)
|
||||
player:hud_change(ids["abbauungen"], "text", digs_text)
|
||||
player:hud_change(ids["balken_vordergrund"],
|
||||
"scale", { x = prozent, y = 1 })
|
||||
else
|
||||
ids = {}
|
||||
gespeicherte_huds[player_name] = ids
|
||||
|
||||
-- Erstellt HUD-Elemente und setzt die IDs in `ids`
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(punkte.update_hud)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
gespeicherte_huds[player:get_player_name()] = nil
|
||||
end)
|
||||
```
|
||||
|
||||
|
||||
## Andere Elemente
|
||||
|
||||
Lesen Sie [lua_api.txt 🇬🇧](https://minetest.gitlab.io/minetest/hud/) für eine vollständige Liste der HUD-Elemente.
|
77
_de/players/player_physics.md
Normal file
77
_de/players/player_physics.md
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
title: Spielerphysiken
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 4.4
|
||||
description: Erfahren Sie, wie Sie einen Spieler schneller laufen, höher springen oder einfach schweben lassen können.
|
||||
redirect_from: /de/chapters/player_physics.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Die Spielerphysik kann mit Hilfe von Physik-Overrides verändert werden.
|
||||
Physik-Overrides können die Gehgeschwindigkeit, die Sprunggeschwindigkeit
|
||||
und Schwerkraftkonstanten einstellen.
|
||||
Physiküberschreibungen werden für jeden Spieler einzeln festgelegt
|
||||
und sind Multiplikatoren.
|
||||
Ein Wert von 2 für die Schwerkraft würde zum Beispiel die Schwerkraft doppelt so stark machen.
|
||||
|
||||
- [Grundlegendes Beispiel](#grundlegendes-beispiel)
|
||||
- [Verfügbare Overrides](#verfügbare-overrides)
|
||||
- [Altes Bewegungsverhalten](#altes-bewegungsverhalten)
|
||||
- [Mod-Inkompatibilität](#mod-inkompatibilität)
|
||||
- [Sie sind dran](#sie-sind-dran)
|
||||
|
||||
## Grundlegendes Beispiel
|
||||
|
||||
Hier ist ein Beispiel für das Hinzufügen eines Antigravitationsbefehls, der
|
||||
den Aufrufer in eine niedrige Schwerkraft versetzt:
|
||||
|
||||
```lua
|
||||
minetest.register_chatcommand("antigravity", {
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
player:set_physics_override({
|
||||
gravity = 0.1, -- setzt die Schwerkraft auf 10% des ursprünglichen Wertes
|
||||
-- (0.1 * 9.81)
|
||||
})
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
## Verfügbare Overrides
|
||||
|
||||
`player:set_physics_override()` wird eine Tabelle mit Overrides übergeben.\\
|
||||
Laut [lua_api.txt](https://minetest.gitlab.io/minetest/class-reference/#player-only-no-op-for-other-objects),
|
||||
können diese sein:
|
||||
|
||||
* speed: Multiplikator zum Standardwert für die Gehgeschwindigkeit (Standard: 1)
|
||||
* jump: Multiplikator auf Standard-Sprungwert (Standard: 1)
|
||||
* gravity: Multiplikator zum Standardwert für die Schwerkraft (Standard: 1)
|
||||
* sneak: ob der Spieler schleichen kann (Standard: true)
|
||||
|
||||
### Altes Bewegungsverhalten
|
||||
|
||||
Die Spielerbewegung vor der Version 0.4.16 beinhaltete den sneak glitch, der
|
||||
verschiedene Bewegungs-Glitches erlaubt, darunter die Fähigkeit
|
||||
einen 'Aufzug' zu erklimmen, der aus einer bestimmten Anordnung von Nodes besteht, indem man sich anschleicht
|
||||
(Umschalttaste drücken) und die Leertaste drücken, um aufzusteigen.
|
||||
Obwohl dieses Verhalten nicht beabsichtigt war, wurde es in den Überschreibungen beibehalten, da es auf vielen Servern verwendet wird.
|
||||
|
||||
Um das alte Bewegungsverhalten vollständig wiederherzustellen, sind zwei Überschreibungen erforderlich:
|
||||
|
||||
* new_move: ob der Spieler neue Bewegungen verwendet (Standard: true)
|
||||
* sneak_glitch: ob der Spieler 'Schleichfahrstühle' benutzen kann (Standard: false)
|
||||
|
||||
## Mod-Inkompatibilität
|
||||
|
||||
Bitte beachten Sie, dass Mods, die denselben Physikwert eines Spielers überschreiben,
|
||||
inkompatibel zueinander sind. Wenn ein Override gesetzt wird, überschreibt er
|
||||
Überschreibungen, die zuvor gesetzt wurden. Das bedeutet, dass wenn mehrere Überschreibungen
|
||||
die Geschwindigkeit eines Spielers festlegen, ist nur die zuletzt ausgeführte wirksam.
|
||||
|
||||
## Sie sind dran
|
||||
|
||||
* **Sonic**: Setzen Sie den Geschwindigkeitsmultiplikator auf einen hohen Wert (mindestens 6), wenn ein Spieler dem Spiel beitritt.
|
||||
* **Super bounce**: Erhöhe den Sprungwert, so dass der Spieler 20 Meter weit springen kann (1 Meter ist 1 Node).
|
||||
* **Space**: Die Schwerkraft sollte abnehmen, wenn der Spieler höher steigt.
|
138
_de/players/privileges.md
Normal file
138
_de/players/privileges.md
Normal file
@ -0,0 +1,138 @@
|
||||
---
|
||||
title: Privilegien
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 4.1
|
||||
description: Privs registrieren.
|
||||
redirect_from: /de/chapters/privileges.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Privilegien, oft privs abgekürzt, geben Spielern die Möglichkeit,
|
||||
bestimmte Aktionen durchzuführen. Serverbesitzer können Privilegien vergeben und entziehen, um zu kontrollieren
|
||||
welche Fähigkeiten jeder Spieler hat.
|
||||
|
||||
- [Wann sollten Sie Privilegien nutzen?](#wann-sollten-sie-privilegien-nutzen)
|
||||
- [Erklärung von Privilegien](#erklärung-von-privilegien)
|
||||
- [Überprüfung der Privilegien](#überprüfung-der-privilegien)
|
||||
- [Abrufen und Festlegen von Privilegien](#abrufen-und-festlegen-von-privilegien)
|
||||
- [Privilegien zu basic\_privs hinzufügen](#privilegien-zu-basic_privs-hinzufügen)
|
||||
|
||||
## Wann sollten Sie Privilegien nutzen?
|
||||
|
||||
Ein Privileg sollte einem Spieler die Möglichkeit geben, etwas zu tun(siehe Gute Privilegien).
|
||||
Privilegien sind **nicht** dazu da, Klassen- oder Statusangaben zu machen(siehe Schlechte Privilegien).
|
||||
|
||||
**Gute Privilegien:**
|
||||
|
||||
* interact
|
||||
* shout
|
||||
* noclip
|
||||
* fly
|
||||
* kick
|
||||
* ban
|
||||
* vote
|
||||
* worldedit
|
||||
* area_admin - Adminfunktionen eines Mods sind in Ordnung
|
||||
|
||||
**Schlechte Privilegien:**
|
||||
|
||||
* moderator
|
||||
* admin
|
||||
* elf
|
||||
* dwarf
|
||||
|
||||
## Erklärung von Privilegien
|
||||
|
||||
Verwenden Sie `register_privilege`, um ein neues Privileg zu deklarieren:
|
||||
|
||||
```lua
|
||||
minetest.register_privilege("vote", {
|
||||
description = "Kann über Themen abstimmen",
|
||||
give_to_singleplayer = true
|
||||
})
|
||||
```
|
||||
|
||||
`give_to_singleplayer` steht standardmäßig auf true, wenn es nicht angegeben wird, so dass es
|
||||
in der obigen Definition nicht wirklich benötigt wird.
|
||||
|
||||
## Überprüfung der Privilegien
|
||||
|
||||
Um schnell zu überprüfen, ob ein Spieler alle erforderlichen Rechte besitzt:
|
||||
|
||||
```lua
|
||||
local has, missing = minetest.check_player_privs(player_or_name, {
|
||||
interact = true,
|
||||
vote = true })
|
||||
```
|
||||
|
||||
In diesem Beispiel ist `has` wahr, wenn der Spieler alle benötigten Privilegien hat.
|
||||
Wenn `has` falsch ist, dann enthält `missing` eine Schlüssel-Wert-Tabelle
|
||||
mit den fehlenden Privilegien.
|
||||
|
||||
```lua
|
||||
local has, missing = minetest.check_player_privs(name, {
|
||||
interact = true,
|
||||
vote = true })
|
||||
|
||||
if has then
|
||||
print("Spieler hat alle Privilegien!")
|
||||
else
|
||||
print("Dem Spieler fehlen folgende Privilegien:" .. dump(missing))
|
||||
end
|
||||
```
|
||||
|
||||
Wenn Sie die fehlenden Privilegien nicht überprüfen müssen, können Sie
|
||||
check_player_privs" direkt in die if-Anweisung einfügen.
|
||||
|
||||
```lua
|
||||
if not minetest.check_player_privs(name, { interact=true }) then
|
||||
return false, "Hierfür brauchen Sie Interaktion!"
|
||||
end
|
||||
```
|
||||
|
||||
## Abrufen und Festlegen von Privilegien
|
||||
|
||||
Auf die Spielerprivilegien kann unabhängig, ob der Spieler
|
||||
online ist, zugegriffen werden.
|
||||
|
||||
|
||||
```lua
|
||||
local privs = minetest.get_player_privs(name)
|
||||
print(dump(privs))
|
||||
|
||||
privs.vote = true
|
||||
minetest.set_player_privs(name, privs)
|
||||
```
|
||||
|
||||
Privilegien werden immer als Schlüssel-Wert-Tabelle angegeben, wobei der Schlüssel der
|
||||
Name der Berechtigung und der Wert ein Boolescher Wert ist.
|
||||
|
||||
```lua
|
||||
{
|
||||
fly = true,
|
||||
interact = true,
|
||||
shout = true
|
||||
}
|
||||
```
|
||||
|
||||
## Privilegien zu basic_privs hinzufügen
|
||||
|
||||
Spieler mit dem Privileg `basic_privs` können eine begrenzte Anzahl von Privilegien gewähren und entziehen.
|
||||
Es ist üblich, dieses Privileg an Moderatoren zu vergeben, so dass
|
||||
sie `interact` und `shout` gewähren und entziehen können, aber nicht sich selbst oder anderen
|
||||
Spieler Privilegien mit größerem Missbrauchspotential wie `give` und `server` gewähren können.
|
||||
|
||||
Um ein Privileg zu `basic_privs` hinzuzufügen, und um einzustellen, welche Privilegien Ihre Moderatoren
|
||||
anderen Spielern gewähren und entziehen können, müssen Sie die Einstellung `basic_privs` ändern.
|
||||
|
||||
Standardmäßig hat `basic_privs` den folgenden Wert:
|
||||
|
||||
basic_privs = interact, shout
|
||||
|
||||
Um `vote` hinzuzufügen, aktualisieren Sie dies zu:
|
||||
|
||||
basic_privs = interact, shout, vote
|
||||
|
||||
Dies wird es Spielern mit `basic_privs` erlauben, das `vote` Privileg zu gewähren und zu entziehen.
|
221
_de/quality/clean_arch.md
Normal file
221
_de/quality/clean_arch.md
Normal file
@ -0,0 +1,221 @@
|
||||
---
|
||||
title: Einführung in saubere Architekturen
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.4
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Sobald Ihre Mod eine beachtliche Größe erreicht hat, wird es immer schwieriger, den Code
|
||||
sauber und frei von Fehlern zu halten. Dies ist ein besonders großes Problem bei der
|
||||
Verwendung von einer dynamisch typisierte Sprache wie Lua, da der Compiler Ihnen sehr
|
||||
wenig Hilfe und Möglichkeiten zur Compilerzeit gibt, wenn es darum geht, sicherzustellen,
|
||||
dass Typen ordnungsgemäß verwendet werden.
|
||||
|
||||
Dieses Kapitel behandelt wichtige Konzepte, um Ihren Code sauber zu halten und gängige
|
||||
Entwurfsmuster, um dies zu erreichen. Bitte beachten Sie, dass dieses Kapitel nicht als
|
||||
Vorschrift gedacht ist, sondern Ihnen eine Vorstellung von den Möglichkeiten geben soll.
|
||||
Es gibt nicht nur einen guten Weg, eine Mod zu entwerfen und gutes Mod-Design ist sehr
|
||||
subjektiv.
|
||||
|
||||
- [Kohäsion, Kopplung und Trennung der Programmbereiche](#kohäsion-kopplung-und-trennung-der-programmbereiche)
|
||||
- [Observer](#observer)
|
||||
- [Modell-View-Controller](#modell-view-controller)
|
||||
- [API-View](#api-view)
|
||||
- [Zusammenfassung](#zusammenfassung)
|
||||
|
||||
|
||||
## Kohäsion, Kopplung und Trennung der Programmbereiche
|
||||
|
||||
Ohne jegliche Planung neigt ein Programmierprojekt dazu, allmählich in
|
||||
Spaghetti-Code zu verfallen. Spaghetti-Code zeichnet sich durch einen Mangel an Struktur
|
||||
aus - der gesamte Code wird ohne klare Grenzen zusammengewürfelt. Das macht ein Projekt
|
||||
völlig unwartbar und endet damit, dass es aufgegeben wird.
|
||||
|
||||
Das Gegenteil davon ist, dass ein Projekt als eine Sammlung interagierender kleinerer
|
||||
Programme oder Code-Bereiche zu entwickeln. <!-- Weird wording? -->
|
||||
|
||||
> Inside every large program, there is a small program trying to get out.
|
||||
>
|
||||
> --C.A.R. Hoare
|
||||
|
||||
Die deutsche Übersetzung davon: <!-- Weird wording? -->
|
||||
> In jedem großen Programm gibt es ein kleines Programm, das versucht, herauszukommen.
|
||||
|
||||
Dies sollte so geschehen, dass Sie eine Trennung der Programmteile erreichen - jeder
|
||||
Bereich sollte klar abgegrenzt sein und einem separaten Bedürfnis oder einer Aufgabe
|
||||
entsprechen.
|
||||
|
||||
Diese Programme/Bereiche sollten die folgenden zwei Eigenschaften haben:
|
||||
|
||||
* **Hohe Kohäsion** - die Bereiche sollten eng miteinander verbunden sein.
|
||||
* **Niedrige Kopplung** - die Abhängigkeiten zwischen den Bereichen sollten so gering
|
||||
wie möglich sein und es sollte vermieden werden, sich auf interne Implementierungen zu
|
||||
verlassen. Es ist eine sehr gute Idee, sicherzustellen, dass Sie eine geringe Kopplung
|
||||
gewährleisten, da dies bedeutet, dass eine Änderung der APIs bestimmter Bereiche
|
||||
leichter durchführbar sein wird.
|
||||
|
||||
Beachten Sie, dass dies sowohl für die Beziehung zwischen Mods gilt,
|
||||
als auch für die Beziehung zwischen Bereichen innerhalb eines Mods.
|
||||
|
||||
|
||||
## Observer
|
||||
|
||||
Eine einfache Möglichkeit, verschiedene Bereiche des Codes zu trennen, ist die Verwendung des Observer-Musters.
|
||||
|
||||
Nehmen wir als Beispiel der Freischaltung einer Leistung, wenn ein Spieler zum ersten Mal ein seltenes Tier tötet. Der naive Ansatz wäre, den Code für die Errungenschaft in der mobkill-Funktion den Mob-Namen überprüfen zu lassen und die Auszeichnung freizuschalten, wenn er übereinstimmt.
|
||||
Dies ist jedoch eine schlechte Idee, da es den Mobs-Mod an die Errungenschaften gekoppelt macht. Wenn man so weitermacht - zum Beispiel, indem man XP zum Mob-Todescode hinzufügt - könnte man eine Menge chaotischer Abhängigkeiten haben.
|
||||
|
||||
Hier kommt das Observer-Muster ins Spiel. Anstatt dass sich die mymobs-Mod um Auszeichnungen kümmert,
|
||||
erhält die mymobs-Mod eine Möglichkeit für andere Bereiche des Codes, ihr Interesse an an einem Ereignis zu registrieren und Daten über das Ereignis zu erhalten.
|
||||
|
||||
```lua
|
||||
mymobs.registered_on_death = {}
|
||||
function mymobs.register_on_death(func)
|
||||
table.insert(mymobs.registered_on_death, func)
|
||||
end
|
||||
|
||||
-- im Mob-Death-Code
|
||||
for i=1, #mymobs.registered_on_death do
|
||||
mymobs.registered_on_death[i](entity, reason)
|
||||
end
|
||||
```
|
||||
|
||||
Dann meldet der andere Code sein Interesse an:
|
||||
|
||||
```lua
|
||||
mymobs.register_on_death(function(mob, reason)
|
||||
if reason.type == "punch" and reason.object and
|
||||
reason.object:is_player() then
|
||||
awards.notify_mob_kill(reason.object, mob.name)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
Vielleicht denken Sie jetzt: Moment mal, das kommt mir doch irgendwie bekannt vor. Und Sie haben Recht!
|
||||
Die Minetest-API ist stark Observer-basiert, damit sich die Engine nicht darum kümmern muss, was auf wen hört.
|
||||
|
||||
|
||||
## Modell-View-Controller
|
||||
|
||||
Im nächsten Kapitel werden wir besprechen, wie Sie Ihren Code automatisch testen können. Eines der Probleme wird sein, wie man die Logik(Berechnungen, was getan werden sollte) von API-Aufrufen (`minetest.*`, andere Mods) so weit wie möglich zu trennen.
|
||||
|
||||
Eine Möglichkeit, dies zu tun, ist, darüber nachzudenken:
|
||||
|
||||
* Welche **Daten** Sie haben.
|
||||
* Welche **Aktionen** man mit diesen Daten durchführen kann.
|
||||
* Wie **Ereignisse** (z.B. Formspecs, Schläge, etc.) diese Aktionen auslösen und wie
|
||||
diese Aktionen in der Engine etwas bewirken.
|
||||
|
||||
Nehmen wir ein Beispiel für einen Landschutz-Mod. Die Daten, die Sie haben, sind die Gebiete und alle zugehörigen Metadaten. Mögliche Aktionen sind `Erzeugen`, `Bearbeiten` oder `löschen`. Die Ereignisse, die diese Aktionen auslösen, sind Chat-Befehle und Formspec-Empfangsfelder. Dies sind 3 Bereiche, die sich in der Regel gut voneinander trennen lassen.
|
||||
|
||||
In Ihren Tests können Sie sicherstellen, dass eine Aktion, wenn sie ausgelöst wird,
|
||||
das Richtige mit den Daten macht. Sie brauchen nicht zu testen, dass ein Ereignis eine
|
||||
Aktion aufruft (dazu müsste die Minetest-API verwendet werden, und dieser Bereich des Codes sollte ohnehin so klein wie möglich gehalten werden).
|
||||
|
||||
Sie sollten Ihre Datendarstellung in reinem Lua schreiben. "Sauber" bedeutet in diesem Zusammenhang, dass die Funktionen außerhalb von Minetest ausgeführt werden können - keiner der Funktionen der Engine aufgerufen werden müssen.
|
||||
|
||||
```lua
|
||||
-- Daten
|
||||
function land.create(name, area_name)
|
||||
land.lands[area_name] = {
|
||||
name = area_name,
|
||||
owner = name,
|
||||
-- mehr Dinge
|
||||
}
|
||||
end
|
||||
|
||||
function land.get_by_name(area_name)
|
||||
return land.lands[area_name]
|
||||
end
|
||||
```
|
||||
|
||||
Ihre Aktionen sollten auch sauber sein, aber der Aufruf anderer Funktionen ist akzeptabler als im obigen Beispiel.
|
||||
|
||||
```lua
|
||||
-- Controller
|
||||
function land.handle_create_submit(name, area_name)
|
||||
-- Prozessmaterial
|
||||
-- (d.h.: auf Überschneidungen prüfen, Quoten prüfen, erechtigungen prüfen)
|
||||
|
||||
land.create(name, area_name)
|
||||
end
|
||||
|
||||
function land.handle_creation_request(name)
|
||||
-- Dies ist ein schlechtes Beispiel, wie später erklärt wird
|
||||
land.show_create_formspec(name)
|
||||
end
|
||||
```
|
||||
|
||||
Ihre Event-Handler müssen mit der Minetest-API interagieren. Sie sollten die die Anzahl der Berechnungen auf ein Minimum beschränken, da Sie diesen Bereich nicht sehr gut testen können.
|
||||
|
||||
```lua
|
||||
-- View
|
||||
function land.show_create_formspec(name)
|
||||
-- Beachten Sie, dass es hier keine komplexen Berechnungen gibt!
|
||||
return [[
|
||||
size[4,3]
|
||||
label[1,0;This is an example]
|
||||
field[0,1;3,1;area_name;]
|
||||
button_exit[0,2;1,1;exit;Exit]
|
||||
]]
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("/land", {
|
||||
privs = { land = true },
|
||||
func = function(name)
|
||||
land.handle_creation_request(name)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
land.handle_create_submit(player:get_player_name(),
|
||||
fields.area_name)
|
||||
end)
|
||||
```
|
||||
|
||||
Das obige Muster ist das Model-View-Controller-Muster. Das Modell ist eine Sammlung von Daten mit minimalen Funktionen. Der View ist eine Sammlung von Funktionen, die auf
|
||||
Ereignisse abhören und an den Controller weiterleiten und auch Aufrufe vom Controller erhalten, um etwas mit der Minetest-API zu tun. Der Controller ist der Ort, an dem die Entscheidungen und die meisten Berechnungen getroffen werden.
|
||||
|
||||
Der Controller sollte keine Kenntnisse über die Minetest-API haben - beachten Sie, dass
|
||||
es keine Minetest-Aufrufe oder View-Funktionen, die ihnen ähneln, gibt. Sie sollten *NICHT* eine Funktion wie `view.hud_add(player, def)` haben. Stattdessen definiert der View einige Aktionen, die der Controller dem View mitteilen kann, wie z. B. `view.add_hud(info)`, wobei info ein Wert oder eine Tabelle ist, die in keiner Weise mit der Minetest-API etwas zu tun hat.
|
||||
|
||||
<figure class="right_image">
|
||||
<img
|
||||
width="100%"
|
||||
src="{{ page.root }}/static/mvc_diagram.svg"
|
||||
alt="Diagramm mit einem zentrierten Textelement">
|
||||
</figure>
|
||||
|
||||
Es ist wichtig, dass jeder Bereich nur mit seinen direkten Nachbarn kommuniziert,
|
||||
wie oben gezeigt, um die Anzahl der Änderungen zu reduzieren, die Sie vornehmen müssen, wenn Sie die Interna oder Externa eines Bereichs ändern. Um beispielsweise die Formularvorgabe zu ändern, müssen Sie nur die Ansicht bearbeiten. Um die View-API zu ändern, müssten Sie nur nur den View und den Controller ändern, nicht aber das Modell.
|
||||
|
||||
In der Praxis wird dieses Design nur selten verwendet, da es die Komplexität erhöht
|
||||
und weil es für die meisten Arten von Mods nicht viele Vorteile bietet. Stattdessen,
|
||||
wird man häufig eine weniger formale und strenge Art von Design sehen -
|
||||
Varianten der API-Ansicht.
|
||||
|
||||
|
||||
### API-View
|
||||
|
||||
In einer idealen Welt würden Sie die oben genannten 3 Bereiche perfekt getrennt haben, wobei alle Ereignisse in den Controller gehen, bevor sie in die normale Ansicht zurückkehren. Aber das ist nicht die reale Welt. Ein guter Kompromiss ist die Reduzierung der Mod in zwei
|
||||
Teile:
|
||||
|
||||
**API** - Dies war das Modell und der Controller oben. Es sollte keine Verwendung von
|
||||
`minetest.` geben.
|
||||
* **View** - Dies war auch die obige Ansicht. Es ist eine gute Idee, dies in separate Dateien für jede Art von Ereignis zu strukturieren.
|
||||
|
||||
rubenwardy's [crafting mod](https://github.com/rubenwardy/crafting 🇬🇧) folgt ungefähr diesem Design. Die Datei `api.lua` besteht fast ausschließlich aus reinen Lua-Funktionen, die die Daten Speicherung und Controller-ähnliche Berechnungen handhaben. `gui.lua` ist die Ansicht für Formspecs und Formspec-Übermittlung, und `async_crafter.lua` ist der View und Controller für einen Node formspec und Nodezeitgeber.
|
||||
|
||||
Wenn man die Mods auf diese Weise trennt, kann man den API-Teil sehr einfach testen,
|
||||
da sie keine Minetest-APIs verwendet - wie im [nächstes Kapitel](unit_testing.html) und in der Crafting-Mod zu sehen.
|
||||
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Gutes Code-Design ist subjektiv und hängt stark von dem Projekt ab, an dem Sie arbeiten. Generell sollte man versuchen, die Kohäsion hoch und die Kopplung niedrig zu halten. Anders formuliert, Halten Sie verwandten Code zusammen und nicht verwandten Code auseinander und halten Sie Abhängigkeiten einfach.
|
||||
|
||||
rubenwardy empfehlt dringend die Lektüre des [Game Programming Patterns 🇬🇧](http://gameprogrammingpatterns.com/) Buch. Es ist frei verfügbar, [online (auf Englisch) lesbar](http://gameprogrammingpatterns.com/contents.html)
|
||||
und geht viel detaillierter als in diesem Buch auf allgemeine Programmiermuster ein, die für Spiele relevant sind.
|
126
_de/quality/common_mistakes.md
Normal file
126
_de/quality/common_mistakes.md
Normal file
@ -0,0 +1,126 @@
|
||||
---
|
||||
title: Häufige Fehler
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.1
|
||||
redirect_from: /de/chapters/common_mistakes.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
In diesem Kapitel werden häufige Fehler beschrieben und es wird erklärt, wie man diese vermeiden kann.
|
||||
|
||||
- [Vorsicht beim Speichern von ObjectRefs (z.B.: Spieler oder Entitäten) ](#vorsicht-beim-speichern-von-objectrefs-zb-spieler-oder-entitäten-)
|
||||
- [Vertrauen Sie keinen Formspec-Einsendungen ](#vertrauen-sie-keinen-formspec-einsendungen-)
|
||||
- [ItemStacks nach dem Ändern einstellen ](#itemstacks-nach-dem-ändern-einstellen-)
|
||||
|
||||
## Vorsicht beim Speichern von ObjectRefs (z.B.: Spieler oder Entitäten) <a name="be-careful-when-storing-objectrefs-ie-players-or-entities"></a>
|
||||
|
||||
Eine ObjectRef wird ungültig, wenn der Spieler oder die Entität, die sie repräsentiert, das Spiel verlässt. Dies kann passieren, wenn der Spieler offline geht oder die Entität nicht mehr geladen ist oder entfernt wird.
|
||||
|
||||
Die Methoden von ObjectRefs geben seit Minetest 5.2 immer `nil` zurück, wenn sie ungültig sind. Jeder Aufruf wird grundsätzlich ignoriert.
|
||||
|
||||
Sie sollten das Speichern von ObjectRefs nach Möglichkeit vermeiden. Wenn Sie dennoch eine ObjectRef speichern, sollten Sie dies vor der Verwendung überprüfen, etwa so:
|
||||
|
||||
```lua
|
||||
-- Funktioniert nur in Minetest 5.2+
|
||||
if obj:get_pos() then
|
||||
-- ist gültig!
|
||||
end
|
||||
```
|
||||
|
||||
## Vertrauen Sie keinen Formspec-Einsendungen <a name="dont-trust-formspec-submissions"></a>
|
||||
|
||||
Hacker können Formulare übermitteln, wann immer sie wollen und mit welchem Inhalt auch immer sie wollen.
|
||||
|
||||
Der folgende Code weist zum Beispiel eine Schwachstelle auf, die es Spielern ermöglicht sich selbst Moderatoren-Rechte zu geben:
|
||||
|
||||
```lua
|
||||
local function show_formspec(name)
|
||||
if not minetest.check_player_privs(name, { privs = true }) then
|
||||
return false
|
||||
end
|
||||
|
||||
minetest.show_formspec(name, "modman:modman", [[
|
||||
size[3,2]
|
||||
field[0,0;3,1;target;Name;]
|
||||
button_exit[0,1;3,1;sub;Promote]
|
||||
]])
|
||||
return true
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
-- SCHLECHT! Fehlender Privilegiencheck!
|
||||
|
||||
local privs = minetest.get_player_privs(fields.target)
|
||||
privs.kick = true
|
||||
privs.ban = true
|
||||
minetest.set_player_privs(fields.target, privs)
|
||||
return true
|
||||
end)
|
||||
```
|
||||
|
||||
Fügen Sie eine Berechtigungsprüfung hinzu, um dieses Problem zu lösen:
|
||||
|
||||
```lua
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
if not minetest.check_player_privs(name, { privs = true }) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- code
|
||||
end)
|
||||
```
|
||||
|
||||
## ItemStacks nach dem Ändern einstellen <a name="set-itemstacks-after-changing-them"></a>
|
||||
|
||||
Haben Sie bemerkt, dass es in der API einfach `ItemStack` genannt wird, nicht `ItemStackRef`, ähnlich wie `InvRef`? Das liegt daran, dass ein `ItemStack` keine Referenz ist - es ist eine Kopie. Stacks arbeiten mit einer Kopie der Daten und nicht mit dem Stapel im Inventar. Das bedeutet, dass das Ändern eines Stacks nicht wirklich den Stack im Inventar verändert.
|
||||
|
||||
Tun Sie zum Beispiel nicht dies:
|
||||
|
||||
```lua
|
||||
local inv = player:get_inventory()
|
||||
local stack = inv:get_stack("main", 1)
|
||||
stack:get_meta():set_string("description", "Teilweise gegessen")
|
||||
-- SCHLECHT! Änderung wird verloren gehen
|
||||
```
|
||||
|
||||
Machen Sie stattdessen Folgendes:
|
||||
|
||||
```lua
|
||||
local inv = player:get_inventory()
|
||||
local stack = inv:get_stack("main", 1)
|
||||
stack:get_meta():set_string("description", "Teilweise gegessen")
|
||||
inv:set_stack("main", 1, stack)
|
||||
-- Richtig! ItemStack ist eingestellt!
|
||||
```
|
||||
|
||||
Das Verhalten von Callbacks ist etwas komplizierter. Das Ändern eines `ItemStack`, der zurückgegeben wird, ändert diesen auch für den Aufrufer und alle nachfolgenden Aufrufe. In jedem Fall wird der ItemStack jedoch nur dann in der Engine gespeichert, wenn der Callback-Aufrufer ihn setzt.
|
||||
|
||||
```lua
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
itemstack:get_meta():set_string("description", "Teilweise gegessen")
|
||||
-- Fast richtig! Die Daten gehen verloren, wenn ein anderer
|
||||
-- Callback das Verhalten abbricht
|
||||
end)
|
||||
```
|
||||
|
||||
Wenn keine Callbacks diesen Vorgang abbrechen, wird der Stack gesetzt und die Beschreibung aktualisiert. Wenn jedoch ein Callback dies abbricht, kann die Aktualisierung verloren gehen.
|
||||
|
||||
Es ist besser, dies stattdessen zu tun:
|
||||
|
||||
```lua
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
itemstack:get_meta():set_string("description", "Teilweise gegessen")
|
||||
user:get_inventory():set_stack("main", user:get_wield_index(),
|
||||
itemstack)
|
||||
-- Richtig, die Beschreibung wird immer eingestellt!
|
||||
end)
|
||||
```
|
||||
|
||||
Wenn die Callbacks abbrechen oder der Callback-Runner den Stack nicht setzt, dann wird die Aktualisierung trotzdem durchgeführt.
|
||||
Wenn die Callbacks oder der Callback-Runner den Stack setzen, spielt die Verwendung von set_stack keine Rolle.
|
93
_de/quality/luacheck.md
Normal file
93
_de/quality/luacheck.md
Normal file
@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Automatische Fehlerüberprüfung
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.2
|
||||
description: Verwenden Sie LuaCheck, um Fehler zu finden
|
||||
redirect_from: /de/chapters/luacheck.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
In diesem Kapitel werden Sie lernen, wie Sie das Werkzeug LuaCheck benutzen, um Ihre Mod auf Fehler zu überprüfen. Dieses Werkzeug kann in Kombination mit deinem Editor verwendet werden, um auf Fehler aufmerksam zu machen.
|
||||
|
||||
- [Installation von LuaCheck](#installation-von-luacheck)
|
||||
- [Windows](#windows)
|
||||
- [Linux](#linux)
|
||||
- [LuaCheck ausführen ](#luacheck-ausführen-)
|
||||
- [LuaCheck konfigurieren](#luacheck-konfigurieren)
|
||||
- [Fehlerbehebung](#fehlerbehebung)
|
||||
- [Verwendung mit einem Editor](#verwendung-mit-einem-editor)
|
||||
|
||||
## Installation von LuaCheck
|
||||
|
||||
### Windows
|
||||
|
||||
Laden Sie einfach luacheck.exe von [der Github-Releaseseite 🇬🇧](https://github.com/mpeterv/luacheck/releases) herunter.
|
||||
|
||||
### Linux
|
||||
|
||||
Zuerst müssen Sie LuaRocks installieren:
|
||||
|
||||
sudo apt install luarocks
|
||||
|
||||
Sie können LuaCheck dann global (also für alle Benutzer) installieren:
|
||||
|
||||
sudo luarocks install luacheck
|
||||
|
||||
Prüfen Sie mit dem folgenden Befehl, ob es installiert ist:
|
||||
|
||||
luacheck -v
|
||||
|
||||
## LuaCheck ausführen <a name="run"></a>
|
||||
|
||||
Wenn Sie LuaCheck zum ersten Mal ausführen, wird es wahrscheinlich eine Menge falscher Fehler erkennen. Das liegt daran, dass es noch konfiguriert werden muss.
|
||||
|
||||
Unter Windows öffnen Sie die Powershell oder Bash im Stammverzeichnis Ihres Projekts und führen Sie `path\to\luacheck.exe .` aus.
|
||||
|
||||
Unter Linux führen Sie `luacheck .` aus, während Sie sich im Stammordner Ihres Projekts befinden.
|
||||
|
||||
## LuaCheck konfigurieren
|
||||
|
||||
Erstellen Sie eine Datei namens `.luacheckrc` im Stammverzeichnis Ihres Projekts. Dies kann das Stammverzeichnis Ihres Spiels, Modpacks oder Mods sein.
|
||||
|
||||
Fügen Sie den folgenden Inhalt in diese Datei ein:
|
||||
|
||||
```lua
|
||||
unused_args = false
|
||||
allow_defined_top = true
|
||||
|
||||
globals = {
|
||||
"minetest",
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
string = {fields = {"split"}},
|
||||
table = {fields = {"copy", "getn"}},
|
||||
|
||||
-- Eingebaut
|
||||
"vector", "ItemStack",
|
||||
"dump", "DIR_DELIM", "VoxelArea", "Settings",
|
||||
|
||||
-- MTG
|
||||
"default", "sfinv", "creative",
|
||||
}
|
||||
```
|
||||
|
||||
Als nächstes müssen Sie testen, ob es funktioniert, indem Sie LuaCheck ausführen. Diesmal sollten Sie viel weniger Fehler erhalten. Ändern Sie ab dem ersten Fehler, den Sie erhalten, den Code, um um das Problem zu beseitigen oder ändern Sie die Konfiguration, wenn der Code korrekt ist. Siehe die Liste unten.
|
||||
|
||||
### Fehlerbehebung
|
||||
|
||||
* **accessing undefined variable foobar** - Wenn `foobar` eine globale Variable sein soll, fügen Sie diese zu `read_globals` hinzu. Andernfalls fügen Sie alle fehlenden `local` zum Mod hinzu.
|
||||
* **setting non-standard global variable foobar** - Wenn `foobar` eine globale Variable sein soll, fügen Sie diese zu `globals` hinzu. Entfernen Sie diese aus `read_globals`, falls vorhanden.
|
||||
Andernfalls fügen Sie alle fehlenden `local` zum Mod hinzu.
|
||||
* **mutating read-only global variable 'foobar'** - Verschieben Sie `foobar` von `read_globals` nach `globals` oder hören Sie auf, in foobar zu schreiben.
|
||||
|
||||
## Verwendung mit einem Editor
|
||||
|
||||
Es wird dringend empfohlen, dass Sie ein Plugin für den Editor Ihrer Wahl finden und installieren, das Ihnen Fehler anzeigt, ohne dass Sie einen Befehl ausführen müssen. Für die meisten Editoren ist wahrscheinlich ein Plugin verfügbar.
|
||||
|
||||
* **VSCode** - Ctrl+P, dann einfügen: `ext install dwenegar.vscode-luacheck`
|
||||
* **Sublime** - Installieren Sie mit Hilfe von package-control:
|
||||
[SublimeLinter 🇬🇧](https://github.com/SublimeLinter/SublimeLinter),
|
||||
[SublimeLinter-luacheck 🇬🇧](https://github.com/SublimeLinter/SublimeLinter-luacheck).
|
28
_de/quality/readmore.md
Normal file
28
_de/quality/readmore.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Mehr lesen
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.7
|
||||
redirect_from: /de/chapters/readmore.html
|
||||
---
|
||||
|
||||
## Liste der Ressourcen
|
||||
|
||||
Nachdem Sie dieses Buch gelesen haben, sollten Sie sich Folgendes ansehen.
|
||||
|
||||
### Minetest Modding
|
||||
|
||||
* Minetests Lua API Referenz - [HTML version 🇬🇧](https://minetest.gitlab.io/minetest/class-reference/#player-only-no-op-for-other-objects) |
|
||||
[Text version 🇬🇧](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt).
|
||||
* Erforschen Sie das [Developer Wiki](https://dev.minetest.net/Main_Page/de).
|
||||
* Schauen Sie sich [existierende Mods 🇬🇧](https://forum.minetest.net/viewforum.php?f=11) an.
|
||||
|
||||
### Lua-Programmierung
|
||||
|
||||
* [Programming in Lua (PIL) 🇬🇧](http://www.lua.org/pil/).
|
||||
* [Lua Crash Course 🇬🇧](http://luatut.com/crash_course.html).
|
||||
|
||||
### 3D Modelling
|
||||
|
||||
* [Blender Dokumentation](https://de.wikibooks.org/wiki/Blender_Dokumentation).
|
||||
* [Using Blender with Minetest 🇬🇧](http://wiki.minetest.net/Using_Blender).
|
161
_de/quality/releasing.md
Normal file
161
_de/quality/releasing.md
Normal file
@ -0,0 +1,161 @@
|
||||
---
|
||||
title: Eine Mod veröffentlichen
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.6
|
||||
redirect_from: /de/chapters/releasing.html
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Das Veröffentlichen einer Mod erlaubt es anderen Leuten, sie zu nutzen. Sobald eine Mod
|
||||
veröffentlicht ist, kann sie in Einzelspieler-Spielen oder auf Servern, einschließlich öffentlichen Servern, verwendet werden.
|
||||
|
||||
- [Eine Lizenz auswählen](#eine-lizenz-auswählen)
|
||||
- [LGPL und CC-BY-SA](#lgpl-und-cc-by-sa)
|
||||
- [CC0](#cc0)
|
||||
- [MIT](#mit)
|
||||
- [Verpacken](#verpacken)
|
||||
- [README.txt](#readmetxt)
|
||||
- [mod.conf / game.conf](#modconf--gameconf)
|
||||
- [screenshot.png](#screenshotpng)
|
||||
- [Hochladen](#hochladen)
|
||||
- [Version Control Systeme](#version-control-systeme)
|
||||
- [Veröffentlichen auf ContentDB](#veröffentlichen-auf-contentdb)
|
||||
- [Forum Thema](#forum-thema)
|
||||
|
||||
## Eine Lizenz auswählen
|
||||
|
||||
Sie müssen eine Lizenz für Ihren Mod angeben. Das ist wichtig, weil sie anderen Leuten
|
||||
mitteilt, auf welche Weise sie Ihre Arbeit verwenden dürfen. Wenn Ihre Mod keine Lizenz hat
|
||||
hat, wissen andere nicht, ob sie Ihre Mod auf einem öffentlichen Server verändern,
|
||||
verbreiten oder verwenden dürfen.
|
||||
|
||||
Ihr Code und Ihre Kunst brauchen unterschiedliche Lizenzen, die sie verwenden. Zum Beispiel sollten Creative-Commons-Lizenzen nicht für Quellcode verwendet werden,
|
||||
können aber für künstlerische Werke wie Bilder, Texte und Meshes eine gute Wahl sein.
|
||||
|
||||
Sie dürfen jede Lizenz verwenden; Mods, die Derivate nicht zulassen, werden jedoch aus dem
|
||||
offiziellen Minetest-Forum verboten. (Damit eine Mod im Forum zugelassen wird, müssen andere Entwickler
|
||||
in der Lage sein, sie zu modifizieren und die modifizierte Version zu veröffentlichen.)
|
||||
|
||||
Bitte beachten Sie, dass **public domain** keine gültige Lizenz ist, da die Definition in verschiedenen
|
||||
Ländern variiert.
|
||||
|
||||
Es ist wichtig zu beachten, dass WTFPL
|
||||
[dringend abgeraten wird 🇬🇧](https://content.minetest.net/help/wtfpl/) und Leute möglicherweise
|
||||
Ihre Mod nicht verwenden, wenn sie diese Lizenz hat.
|
||||
|
||||
### LGPL und CC-BY-SA
|
||||
|
||||
Dies ist eine gängige Lizenzkombination in der Minetest-Gemeinschaft und wird von
|
||||
Minetest und Minetest Game verwendet.
|
||||
|
||||
Sie lizenzieren Ihren Code unter LGPL 2.1 und Ihre Kunst unter CC-BY-SA.
|
||||
|
||||
Dies bedeutet, dass:
|
||||
|
||||
* Jeder kann veränderte oder unveränderte Versionen verändern, weitergeben und verkaufen.
|
||||
* Wenn jemand Ihre Modifikation verändert, muss er seiner Version die gleiche Lizenz geben.
|
||||
* Ihr Urheberrechtsvermerk muss beibehalten werden.
|
||||
|
||||
### CC0
|
||||
|
||||
Diese Lizenz kann sowohl für Code als auch für Kunst verwendet werden und erlaubt es jedem, das
|
||||
mit Ihrem Werk zu machen, was er will. Das heißt, er kann es verändern, weitergeben, verkaufen oder
|
||||
die Namensnennung weglassen.
|
||||
|
||||
### MIT
|
||||
|
||||
Dies ist eine gängige Lizenz für Code. Die einzige Einschränkung, die die Nutzer
|
||||
Ihres Codes haben, dass sie denselben Copyright-Vermerk und dieselbe Lizenz
|
||||
in alle Kopien des Codes oder wesentlicher Teile des Codes aufnehmen müssen.
|
||||
|
||||
## Verpacken
|
||||
|
||||
Es gibt einige Dateien, die Sie in Ihre Mod oder Ihr Spiel aufnehmen sollten
|
||||
bevor Sie es veröffentlichen.
|
||||
|
||||
### README.txt
|
||||
|
||||
In der README-Datei sollte stehen:
|
||||
|
||||
* Was der Mod/das Spiel macht und wie man es benutzt.
|
||||
* Was die Lizenz ist
|
||||
* Optional:
|
||||
* Wo man Probleme melden oder Hilfe bekommen kann.
|
||||
* Namensnennungen
|
||||
|
||||
### mod.conf / game.conf
|
||||
|
||||
Vergewissern Sie sich, dass Sie einen Beschreibungsschlüssel hinzufügen, um zu erklären,
|
||||
was Ihr Mod oder Spiel tut. Seien Sie prägnant, ohne vage zu sein. Sie sollte kurz sein,
|
||||
denn sie wird im Content-Installer angezeigt, der nur begrenzten Platz hat.
|
||||
|
||||
Gutes Beispiel:
|
||||
|
||||
description = Fügt Suppe, Kuchen, Gebäck und Säfte hinzu.
|
||||
|
||||
Vermeiden Sie dies:
|
||||
|
||||
description = Die Lebensmittel-Mod für Minetest. (<-- SCHLECHT! Es ist vage)
|
||||
|
||||
### screenshot.png
|
||||
|
||||
Screenshots sollten im Verhältnis 3:2 (3 Pixel Breite für 2 Pixel Höhe)
|
||||
und eine Mindestgröße von 300 x 200 Pixel haben.
|
||||
|
||||
Der Screenshot wird innerhalb von Minetest als Vorschaubild für den Inhalt angezeigt.
|
||||
|
||||
## Hochladen
|
||||
|
||||
Damit ein potenzieller Benutzer Ihre Mod herunterladen kann, müssen Sie sie irgendwo hochladen und
|
||||
öffentlich zugänglich machen. Es gibt mehrere Möglichkeiten, dies zu tun, aber Sie sollten die Möglichkeit wählen, die für Sie am besten geeignet ist, solange diese die folgenden Anforderungen erfüllt und auch alle anderen, die von den Moderatoren des Forums hinzugefügt werden können:
|
||||
|
||||
* **Stabil** - Es sollte unwahrscheinlich sein, dass die Hosting-Webseite ohne Vorwarnung abgeschaltet wird.
|
||||
* **Direkter Link** - Sie sollten in der Lage sein, auf einen Link zu klicken und die Datei herunterzuladen, ohne eine andere Seite aufrufen zu müssen.
|
||||
* **Virenfrei** - Betrügerische Upload-Hosts können unsichere Werbung enthalten.
|
||||
|
||||
ContentDB ermöglicht das Hochladen von Zip-Dateien und erfüllt diese Kriterien.
|
||||
|
||||
### Version Control Systeme
|
||||
|
||||
Ein Versionskontrollsystem (VCS) ist eine Software, die Änderungen an Software verwaltet,
|
||||
Dadurch wird es oft einfacher, Änderungen zu verteilen und zu erhalten.
|
||||
|
||||
Die meisten Minetest-Modder verwenden Git und eine Webseite wie [Mesehub 🇬🇧](https://git.minetest.land/),
|
||||
um ihren Code zu veröffentlichen.
|
||||
|
||||
Die Verwendung von git kann anfangs schwierig sein. Wenn Sie dabei Hilfe benötigen, lesen Sie bitte:
|
||||
|
||||
* [Pro Git Buch](https://git-scm.com/book/de/v2/Erste-Schritte-Was-ist-Versionsverwaltung%3F) - Kostenlos online lesbar.
|
||||
|
||||
## Veröffentlichen auf ContentDB
|
||||
|
||||
ContentDB ist der offizielle Ort, um Inhalte wie Mods, Spiele und Texturpakete zu finden und zu verbreiten.
|
||||
Benutzer können Inhalte über die Webseite finden oder über die Integration in das Minetest-Hauptmenü
|
||||
herunterladen und installieren, indem sie die Integration des Hauptmenü von Minetest nutzen.
|
||||
|
||||
Melden Sie sich bei [ContentDB](https://content.minetest.net) an und fügen Sie Ihre Inhalte hinzu.
|
||||
Lesen Sie unbedingt die Hinweise im Abschnitt "Hilfe".
|
||||
|
||||
## Forum Thema
|
||||
|
||||
Sie können auch ein Forumthema erstellen, damit die Benutzer über Ihre Kreation diskutieren können.
|
||||
|
||||
Mod-Themen sollten im ["WIP Mods" 🇬🇧](https://forum.minetest.net/viewforum.php?f=9) (Work In Progress(deutsch: In Arbeit))
|
||||
Forum erstellt werden, und Spiele-Themen im ["WIP Games"🇬🇧](https://forum.minetest.net/viewforum.php?f=50) Forum.
|
||||
Wenn Sie Ihren Mod nicht mehr als "Work In Progress" betrachten, können Sie
|
||||
[beantragen, dass sie in "Mod-Veröffentlichungen" verschoben wird 🇬🇧](https://forum.minetest.net/viewtopic.php?f=11&t=10418).
|
||||
|
||||
Das Forumsthema sollte einen ähnlichen Inhalt wie das README haben, aber
|
||||
mehr Werbung machen und auch einen Link zum Download der Mod enthalten.
|
||||
Es ist eine gute Idee, wenn möglich, Screenshots von Ihrer Mod in Aktion zu zeigen.
|
||||
|
||||
Das Thema muss in einem dieser Formate angegeben werden:
|
||||
|
||||
* [Mod] Mod Titel [Modname]
|
||||
* [Mod] Mod Titel [Versionsnummer] [Modname]
|
||||
|
||||
Zum Beispiel:
|
||||
|
||||
* [Mod] More Blox [0.1] [moreblox]
|
107
_de/quality/security.md
Normal file
107
_de/quality/security.md
Normal file
@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Sicherheit
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.3
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Sicherheit ist sehr wichtig, um sicherzustellen, dass Serverbesitzer durch die Mod keine Daten oder die Kontrolle verlieren.
|
||||
|
||||
- [Zentrale Konzepte](#zentrale-konzepte)
|
||||
- [Formspecs](#formspecs)
|
||||
- [Trauen Sie niemals Einsendungen](#trauen-sie-niemals-einsendungen)
|
||||
- [Der Zeitpunkt der Prüfung ist nicht der Zeitpunkt der Nutzung ](#der-zeitpunkt-der-prüfung-ist-nicht-der-zeitpunkt-der-nutzung-)
|
||||
- [(Unsichere) Umgebungen ](#unsichere-umgebungen-)
|
||||
|
||||
## Zentrale Konzepte
|
||||
|
||||
Das wichtigste Sicherheitskonzept lautet: **Niemals dem Benutzer vertrauen**.
|
||||
Alles, was der Benutzer eingibt, sollte als bösartig betrachtet werden. Das
|
||||
bedeutet, dass Sie immer prüfen sollten, ob die eingegebenen Informationen
|
||||
gültig sind, dass der Benutzer über die richtigen Berechtigungen verfügt und
|
||||
dass er ansonsten berechtigt ist, diese Aktion durchzuführen
|
||||
(d.h. im Bereich oder als Eigentümer).
|
||||
|
||||
Eine böswillige Aktion ist nicht unbedingt die Änderung oder Zerstörung von Daten,
|
||||
sondern kann der Zugriff auf sensible Daten sein, z. B. Passwort-Hashes oder
|
||||
private Nachrichten. Dies ist besonders schlimm, wenn der Server Informationen
|
||||
wie E-Mails oder das Alter speichert, was manche zu Überprüfungszwecken tun.
|
||||
|
||||
## Formspecs
|
||||
|
||||
### Trauen Sie niemals Einsendungen
|
||||
|
||||
Jeder Benutzer kann jederzeit fast jeden Formspec mit beliebigen Werten eingeben.
|
||||
|
||||
Hier ist ein echter Code, der in einer Mod gefunden wurde:
|
||||
|
||||
```lua
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
for key, field in pairs(fields) do
|
||||
local x,y,z = string.match(key,
|
||||
"goto_([%d-]+)_([%d-]+)_([%d-]+)")
|
||||
if x and y and z then
|
||||
player:set_pos({ x=tonumber(x), y=tonumber(y),
|
||||
z=tonumber(z) })
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Können Sie das Problem erkennen? Ein böswilliger Benutzer könnte eine Formularvorgabe
|
||||
mit selbst bestimmten Positionswerten übermitteln, die es ihm ermöglichen, sich an
|
||||
jeden beliebigen Ort zu teleportieren. Dies könnte sogar automatisiert werden, indem
|
||||
man Änderungen am Client vornimmt, um im Wesentlichen den Befehl den Befehl `/teleport`
|
||||
zu replizieren, ohne dass ein Privileg erforderlich ist.
|
||||
|
||||
Die Lösung für diese Art von Problem ist die Verwendung eines
|
||||
[Contexts](../players/formspecs.html#contexts), wie zuvor im Kapitel Formspecs gezeigt.
|
||||
|
||||
### Der Zeitpunkt der Prüfung ist nicht der Zeitpunkt der Nutzung <a name="check-use"></a>
|
||||
|
||||
Jeder Benutzer kann jederzeit einen beliebigen Formspec mit beliebigen Werten übermitteln,
|
||||
außer wenn die Engine dies verbietet:
|
||||
|
||||
* Die Eingabe einer `Node Meta Formspec-Einsendungen` wird blockiert, wenn der Benutzer zu weit entfernt ist.
|
||||
* Ab 5.0 werden benannte formspecs blockiert, wenn sie noch nicht angezeigt wurden.
|
||||
|
||||
Das bedeutet, dass Sie im Handler prüfen sollten, ob der Benutzer die Bedingungen erfüllt,
|
||||
um die Formspec überhaupt anzuzeigen, sowie alle entsprechenden Aktionen.
|
||||
|
||||
Die Schwachstelle, die dadurch entsteht, dass die Berechtigungen in der show formspec geprüft werden,
|
||||
aber nicht in der handle formspec verursacht wird, wird Time Of Check is not Time Of Use (TOCTOU) genannt.
|
||||
|
||||
|
||||
## (Unsichere) Umgebungen <a name="umgebungen"></a>
|
||||
|
||||
Minetest erlaubt es Mods, eine unsandboxed Umgebung anzufordern, die ihnen Zugang auf die gesamte Lua-API gibt.
|
||||
|
||||
Können Sie die Schwachstelle im Folgenden erkennen?
|
||||
|
||||
```lua
|
||||
local ie = minetest.request_insecure_environment()
|
||||
ie.os.execute(("path/to/prog %d"):format(3))
|
||||
```
|
||||
|
||||
`string.format` ist eine Funktion in der globalen gemeinsamen Tabelle `string`. Eine
|
||||
bösartige Mod könnte diese Funktion überschreiben und Material an os.execute übergeben:
|
||||
|
||||
```lua
|
||||
string.format = function()
|
||||
return "xdg-open 'http://example.com'"
|
||||
end
|
||||
```
|
||||
|
||||
Die Mod könnte etwas viel Bösartigeres als das Öffnen einer Website weitergeben, wie z. B.
|
||||
einem entfernten Benutzer die Kontrolle über den Rechner zu geben.
|
||||
|
||||
Einige Regeln für die Verwendung einer unsicheren Umgebung:
|
||||
|
||||
* Speichern Sie sie immer in einem lokalen Verzeichnis und geben Sie sie nie in eine Funktion ein.
|
||||
* Stellen Sie sicher, dass Sie jeder Eingabe in eine unsichere Funktion vertrauen können, um das
|
||||
oben genannte Problem zu vermeiden. Dies bedeutet, dass global umdefinierbare Funktionen vermieden
|
||||
werden sollten.
|
159
_de/quality/translations.md
Normal file
159
_de/quality/translations.md
Normal file
@ -0,0 +1,159 @@
|
||||
---
|
||||
title: Übersetzung (i18n / l10n)
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.05
|
||||
marked_text_encoding:
|
||||
level: info
|
||||
title: Markierter-Text-Kodierung
|
||||
message: |
|
||||
Sie müssen das genaue Format des markierten Textes nicht kennen, aber es könnte Ihnen helfen Sie zu verstehen.
|
||||
|
||||
```
|
||||
"\27(T@mymod)Hello everyone!\27E"
|
||||
```
|
||||
|
||||
* `\27` ist das Escape-Zeichen - es wird verwendet, um Minetest mitzuteilen, dass er aufpassen soll, da
|
||||
etwas Besonderes bevorsteht. Es wird sowohl für Übersetzungen als auch für Text
|
||||
Einfärbung benutzt.
|
||||
* `(T@mymod)` besagt, dass der folgende Text mit Hilfe der `mymod` Textdomäne
|
||||
übersetzbar wird.
|
||||
* `Hello everyone!` ist der übersetzbare Text in Englisch, wie er an die
|
||||
Übersetzer-Funktion übergeben wird.
|
||||
* `\27E` ist das Escape-Zeichen mit `E`, um zu signalisieren, dass das Ende
|
||||
erreicht wurde.
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Wenn Sie ihre Mods und Spiele mit Übersetzungsunterstützung versehen, können mehr Menschen sie genießen. Laut Google Play haben 64 % der Minetest-Android-Nutzer Englisch nicht als ihre Hauptsprache. Minetest verfolgt keine Statistiken für Benutzer
|
||||
über alle Plattformen hinweg, aber es ist wahrscheinlich, dass es einen hohen Anteil an
|
||||
nicht-englischsprachiger Benutzer gibt.
|
||||
|
||||
Minetest ermöglicht es Ihnen, ihre Mods und Spiele in verschiedene Sprachen zu übersetzen, indem Sie Texte auf Englisch verfassen und mit Hilfe von Übersetzungsdateien die Texte in unterschiedliche Sprachen zu übersetzen. Die Übersetzung wird auf dem Client jedes Spielers durchgeführt, so dass jeder Spieler eine andere Sprache sieht.
|
||||
|
||||
|
||||
- [Wie funktioniert die clientseitige Übersetzung?](#wie-funktioniert-die-clientseitige-übersetzung)
|
||||
- [Markierter Text](#markierter-text)
|
||||
- [Übersetzungsdateien](#übersetzungsdateien)
|
||||
- [Formatierte Zeichenketten](#formatierte-zeichenketten)
|
||||
- [Beste Verfahren und verbreitete Unwahrheiten über die Übersetzung ](#beste-verfahren-und-verbreitete-unwahrheiten-über-die-übersetzung-)
|
||||
- [Server-seitige Übersetzungen ](#server-seitige-übersetzungen-)
|
||||
- [Zusammenfassung](#zusammenfassung)
|
||||
|
||||
|
||||
## Wie funktioniert die clientseitige Übersetzung?
|
||||
|
||||
### Markierter Text
|
||||
|
||||
Der Server muss den Clients mitteilen, wie der Text zu übersetzen ist. Dies geschieht durch die Platzierung von Kontrollzeichen im Text, die Minetest mitteilen, wo und wie der Text zu übersetzen ist. Dies wird als *markierter Text* bezeichnet und wird später näher erläutert.
|
||||
|
||||
Um Text als übersetzbar zu markieren, verwenden Sie eine Übersetzungsfunktion (`S()`), die Sie mit `minetest.get_translator(textdomain)` nutzen können`:
|
||||
|
||||
```lua
|
||||
local S = minetest.get_translator("mymod")
|
||||
|
||||
minetest.register_craftitem("mymod:item", {
|
||||
description = S("My Item"),
|
||||
})
|
||||
```
|
||||
|
||||
Das erste Argument von `get_translator` ist `textdomain`, die als Namensraum dient. Anstatt alle Übersetzungen für eine Sprache in einer einzigen Datei zu speichern, werden die Übersetzungen in Textdomänen aufgeteilt (eine Datei pro Textdomäne
|
||||
pro Sprache). Die Textdomäne sollte mit dem Namen des Mods übereinstimmen, da dies hilft, Mod-Konflikte zu vermeiden.
|
||||
|
||||
Markierter Text kann an den meisten Stellen verwendet werden, wo menschenlesbarer Text akzeptiert wird, einschließlich *formspecs*, *item def*-Felder, Infotext und mehr. Wenn Sie markierten Text in Formspecs einfügen, müssen Sie den Text mit `minetest.formspec_escape` entschlüsseln.
|
||||
|
||||
Wenn der Client auf übersetzbaren Text stößt, wie den, der an `description` übergeben wird, schaut er in der Übersetzungsdatei der Sprache des Spielers nach. Wenn eine Übersetzung nicht gefunden wird, greift er auf die englische Übersetzung zurück.
|
||||
|
||||
Der übersetzbare markierte Text enthält den englischen Ausgangstext, die Textdomäne,
|
||||
und alle zusätzlichen Argumente, die an `S()` übergeben werden. Es ist im wesentlichen eine Textkodierung des `S`-Aufrufs, die alle erforderlichen Informationen enthält.
|
||||
|
||||
Eine andere Art von markiertem Text ist der, der von `minetest.colorize` zurückgegeben wird.
|
||||
|
||||
{% include notice.html notice=page.marked_text_encoding %}
|
||||
|
||||
|
||||
### Übersetzungsdateien
|
||||
|
||||
Übersetzungsdateien sind Mediendateien, die sich im Ordner `locale` von jeder Mod befinden. Zurzeit wird nur das Format `.tr` unterstützt, aber die Unterstützung für weitere gängige Formate wird wahrscheinlich in der Zukunft hinzugefügt. Übersetzungsdateien müssen folgendermaßen benannt werden: `[Textdomäne].[Sprache].tr`.
|
||||
|
||||
Dateien im `.tr` beginnen mit einem Kommentar, der die Textdomäne angibt, und dann weiteren Zeilen, die den englischen Ausgangstext in die Übersetzung übertragen.
|
||||
|
||||
Zum Beispiel: `mymod.de.tr`:
|
||||
|
||||
```
|
||||
# textdomain: mymod
|
||||
Hello everyone!=Hallo zusammen!
|
||||
I like grapefruit=Ich mag Traubenfrüchte
|
||||
```
|
||||
|
||||
Sie sollten Übersetzungsdateien auf der Grundlage des Quellcodes Ihres Mods/Spiels erstellen, unter Verwendung eines Tools wie [update_translations 🇬🇧](https://github.com/minetest-tools/update_translations). Dieses Tool sucht nach `S(` in Ihrem Lua-Code und erstellt automatisch eine Vorlage, die Übersetzer für die Übersetzung in ihre Sprache verwenden können. Es kümmert sich auch darum, die Übersetzungsdateien zu aktualisieren, wenn sich Ihr Quelltext ändert.
|
||||
|
||||
Sie sollten immer wörtlichen Text (`"`) in S einfügen, anstatt eine Variable zu verwenden,
|
||||
da dies den Werkzeugen hilft, Übersetzungen zu finden.
|
||||
|
||||
## Formatierte Zeichenketten
|
||||
|
||||
Es ist üblich, variable Informationen in einen Übersetzungsstring aufzunehmen. Dabei ist es ist wichtig, dass der Text nicht einfach aneinandergereiht wird, da das Übersetzer daran hindert, die Reihenfolge der Variablen innerhalb eines Satzes zu ändern. Stattdessen, sollten Sie das Format/Argumente-System des Übersetzungssystems verwenden:
|
||||
|
||||
```lua
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
minetest.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name()))
|
||||
end)
|
||||
```
|
||||
|
||||
Wenn Sie ein wörtliches `@` in Ihre Übersetzung einfügen wollen, müssen Sie die Escape-Schreibweise verwenden, indem Sie `@@` schreiben.
|
||||
|
||||
Sie sollten die Konkatenation *innerhalb* eines Satzes vermeiden, aber es wird empfohlen, mehrere Sätze durch Konkatenation zu verbinden. Dies hilft den Übersetzern, weil Zeichenketten kleiner gehalten sind.
|
||||
|
||||
```lua
|
||||
S("Hello @1!", player_name) .. " " .. S("You have @1 new messages.", #msgs)
|
||||
```
|
||||
|
||||
|
||||
## Beste Verfahren und verbreitete Unwahrheiten über die Übersetzung <a name="verfahren"></a>
|
||||
|
||||
* Vermeiden Sie die Konkatenation von Text und verwenden Sie stattdessen Formatierungsargumente.
|
||||
Dies gibt den Übersetzern die volle Kontrolle über die Änderung der Reihenfolge.
|
||||
* Erstellen Sie Übersetzungsdateien automatisch, indem Sie
|
||||
[update_translations 🇬🇧](https://github.com/minetest-tools/update_translations) verwenden.
|
||||
* Es ist üblich, dass Variablen den umgebenden Text verändern, zum Beispiel durch
|
||||
Geschlecht und Pluralisierung. Dies ist oft schwer zu behandeln, daher wird
|
||||
häufig mit geschlechtsneutralen Formulierungen umgangen.
|
||||
* Übersetzungen können viel länger oder viel kleiner als der englische Text sein. Stellen Sie
|
||||
sicher, dass Sie viel Platz lassen.
|
||||
* In anderen Sprachen werden Zahlen möglicherweise anders geschrieben, zum Beispiel
|
||||
mit Kommas als Dezimalkomma(also wie im Deutschen). `1.000,23`, `1'000'000,32`
|
||||
* Gehen Sie nicht davon aus, dass andere Sprachen die Großschreibung auf die gleiche Weise verwenden.
|
||||
|
||||
|
||||
## Server-seitige Übersetzungen <a name="server"></a>
|
||||
|
||||
Manchmal muss man z. B. die Übersetzung eines Textes auf dem Server kennen, um Text zu sortieren oder zu suchen. Sie können `get_player_information` benutzen, um die Sprache des Spielers zu erhalten und `get_translated_string`, um markierten Text zu übersetzen.
|
||||
|
||||
```lua
|
||||
local list = {
|
||||
S("Hello world!"),
|
||||
S("Potato")
|
||||
}
|
||||
|
||||
minetest.register_chatcommand("find", {
|
||||
func = function(name, param)
|
||||
local info = minetest.get_player_information(name)
|
||||
local language = info and info.language or "en"
|
||||
|
||||
for _, line in ipairs(list) do
|
||||
local trans = minetest.get_translated_string(language, line)
|
||||
if trans:contains(query) then
|
||||
return line
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Die Übersetzungs-API ermöglicht es, Mods und Spiele besser zugänglich zu machen, aber es ist Vorsicht geboten, um sie richtig zu nutzen.
|
||||
|
||||
Minetest wird ständig verbessert und die Übersetzungs-API wird wahrscheinlich in Zukunft erweitert werden. Zum Beispiel wird die Unterstützung für gettext-Übersetzungsdateien die Verwendung gängiger Übersetzungswerkzeuge und -plattformen (wie weblate) ermöglichen und es wird wahrscheinlich eine Unterstützung für Pluralisierung und Geschlecht hinzugefügt werden.
|
184
_de/quality/unit_testing.md
Normal file
184
_de/quality/unit_testing.md
Normal file
@ -0,0 +1,184 @@
|
||||
---
|
||||
title: Automatische Unit-Tests
|
||||
layout: default
|
||||
root: ../..
|
||||
idx: 8.5
|
||||
---
|
||||
|
||||
## Einleitung <!-- omit in toc -->
|
||||
|
||||
Unit-Tests sind ein wichtiges Instrument, um zu beweisen und sich zu vergewissern,
|
||||
dass der Code korrekt ist. Dieses Kapitel zeigt Ihnen, wie Sie Tests für
|
||||
Minetest-Mods und Spiele mit Busted schreiben. Das Schreiben von Unit-Tests für
|
||||
Funktionen, die Minetest Funktionen aufruft, ist ziemlich schwierig, aber zum Glück
|
||||
haben wir [im vorherigen Kapitel](clean_arch.html), besprochen, wie man seinen Code
|
||||
strukturiert, um dies zu vermeiden.
|
||||
|
||||
- [Busted installieren](#busted-installieren)
|
||||
- [Ihr erster Test](#ihr-erster-test)
|
||||
- [init.lua](#initlua)
|
||||
- [api.lua](#apilua)
|
||||
- [tests/api\_spec.lua](#testsapi_speclua)
|
||||
- [Mocking: Externe Funktionen verwenden](#mocking-externe-funktionen-verwenden)
|
||||
- [Überprüfen von Commits mit Travis](#überprüfen-von-commits-mit-travis)
|
||||
- [Zusammenfassung](#zusammenfassung)
|
||||
|
||||
## Busted installieren
|
||||
|
||||
Zuerst müssen Sie LuaRocks installieren.
|
||||
|
||||
* Windows: Folgen Sie den [Installationsanweisungen im LuaRocks-Wiki 🇬🇧](https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Windows).
|
||||
* Debian/Ubuntu Linux: `sudo apt install luarocks`
|
||||
|
||||
Als nächstes sollten Sie Busted global, also für alle Benutzer, installieren:
|
||||
|
||||
sudo luarocks install busted
|
||||
|
||||
Prüfen Sie schließlich, ob es installiert ist:
|
||||
|
||||
busted --version
|
||||
|
||||
|
||||
## Ihr erster Test
|
||||
|
||||
Busted ist das führende Unit-Test-Framework für Lua. Busted sucht nach Lua-Dateien mit Namen, die auf `_spec` enden und führt sie dann in einer eigenständigen Lua-Umgebung aus.
|
||||
|
||||
mymod/
|
||||
├── init.lua
|
||||
├── api.lua
|
||||
└── tests
|
||||
└── api_spec.lua
|
||||
|
||||
|
||||
### init.lua
|
||||
|
||||
```lua
|
||||
mymod = {}
|
||||
|
||||
dofile(minetest.get_modpath("mymod") .. "/api.lua")
|
||||
```
|
||||
|
||||
|
||||
|
||||
### api.lua
|
||||
|
||||
```lua
|
||||
function mymod.add(x, y)
|
||||
return x + y
|
||||
end
|
||||
```
|
||||
|
||||
### tests/api_spec.lua<a name="testsapispeclua"></a>
|
||||
|
||||
```lua
|
||||
-- Sucht nach erforderlichen Dingen in
|
||||
package.path = "../?.lua;" .. package.path
|
||||
|
||||
-- Setzt mymod global für API zum Schreiben in
|
||||
_G.mymod = {} --_
|
||||
-- Ausführen von der api.lua-Datei
|
||||
require("api")
|
||||
|
||||
-- Testen
|
||||
describe("add", function()
|
||||
it("adds", function()
|
||||
assert.equals(2, mymod.add(1, 1))
|
||||
end)
|
||||
|
||||
it("supports negatives", function()--in Deutschen ist "supports negatives" "unterstützt Negative (Zahlen)"
|
||||
assert.equals(0, mymod.add(-1, 1))
|
||||
assert.equals(-2, mymod.add(-1, -1))
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
Sie können die Tests nun ausführen, indem Sie ein Terminal im Verzeichnis der Mods öffnen und `busted` ausführen.
|
||||
|
||||
Es ist wichtig, dass die API-Datei die Tabelle nicht selbst erstellt, da Globals in Busted anders funktionieren. Jede Variable, die in Minetest global wäre, ist stattdessen
|
||||
eine lokale Datei in Busted. Dies wäre ein besserer Weg für Minetest gewesen, die Dinge zu erledigen, aber dafür ist es jetzt zu spät.
|
||||
|
||||
Eine weitere Sache, die man beachten sollte, ist, dass alle Dateien, die man testet, keine Aufrufe an irgendwelche Funktionen vermeiden, die nicht darin enthalten sind. In der Regel schreibt man nur Tests für eine einzige Datei auf einmal.
|
||||
|
||||
|
||||
## Mocking: Externe Funktionen verwenden
|
||||
|
||||
Beim Mocking werden die Funktionen ersetzt, von denen das zu testende Objekt abhängt. Dies kann zwei Zwecke haben: Erstens ist die Funktion in der Testumgebung möglicherweise nicht verfügbar Testumgebung nicht zur Verfügung und zweitens möchten Sie vielleicht die Aufrufe der Funktion und alle übergebenen Argumente.
|
||||
|
||||
Wenn Sie die Ratschläge im Kapitel [Saubere Architekturen](clean_arch.html) befolgen,
|
||||
haben Sie bereits eine ziemlich saubere Datei zum Testen. Sie müssen jedoch noch
|
||||
Dinge, die nicht in Ihrem Bereich liegen, mocken - zum Beispiel müssen Sie den View mocken, wenn Sie Controller/API testen. Wenn Sie die Ratschläge nicht befolgt haben, ist es etwas etwas schwieriger, da Sie möglicherweise die Minetest-API nachbilden müssen.
|
||||
|
||||
```lua
|
||||
-- Wie oben eine Tabelle erstellen
|
||||
_G.minetest = {}
|
||||
|
||||
-- Definieren Sie die Mock-Funktion
|
||||
local chat_send_all_calls = {}
|
||||
function minetest.chat_send_all(name, message)
|
||||
table.insert(chat_send_all_calls, { name = name, message = message })
|
||||
end
|
||||
|
||||
-- Tests
|
||||
describe("list_areas", function()
|
||||
it("returns a line for each area", function()
|
||||
chat_send_all_calls = {} -- reset table
|
||||
|
||||
mymod.list_areas_to_chat("singleplayer", "singleplayer")
|
||||
|
||||
assert.equals(2, #chat_send_all_calls)
|
||||
end)
|
||||
|
||||
it("sends to right player", function()
|
||||
chat_send_all_calls = {} -- reset table
|
||||
|
||||
mymod.list_areas_to_chat("singleplayer", "singleplayer")
|
||||
|
||||
for _, call in pairs(chat_send_all_calls) do --_
|
||||
assert.equals("singleplayer", call.name)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Die beiden oben genannten Tests sind eigentlich sinnlos,
|
||||
-- denn dieser testet beide Dinge
|
||||
it("returns correct thing", function()
|
||||
chat_send_all_calls = {} -- reset table
|
||||
|
||||
mymod.list_areas_to_chat("singleplayer", "singleplayer")
|
||||
|
||||
local expected = {
|
||||
{ name = "singleplayer", message = "Town Hall (2,43,63)" },
|
||||
{ name = "singleplayer", message = "Airport (43,45,63)" },
|
||||
}
|
||||
assert.same(expected, chat_send_all_calls)
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
|
||||
## Überprüfen von Commits mit Travis
|
||||
|
||||
Das Travis-Skript aus dem Kapitel [Automatische Fehlerüberprüfung](luacheck.html) kann so modifiziert werden, dass es auch Busted ausführt.
|
||||
|
||||
```yml
|
||||
language: generic
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- luarocks
|
||||
before_install:
|
||||
- luarocks install --local luacheck && luarocks install --local busted
|
||||
script:
|
||||
- $HOME/.luarocks/bin/luacheck .
|
||||
- $HOME/.luarocks/bin/busted .
|
||||
notifications:
|
||||
email: false
|
||||
```
|
||||
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Unit-Tests können die Qualität und Zuverlässigkeit Ihres Projekts erheblich steigern. Diese erfordern jedoch, dass Sie Ihren Code anders strukturieren als üblich.
|
||||
|
||||
Ein Beispiel für einen Mod mit vielen Unit-Tests finden Sie unter
|
||||
[crafting von rubenwardy 🇬🇧](https://github.com/rubenwardy/crafting).
|
143
_de/wörterbuch.txt
Normal file
143
_de/wörterbuch.txt
Normal file
@ -0,0 +1,143 @@
|
||||
Active Block Modifiers = Aktive Mapblock Modifikatoren
|
||||
Admin = Admin
|
||||
Administrator = Administrator
|
||||
Alignment = Ausrichtung
|
||||
anchor = Anker
|
||||
Armor Groups = Armor Groups
|
||||
Attachment = Anhang
|
||||
block = Mapblock
|
||||
bulk = bulk
|
||||
callback = Callback
|
||||
callback runner = Callback-Runner
|
||||
Chapter = Kapitel
|
||||
chat message = Chat-Nachricht
|
||||
child = Kind
|
||||
client = Client
|
||||
Cohesion = Kohäsion
|
||||
Command = Befehl
|
||||
concatenation = Konkatenation
|
||||
Context = Context
|
||||
Cubic Nodes = Würfelförmiger Block
|
||||
craft slots = Handwerksplätze
|
||||
Credits = Namensnennungen
|
||||
Damage = Schaden
|
||||
Damage Groups = Damage Groups
|
||||
database = Datenbank
|
||||
Degrees = Grad
|
||||
detached inventory = freistehendes Inventar
|
||||
dig = abbauen
|
||||
Dig Types = Grabungstypen
|
||||
drawtype = Zeichnungstyp
|
||||
element = Element
|
||||
Entities = Entitäten
|
||||
Entity = Entität
|
||||
false = falsch
|
||||
Filler node = Füllender Node
|
||||
Folder = Verzeichnis
|
||||
form = Form
|
||||
forms = Forms
|
||||
formspec = formspec
|
||||
formspec language version = formspec-Sprachversion
|
||||
formspec slot = formspec-Slot
|
||||
formspec submission = formspec-Einsendung
|
||||
games = Spiele
|
||||
getter = Getter
|
||||
glasslike = Glasartig
|
||||
glitch = Störung
|
||||
grid = Raster
|
||||
hard dependency = feste Abhängigkeit
|
||||
Header = Header
|
||||
Heads Up Display = Heads Up Display
|
||||
Health = Leben
|
||||
Health Points = Lebenspunkte
|
||||
HP = HP
|
||||
hud = hud
|
||||
HUD = HUD
|
||||
initial properties = Erst-Eigenschaften
|
||||
insecure environment = unsichere Umgebung
|
||||
Introduction = Einleitung
|
||||
Inventory = Inventar
|
||||
Inventory List = Inventarliste
|
||||
Inventory Location = Inventarstandort
|
||||
Inventory reference = Inventar-Referenz
|
||||
Item metadata = Item-Metadaten
|
||||
ItemStack = ItemStack
|
||||
key = Schlüssel
|
||||
key-value table = Schlüssel-Wert-Tabelle
|
||||
large data = große Daten
|
||||
Legacy Coordinates = Legacy-Koordinaten
|
||||
Lua entity = Lua entity
|
||||
Lua Voxel Manipulator
|
||||
main inventory = Hauptinventar
|
||||
map = Karte
|
||||
MapBlock = Map-Block
|
||||
mapgen = Mapgen
|
||||
medium data = mittlere Daten
|
||||
Mesh = Mesh
|
||||
Mesh Nodes = Mesh Nodes
|
||||
Metadata Object = Metadatenobjekt
|
||||
Mocking = Mocking
|
||||
mod load paths = Mod-Lade-Verzeichnisse
|
||||
mod's folder = Mod-Verzeichnis
|
||||
mods = Mods
|
||||
Mod storage = Mod-Storage
|
||||
Mod Packs = Mod-Pakete
|
||||
module = Modul
|
||||
mymod = mymod
|
||||
network latency = Latenzzeit im Netz
|
||||
node = Node
|
||||
Node metadata = Node-Metadaten
|
||||
nodes = Blöcke
|
||||
nodebox = Nodebox
|
||||
nodeboxes = Nodeboxen
|
||||
Node inventory = Blockinventar
|
||||
node timer = Blocktimer
|
||||
noise parameters = Noise-Parameter
|
||||
Object properties = Objekt-Eigenschaften
|
||||
offset = Versatz
|
||||
optional dependency = Optionale Abhängigkeiten
|
||||
Overrides = Overrides
|
||||
path = Verzeichnis
|
||||
parent = Elternteil
|
||||
per-player = pro-Spieler
|
||||
pixel art = Pixel-Art
|
||||
physics overrides = Physik-Overrides
|
||||
placer = plazierer
|
||||
player = Spieler
|
||||
player inventory = Spielerinventar
|
||||
player reference = Spielerreferenz
|
||||
Player Physic = Spielerphysik
|
||||
pointed_thing = angeschautes_ding
|
||||
priv = priv
|
||||
privilege = Privileg
|
||||
punch = schlagen
|
||||
put = ablegen
|
||||
Radians = Radiant
|
||||
Real Coordinates = Reale Koordinaten
|
||||
releasing=veröffentlichen
|
||||
scale = scale
|
||||
schematic = Schematic
|
||||
Schematic Decoration = schematischen Dekoration
|
||||
Scoreboard = Anzeigetafel
|
||||
score panel = Anzeigetafel
|
||||
shout = shout
|
||||
small data = kleine Daten
|
||||
sneak elevators = Schleichfahrstühle
|
||||
sneak glitch = sneak glitch
|
||||
Stack = Stack
|
||||
string = Zeichenkette
|
||||
storage = Storage
|
||||
Subcommand = Unterbefehl
|
||||
table = Tabelle
|
||||
textdomain = Textdomäne
|
||||
tile = Kachel
|
||||
tiles = Kacheln
|
||||
timer = timer
|
||||
Top node = Oberer Node
|
||||
true = wahr
|
||||
Unit Testing = Unit-Tests
|
||||
unsandboxed = unsandboxed
|
||||
unsandboxed environment = unsandboxed Umgebung
|
||||
Version Control System = Version Control System
|
||||
wear = Abnutzung
|
||||
Your turn = Sie sind dran
|
@ -9,6 +9,9 @@ layout: base
|
||||
{% if language == "_it" %}
|
||||
{% assign language = "it" %}
|
||||
{% assign links = site.it %}
|
||||
{% elsif language == "_de" %}
|
||||
{% assign language = "de" %}
|
||||
{% assign links = site.de %}
|
||||
{% else %}
|
||||
{% assign language = "en" %}
|
||||
{% assign links = site.en %}
|
||||
|
Loading…
Reference in New Issue
Block a user