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 |
@ -1,4 +1,4 @@
|
||||
# Luanti Modding Book
|
||||
# Minetest Modding Book
|
||||
|
||||
[![Build status](https://gitlab.com/rubenwardy/minetest_modding_book/badges/master/pipeline.svg)](https://gitlab.com/rubenwardy/minetest_modding_book/pipelines)<br>
|
||||
[Read Online](https://rubenwardy.com/minetest_modding_book/)
|
||||
|
@ -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
|
@ -130,7 +130,7 @@ programs such as [Geogebra](https://www.geogebra.org).
|
||||
The following code registers a simple biome named grasslands biome:
|
||||
|
||||
```lua
|
||||
core.register_biome({
|
||||
minetest.register_biome({
|
||||
name = "grasslands",
|
||||
node_top = "default:dirt_with_grass",
|
||||
depth_top = 1,
|
||||
@ -174,7 +174,7 @@ details for where it can be placed, and how frequently it occurs.
|
||||
For example:
|
||||
|
||||
```lua
|
||||
core.register_decoration({
|
||||
minetest.register_decoration({
|
||||
deco_type = "simple",
|
||||
place_on = {"base:dirt_with_grass"},
|
||||
sidelen = 16,
|
||||
@ -199,7 +199,7 @@ Schematic decorations are very similar to simple decoration, but involve the pla
|
||||
of a schematic instead of the placement of a single node. For example:
|
||||
|
||||
```lua
|
||||
core.register_decoration({
|
||||
minetest.register_decoration({
|
||||
deco_type = "schematic",
|
||||
place_on = {"base:desert_sand"},
|
||||
sidelen = 16,
|
||||
@ -207,7 +207,7 @@ core.register_decoration({
|
||||
biomes = {"desert"},
|
||||
y_max = 200,
|
||||
y_min = 1,
|
||||
schematic = core.get_modpath("plants") .. "/schematics/cactus.mts",
|
||||
schematic = minetest.get_modpath("plants") .. "/schematics/cactus.mts",
|
||||
flags = "place_center_x, place_center_z",
|
||||
rotation = "random",
|
||||
})
|
||||
@ -229,7 +229,7 @@ to consider registering mapgen aliases of your own if you are making your own ga
|
||||
Mapgen aliases provide information to the core mapgen, and can be registered in the form:
|
||||
|
||||
```lua
|
||||
core.register_alias("mapgen_stone", "base:smoke_stone")
|
||||
minetest.register_alias("mapgen_stone", "base:smoke_stone")
|
||||
```
|
||||
|
||||
At a minimum you should register:
|
||||
|
@ -10,8 +10,8 @@ redirect_from:
|
||||
mapgen_object:
|
||||
level: warning
|
||||
title: LVMs and Mapgen
|
||||
message: Don't use `core.get_voxel_manip()` with mapgen, as it can cause glitches.
|
||||
Use `core.get_mapgen_object("voxelmanip")` instead.
|
||||
message: Don't use `minetest.get_voxel_manip()` with mapgen, as it can cause glitches.
|
||||
Use `minetest.get_mapgen_object("voxelmanip")` instead.
|
||||
---
|
||||
|
||||
## Introduction <!-- omit in toc -->
|
||||
@ -45,7 +45,7 @@ and maximum positions that you need to modify. Then you can create and read into
|
||||
an LVM. For example:
|
||||
|
||||
```lua
|
||||
local vm = core.get_voxel_manip()
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
```
|
||||
|
||||
@ -93,7 +93,7 @@ You can find out the content ID for a particular type of node with
|
||||
`get_content_id()`. For example:
|
||||
|
||||
```lua
|
||||
local c_stone = core.get_content_id("default:stone")
|
||||
local c_stone = minetest.get_content_id("default:stone")
|
||||
```
|
||||
|
||||
You can then check whether the node is stone:
|
||||
@ -164,17 +164,17 @@ For setting lighting and param2 data, use the appropriately named
|
||||
|
||||
`write_to_map()` takes a Boolean which is true if you want lighting to be
|
||||
calculated. If you pass false, you need to recalculate lighting at a future
|
||||
time using `core.fix_light`.
|
||||
time using `minetest.fix_light`.
|
||||
|
||||
## Example
|
||||
|
||||
```lua
|
||||
local function grass_to_dirt(pos1, pos2)
|
||||
local c_dirt = core.get_content_id("default:dirt")
|
||||
local c_grass = core.get_content_id("default:dirt_with_grass")
|
||||
local c_dirt = minetest.get_content_id("default:dirt")
|
||||
local c_grass = minetest.get_content_id("default:dirt_with_grass")
|
||||
|
||||
-- Read data into LVM
|
||||
local vm = core.get_voxel_manip()
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
local a = VoxelArea:new{
|
||||
MinEdge = emin,
|
||||
|
@ -122,13 +122,13 @@ Create an init.lua file with the following content:
|
||||
```lua
|
||||
print("This file will be run at load time!")
|
||||
|
||||
core.register_node("mymod:node", {
|
||||
minetest.register_node("mymod:node", {
|
||||
description = "This is a node",
|
||||
tiles = {"mymod_node.png"},
|
||||
groups = {cracky = 1}
|
||||
})
|
||||
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mymod:node 3",
|
||||
recipe = { "default:dirt", "default:stone" },
|
||||
|
@ -177,7 +177,7 @@ nicer way to write it.
|
||||
The recommended way to include other Lua scripts in a mod is to use *dofile*.
|
||||
|
||||
```lua
|
||||
dofile(core.get_modpath("modname") .. "/script.lua")
|
||||
dofile(minetest.get_modpath("modname") .. "/script.lua")
|
||||
```
|
||||
|
||||
A script can return a value, which is useful for sharing private locals:
|
||||
@ -189,7 +189,7 @@ module.message = "Hello World!"
|
||||
return module
|
||||
|
||||
-- init.lua
|
||||
local ret = dofile(core.get_modpath("modname") .. "/script.lua")
|
||||
local ret = dofile(minetest.get_modpath("modname") .. "/script.lua")
|
||||
print(ret.message) -- Hello world!
|
||||
```
|
||||
|
||||
|
@ -9,7 +9,7 @@ idx: 0.1
|
||||
---
|
||||
|
||||
<header>
|
||||
<h1>Luanti Modding Book (formerly Minetest)</h1>
|
||||
<h1>Minetest Modding Book</h1>
|
||||
|
||||
<span>by <a href="https://rubenwardy.com" rel="author">rubenwardy</a></span>
|
||||
<span>with editing by <a href="http://rc.minetest.tv/">Shara</a></span>
|
||||
|
@ -12,7 +12,7 @@ Minetest heavily uses a callback-based modding design. A callback is a function
|
||||
that you give to an API and is called when an event happens. For example, you
|
||||
can provide an `on_punch` function in a node definition to be called when a player
|
||||
punches a node. There are also global callbacks like
|
||||
`core.register_on_punchnode` to receive events for all nodes.
|
||||
`minetest.register_on_punchnode` to receive events for all nodes.
|
||||
|
||||
- [Item Callbacks](#item-callbacks)
|
||||
- [on_use](#on_use)
|
||||
@ -34,9 +34,9 @@ certain events:
|
||||
| Callback | Default binding | Default value |
|
||||
|------------------|---------------------------|----------------------------------------------|
|
||||
| on_use | left-click | nil |
|
||||
| on_place | right-click on a node | `core.item_place` |
|
||||
| on_secondary_use | right-click not on a node | `core.item_secondary_use` (does nothing) |
|
||||
| on_drop | Q | `core.item_drop` |
|
||||
| on_place | right-click on a node | `minetest.item_place` |
|
||||
| on_secondary_use | right-click not on a node | `minetest.item_secondary_use` (does nothing) |
|
||||
| on_drop | Q | `minetest.item_drop` |
|
||||
| after_use | digging a node | nil |
|
||||
|
||||
|
||||
@ -46,27 +46,27 @@ Having a use callback prevents the item from being used to dig nodes. One common
|
||||
use of the use callback is for food:
|
||||
|
||||
```lua
|
||||
core.register_craftitem("mymod:mudpie", {
|
||||
minetest.register_craftitem("mymod:mudpie", {
|
||||
description = "Alien Mud Pie",
|
||||
inventory_image = "myfood_mudpie.png",
|
||||
on_use = core.item_eat(20),
|
||||
on_use = minetest.item_eat(20),
|
||||
})
|
||||
```
|
||||
|
||||
The number supplied to the core.item_eat function is the number of hit
|
||||
The number supplied to the minetest.item_eat function is the number of hit
|
||||
points healed when this food is consumed. Each heart icon the player has is
|
||||
worth two hitpoints. A player can usually have up to 10 hearts, which is equal
|
||||
to 20 hitpoints.
|
||||
|
||||
core.item_eat() is a function that returns a function, setting it as the
|
||||
minetest.item_eat() is a function that returns a function, setting it as the
|
||||
on_use callback. This means the code above is equivalent to this:
|
||||
|
||||
```lua
|
||||
core.register_craftitem("mymod:mudpie", {
|
||||
minetest.register_craftitem("mymod:mudpie", {
|
||||
description = "Alien Mud Pie",
|
||||
inventory_image = "myfood_mudpie.png",
|
||||
on_use = function(...)
|
||||
return core.do_item_eat(20, nil, ...)
|
||||
return minetest.do_item_eat(20, nil, ...)
|
||||
end,
|
||||
})
|
||||
```
|
||||
@ -82,7 +82,7 @@ called when the player is pointing at a node and `on_secondary_use` when the
|
||||
player isn't.
|
||||
|
||||
Both callbacks are called for all types of items. `on_place` defaults to the
|
||||
`core.item_place` function, which handles calling the `on_rightclick`
|
||||
`minetest.item_place` function, which handles calling the `on_rightclick`
|
||||
callback of the pointed node or placing the wielded item if it is a node.
|
||||
|
||||
|
||||
@ -90,7 +90,7 @@ callback of the pointed node or placing the wielded item if it is a node.
|
||||
|
||||
on_drop is called when the player requests to drop an item, for example using
|
||||
the drop key (Q) or dragging it outside of the inventory. It defaults to the
|
||||
`core.item_drop` function, which will handle dropping the item.
|
||||
`minetest.item_drop` function, which will handle dropping the item.
|
||||
|
||||
|
||||
### after_use
|
||||
@ -110,20 +110,20 @@ end
|
||||
|
||||
Minetest's API includes many different built-in callback implementations for you
|
||||
to use. These callbacks are named with the item type first, for example,
|
||||
`core.item_place` and `core.node_dig`. Some callback implementations are
|
||||
`minetest.item_place` and `minetest.node_dig`. Some callback implementations are
|
||||
used directly whereas some are functions that return the callback:
|
||||
|
||||
```lua
|
||||
core.register_item("mymod:example", {
|
||||
on_place = core.item_place,
|
||||
on_use = core.item_eat(10),
|
||||
minetest.register_item("mymod:example", {
|
||||
on_place = minetest.item_place,
|
||||
on_use = minetest.item_eat(10),
|
||||
})
|
||||
```
|
||||
|
||||
Minetest's API also includes built-in functions that _do_ something. These are
|
||||
often named in a confusingly similar way to built-in callback implementations
|
||||
but have the verb first. Examples include `core.place_item` and
|
||||
`core.dig_node` - these functions allow you to dig and place nodes with a
|
||||
but have the verb first. Examples include `minetest.place_item` and
|
||||
`minetest.dig_node` - these functions allow you to dig and place nodes with a
|
||||
similar effect to players.
|
||||
|
||||
|
||||
@ -144,9 +144,9 @@ callbacks to always be called.
|
||||
### Right-clicking and placing a node
|
||||
|
||||
When the user right-clicks with an item whilst pointing at a node, the item's
|
||||
`on_place` callback is called. By default, this is set to `core.item_place`.
|
||||
`on_place` callback is called. By default, this is set to `minetest.item_place`.
|
||||
If the pointed node has an `on_rightclick` callback and sneak (shift) is held,
|
||||
then the `on_rightclick` callback is called. Otherwise, `core.item_place`
|
||||
then the `on_rightclick` callback is called. Otherwise, `minetest.item_place`
|
||||
will place the node.
|
||||
|
||||
Placing a node will call both `on_construct` and `after_place_node`.
|
||||
@ -159,20 +159,20 @@ nodes; it's common for mobs and mods to place nodes. To account for this,
|
||||
`placer` could be a player, entity, or nil.
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:mynode", {
|
||||
minetest.register_node("mymod:mynode", {
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
if clicker:is_player() then
|
||||
core.chat_send_player(clicker:get_player_name(), "Hello world!")
|
||||
minetest.chat_send_player(clicker:get_player_name(), "Hello world!")
|
||||
end
|
||||
end,
|
||||
on_construct = function(pos, node)
|
||||
local meta = core.get_meta(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "My node!")
|
||||
end,
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
-- Make sure to check placer
|
||||
if placer and placer:is_player() then
|
||||
local meta = core.get_meta(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
end
|
||||
end,
|
||||
@ -186,15 +186,15 @@ has an `on_use` callback, this will be called. Otherwise, the `on_punch`
|
||||
callback on the pointed node will be called.
|
||||
|
||||
When the player attempts to dig a node, the `on_dig` callback on the node will be called.
|
||||
This defaults to `core.node_dig`, which will check for area protection, wear
|
||||
This defaults to `minetest.node_dig`, which will check for area protection, wear
|
||||
out the tool, remove the node, and run the `after_dig_node` callback.
|
||||
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:mynode", {
|
||||
minetest.register_node("mymod:mynode", {
|
||||
on_punch = function(pos, node, puncher, pointed_thing)
|
||||
if puncher:is_player() then
|
||||
core.chat_send_player(puncher:get_player_name(), "Ow!")
|
||||
minetest.chat_send_player(puncher:get_player_name(), "Ow!")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
@ -106,7 +106,7 @@ chest. The node must be loaded because it is stored in
|
||||
|
||||
```lua
|
||||
on_punch = function(pos, node)
|
||||
local inv = core.get_inventory({ type="node", pos=pos })
|
||||
local inv = minetest.get_inventory({ type="node", pos=pos })
|
||||
-- now use the inventory
|
||||
end,
|
||||
```
|
||||
@ -128,7 +128,7 @@ Player inventories can be obtained similarly or using a player reference.
|
||||
The player must be online to access their inventory.
|
||||
|
||||
```lua
|
||||
local inv = core.get_inventory({ type="player", name="player1" })
|
||||
local inv = minetest.get_inventory({ type="player", name="player1" })
|
||||
-- or
|
||||
local inv = player:get_inventory()
|
||||
```
|
||||
@ -139,7 +139,7 @@ A detached inventory is one that is independent of players or nodes. Detached
|
||||
inventories also don't save over a restart.
|
||||
|
||||
```lua
|
||||
local inv = core.get_inventory({
|
||||
local inv = minetest.get_inventory({
|
||||
type="detached", name="inventory_name" })
|
||||
```
|
||||
|
||||
@ -147,7 +147,7 @@ Unlike the other types of inventory, you must first create a detached inventory
|
||||
before accessing it:
|
||||
|
||||
```lua
|
||||
core.create_detached_inventory("inventory_name")
|
||||
minetest.create_detached_inventory("inventory_name")
|
||||
```
|
||||
|
||||
The `create_detached_inventory` function accepts 3 arguments, where only the
|
||||
@ -156,7 +156,7 @@ callbacks, which can be used to control how players interact with the inventory:
|
||||
|
||||
```lua
|
||||
-- Input only detached inventory
|
||||
core.create_detached_inventory("inventory_name", {
|
||||
minetest.create_detached_inventory("inventory_name", {
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
return count -- allow moving
|
||||
end,
|
||||
@ -170,9 +170,9 @@ core.create_detached_inventory("inventory_name", {
|
||||
end,
|
||||
|
||||
on_put = function(inv, listname, index, stack, player)
|
||||
core.chat_send_all(player:get_player_name() ..
|
||||
minetest.chat_send_all(player:get_player_name() ..
|
||||
" gave " .. stack:to_string() ..
|
||||
" to the donation chest from " .. core.pos_to_string(player:get_pos()))
|
||||
" to the donation chest from " .. minetest.pos_to_string(player:get_pos()))
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
@ -19,7 +19,7 @@ In the previous chapter, the concept of nodes and items was introduced, but a
|
||||
full definition of a node wasn't given. The Minetest world is a 3D grid of
|
||||
positions. Each position is called a node, and consists of the node type
|
||||
(name) and two parameters (param1 and param2). The function
|
||||
`core.register_node` is a bit misleading in that it doesn't actually
|
||||
`minetest.register_node` is a bit misleading in that it doesn't actually
|
||||
register a node - it registers a new *type* of node.
|
||||
|
||||
The node params are used to control how a node is individually rendered.
|
||||
@ -60,13 +60,13 @@ leaf nodes. You can use the allfaces_optional drawtype to allow users to opt-out
|
||||
of the slower drawing, in which case it'll act like a normal node.
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:diamond", {
|
||||
minetest.register_node("mymod:diamond", {
|
||||
description = "Alien Diamond",
|
||||
tiles = {"mymod_diamond.png"},
|
||||
groups = {cracky = 3},
|
||||
})
|
||||
|
||||
core.register_node("default:leaves", {
|
||||
minetest.register_node("default:leaves", {
|
||||
description = "Leaves",
|
||||
drawtype = "allfaces_optional",
|
||||
tiles = {"default_leaves.png"}
|
||||
@ -92,7 +92,7 @@ drawtype would result in the ability to see through the world.
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
core.register_node("default:obsidian_glass", {
|
||||
minetest.register_node("default:obsidian_glass", {
|
||||
description = "Obsidian Glass",
|
||||
drawtype = "glasslike",
|
||||
tiles = {"default_obsidian_glass.png"},
|
||||
@ -120,11 +120,11 @@ You can use the glasslike_framed_optional drawtype to allow the user to *opt-in*
|
||||
to the framed appearance.
|
||||
|
||||
```lua
|
||||
core.register_node("default:glass", {
|
||||
minetest.register_node("default:glass", {
|
||||
description = "Glass",
|
||||
drawtype = "glasslike_framed",
|
||||
tiles = {"default_glass.png", "default_glass_detail.png"},
|
||||
inventory_image = core.inventorycube("default_glass.png"),
|
||||
inventory_image = minetest.inventorycube("default_glass.png"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true, -- Sunlight can shine through block
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 3},
|
||||
@ -138,7 +138,7 @@ core.register_node("default:glass", {
|
||||
These nodes are not rendered and thus have no textures.
|
||||
|
||||
```lua
|
||||
core.register_node("myair:air", {
|
||||
minetest.register_node("myair:air", {
|
||||
description = "MyAir (you hacker you!)",
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
@ -192,11 +192,11 @@ another for flowing liquid.
|
||||
```lua
|
||||
-- Some properties have been removed as they are beyond
|
||||
-- the scope of this chapter.
|
||||
core.register_node("default:water_source", {
|
||||
minetest.register_node("default:water_source", {
|
||||
drawtype = "liquid",
|
||||
paramtype = "light",
|
||||
|
||||
inventory_image = core.inventorycube("default_water.png"),
|
||||
inventory_image = minetest.inventorycube("default_water.png"),
|
||||
-- ^ this is required to stop the inventory image from being animated
|
||||
|
||||
tiles = {
|
||||
@ -271,7 +271,7 @@ Node boxes allow you to create a node which is not cubic, but is instead made ou
|
||||
of as many cuboids as you like.
|
||||
|
||||
```lua
|
||||
core.register_node("stairs:stair_stone", {
|
||||
minetest.register_node("stairs:stair_stone", {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
@ -305,7 +305,7 @@ create node boxes by dragging the edges, it is more visual than doing it by hand
|
||||
Sometimes you want different nodeboxes for when it is placed on the floor, wall, or ceiling like with torches.
|
||||
|
||||
```lua
|
||||
core.register_node("default:sign_wall", {
|
||||
minetest.register_node("default:sign_wall", {
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "wallmounted",
|
||||
@ -341,7 +341,7 @@ invisible but still rendered.
|
||||
You can register a mesh node as so:
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:meshy", {
|
||||
minetest.register_node("mymod:meshy", {
|
||||
drawtype = "mesh",
|
||||
|
||||
-- Holds the texture for each "material"
|
||||
@ -370,7 +370,7 @@ instead use the `nodebox` drawtype to provide a 3D effect. The `signlike` drawty
|
||||
is, however, commonly used by ladders.
|
||||
|
||||
```lua
|
||||
core.register_node("default:ladder_wood", {
|
||||
minetest.register_node("default:ladder_wood", {
|
||||
drawtype = "signlike",
|
||||
|
||||
tiles = {"default_ladder_wood.png"},
|
||||
@ -397,7 +397,7 @@ core.register_node("default:ladder_wood", {
|
||||
Plantlike nodes draw their tiles in an X like pattern.
|
||||
|
||||
```lua
|
||||
core.register_node("default:papyrus", {
|
||||
minetest.register_node("default:papyrus", {
|
||||
drawtype = "plantlike",
|
||||
|
||||
-- Only one texture used
|
||||
@ -423,7 +423,7 @@ and ceilings.
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:clingere", {
|
||||
minetest.register_node("mymod:clingere", {
|
||||
drawtype = "firelike",
|
||||
|
||||
-- Only one texture used
|
||||
|
@ -48,7 +48,7 @@ Item definitions consist of an *item name* and a *definition table*.
|
||||
The definition table contains attributes that affect the behaviour of the item.
|
||||
|
||||
```lua
|
||||
core.register_craftitem("modname:itemname", {
|
||||
minetest.register_craftitem("modname:itemname", {
|
||||
description = "My Special Item",
|
||||
inventory_image = "modname_itemname.png"
|
||||
})
|
||||
@ -83,7 +83,7 @@ Registering an alias is pretty simple. A good way to remember the order of the
|
||||
arguments is `from → to` where *from* is the alias and *to* is the target.
|
||||
|
||||
```lua
|
||||
core.register_alias("dirt", "default:dirt")
|
||||
minetest.register_alias("dirt", "default:dirt")
|
||||
```
|
||||
|
||||
Mods need to make sure to resolve aliases before dealing directly with item names,
|
||||
@ -91,7 +91,7 @@ as the engine won't do this.
|
||||
This is pretty simple though:
|
||||
|
||||
```lua
|
||||
itemname = core.registered_aliases[itemname] or itemname
|
||||
itemname = minetest.registered_aliases[itemname] or itemname
|
||||
```
|
||||
|
||||
### Textures
|
||||
@ -113,7 +113,7 @@ Registering nodes is similar to registering items, just with a different
|
||||
function:
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:diamond", {
|
||||
minetest.register_node("mymod:diamond", {
|
||||
description = "Alien Diamond",
|
||||
tiles = {"mymod_diamond.png"},
|
||||
is_ground_content = true,
|
||||
@ -135,7 +135,7 @@ Remember that +Y is upwards in Minetest, as is the convention with
|
||||
most 3D computer games.
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:diamond", {
|
||||
minetest.register_node("mymod:diamond", {
|
||||
description = "Alien Diamond",
|
||||
tiles = {
|
||||
"mymod_diamond_up.png", -- y+
|
||||
@ -178,7 +178,7 @@ pattern to work. In the example below, the fragments need to be in a
|
||||
chair-like pattern for the craft to work.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shaped",
|
||||
output = "mymod:diamond_chair 99",
|
||||
recipe = {
|
||||
@ -196,7 +196,7 @@ If this empty column shouldn't be required, then the empty strings can be left
|
||||
out like so:
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
output = "mymod:diamond_chair 99",
|
||||
recipe = {
|
||||
{"mymod:diamond_fragments", "" },
|
||||
@ -215,7 +215,7 @@ Shapeless recipes are a type of recipe which is used when it doesn't matter
|
||||
where the ingredients are placed, just that they're there.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mymod:diamond 3",
|
||||
recipe = {
|
||||
@ -232,7 +232,7 @@ Recipes with the type "cooking" are not made in the crafting grid,
|
||||
but are cooked in furnaces, or other cooking tools that might be found in mods.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "mymod:diamond_fragments",
|
||||
recipe = "default:coalblock",
|
||||
@ -254,7 +254,7 @@ This type is an accompaniment to the cooking type, as it defines
|
||||
what can be burned in furnaces and other cooking tools from mods.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "mymod:diamond",
|
||||
burntime = 300,
|
||||
@ -281,7 +281,7 @@ Secondly, groups can be used in a craft recipe instead of an item name to allow
|
||||
any item in the group to be used.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mymod:diamond_thing 3",
|
||||
recipe = {"group:wood", "mymod:diamond"}
|
||||
@ -322,7 +322,7 @@ If the item a player is currently wielding doesn't have an explicit tool
|
||||
capability, then the capability of the current hand is used instead.
|
||||
|
||||
```lua
|
||||
core.register_tool("mymod:tool", {
|
||||
minetest.register_tool("mymod:tool", {
|
||||
description = "My Tool",
|
||||
inventory_image = "mymod_tool.png",
|
||||
tool_capabilities = {
|
||||
|
@ -48,7 +48,7 @@ loaded from the world database outside of the generation limit.
|
||||
You can read from the map once you have a position:
|
||||
|
||||
```lua
|
||||
local node = core.get_node({ x = 1, y = 3, z = 4 })
|
||||
local node = minetest.get_node({ x = 1, y = 3, z = 4 })
|
||||
print(dump(node)) --> { name=.., param1=.., param2=.. }
|
||||
```
|
||||
|
||||
@ -62,7 +62,7 @@ The function will always return a table containing the node information:
|
||||
It's worth noting that the function won't load the containing block if the block
|
||||
is inactive, but will instead return a table with `name` being `ignore`.
|
||||
|
||||
You can use `core.get_node_or_nil` instead, which will return `nil` rather
|
||||
You can use `minetest.get_node_or_nil` instead, which will return `nil` rather
|
||||
than a table with a name of `ignore`. It still won't load the block, however.
|
||||
This may still return `ignore` if a block actually contains ignore.
|
||||
This will happen near the edge of the map as defined by the map generation
|
||||
@ -77,15 +77,15 @@ For example, say we wanted to make a certain type of plant that grows
|
||||
better near mese; you would need to search for any nearby mese nodes,
|
||||
and adapt the growth rate accordingly.
|
||||
|
||||
`core.find_node_near` will return the first found node in a certain radius
|
||||
`minetest.find_node_near` will return the first found node in a certain radius
|
||||
which matches the node names or groups given. In the following example,
|
||||
we look for a mese node within 5 nodes of the position:
|
||||
|
||||
```lua
|
||||
local grow_speed = 1
|
||||
local node_pos = core.find_node_near(pos, 5, { "default:mese" })
|
||||
local node_pos = minetest.find_node_near(pos, 5, { "default:mese" })
|
||||
if node_pos then
|
||||
core.chat_send_all("Node found at: " .. dump(node_pos))
|
||||
minetest.chat_send_all("Node found at: " .. dump(node_pos))
|
||||
grow_speed = 2
|
||||
end
|
||||
```
|
||||
@ -97,7 +97,7 @@ nearby. You should then use a function that can find multiple nodes in the area:
|
||||
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 =
|
||||
core.find_nodes_in_area(pos1, pos2, { "default:mese" })
|
||||
minetest.find_nodes_in_area(pos1, pos2, { "default:mese" })
|
||||
local grow_speed = 1 + #pos_list
|
||||
```
|
||||
|
||||
@ -109,7 +109,7 @@ order to fix this, we will need to manually check the range ourselves:
|
||||
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 =
|
||||
core.find_nodes_in_area(pos1, pos2, { "default:mese" })
|
||||
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)
|
||||
@ -138,9 +138,9 @@ lighting to be recalculated and node callbacks to run, which means that set_node
|
||||
is fairly slow for large numbers of nodes.
|
||||
|
||||
```lua
|
||||
core.set_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
|
||||
minetest.set_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
|
||||
|
||||
local node = core.get_node({ x = 1, y = 3, z = 4 })
|
||||
local node = minetest.get_node({ x = 1, y = 3, z = 4 })
|
||||
print(node.name) --> default:mese
|
||||
```
|
||||
|
||||
@ -153,7 +153,7 @@ two.
|
||||
You can set a node without deleting metadata or the inventory like so:
|
||||
|
||||
```lua
|
||||
core.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
|
||||
minetest.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
|
||||
```
|
||||
|
||||
### Removing Nodes
|
||||
@ -163,15 +163,15 @@ A node must always be present. To remove a node, you set the position to `air`.
|
||||
The following two lines will both remove a node, and are both identical:
|
||||
|
||||
```lua
|
||||
core.remove_node(pos)
|
||||
core.set_node(pos, { name = "air" })
|
||||
minetest.remove_node(pos)
|
||||
minetest.set_node(pos, { name = "air" })
|
||||
```
|
||||
|
||||
In fact, remove_node is just a helper function that calls set_node with `"air"`.
|
||||
|
||||
## Loading Blocks
|
||||
|
||||
You can use `core.emerge_area` to load map blocks. Emerge area is asynchronous,
|
||||
You can use `minetest.emerge_area` to load map blocks. Emerge area is asynchronous,
|
||||
meaning the blocks won't be loaded instantly. Instead, they will be loaded
|
||||
soon in the future, and the callback will be called each time.
|
||||
|
||||
@ -182,7 +182,7 @@ local pos1 = vector.subtract(pos, halfsize)
|
||||
local pos2 = vector.add (pos, halfsize)
|
||||
|
||||
local context = {} -- persist data between callback calls
|
||||
core.emerge_area(pos1, pos2, emerge_callback, context)
|
||||
minetest.emerge_area(pos1, pos2, emerge_callback, context)
|
||||
```
|
||||
|
||||
Minetest will call `emerge_callback` whenever it loads a block, with some
|
||||
@ -202,12 +202,12 @@ local function emerge_callback(pos, action,
|
||||
|
||||
-- Send progress message
|
||||
if context.total_blocks == context.loaded_blocks then
|
||||
core.chat_send_all("Finished loading blocks!")
|
||||
minetest.chat_send_all("Finished loading blocks!")
|
||||
else
|
||||
local perc = 100 * context.loaded_blocks / context.total_blocks
|
||||
local msg = string.format("Loading blocks %d/%d (%.2f%%)",
|
||||
context.loaded_blocks, context.total_blocks, perc)
|
||||
core.chat_send_all(msg)
|
||||
minetest.chat_send_all(msg)
|
||||
end
|
||||
end
|
||||
```
|
||||
@ -226,7 +226,7 @@ local halfsize = { x = 10, y = 10, z = 10 }
|
||||
local pos1 = vector.subtract(pos, halfsize)
|
||||
local pos2 = vector.add (pos, halfsize)
|
||||
|
||||
core.delete_area(pos1, pos2)
|
||||
minetest.delete_area(pos1, pos2)
|
||||
```
|
||||
|
||||
This will delete all map blocks in that area, *inclusive*. This means that some
|
||||
|
@ -48,7 +48,7 @@ which is referred to as a Lua entity, as discussed later.
|
||||
`get_pos` and `set_pos` exist to allow you to get and set an entity's position.
|
||||
|
||||
```lua
|
||||
local object = core.get_player_by_name("bob")
|
||||
local object = minetest.get_player_by_name("bob")
|
||||
local pos = object:get_pos()
|
||||
object:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z })
|
||||
```
|
||||
@ -152,7 +152,7 @@ Both an ObjectRef and an entity table provide ways to get the counterpart:
|
||||
```lua
|
||||
local entity = object:get_luaentity()
|
||||
local object = entity.object
|
||||
print("entity is at " .. core.pos_to_string(object:get_pos()))
|
||||
print("entity is at " .. minetest.pos_to_string(object:get_pos()))
|
||||
```
|
||||
|
||||
There are a number of available callbacks for use with entities.
|
||||
@ -164,9 +164,9 @@ function MyEntity:on_step(dtime)
|
||||
local pos_down = vector.subtract(pos, vector.new(0, 1, 0))
|
||||
|
||||
local delta
|
||||
if core.get_node(pos_down).name == "air" then
|
||||
if minetest.get_node(pos_down).name == "air" then
|
||||
delta = vector.new(0, -1, 0)
|
||||
elseif core.get_node(pos).name == "air" then
|
||||
elseif minetest.get_node(pos).name == "air" then
|
||||
delta = vector.new(0, 0, 1)
|
||||
else
|
||||
delta = vector.new(0, 1, 0)
|
||||
@ -178,7 +178,7 @@ function MyEntity:on_step(dtime)
|
||||
end
|
||||
|
||||
function MyEntity:on_punch(hitter)
|
||||
core.chat_send_player(hitter:get_player_name(), self.message)
|
||||
minetest.chat_send_player(hitter:get_player_name(), self.message)
|
||||
end
|
||||
```
|
||||
|
||||
@ -192,14 +192,14 @@ needs to stored.
|
||||
|
||||
```lua
|
||||
function MyEntity:get_staticdata()
|
||||
return core.write_json({
|
||||
return minetest.write_json({
|
||||
message = self.message,
|
||||
})
|
||||
end
|
||||
|
||||
function MyEntity:on_activate(staticdata, dtime_s)
|
||||
if staticdata ~= "" and staticdata ~= nil then
|
||||
local data = core.parse_json(staticdata) or {}
|
||||
local data = minetest.parse_json(staticdata) or {}
|
||||
self:set_message(data.message)
|
||||
end
|
||||
end
|
||||
@ -217,14 +217,14 @@ This means that staticdata could be empty.
|
||||
Finally, you need to register the type table using the aptly named `register_entity`.
|
||||
|
||||
```lua
|
||||
core.register_entity("mymod:entity", MyEntity)
|
||||
minetest.register_entity("mymod:entity", MyEntity)
|
||||
```
|
||||
|
||||
The entity can be spawned by a mod like so:
|
||||
|
||||
```lua
|
||||
local pos = { x = 1, y = 2, z = 3 }
|
||||
local obj = core.add_entity(pos, "mymod:entity", nil)
|
||||
local obj = minetest.add_entity(pos, "mymod:entity", nil)
|
||||
```
|
||||
|
||||
The third parameter is the initial staticdata.
|
||||
|
@ -14,13 +14,13 @@ redirect_from:
|
||||
In this chapter, you will learn how you can store data.
|
||||
|
||||
- [Metadata](#metadata)
|
||||
- [What is Metadata?](#what-is-metadata)
|
||||
- [Obtaining a Metadata Object](#obtaining-a-metadata-object)
|
||||
- [Reading and Writing](#reading-and-writing)
|
||||
- [Special Keys](#special-keys)
|
||||
- [Storing Tables](#storing-tables)
|
||||
- [Private Metadata](#private-metadata)
|
||||
- [Lua Tables](#lua-tables)
|
||||
- [What is Metadata?](#what-is-metadata)
|
||||
- [Obtaining a Metadata Object](#obtaining-a-metadata-object)
|
||||
- [Reading and Writing](#reading-and-writing)
|
||||
- [Special Keys](#special-keys)
|
||||
- [Storing Tables](#storing-tables)
|
||||
- [Private Metadata](#private-metadata)
|
||||
- [Lua Tables](#lua-tables)
|
||||
- [Mod Storage](#mod-storage)
|
||||
- [Databases](#databases)
|
||||
- [Deciding Which to Use](#deciding-which-to-use)
|
||||
@ -53,7 +53,7 @@ The data itself, such as a node's type or an stack's count, is not metadata.
|
||||
If you know the position of a node, you can retrieve its metadata:
|
||||
|
||||
```lua
|
||||
local meta = core.get_meta({ x = 1, y = 2, z = 3 })
|
||||
local meta = minetest.get_meta({ x = 1, y = 2, z = 3 })
|
||||
```
|
||||
|
||||
Player and ItemStack metadata are obtained using `get_meta()`:
|
||||
@ -99,7 +99,7 @@ This is useful when showing the ownership or status of a node.
|
||||
|
||||
`description` is used in ItemStack Metadata to override the description when
|
||||
hovering over the stack in an inventory.
|
||||
You can use colours by encoding them with `core.colorize()`.
|
||||
You can use colours by encoding them with `minetest.colorize()`.
|
||||
|
||||
`owner` is a common key used to store the username of the player that owns the
|
||||
item or node.
|
||||
@ -116,9 +116,9 @@ with another program.
|
||||
|
||||
```lua
|
||||
local data = { username = "player1", score = 1234 }
|
||||
meta:set_string("foo", core.serialize(data))
|
||||
meta:set_string("foo", minetest.serialize(data))
|
||||
|
||||
data = core.deserialize(meta:get_string("foo"))
|
||||
data = minetest.deserialize(minetest:get_string("foo"))
|
||||
```
|
||||
|
||||
### Private Metadata
|
||||
@ -149,7 +149,7 @@ Mod storage is per-mod, and can only be obtained during load time in order to
|
||||
know which mod is requesting it.
|
||||
|
||||
```lua
|
||||
local storage = core.get_mod_storage()
|
||||
local storage = minetest.get_mod_storage()
|
||||
```
|
||||
|
||||
You can now manipulate the storage just like metadata:
|
||||
@ -169,10 +169,10 @@ it is used.
|
||||
local backend
|
||||
if use_database then
|
||||
backend =
|
||||
dofile(core.get_modpath("mymod") .. "/backend_sqlite.lua")
|
||||
dofile(minetest.get_modpath("mymod") .. "/backend_sqlite.lua")
|
||||
else
|
||||
backend =
|
||||
dofile(core.get_modpath("mymod") .. "/backend_storage.lua")
|
||||
dofile(minetest.get_modpath("mymod") .. "/backend_storage.lua")
|
||||
end
|
||||
|
||||
backend.get_foo("a")
|
||||
@ -182,15 +182,15 @@ backend.set_foo("a", { score = 3 })
|
||||
The backend_storage.lua file should include a mod storage implementation:
|
||||
|
||||
```lua
|
||||
local storage = core.get_mod_storage()
|
||||
local storage = minetest.get_mod_storage()
|
||||
local backend = {}
|
||||
|
||||
function backend.set_foo(key, value)
|
||||
storage:set_string(key, core.serialize(value))
|
||||
storage:set_string(key, minetest.serialize(value))
|
||||
end
|
||||
|
||||
function backend.get_foo(key)
|
||||
return core.deserialize(storage:get_string(key))
|
||||
return minetest.deserialize(storage:get_string(key))
|
||||
end
|
||||
|
||||
return backend
|
||||
@ -207,7 +207,7 @@ Insecure environments will be covered in more detail in the
|
||||
[Security](../quality/security.html) chapter.
|
||||
|
||||
```lua
|
||||
local ie = core.request_insecure_environment()
|
||||
local ie = minetest.request_insecure_environment()
|
||||
assert(ie, "Please add mymod to secure.trusted_mods in the settings")
|
||||
|
||||
local _sql = ie.require("lsqlite3")
|
||||
@ -244,4 +244,4 @@ They're well suited for large data sets.
|
||||
## Your Turn
|
||||
|
||||
* Make a node which disappears after it has been punched five times.
|
||||
(Use `on_punch` in the node definition and `core.set_node`.)
|
||||
(Use `on_punch` in the node definition and `minetest.set_node`.)
|
||||
|
@ -37,7 +37,7 @@ Node timers are directly tied to a single node.
|
||||
You can manage node timers by obtaining a NodeTimerRef object.
|
||||
|
||||
```lua
|
||||
local timer = core.get_node_timer(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(10.5) -- in seconds
|
||||
```
|
||||
|
||||
@ -45,9 +45,9 @@ When a node timer is up, the `on_timer` method in the node's definition table wi
|
||||
be called. The method only takes a single parameter, the position of the node:
|
||||
|
||||
```lua
|
||||
core.register_node("autodoors:door_open", {
|
||||
minetest.register_node("autodoors:door_open", {
|
||||
on_timer = function(pos)
|
||||
core.set_node(pos, { name = "autodoors:door" })
|
||||
minetest.set_node(pos, { name = "autodoors:door" })
|
||||
return false
|
||||
end
|
||||
})
|
||||
@ -68,15 +68,15 @@ has a chance to appear near water.
|
||||
|
||||
|
||||
```lua
|
||||
core.register_node("aliens:grass", {
|
||||
minetest.register_node("aliens:grass", {
|
||||
description = "Alien Grass",
|
||||
light_source = 3, -- The node radiates light. Min 0, max 14
|
||||
tiles = {"aliens_grass.png"},
|
||||
groups = {choppy=1},
|
||||
on_use = core.item_eat(20)
|
||||
on_use = minetest.item_eat(20)
|
||||
})
|
||||
|
||||
core.register_abm({
|
||||
minetest.register_abm({
|
||||
nodenames = {"default:dirt_with_grass"},
|
||||
neighbors = {"default:water_source", "default:water_flowing"},
|
||||
interval = 10.0, -- Run every 10 seconds
|
||||
@ -84,7 +84,7 @@ core.register_abm({
|
||||
action = function(pos, node, active_object_count,
|
||||
active_object_count_wider)
|
||||
local pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
core.set_node(pos, {name = "aliens:grass"})
|
||||
minetest.set_node(pos, {name = "aliens:grass"})
|
||||
end
|
||||
})
|
||||
```
|
||||
@ -93,7 +93,7 @@ This ABM runs every ten seconds, and for each matching node, there is
|
||||
a 1 in 50 chance of it running.
|
||||
If the ABM runs on a node, an alien grass node is placed above it.
|
||||
Please be warned, this will delete any node previously located in that position.
|
||||
To prevent this you should include a check using core.get_node to make sure there is space for the grass.
|
||||
To prevent this you should include a check using minetest.get_node to make sure there is space for the grass.
|
||||
|
||||
Specifying a neighbour is optional.
|
||||
If you specify multiple neighbours, only one of them needs to be
|
||||
|
@ -14,7 +14,7 @@ cmd_online:
|
||||
bridge allows players to run commands without joining the game.
|
||||
|
||||
So make sure that you don't assume that the player is online.
|
||||
You can check by seeing if `core.get_player_by_name` returns a player.
|
||||
You can check by seeing if `minetest.get_player_by_name` returns a player.
|
||||
|
||||
cb_cmdsprivs:
|
||||
level: warning
|
||||
@ -47,7 +47,7 @@ sending messages, intercepting messages, and registering chat commands.
|
||||
To send a message to every player in the game, call the `chat_send_all` function.
|
||||
|
||||
```lua
|
||||
core.chat_send_all("This is a chat message to all players")
|
||||
minetest.chat_send_all("This is a chat message to all players")
|
||||
```
|
||||
|
||||
Here is an example of how this appears in-game:
|
||||
@ -63,7 +63,7 @@ The message appears on a separate line to distinguish it from in-game player cha
|
||||
To send a message to a specific player, call the `chat_send_player` function:
|
||||
|
||||
```lua
|
||||
core.chat_send_player("player1", "This is a chat message for player1")
|
||||
minetest.chat_send_player("player1", "This is a chat message for player1")
|
||||
```
|
||||
|
||||
This message displays in the same manner as messages to all players, but is
|
||||
@ -74,7 +74,7 @@ only visible to the named player, in this case, player1.
|
||||
To register a chat command, for example `/foo`, use `register_chatcommand`:
|
||||
|
||||
```lua
|
||||
core.register_chatcommand("foo", {
|
||||
minetest.register_chatcommand("foo", {
|
||||
privs = {
|
||||
interact = true,
|
||||
},
|
||||
@ -166,7 +166,7 @@ or the [PIL documentation](https://www.lua.org/pil/20.2.html).
|
||||
To intercept a message, use register_on_chat_message:
|
||||
|
||||
```lua
|
||||
core.register_on_chat_message(function(name, message)
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
print(name .. " said " .. message)
|
||||
return false
|
||||
end)
|
||||
@ -182,10 +182,10 @@ You should make sure you take into account that it may be a chat command,
|
||||
or the user may not have `shout`.
|
||||
|
||||
```lua
|
||||
core.register_on_chat_message(function(name, message)
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
if message:sub(1, 1) == "/" then
|
||||
print(name .. " ran chat command")
|
||||
elseif core.check_player_privs(name, { shout = true }) then
|
||||
elseif minetest.check_player_privs(name, { shout = true }) then
|
||||
print(name .. " said " .. message)
|
||||
else
|
||||
print(name .. " tried to say " .. message ..
|
||||
|
@ -146,7 +146,7 @@ function guessing.get_formspec(name)
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
"size[6,3.476]",
|
||||
"label[0.375,0.5;", core.formspec_escape(text), "]",
|
||||
"label[0.375,0.5;", minetest.formspec_escape(text), "]",
|
||||
"field[0.375,1.25;5.25,0.8;number;Number;]",
|
||||
"button[1.5,2.3;3,0.8;guess;Guess]"
|
||||
}
|
||||
@ -166,10 +166,10 @@ is using `show_formspec`:
|
||||
|
||||
```lua
|
||||
function guessing.show_to(name)
|
||||
core.show_formspec(name, "guessing:game", guessing.get_formspec(name))
|
||||
minetest.show_formspec(name, "guessing:game", guessing.get_formspec(name))
|
||||
end
|
||||
|
||||
core.register_chatcommand("game", {
|
||||
minetest.register_chatcommand("game", {
|
||||
func = function(name)
|
||||
guessing.show_to(name)
|
||||
end,
|
||||
@ -206,19 +206,19 @@ The method for this is called formspec field submission, and for `show_formspec`
|
||||
submission is received using a global callback:
|
||||
|
||||
```lua
|
||||
core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
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()
|
||||
core.chat_send_all(pname .. " guessed " .. fields.number)
|
||||
minetest.chat_send_all(pname .. " guessed " .. fields.number)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
The function given in `core.register_on_player_receive_fields` is called
|
||||
The function given in `minetest.register_on_player_receive_fields` is called
|
||||
every time a user submits a form. Most callbacks will need to check the formname given
|
||||
to the function, and exit if it is not the right form; however, some callbacks
|
||||
may need to work on multiple forms, or on all forms.
|
||||
@ -239,7 +239,7 @@ the formspec based on guesses. The way to do this is using a concept called
|
||||
|
||||
### Contexts
|
||||
|
||||
In many cases you want core.show_formspec to give information
|
||||
In many cases you want minetest.show_formspec to give information
|
||||
to the callback which you don't want to send to the client. This might include
|
||||
what a chat command was called with, or what the dialog is about. In this case,
|
||||
the target value that needs to be remembered.
|
||||
@ -255,7 +255,7 @@ local function get_context(name)
|
||||
return context
|
||||
end
|
||||
|
||||
core.register_on_leaveplayer(function(player)
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
_contexts[player:get_player_name()] = nil
|
||||
end)
|
||||
```
|
||||
@ -269,7 +269,7 @@ function guessing.show_to(name)
|
||||
context.target = context.target or math.random(1, 10)
|
||||
|
||||
local fs = guessing.get_formspec(name, context)
|
||||
core.show_formspec(name, "guessing:game", fs)
|
||||
minetest.show_formspec(name, "guessing:game", fs)
|
||||
end
|
||||
```
|
||||
|
||||
@ -318,13 +318,13 @@ There are three different ways that a formspec can be delivered to the client:
|
||||
|
||||
### Node Meta Formspecs
|
||||
|
||||
`core.show_formspec` is not the only way to show a formspec; you can also
|
||||
`minetest.show_formspec` is not the only way to show a formspec; you can also
|
||||
add formspecs to a [node's metadata](../map/storage.html). For example,
|
||||
this is used with chests to allow for faster opening times -
|
||||
you don't need to wait for the server to send the player the chest formspec.
|
||||
|
||||
```lua
|
||||
core.register_node("mymod:rightclick", {
|
||||
minetest.register_node("mymod:rightclick", {
|
||||
description = "Rightclick me!",
|
||||
tiles = {"mymod_rightclick.png"},
|
||||
groups = {cracky = 1},
|
||||
@ -333,7 +333,7 @@ core.register_node("mymod:rightclick", {
|
||||
-- The following code sets the formspec for chest.
|
||||
-- Meta is a way of storing data onto a node.
|
||||
|
||||
local meta = core.get_meta(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"formspec_version[4]" ..
|
||||
"size[5,5]" ..
|
||||
@ -355,7 +355,7 @@ receive form input for meta formspecs, you must include an
|
||||
`on_receive_fields` entry when registering the node.
|
||||
|
||||
This style of callback triggers when you press enter
|
||||
in a field, which is impossible with `core.show_formspec`;
|
||||
in a field, which is impossible with `minetest.show_formspec`;
|
||||
however, this kind of form can only be shown by right-clicking on a
|
||||
node. It cannot be triggered programmatically.
|
||||
|
||||
|
@ -89,7 +89,7 @@ to the right of the window, but to resize without breaking.
|
||||
You can create a HUD element once you have a copy of the player object:
|
||||
|
||||
```lua
|
||||
local player = core.get_player_by_name("username")
|
||||
local player = minetest.get_player_by_name("username")
|
||||
local idx = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
@ -281,9 +281,9 @@ function score.update_hud(player)
|
||||
end
|
||||
end
|
||||
|
||||
core.register_on_joinplayer(score.update_hud)
|
||||
minetest.register_on_joinplayer(score.update_hud)
|
||||
|
||||
core.register_on_leaveplayer(function(player)
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
saved_huds[player:get_player_name()] = nil
|
||||
end)
|
||||
```
|
||||
|
@ -28,9 +28,9 @@ Here is an example of how to add an antigravity command, which
|
||||
puts the caller in low G:
|
||||
|
||||
```lua
|
||||
core.register_chatcommand("antigravity", {
|
||||
minetest.register_chatcommand("antigravity", {
|
||||
func = function(name, param)
|
||||
local player = core.get_player_by_name(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
player:set_physics_override({
|
||||
gravity = 0.1, -- set gravity to 10% of its original value
|
||||
-- (0.1 * 9.81)
|
||||
|
@ -48,7 +48,7 @@ Privileges are **not** for indicating class or status.
|
||||
Use `register_privilege` to declare a new privilege:
|
||||
|
||||
```lua
|
||||
core.register_privilege("vote", {
|
||||
minetest.register_privilege("vote", {
|
||||
description = "Can vote on issues",
|
||||
give_to_singleplayer = true
|
||||
})
|
||||
@ -62,7 +62,7 @@ actually needed in the above definition.
|
||||
To quickly check whether a player has all the required privileges:
|
||||
|
||||
```lua
|
||||
local has, missing = core.check_player_privs(player_or_name, {
|
||||
local has, missing = minetest.check_player_privs(player_or_name, {
|
||||
interact = true,
|
||||
vote = true })
|
||||
```
|
||||
@ -72,7 +72,7 @@ If `has` is false, then `missing` will contain a key-value table
|
||||
of the missing privileges.
|
||||
|
||||
```lua
|
||||
local has, missing = core.check_player_privs(name, {
|
||||
local has, missing = minetest.check_player_privs(name, {
|
||||
interact = true,
|
||||
vote = true })
|
||||
|
||||
@ -87,7 +87,7 @@ If you don't need to check the missing privileges, you can put
|
||||
`check_player_privs` directly into the if statement.
|
||||
|
||||
```lua
|
||||
if not core.check_player_privs(name, { interact=true }) then
|
||||
if not minetest.check_player_privs(name, { interact=true }) then
|
||||
return false, "You need interact for this!"
|
||||
end
|
||||
```
|
||||
@ -99,11 +99,11 @@ being online.
|
||||
|
||||
|
||||
```lua
|
||||
local privs = core.get_player_privs(name)
|
||||
local privs = minetest.get_player_privs(name)
|
||||
print(dump(privs))
|
||||
|
||||
privs.vote = true
|
||||
core.set_player_privs(name, privs)
|
||||
minetest.set_player_privs(name, privs)
|
||||
```
|
||||
|
||||
Privileges are always specified as a key-value table with the key being
|
||||
|
@ -100,7 +100,7 @@ what is listening to something.
|
||||
|
||||
In the next chapter, we will discuss how to automatically test your
|
||||
code and one of the problems we will have is how to separate your logic
|
||||
(calculations, what should be done) from API calls (`core.*`, other mods)
|
||||
(calculations, what should be done) from API calls (`minetest.*`, other mods)
|
||||
as much as possible.
|
||||
|
||||
One way to do this is to think about:
|
||||
@ -173,14 +173,14 @@ function land.show_create_formspec(name)
|
||||
]]
|
||||
end
|
||||
|
||||
core.register_chatcommand("/land", {
|
||||
minetest.register_chatcommand("/land", {
|
||||
privs = { land = true },
|
||||
func = function(name)
|
||||
land.handle_creation_request(name)
|
||||
end,
|
||||
})
|
||||
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
land.handle_create_submit(player:get_player_name(),
|
||||
fields.area_name)
|
||||
@ -227,7 +227,7 @@ this isn't the real world. A good compromise is to reduce the mod into two
|
||||
parts:
|
||||
|
||||
* **API** - This was the model and controller above. There should be no uses of
|
||||
`core.` here.
|
||||
`minetest.` here.
|
||||
* **View** - This was also the view above. It's a good idea to structure this into separate
|
||||
files for each type of event.
|
||||
|
||||
|
@ -43,11 +43,11 @@ give themselves moderator privileges:
|
||||
|
||||
```lua
|
||||
local function show_formspec(name)
|
||||
if not core.check_player_privs(name, { privs = true }) then
|
||||
if not minetest.check_player_privs(name, { privs = true }) then
|
||||
return false
|
||||
end
|
||||
|
||||
core.show_formspec(name, "modman:modman", [[
|
||||
minetest.show_formspec(name, "modman:modman", [[
|
||||
size[3,2]
|
||||
field[0,0;3,1;target;Name;]
|
||||
button_exit[0,1;3,1;sub;Promote]
|
||||
@ -55,14 +55,14 @@ local function show_formspec(name)
|
||||
return true
|
||||
})
|
||||
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
-- BAD! Missing privilege check here!
|
||||
|
||||
local privs = core.get_player_privs(fields.target)
|
||||
local privs = minetest.get_player_privs(fields.target)
|
||||
privs.kick = true
|
||||
privs.ban = true
|
||||
core.set_player_privs(fields.target, privs)
|
||||
minetest.set_player_privs(fields.target, privs)
|
||||
return true
|
||||
end)
|
||||
```
|
||||
@ -70,9 +70,9 @@ end)
|
||||
Add a privilege check to solve this:
|
||||
|
||||
```lua
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
if not core.check_player_privs(name, { privs = true }) then
|
||||
if not minetest.check_player_privs(name, { privs = true }) then
|
||||
return false
|
||||
end
|
||||
|
||||
@ -111,7 +111,7 @@ are given will change it for the caller too, and any subsequent callbacks. Howev
|
||||
it will only be saved in the engine if the callback caller sets it.
|
||||
|
||||
```lua
|
||||
core.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
itemstack:get_meta():set_string("description", "Partially eaten")
|
||||
-- Almost correct! Data will be lost if another
|
||||
@ -125,7 +125,7 @@ but if a callback does cancel this, then the update may be lost.
|
||||
It's better to do this instead:
|
||||
|
||||
```lua
|
||||
core.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
itemstack:get_meta():set_string("description", "Partially eaten")
|
||||
user:get_inventory():set_stack("main", user:get_wield_index(),
|
||||
|
@ -19,6 +19,7 @@ After you've read this book, take a look at the following.
|
||||
### Lua Programming
|
||||
|
||||
* [Programming in Lua (PIL)](http://www.lua.org/pil/).
|
||||
* [Lua Crash Course](http://luatut.com/crash_course.html).
|
||||
|
||||
### 3D Modelling
|
||||
|
||||
|
@ -40,7 +40,7 @@ Any users can submit almost any formspec with any values at any time.
|
||||
Here's some real code found in a mod:
|
||||
|
||||
```lua
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
for key, field in pairs(fields) do
|
||||
local x,y,z = string.match(key,
|
||||
@ -87,7 +87,7 @@ to the full Lua API.
|
||||
Can you spot the vulnerability in the following?
|
||||
|
||||
```lua
|
||||
local ie = core.request_insecure_environment()
|
||||
local ie = minetest.request_insecure_environment()
|
||||
ie.os.execute(("path/to/prog %d"):format(3))
|
||||
```
|
||||
|
||||
|
@ -57,12 +57,12 @@ control characters in text, telling Minetest where and how to translate
|
||||
text. This is referred to as marked up text, and will be discussed more later.
|
||||
|
||||
To mark text as translatable, use a translator function (`S()`), obtained using
|
||||
`core.get_translator(textdomain)`:
|
||||
`minetest.get_translator(textdomain)`:
|
||||
|
||||
```lua
|
||||
local S = core.get_translator("mymod")
|
||||
local S = minetest.get_translator("mymod")
|
||||
|
||||
core.register_craftitem("mymod:item", {
|
||||
minetest.register_craftitem("mymod:item", {
|
||||
description = S("My Item"),
|
||||
})
|
||||
```
|
||||
@ -75,7 +75,7 @@ avoid mod conflicts.
|
||||
|
||||
Marked up text can be used in most places where human-readable text is accepted,
|
||||
including formspecs, item def fields, infotext, and more. When including marked
|
||||
text in formspecs, you need to escape the text using `core.formspec_escape`.
|
||||
text in formspecs, you need to escape the text using `minetest.formspec_escape`.
|
||||
|
||||
When the client encounters translatable text, such as that passed to
|
||||
`description`, it looks it up in the player's language's translation file. If a
|
||||
@ -85,7 +85,7 @@ Translatable marked up text contains the English source text, the textdomain,
|
||||
and any additional arguments passed to `S()`. It's essentially a text encoding
|
||||
of the `S` call, containing all the required information.
|
||||
|
||||
Another type of marked up text is that returned by `core.colorize`.
|
||||
Another type of marked up text is that returned by `minetest.colorize`.
|
||||
|
||||
{% include notice.html notice=page.marked_text_encoding %}
|
||||
|
||||
@ -127,8 +127,8 @@ translators from changing the order of variables within a sentence. Instead,
|
||||
you should use the translation system's format/arguments system:
|
||||
|
||||
```lua
|
||||
core.register_on_joinplayer(function(player)
|
||||
core.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name()))
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
minetest.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name()))
|
||||
end)
|
||||
```
|
||||
|
||||
@ -172,13 +172,13 @@ local list = {
|
||||
S("Potato")
|
||||
}
|
||||
|
||||
core.register_chatcommand("find", {
|
||||
minetest.register_chatcommand("find", {
|
||||
func = function(name, param)
|
||||
local info = core.get_player_information(name)
|
||||
local info = minetest.get_player_information(name)
|
||||
local language = info and info.language or "en"
|
||||
|
||||
for _, line in ipairs(list) do
|
||||
local trans = core.get_translated_string(language, line)
|
||||
local trans = minetest.get_translated_string(language, line)
|
||||
if trans:contains(query) then
|
||||
return line
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ names ending in `_spec`, and then executes them in a standalone Lua environment.
|
||||
```lua
|
||||
mymod = {}
|
||||
|
||||
dofile(core.get_modpath("mymod") .. "/api.lua")
|
||||
dofile(minetest.get_modpath("mymod") .. "/api.lua")
|
||||
```
|
||||
|
||||
|
||||
@ -122,7 +122,7 @@ _G.minetest = {}
|
||||
|
||||
-- Define the mock function
|
||||
local chat_send_all_calls = {}
|
||||
function core.chat_send_all(name, message)
|
||||
function minetest.chat_send_all(name, message)
|
||||
table.insert(chat_send_all_calls, { name = name, message = message })
|
||||
end
|
||||
|
||||
|
@ -107,7 +107,7 @@ Oltre che farli a mano, per creare dei diagrammi di Voronoi si possono usare pro
|
||||
Il seguente codice registra un semplice bioma chiamato "distesa_erbosa":
|
||||
|
||||
```lua
|
||||
core.register_biome({
|
||||
minetest.register_biome({
|
||||
name = "distesa_erbosa",
|
||||
node_top = "default:dirt_with_grass",
|
||||
depth_top = 1,
|
||||
@ -144,7 +144,7 @@ Ricordati che devi specificare il nodo che vuoi usare in quanto decorazione, i d
|
||||
Per esempio:
|
||||
|
||||
```lua
|
||||
core.register_decoration({
|
||||
minetest.register_decoration({
|
||||
deco_type = "simple",
|
||||
place_on = {"base:dirt_with_grass"},
|
||||
sidelen = 16,
|
||||
@ -167,7 +167,7 @@ Le schematic sono molto simili alle decorazioni semplici, solo che piazzano più
|
||||
Per esempio:
|
||||
|
||||
```lua
|
||||
core.register_decoration({
|
||||
minetest.register_decoration({
|
||||
deco_type = "schematic",
|
||||
place_on = {"base:desert_sand"},
|
||||
sidelen = 16,
|
||||
@ -175,7 +175,7 @@ core.register_decoration({
|
||||
biomes = {"desert"},
|
||||
y_max = 200,
|
||||
y_min = 1,
|
||||
schematic = core.get_modpath("plants") .. "/schematics/cactus.mts",
|
||||
schematic = minetest.get_modpath("plants") .. "/schematics/cactus.mts",
|
||||
flags = "place_center_x, place_center_z",
|
||||
rotation = "random",
|
||||
})
|
||||
@ -195,7 +195,7 @@ I giochi disponibili dovrebbero già includere un alias del generatore mappa (*m
|
||||
Gli alias del generatore mappa forniscono informazioni al generatore principale, e possono essere registrati secondo lo schema:
|
||||
|
||||
```lua
|
||||
core.register_alias("mapgen_stone", "base:smoke_stone")
|
||||
minetest.register_alias("mapgen_stone", "base:smoke_stone")
|
||||
```
|
||||
|
||||
Almeno almeno dovresti registrare:
|
||||
|
@ -10,8 +10,8 @@ redirect_from:
|
||||
mapgen_object:
|
||||
level: warning
|
||||
title: LVM e generatore mappa
|
||||
message: Non usare `core.get_voxel_manip()` con il generatore mappa, in quanto può causare glitch.
|
||||
Usa invece `core.get_mapgen_object("voxelmanip")`.
|
||||
message: Non usare `minetest.get_voxel_manip()` con il generatore mappa, in quanto può causare glitch.
|
||||
Usa invece `minetest.get_mapgen_object("voxelmanip")`.
|
||||
---
|
||||
|
||||
## Introduzione <!-- omit in toc -->
|
||||
@ -39,7 +39,7 @@ Si possono caricare solamente aree cubiche negli LVM, quindi devi capire da te q
|
||||
Fatto ciò, puoi creare l'LVM:
|
||||
|
||||
```lua
|
||||
local vm = core.get_voxel_manip()
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
```
|
||||
|
||||
@ -82,7 +82,7 @@ Per scoprire qual è l'ID assegnato a un tipo di nodo, si usa `get_content_id()`
|
||||
Per esempio:
|
||||
|
||||
```lua
|
||||
local c_pietra = core.get_content_id("default:stone")
|
||||
local c_pietra = minetest.get_content_id("default:stone")
|
||||
```
|
||||
|
||||
Si può ora controllare se un nodo è effettivamente di pietra:
|
||||
@ -143,16 +143,16 @@ vm:write_to_map(true)
|
||||
Per la luce e param2, invece si usano `set_light_data()` e `set_param2_data()`.
|
||||
|
||||
`write_to_map()` richiede un booleano che è `true` se si vuole che venga calcolata anche la luce.
|
||||
Se si passa `false` invece, ci sarà bisogno di ricalcolarla in un secondo tempo usando `core.fix_light`.
|
||||
Se si passa `false` invece, ci sarà bisogno di ricalcolarla in un secondo tempo usando `minetest.fix_light`.
|
||||
|
||||
## Esempio
|
||||
|
||||
```lua
|
||||
local function da_erba_a_terra(pos1, pos2)
|
||||
local c_terra = core.get_content_id("default:dirt")
|
||||
local c_erba = core.get_content_id("default:dirt_with_grass")
|
||||
local c_terra = minetest.get_content_id("default:dirt")
|
||||
local c_erba = minetest.get_content_id("default:dirt_with_grass")
|
||||
-- legge i dati nella LVM
|
||||
local vm = core.get_voxel_manip()
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local emin, emax = vm:read_from_map(pos1, pos2)
|
||||
local a = VoxelArea:new{
|
||||
MinEdge = emin,
|
||||
|
@ -81,7 +81,7 @@ La posizione di ogni percorso dipende da quale sistema operativo si sta usando,
|
||||
|
||||
Il *nome mod* è usato per riferirsi a una mod e ognuna di esse dovrebbe averne uno unico.
|
||||
Questi possono includere lettere, numeri e trattini bassi, e un buon nome dovrebbe descrivere brevemente cosa fa la mod (è anche consigliato rinominare la cartella della mod con il nome di quest'ultima).
|
||||
Per scoprire se un nome è disponibile, prova a cercarlo su
|
||||
Per scoprire se un nome è disponibile, prova a cercarlo su
|
||||
[content.minetest.net](https://content.minetest.net).
|
||||
|
||||
|
||||
@ -150,7 +150,7 @@ Segue un esempio che mette insieme tutto ciò discusso finora:
|
||||
```lua
|
||||
print("Questo file parte all'avvio!")
|
||||
|
||||
core.register_node("lamiamod:nodo", {
|
||||
minetest.register_node("lamiamod:nodo", {
|
||||
description = "Questo è un nodo",
|
||||
tiles = {"lamiamod_nodo.png"},
|
||||
groups = {cracky = 1}
|
||||
|
@ -159,7 +159,7 @@ mymod.foo("foobar")
|
||||
Il metodo consigliato per includere in una mod altri script Lua è usare *dofile*.
|
||||
|
||||
```lua
|
||||
dofile(core.get_modpath("modname") .. "/script.lua")
|
||||
dofile(minetest.get_modpath("modname") .. "/script.lua")
|
||||
```
|
||||
|
||||
Uno script può ritornare un valore, che è utile per condividere variabili locali private:
|
||||
@ -169,7 +169,7 @@ Uno script può ritornare un valore, che è utile per condividere variabili loca
|
||||
return "Hello world!"
|
||||
|
||||
-- init.lua
|
||||
local ret = dofile(core.get_modpath("modname") .. "/script.lua")
|
||||
local ret = dofile(minetest.get_modpath("modname") .. "/script.lua")
|
||||
print(ret) -- Hello world!
|
||||
```
|
||||
|
||||
|
@ -10,7 +10,7 @@ description: Scopri i richiami, le azioni e gli eventi, come on_use, on_punch, o
|
||||
|
||||
Minetest usa una struttura di moddaggio estensivamente incentrata sui richiami. Un richiamo è una funzione che si dà a un'API e che viene chiamata quando l'evento registrato si verifica.
|
||||
Per esempio, puoi aggiungere una funzione `on_punch` nella definizione di un nodo, che verrà chiamata quando questo viene colpito.
|
||||
Ci sono poi anche dei richiami globali, come `core.register_on_punchnode`, che in questo caso verrà invocato al colpire qualsiasi nodo.
|
||||
Ci sono poi anche dei richiami globali, come `minetest.register_on_punchnode`, che in questo caso verrà invocato al colpire qualsiasi nodo.
|
||||
|
||||
- [Richiami degli oggetti](#richiami-degli-oggetti)
|
||||
- [on_use](#on_use)
|
||||
@ -31,9 +31,9 @@ Quando un giocatore ha un nodo, un oggetto fabbricabile o uno strumento nel prop
|
||||
| Richiamo | Assegnazione base | Valore base |
|
||||
|------------------|---------------------------|----------------------------------------------|
|
||||
| on_use | clic sinistro | nil |
|
||||
| on_place | clic destro su un nodo | `core.item_place` |
|
||||
| on_secondary_use | clic destro a vuoto | `core.item_secondary_use` (non fa nulla) |
|
||||
| on_drop | Q | `core.item_drop` |
|
||||
| on_place | clic destro su un nodo | `minetest.item_place` |
|
||||
| on_secondary_use | clic destro a vuoto | `minetest.item_secondary_use` (non fa nulla) |
|
||||
| on_drop | Q | `minetest.item_drop` |
|
||||
| after_use | allo scavare un nodo | nil |
|
||||
|
||||
|
||||
@ -43,26 +43,26 @@ Sovrascrivere l'uso dell'oggetto impedisce che quest'ultimo possa essere usato p
|
||||
Un impiego comune di questo richiamo lo si trova nel cibo:
|
||||
|
||||
```lua
|
||||
core.register_craftitem("miamod:fangotorta", {
|
||||
minetest.register_craftitem("miamod:fangotorta", {
|
||||
description = "Torta aliena di fango",
|
||||
inventory_image = "miamod_fangotorta.png",
|
||||
on_use = core.item_eat(20),
|
||||
on_use = minetest.item_eat(20),
|
||||
})
|
||||
```
|
||||
|
||||
Il numero fornito alla funzione core.item_eat è il numero di punti salute ripristinati al consumare il cibo.
|
||||
Il numero fornito alla funzione minetest.item_eat è il numero di punti salute ripristinati al consumare il cibo.
|
||||
In gioco ogni cuore equivale a due punti.
|
||||
Un giocatore ha solitamente un massimo di 10 cuori, ovvero 20 punti salute, e quest'ultimi non devono per forza essere interi - bensì anche decimali.
|
||||
|
||||
`core.item_eat()` è una funzione che ritorna un'altra funzione, in questo caso quindi impostandola come richiamo di on_use.
|
||||
`minetest.item_eat()` è una funzione che ritorna un'altra funzione, in questo caso quindi impostandola come richiamo di on_use.
|
||||
Ciò significa che il codice in alto è alquanto simile al seguente:
|
||||
|
||||
```lua
|
||||
core.register_craftitem("miamod:fangotorta", {
|
||||
minetest.register_craftitem("miamod:fangotorta", {
|
||||
description = "Torta aliena di fango",
|
||||
inventory_image = "miamod_fangotorta.png",
|
||||
on_use = function(...)
|
||||
return core.do_item_eat(20, nil, ...)
|
||||
return minetest.do_item_eat(20, nil, ...)
|
||||
end,
|
||||
})
|
||||
```
|
||||
@ -76,13 +76,13 @@ come per esempio riprodurre un suono personalizzato.
|
||||
La differenza tra `on_place` e `on_secondary_use` consiste nel fatto che `on_place` viene chiamato quando il giocatore sta puntando un nodo, mentre `on_secondary_use` quando non ne punta uno.
|
||||
|
||||
Entrambi i richiami sono invocati per tutti i tipi di oggetti.
|
||||
`on_place` risponde alla funzione `core.item_place`, la quale o gestisce la chiamata a `on_rightclick` del nodo puntato, o piazza l'oggetto in mano se questo è un nodo.
|
||||
`on_place` risponde alla funzione `minetest.item_place`, la quale o gestisce la chiamata a `on_rightclick` del nodo puntato, o piazza l'oggetto in mano se questo è un nodo.
|
||||
|
||||
|
||||
### on_drop
|
||||
|
||||
`on_drop` viene chiamato quando il giocatore fa richiesta per buttare un oggetto, per esempio usando il tasto apposito (Q) o trascinando l'oggetto fuori dall'inventario.
|
||||
Risponde alla funzione `core.item_drop`, la quale gestisce il buttare l'oggetto.
|
||||
Risponde alla funzione `minetest.item_drop`, la quale gestisce il buttare l'oggetto.
|
||||
|
||||
### after_use
|
||||
|
||||
@ -100,18 +100,18 @@ end
|
||||
## item_place contro place_item
|
||||
|
||||
L'API di Minetest include varie implementazioni già pronte di richiami.
|
||||
Queste seguono la nomenclatura "tipodioggetto_azione", per esempio `core.item_place` e `core.node_dig`.
|
||||
Queste seguono la nomenclatura "tipodioggetto_azione", per esempio `minetest.item_place` e `minetest.node_dig`.
|
||||
Alcune sono usate direttamente, mentre altre sono funzioni che ritornano il richiamo vero e proprio:
|
||||
|
||||
```lua
|
||||
core.register_item("miamod:esempio", {
|
||||
on_place = core.item_place,
|
||||
on_use = core.item_eat(10),
|
||||
minetest.register_item("miamod:esempio", {
|
||||
on_place = minetest.item_place,
|
||||
on_use = minetest.item_eat(10),
|
||||
})
|
||||
```
|
||||
|
||||
Inoltre, l'API di Minetest include funzioni già pronte che _fanno_ qualcosa.
|
||||
Queste sono spesso chiamate con nomi che rischiano di farle confondere con le implementazioni dei richiami, tuttavia hanno un verbo all'inizio (per esempio `core.place_item` e `core.dig_node`, che permettono rispettivamente di scavare e piazzare nodi come se lo stesse facendo un giocatore).
|
||||
Queste sono spesso chiamate con nomi che rischiano di farle confondere con le implementazioni dei richiami, tuttavia hanno un verbo all'inizio (per esempio `minetest.place_item` e `minetest.dig_node`, che permettono rispettivamente di scavare e piazzare nodi come se lo stesse facendo un giocatore).
|
||||
|
||||
|
||||
## Richiami dei nodi
|
||||
@ -128,9 +128,9 @@ Molti richiami dei nodi sono collegati alle operazioni effettuate - appunto - su
|
||||
### Tasto destro e nodi piazzati
|
||||
|
||||
Quando un utente preme col tasto destro un nodo mentre ha un oggetto in mano, viene invocato il richiamo `on_place` dell'oggetto.
|
||||
Di base, questo è impostato a `core.item_place`.
|
||||
Di base, questo è impostato a `minetest.item_place`.
|
||||
Se il nodo puntato ha un richiamo `on_rightclick` e il tasto accovacciati (shift) è tenuto premuto, allora verrà chiamato `on_rightclick`.
|
||||
Diversamente, `core.item_place` piazzerà il nodo.
|
||||
Diversamente, `minetest.item_place` piazzerà il nodo.
|
||||
|
||||
Piazzare un nodo invocherà simultaneamente `on_construct` e `after_place_node`: il primo è chiamato da ogni evento che cambia i singoli nodi (quindi non in blocco) e ritorna la posizione e il valore del nodo.
|
||||
`after_place_node` viene invece chiamato solamente al piazzare un nodo, contenendo di conseguenza più informazioni - come chi l'ha piazzato e l'ItemStack.
|
||||
@ -139,20 +139,20 @@ Piazzare un nodo invocherà simultaneamente `on_construct` e `after_place_node`:
|
||||
Per via di ciò, `place` potrebbe essere un giocatore, ma anche un'entità o `nil`.
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:mionodo", {
|
||||
minetest.register_node("miamod:mionodo", {
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
if clicker:is_player() then
|
||||
core.chat_send_player(clicker:get_player_name(), "Ciao mondo!")
|
||||
minetest.chat_send_player(clicker:get_player_name(), "Ciao mondo!")
|
||||
end
|
||||
end,
|
||||
on_construct = function(pos, node)
|
||||
local meta = core.get_meta(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Il mio nodo!")
|
||||
end,
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
-- controlla chi sta piazzando
|
||||
if placer and placer:is_player() then
|
||||
local meta = core.get_meta(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("proprietario", placer:get_player_name())
|
||||
end
|
||||
end,
|
||||
@ -166,14 +166,14 @@ Se l'oggetto in mano possiede un richiamo `on_use`, questo verrà chiamato.
|
||||
Diversamente, verrà chiamato il richiamo `on_punch` sul nodo selezionato.
|
||||
|
||||
Quando il giocatore tenta di scavare un nodo, viene eseguito il richiamo `on_dig` del nodo.
|
||||
Di base, ciò equivale a `core.node_dig`, che controlla eventuali protezioni dell'area, usura l'oggetto, rimuove il nodo, e ne esegue il richiamo `after_dig_node`.
|
||||
Di base, ciò equivale a `minetest.node_dig`, che controlla eventuali protezioni dell'area, usura l'oggetto, rimuove il nodo, e ne esegue il richiamo `after_dig_node`.
|
||||
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:mionodo", {
|
||||
minetest.register_node("miamod:mionodo", {
|
||||
on_punch = function(pos, node, puncher, pointed_thing)
|
||||
if puncher:is_player() then
|
||||
core.chat_send_player(puncher:get_player_name(), "Ahia!")
|
||||
minetest.chat_send_player(puncher:get_player_name(), "Ahia!")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
@ -90,7 +90,7 @@ Gli inventari collocati nei nodi sono associati alle coordinate di un nodo speci
|
||||
Il nodo deve essere stato caricato perché viene salvato [nei suoi metadati](../map/storage.html#metadata).
|
||||
|
||||
```lua
|
||||
local inv = core.get_inventory({ type="node", pos={x=1, y=2, z=3} })
|
||||
local inv = minetest.get_inventory({ type="node", pos={x=1, y=2, z=3} })
|
||||
```
|
||||
|
||||
L'esempio in alto ottiene il *riferimento a un inventario*, comunemente definito *InvRef*.
|
||||
@ -107,7 +107,7 @@ Gli inventari dei giocatori si ottengono in maniera simile, oppure usando il rif
|
||||
In entrambi casi, il giocatore deve essere connesso.
|
||||
|
||||
```lua
|
||||
local inv = core.get_inventory({ type="player", name="player1" })
|
||||
local inv = minetest.get_inventory({ type="player", name="player1" })
|
||||
-- oppure
|
||||
local inv = player:get_inventory()
|
||||
```
|
||||
@ -115,14 +115,14 @@ local inv = player:get_inventory()
|
||||
Gli inventari separati, infine, sono quelli non collegati né a nodi né a giocatori, e al contrario degli altri, vengono persi dopo un riavvio.
|
||||
|
||||
```lua
|
||||
local inv = core.get_inventory({
|
||||
local inv = minetest.get_inventory({
|
||||
type="detached", name="nome_inventario" })
|
||||
```
|
||||
|
||||
Un'ulteriore differenza, è che gli inventari separati devono essere creati prima di poterci accedere:
|
||||
|
||||
```lua
|
||||
core.create_detached_inventory("inventory_name")
|
||||
minetest.create_detached_inventory("inventory_name")
|
||||
```
|
||||
|
||||
La funzione `create_detached_inventory` accetta 3 parametri, di cui solo il primo - il nome - è necessario.
|
||||
@ -130,7 +130,7 @@ Il secondo parametro prende una tabella di callback, che possono essere utilizza
|
||||
|
||||
```lua
|
||||
-- Input only detached inventory
|
||||
core.create_detached_inventory("inventory_name", {
|
||||
minetest.create_detached_inventory("inventory_name", {
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
return count -- permette di spostare gli oggetti
|
||||
end,
|
||||
@ -144,9 +144,9 @@ core.create_detached_inventory("inventory_name", {
|
||||
end,
|
||||
|
||||
on_put = function(inv, listname, index, stack, player)
|
||||
core.chat_send_all(player:get_player_name() ..
|
||||
minetest.chat_send_all(player:get_player_name() ..
|
||||
" ha messo " .. stack:to_string() ..
|
||||
" nella cassa delle donazioni da " .. core.pos_to_string(player:get_pos()))
|
||||
" nella cassa delle donazioni da " .. minetest.pos_to_string(player:get_pos()))
|
||||
end,
|
||||
})
|
||||
```
|
||||
|
@ -15,7 +15,7 @@ Queste proprietà sono fisse, uguali per tutte le istanze, tuttavia è possibile
|
||||
|
||||
Il concetto di nodo è stato introdotto nello scorso capitolo, ma non è mai stata data una definizione completa.
|
||||
Il mondo di Minetest è una griglia 3D: un nodo è un punto di quella griglia ed è composto da un tipo (`name`) e due parametri (`param1` e `param2`).
|
||||
Non farti inoltre ingannare dalla funzione `core.register_node`, in quanto è un po' fuorviante: essa non registra infatti un nuovo nodo (c'è solo una definizione di nodo), bensì un nuovo *tipo* di nodo.
|
||||
Non farti inoltre ingannare dalla funzione `minetest.register_node`, in quanto è un po' fuorviante: essa non registra infatti un nuovo nodo (c'è solo una definizione di nodo), bensì un nuovo *tipo* di nodo.
|
||||
|
||||
I parametri sono infine usati per controllare come un nodo viene renderizzato individualmente: `param1` immagazzina le proprietà di luce, mentre il ruolo di `param2` dipende dalla proprietà `paramtype2`, la quale è situata nella definizione dei singoli tipi.
|
||||
|
||||
@ -51,13 +51,13 @@ Ciò è buono per quei nodi con facce in parte trasparenti come le foglie.
|
||||
Puoi inoltre usare il drawtype `allfaces_optional` per permettere agli utenti di fare opt-out dal rendering più pesante, facendo comportare il nodo come se fosse di tipo normale.
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:diamante", {
|
||||
minetest.register_node("miamod:diamante", {
|
||||
description = "Diamante alieno",
|
||||
tiles = {"miamod_diamante.png"},
|
||||
groups = {cracky = 3},
|
||||
})
|
||||
|
||||
core.register_node("default:foglie", {
|
||||
minetest.register_node("default:foglie", {
|
||||
description = "Foglie",
|
||||
drawtype = "allfaces_optional",
|
||||
tiles = {"default_foglie.png"}
|
||||
@ -79,7 +79,7 @@ Questo è utile in quanto i nodi vitrei tendono a essere trasparenti, perciò pe
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
core.register_node("default:obsidian_glass", {
|
||||
minetest.register_node("default:obsidian_glass", {
|
||||
description = "Vetro d'ossidiana",
|
||||
drawtype = "glasslike",
|
||||
tiles = {"default_obsidian_glass.png"},
|
||||
@ -103,11 +103,11 @@ Questa opzione crea un solo bordo lungo tutto l'insieme di nodi, al posto di cre
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
core.register_node("default:glass", {
|
||||
minetest.register_node("default:glass", {
|
||||
description = "Vetro",
|
||||
drawtype = "glasslike_framed",
|
||||
tiles = {"default_glass.png", "default_glass_detail.png"},
|
||||
inventory_image = core.inventorycube("default_glass.png"),
|
||||
inventory_image = minetest.inventorycube("default_glass.png"),
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true, -- Sunlight can shine through block
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 3},
|
||||
@ -122,7 +122,7 @@ Puoi inoltre usare il *drawtype* `glasslike_framed_optional` per permettere un o
|
||||
I nodi d'aria (*airlike*) non sono renderizzati e perciò non hanno texture.
|
||||
|
||||
```lua
|
||||
core.register_node("miaaria:aria", {
|
||||
minetest.register_node("miaaria:aria", {
|
||||
description = "Mia Aria",
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
@ -172,11 +172,11 @@ Ogni tipo di liquido richiede due definizioni di nodi: una per la sorgente e l'a
|
||||
```lua
|
||||
-- Alcune proprietà sono state rimosse perché non
|
||||
-- rilevanti per questo capitolo
|
||||
core.register_node("default:water_source", {
|
||||
minetest.register_node("default:water_source", {
|
||||
drawtype = "liquid",
|
||||
paramtype = "light",
|
||||
|
||||
inventory_image = core.inventorycube("default_water.png"),
|
||||
inventory_image = minetest.inventorycube("default_water.png"),
|
||||
-- ^ questo è necessario per impedire che l'immagine nell'inventario sia animata
|
||||
|
||||
tiles = {
|
||||
@ -250,7 +250,7 @@ Guarda default:water_flowing nella mod default di minetest_game per un esempio c
|
||||
I nodi complessi (*nodebox*) ti permettono di creare un nodo che non è cubico, bensì un insieme di più cuboidi.
|
||||
|
||||
```lua
|
||||
core.register_node("stairs:stair_stone", {
|
||||
minetest.register_node("stairs:stair_stone", {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
@ -281,7 +281,7 @@ Puoi usare [NodeBoxEditor](https://forum.minetest.net/viewtopic.php?f=14&t=2840)
|
||||
Certe volte si vogliono avere nodi complessi che cambiano a seconda della loro posizione sul pavimento, sul muro e sul soffitto, come le torce.
|
||||
|
||||
```lua
|
||||
core.register_node("default:sign_wall", {
|
||||
minetest.register_node("default:sign_wall", {
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "wallmounted",
|
||||
@ -315,7 +315,7 @@ Una faccia interna appare quando le facce di due nodi complessi si sovrappongono
|
||||
Puoi registrare un nodo mesh come segue:
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:meshy", {
|
||||
minetest.register_node("miamod:meshy", {
|
||||
drawtype = "mesh",
|
||||
|
||||
-- Contiene le texture di ogni materiale
|
||||
@ -340,7 +340,7 @@ Al contrario del loro nome, i cartelli non rientrano nei nodi insegna bensì in
|
||||
I tipi insegna tuttavia, sono comunemente usati dalle scale a pioli.
|
||||
|
||||
```lua
|
||||
core.register_node("default:ladder_wood", {
|
||||
minetest.register_node("default:ladder_wood", {
|
||||
drawtype = "signlike",
|
||||
|
||||
tiles = {"default_ladder_wood.png"},
|
||||
@ -366,7 +366,7 @@ core.register_node("default:ladder_wood", {
|
||||
I nodi pianta (*plantlike*) raffigurano la loro texture in un pattern a forma di X.
|
||||
|
||||
```lua
|
||||
core.register_node("default:papyrus", {
|
||||
minetest.register_node("default:papyrus", {
|
||||
drawtype = "plantlike",
|
||||
|
||||
-- Viene usata solo una texture
|
||||
@ -391,7 +391,7 @@ I nodi fiamma (*firelike*) sono simili ai pianta, ad eccezione del fatto che son
|
||||
</figure>
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:avvinghiatutto", {
|
||||
minetest.register_node("miamod:avvinghiatutto", {
|
||||
drawtype = "firelike",
|
||||
|
||||
-- Viene usata solo una texture
|
||||
|
@ -43,7 +43,7 @@ Le definizioni degli oggetti consistono in un *nome oggetto* e una *tabella di d
|
||||
La tabella di definizioni contiene attributi che influenzano il comportamento dell'oggetto.
|
||||
|
||||
```lua
|
||||
core.register_craftitem("nomemod:nomeoggetto", {
|
||||
minetest.register_craftitem("nomemod:nomeoggetto", {
|
||||
description = "Il Mio Super Oggetto",
|
||||
inventory_image = "nomemod_nomeoggetto.png"
|
||||
})
|
||||
@ -72,7 +72,7 @@ Ciò è comunemente usato in due casi:
|
||||
Registrare un alias è alquanto semplice.
|
||||
|
||||
```lua
|
||||
core.register_alias("dirt", "default:dirt")
|
||||
minetest.register_alias("dirt", "default:dirt")
|
||||
```
|
||||
|
||||
Un buon modo per ricordarne il funzionamento è `da → a`, dove *da*
|
||||
@ -82,7 +82,7 @@ Le mod devono inoltre assicurarsi di elaborare gli alias prima di occuparsi dire
|
||||
Anche in questo caso non è difficile:
|
||||
|
||||
```lua
|
||||
itemname = core.registered_aliases[itemname] or itemname
|
||||
itemname = minetest.registered_aliases[itemname] or itemname
|
||||
```
|
||||
|
||||
### Texture
|
||||
@ -98,7 +98,7 @@ Questo perché dimensioni differenti potrebbero non essere supportate dai vecchi
|
||||
## Registrare un nodo base
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:diamante", {
|
||||
minetest.register_node("miamod:diamante", {
|
||||
description = "Diamante alieno",
|
||||
tiles = {"miamod_diamante.png"},
|
||||
is_ground_content = true,
|
||||
@ -116,7 +116,7 @@ Per assegnarne invece di diverse, bisogna fornire il nome di 6 texture in quest'
|
||||
Ricorda che su Minetest, come nella convenzione della computer grafica 3D, +Y punta verso l'alto.
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:diamante", {
|
||||
minetest.register_node("miamod:diamante", {
|
||||
description = "Diamante alieno",
|
||||
tiles = {
|
||||
"miamod_diamante_up.png", -- y+
|
||||
@ -154,7 +154,7 @@ Le ricette fisse avvengono quando gli ingredienti devono essere nella forma o se
|
||||
Nell'esempio sotto, i frammenti necessitano di essere in una figura a forma di sedia per poter fabbricare appunto 99 sedie.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shaped",
|
||||
output = "miamod:diamante_sedia 99",
|
||||
recipe = {
|
||||
@ -170,7 +170,7 @@ Questo significa che ci *deve* essere una colonna vuota a destra della forma, al
|
||||
Se invece la colonna non dovesse servire, basta ometterla in questo modo:
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
output = "miamod:diamante_sedia 99",
|
||||
recipe = {
|
||||
{"miamod:diamante_frammenti", "" },
|
||||
@ -187,7 +187,7 @@ Il campo type non è davvero necessario per le ricette fisse, in quanto sono il
|
||||
Le ricette informi sono ricette che vengono usate quando non importa dove sono posizionati gli ingredienti, ma solo che ci siano.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "miamod:diamante 3",
|
||||
recipe = {
|
||||
@ -203,7 +203,7 @@ core.register_craft({
|
||||
Le ricette di tipo "cottura" non vengono elaborate nella griglia di fabbricazione, bensì nelle fornaci o in qualsivoglia altro strumento di cottura che può essere trovato nelle mod.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = "miamod_diamante_frammenti",
|
||||
recipe = "default:coalblock",
|
||||
@ -220,7 +220,7 @@ La ricetta qui sopra genera un'unità di frammenti di diamante dopo 10 secondi q
|
||||
Il tipo "carburante" invece funge da accompagnamento alle ricette di cottura, in quanto definisce cosa può alimentare il fuoco.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "miamod:diamante",
|
||||
burntime = 300,
|
||||
@ -244,7 +244,7 @@ In primis, vengono utilizzati per descrivere proprietà come friabilità e infia
|
||||
In secundis, possono essere usati in una ricetta al posto di un nome oggetto per permettere a qualsiasi oggetto nel gruppo di essere utilizzato.
|
||||
|
||||
```lua
|
||||
core.register_craft({
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "miamod:diamante_qualcosa 3",
|
||||
recipe = {"group:wood", "miamod:diamante"}
|
||||
@ -275,7 +275,7 @@ Gli strumenti possono anche avere una durezza massima supportata per ogni tipo;
|
||||
Se l'oggetto impugnato dal giocatore non ha una capacità esplicitata, verrà allora usata quella della mano.
|
||||
|
||||
```lua
|
||||
core.register_tool("miamod:strumento", {
|
||||
minetest.register_tool("miamod:strumento", {
|
||||
description = "Il mio strumento",
|
||||
inventory_image = "miamod_strumento.png",
|
||||
tool_capabilities = {
|
||||
|
@ -44,7 +44,7 @@ I Blocchi Mappa esistenti, tuttavia, ignorano questo limite quando caricati dal
|
||||
Un nodo può essere letto da un mondo fornendone la posizione:
|
||||
|
||||
```lua
|
||||
local nodo = core.get_node({ x = 1, y = 3, z = 4 })
|
||||
local nodo = minetest.get_node({ x = 1, y = 3, z = 4 })
|
||||
print(dump(nodo)) --> { name=.., param1=.., param2=.. }
|
||||
```
|
||||
|
||||
@ -55,7 +55,7 @@ Se la posizione è un decimale, verrà arrotondata alle coordinate del nodo.
|
||||
* `param1` - Guarda la definizione dei nodi. È solitamente associato alla luce.
|
||||
* `param2` - Guarda la definizione dei nodi.
|
||||
|
||||
Per vedere se un nodo è caricato si può utilizzare `core.get_node_or_nil`, che ritornerà `nil` se il nome del nodo risulta `ignore`
|
||||
Per vedere se un nodo è caricato si può utilizzare `minetest.get_node_or_nil`, che ritornerà `nil` se il nome del nodo risulta `ignore`
|
||||
(la funzione non caricherà comunque il nodo).
|
||||
Potrebbe comunque ritornare `ignore` se un blocco contiene effettivamente `ignore`: questo succede ai limiti della mappa.
|
||||
|
||||
@ -67,14 +67,14 @@ Le più frequenti sono quelle per trovare i nodi.
|
||||
Per esempio, mettiamo che si voglia creare un certo tipo di pianta che cresce più velocemente vicino alla pietra;
|
||||
si dovrebbe controllare che ogni nodo nei pressi della pianta sia pietra, e modificarne il suo indice di crescita di conseguenza.
|
||||
|
||||
`core.find_node_near` ritornerà il primo nodo trovato in un dato raggio, combaciante con le informazioni passategli (nomi di nodi o gruppi).
|
||||
`minetest.find_node_near` ritornerà il primo nodo trovato in un dato raggio, combaciante con le informazioni passategli (nomi di nodi o gruppi).
|
||||
Nell'esempio che segue, andiamo alla ricerca di un nodo di mese nel raggio di 5 nodi:
|
||||
|
||||
```lua
|
||||
local vel_crescita = 1
|
||||
local pos_nodo = core.find_node_near(pos, 5, { "default:stone" })
|
||||
local pos_nodo = minetest.find_node_near(pos, 5, { "default:stone" })
|
||||
if pos_nodo then
|
||||
core.chat_send_all("Nodo trovato a: " .. dump(pos_nodo))
|
||||
minetest.chat_send_all("Nodo trovato a: " .. dump(pos_nodo))
|
||||
vel_crescita = 2
|
||||
end
|
||||
```
|
||||
@ -86,7 +86,7 @@ Si dovrebbe quindi usare una funzione in grado di trovare più nodi in un'area:
|
||||
local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 })
|
||||
local pos2 = vector.add(pos, { x = 5, y = 5, z = 5 })
|
||||
local lista_pos =
|
||||
core.find_nodes_in_area(pos1, pos2, { "default:stone" })
|
||||
minetest.find_nodes_in_area(pos1, pos2, { "default:stone" })
|
||||
local vel_crescita = 1 + #lista_pos
|
||||
```
|
||||
|
||||
@ -98,7 +98,7 @@ Per ovviare a ciò, bisogna controllare l'intervallo manualmente.
|
||||
local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 })
|
||||
local pos2 = vector.add(pos, { x = 5, y = 5, z = 5 })
|
||||
local lista_pos =
|
||||
core.find_nodes_in_area(pos1, pos2, { "default:stone" })
|
||||
minetest.find_nodes_in_area(pos1, pos2, { "default:stone" })
|
||||
local vel_crescita = 1
|
||||
for i=1, #lista_pos do
|
||||
local delta = vector.subtract(lista_pos[i], pos)
|
||||
@ -123,9 +123,9 @@ Puoi usare `set_node` per sovrascrivere nodi nella mappa.
|
||||
Ogni chiamata a `set_node` ricalcolerà la luce e richiamerà i suoi callback, il che significa che `set_node` è alquanto lento quando usato su un elevato numero di nodi.
|
||||
|
||||
```lua
|
||||
core.set_node({ x = 1, y = 3, z = 4 }, { name = "default:stone" })
|
||||
minetest.set_node({ x = 1, y = 3, z = 4 }, { name = "default:stone" })
|
||||
|
||||
local nodo = core.get_node({ x = 1, y = 3, z = 4 })
|
||||
local nodo = minetest.get_node({ x = 1, y = 3, z = 4 })
|
||||
print(nodo.name) --> default:stone
|
||||
```
|
||||
|
||||
@ -136,7 +136,7 @@ sono in verità due.
|
||||
Si può impostare un nuovo nodo senza rimuoverne metadati e inventario con `swap_node`:
|
||||
|
||||
```lua
|
||||
core.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:stone" })
|
||||
minetest.swap_node({ x = 1, y = 3, z = 4 }, { name = "default:stone" })
|
||||
```
|
||||
|
||||
### Rimozione dei nodi
|
||||
@ -146,15 +146,15 @@ Un nodo deve sempre essere presente. Per rimuoverlo, basta impostarlo uguale a `
|
||||
Le seguenti due linee di codice sono equivalenti, rimuovendo in entrambi i casi il nodo:
|
||||
|
||||
```lua
|
||||
core.remove_node(pos)
|
||||
core.set_node(pos, { name = "air" })
|
||||
minetest.remove_node(pos)
|
||||
minetest.set_node(pos, { name = "air" })
|
||||
```
|
||||
|
||||
Infatti, `remove_node` non fa altro che richiamare `set_node` con nome `air`.
|
||||
|
||||
## Caricamento blocchi
|
||||
|
||||
Puoi usare `core.emerge_area` per caricare i blocchi mappa.
|
||||
Puoi usare `minetest.emerge_area` per caricare i blocchi mappa.
|
||||
Questo comando è asincrono, ovvero i blocchi non saranno caricati istantaneamente; al contrario, verranno caricati man mano e il callback associato sarà richiamato a ogni passaggio.
|
||||
|
||||
```lua
|
||||
@ -164,7 +164,7 @@ local pos1 = vector.subtract(pos, mezza_dimensione)
|
||||
local pos2 = vector.add (pos, mezza_dimensione)
|
||||
|
||||
local param = {} -- dati persistenti tra un callback e l'altro
|
||||
core.emerge_area(pos1, pos2, mio_callback, param)
|
||||
minetest.emerge_area(pos1, pos2, mio_callback, param)
|
||||
```
|
||||
|
||||
Minetest chiamerà la funzione locale definita qua sotto `mio_callback` ogni volta che carica un blocco, con delle informazioni sul progresso.
|
||||
@ -183,12 +183,12 @@ local function mio_callback(pos, action,
|
||||
|
||||
-- Invia messaggio indicante il progresso
|
||||
if param.blocchi_totali == param.blocchi_caricati then
|
||||
core.chat_send_all("Ho finito di caricare blocchi!")
|
||||
minetest.chat_send_all("Ho finito di caricare blocchi!")
|
||||
else
|
||||
local percentuale = 100 * param.blocchi_caricati / param.blocchi_totali
|
||||
local msg = string.format("Caricamento blocchi %d/%d (%.2f%%)",
|
||||
param.blocchi_caricati, param.blocchi_totali, percentuale)
|
||||
core.chat_send_all(msg)
|
||||
minetest.chat_send_all(msg)
|
||||
end
|
||||
end
|
||||
```
|
||||
@ -205,7 +205,7 @@ local mezza_dimensione = { x = 10, y = 10, z = 10 }
|
||||
local pos1 = vector.subtract(pos, mezza_dimensione)
|
||||
local pos2 = vector.add (pos, mezza_dimensione)
|
||||
|
||||
core.delete_area(pos1, pos2)
|
||||
minetest.delete_area(pos1, pos2)
|
||||
```
|
||||
|
||||
Questo cancellerà tutti i blocchi mappa in quell'area, anche quelli solo parzialmente selezionati.
|
||||
|
@ -43,7 +43,7 @@ Questa distinzione è resa meno chiara dal fatto che le entità sono controllate
|
||||
`get_pos` e `set_pos` permettono di ottenere e impostare la posizione di un oggetto.
|
||||
|
||||
```lua
|
||||
local giocatore = core.get_player_by_name("bob")
|
||||
local giocatore = minetest.get_player_by_name("bob")
|
||||
local pos = giocatore:get_pos()
|
||||
giocatore:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z })
|
||||
```
|
||||
@ -130,7 +130,7 @@ Sia la tabella di un ObjectRef che quella di un'entità forniscono modi per otte
|
||||
```lua
|
||||
local entita = oggetto:get_luaentity()
|
||||
local oggetto = entita.object
|
||||
print("L'entità si trova a " .. core.pos_to_string(oggetto:get_pos()))
|
||||
print("L'entità si trova a " .. minetest.pos_to_string(oggetto:get_pos()))
|
||||
```
|
||||
|
||||
Ci sono diversi callback disponibili da usare per le entità.
|
||||
@ -142,9 +142,9 @@ function MiaEntita:on_step(dtime)
|
||||
local pos_giu = vector.subtract(pos, vector.new(0, 1, 0))
|
||||
|
||||
local delta
|
||||
if core.get_node(pos_giu).name == "air" then
|
||||
if minetest.get_node(pos_giu).name == "air" then
|
||||
delta = vector.new(0, -1, 0)
|
||||
elseif core.get_node(pos).name == "air" then
|
||||
elseif minetest.get_node(pos).name == "air" then
|
||||
delta = vector.new(0, 0, 1)
|
||||
else
|
||||
delta = vector.new(0, 1, 0)
|
||||
@ -156,7 +156,7 @@ function MiaEntita:on_step(dtime)
|
||||
end
|
||||
|
||||
function MiaEntita:on_punch(hitter)
|
||||
core.chat_send_player(hitter:get_player_name(), self.message)
|
||||
minetest.chat_send_player(hitter:get_player_name(), self.message)
|
||||
end
|
||||
```
|
||||
|
||||
@ -167,14 +167,14 @@ Questo succede nella *Staticdata*, una stringa che contiene tutte le informazion
|
||||
|
||||
```lua
|
||||
function MiaEntita:get_staticdata()
|
||||
return core.write_json({
|
||||
return minetest.write_json({
|
||||
messaggio = self.messaggio,
|
||||
})
|
||||
end
|
||||
|
||||
function MiaEntita:on_activate(staticdata, dtime_s)
|
||||
if staticdata ~= "" and staticdata ~= nil then
|
||||
local data = core.parse_json(staticdata) or {}
|
||||
local data = minetest.parse_json(staticdata) or {}
|
||||
self:imposta_messaggio(data.messaggio)
|
||||
end
|
||||
end
|
||||
@ -190,14 +190,14 @@ Questo significa che il suo staticdata inizialmente potrebbe essere vuoto (dato
|
||||
Infine, c'è bisogno di registrare la tabella usando `register_entity`.
|
||||
|
||||
```lua
|
||||
core.register_entity("miamod:entita", MiaEntita)
|
||||
minetest.register_entity("miamod:entita", MiaEntita)
|
||||
```
|
||||
|
||||
L'entità può essere spawnata da una mod nel seguente modo:
|
||||
|
||||
```lua
|
||||
local pos = { x = 1, y = 2, z = 3 }
|
||||
local oggetto = core.add_entity(pos, "miamod:entita", nil)
|
||||
local oggetto = minetest.add_entity(pos, "miamod:entita", nil)
|
||||
```
|
||||
|
||||
Il terzo parametro è lo staticdata inziale.
|
||||
|
@ -14,14 +14,14 @@ redirect_from:
|
||||
In questo capitolo imparerai i vari modi per immagazzinare dati.
|
||||
|
||||
- [Metadati](#metadati)
|
||||
- [Cos'è un metadato?](#cosè-un-metadato)
|
||||
- [Ottenere i metadati di un oggetto](#ottenere-i-metadati-di-un-oggetto)
|
||||
- [Lettura e scrittura](#lettura-e-scrittura)
|
||||
- [Chiavi speciali](#chiavi-speciali)
|
||||
- [Immagazzinare tabelle](#immagazzinare-tabelle)
|
||||
- [Metadati privati](#metadati-privati)
|
||||
- [Tabelle Lua](#tabelle-lua)
|
||||
- [Storaggio Mod](#storaggio-mod)
|
||||
- [Cos'è un metadato?](#cos-e-un-metadato)
|
||||
- [Ottenere i metadati di un oggetto](#ottenere-i-metadati-di-un-oggetto)
|
||||
- [Lettura e scrittura](#lettura-e-scrittura)
|
||||
- [Chiavi speciali](#chiavi-speciali)
|
||||
- [Immagazzinare tabelle](#immagazzinare-tabelle)
|
||||
- [Metadati privati](#metadati-privati)
|
||||
- [Tabelle Lua](#tabelle-lua)
|
||||
- [Storaggio mod](#storaggio-mod)
|
||||
- [Database](#database)
|
||||
- [Decidere quale usare](#decidere-quale-usare)
|
||||
- [Il tuo turno](#il-tuo-turno)
|
||||
@ -47,7 +47,7 @@ Il dato in sé, come il tipo di un nodo o la quantità di un ItemStack, non rien
|
||||
Se si conosce la posizione di un nodo, si possono ottenere i suoi metadati:
|
||||
|
||||
```lua
|
||||
local meta = core.get_meta({ x = 1, y = 2, z = 3 })
|
||||
local meta = minetest.get_meta({ x = 1, y = 2, z = 3 })
|
||||
```
|
||||
|
||||
Quelli dei giocatori e degli ItemStack invece sono ottenuti tramite `get_meta()`:
|
||||
@ -87,7 +87,7 @@ print(meta:get_string("count")) --> "3"
|
||||
Questo è utile, per esempio, per mostrare lo stato o il proprietario di un nodo.
|
||||
|
||||
`description` è usato negli ItemStack per sovrascrivere la descrizione al passare il mouse sopra l'oggetto in un formspec (come l'inventario, li vedremo più avanti).
|
||||
È possibile utilizzare `core.colorize()` per cambiarne il colore.
|
||||
È possibile utilizzare `minetest.colorize()` per cambiarne il colore.
|
||||
|
||||
`owner` è una chiave comune, usata per immagazzinare il nome del giocatore a cui appartiene l'oggetto o il nodo.
|
||||
|
||||
@ -100,9 +100,9 @@ Quello in Lua tende a essere molto più veloce e corrisponde al formato usato da
|
||||
|
||||
```lua
|
||||
local data = { username = "utente1", score = 1234 }
|
||||
meta:set_string("foo", core.serialize(data))
|
||||
meta:set_string("foo", minetest.serialize(data))
|
||||
|
||||
data = core.deserialize(meta:get_string("foo"))
|
||||
data = minetest.deserialize(minetest:get_string("foo"))
|
||||
```
|
||||
|
||||
### Metadati privati
|
||||
@ -130,7 +130,7 @@ Lo spazio d'archiviazione della mod (*storage*) usa la stessa identica API dei m
|
||||
Il primo infatti è per mod, e può essere ottenuto solo durante l'inizializzazione - appunto - della mod.
|
||||
|
||||
```lua
|
||||
local memoria = core.get_mod_storage()
|
||||
local memoria = minetest.get_mod_storage()
|
||||
```
|
||||
|
||||
Nell'esempio è ora possibile manipolare lo spazio d'archiviazione come se fosse un metadato:
|
||||
@ -148,10 +148,10 @@ Dovresti rendere ciò opzionale, separando il come i dati vengono salvati e il d
|
||||
local backend
|
||||
if use_database then
|
||||
backend =
|
||||
dofile(core.get_modpath("miamod") .. "/backend_sqlite.lua")
|
||||
dofile(minetest.get_modpath("miamod") .. "/backend_sqlite.lua")
|
||||
else
|
||||
backend =
|
||||
dofile(core.get_modpath("miamod") .. "/backend_storage.lua")
|
||||
dofile(minetest.get_modpath("miamod") .. "/backend_storage.lua")
|
||||
end
|
||||
|
||||
backend.get_foo("a")
|
||||
@ -161,15 +161,15 @@ backend.set_foo("a", { score = 3 })
|
||||
Il file `backend_storage.lua` dell'esempio (puoi nominarlo come vuoi) dovrebbe includere l'implementazione del metodo di storaggio:
|
||||
|
||||
```lua
|
||||
local memoria = core.get_mod_storage()
|
||||
local memoria = minetest.get_mod_storage()
|
||||
local backend = {}
|
||||
|
||||
function backend.set_foo(key, value)
|
||||
memoria:set_string(key, core.serialize(value))
|
||||
memoria:set_string(key, minetest.serialize(value))
|
||||
end
|
||||
|
||||
function backend.get_foo(key)
|
||||
return core.deserialize(memoria:get_string(key))
|
||||
return minetest.deserialize(memoria:get_string(key))
|
||||
end
|
||||
|
||||
return backend
|
||||
@ -182,7 +182,7 @@ Un ambiente non sicuro è una tabella disponibile solamente alle mod con accesso
|
||||
Gli ambienti non sicuri saranno trattati più nel dettaglio nel capitolo sulla [Sicurezza](../quality/security.html).
|
||||
|
||||
```lua
|
||||
local amb_nonsicuro = core.request_insecure_environment()
|
||||
local amb_nonsicuro = minetest.request_insecure_environment()
|
||||
assert(amb_nonsicuro, "Per favore aggiungi miamod a secure.trusted_mods nelle impostazioni")
|
||||
|
||||
local _sql = amb_nonsicuro.require("lsqlite3")
|
||||
@ -214,4 +214,4 @@ Si prestano bene per i grandi ammontare di dati.
|
||||
## Il tuo turno
|
||||
|
||||
* Crea un nodo che sparisce dopo essere stato colpito cinque volte.
|
||||
(Usa `on_punch` nella definizione del nodo e `core.set_node`)
|
||||
(Usa `on_punch` nella definizione del nodo e `minetest.set_node`)
|
||||
|
@ -32,7 +32,7 @@ A ogni nodo è associato un timer.
|
||||
Questi timer possono essere gestiti ottenendo un oggetto NodeTimerRef (quindi un riferimento, come già visto per gli inventari).
|
||||
|
||||
```lua
|
||||
local timer = core.get_node_timer(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(10.5) -- in secondi
|
||||
```
|
||||
|
||||
@ -40,9 +40,9 @@ Quando un timer raggiunge lo zero, viene eseguito il metodo `on_timer`, che va d
|
||||
`on_timer` richiede un solo parametro, ovvero la posizione del nodo.
|
||||
|
||||
```lua
|
||||
core.register_node("porteautomatiche:porta_aperta", {
|
||||
minetest.register_node("porteautomatiche:porta_aperta", {
|
||||
on_timer = function(pos)
|
||||
core.set_node(pos, { name = "porteautomatiche:porta_chiusa" })
|
||||
minetest.set_node(pos, { name = "porteautomatiche:porta_chiusa" })
|
||||
return false
|
||||
end
|
||||
})
|
||||
@ -60,15 +60,15 @@ Potresti aver tuttavia notato una limitazione: per questioni di ottimizzazione,
|
||||
Erba aliena, a scopo illustrativo del capitolo, è un tipo d'erba che ha una probabilità di apparire vicino all'acqua.
|
||||
|
||||
```lua
|
||||
core.register_node("alieni:erba", {
|
||||
minetest.register_node("alieni:erba", {
|
||||
description = "Erba Aliena",
|
||||
light_source = 3, -- Il nodo irradia luce. Min 0, max 14
|
||||
tiles = {"alieni_erba.png"},
|
||||
groups = {choppy=1},
|
||||
on_use = core.item_eat(20)
|
||||
on_use = minetest.item_eat(20)
|
||||
})
|
||||
|
||||
core.register_abm({
|
||||
minetest.register_abm({
|
||||
nodenames = {"default:dirt_with_grass"}, -- nodo sul quale applicare l'ABM
|
||||
neighbors = {"default:water_source", "default:water_flowing"}, -- nodi che devono essere nei suoi dintorni (almeno uno)
|
||||
interval = 10.0, -- viene eseguito ogni 10 secondi
|
||||
@ -76,7 +76,7 @@ core.register_abm({
|
||||
action = function(pos, node, active_object_count,
|
||||
active_object_count_wider)
|
||||
local pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
core.set_node(pos, {name = "alieni:erba"})
|
||||
minetest.set_node(pos, {name = "alieni:erba"})
|
||||
end
|
||||
})
|
||||
```
|
||||
|
@ -13,7 +13,7 @@ cmd_online:
|
||||
Per esempio, il ponte IRC permette ai giocatori di eseguire comandi senza dover entrare in gioco.
|
||||
|
||||
Assicurati quindi di non dar per scontato che un giocatore sia connesso.
|
||||
Puoi controllare ciò tramite `core.get_player_by_name`, per vedere se ritorna qualcosa o meno.
|
||||
Puoi controllare ciò tramite `minetest.get_player_by_name`, per vedere se ritorna qualcosa o meno.
|
||||
|
||||
cb_cmdsprivs:
|
||||
level: warning
|
||||
@ -44,7 +44,7 @@ Le mod possono interagire con la chat del giocatore, tra l'inviare messaggi, int
|
||||
Per inviare un messaggio a tutti i giocatori connessi in gioco, si usa la funzione `chat_send_all`:
|
||||
|
||||
```lua
|
||||
core.chat_send_all("Questo è un messaggio visualizzabile da tutti")
|
||||
minetest.chat_send_all("Questo è un messaggio visualizzabile da tutti")
|
||||
```
|
||||
|
||||
Segue un esempio di come apparirerebbe in gioco:
|
||||
@ -60,7 +60,7 @@ Il messaggio appare su una nuova riga, per distinguerlo dai messaggi dei giocato
|
||||
Per inviare un messaggio a un giocatore in particolare, si usa invece la funzione `chat_send_player`:
|
||||
|
||||
```lua
|
||||
core.chat_send_player("Tizio", "Questo è un messaggio per Tizio")
|
||||
minetest.chat_send_player("Tizio", "Questo è un messaggio per Tizio")
|
||||
```
|
||||
|
||||
Questo messaggio viene mostrato esattamente come il precedente, ma solo, in questo caso, a Tizio.
|
||||
@ -70,7 +70,7 @@ Questo messaggio viene mostrato esattamente come il precedente, ma solo, in ques
|
||||
Per registrare un comando, per esempio `/foo`, si usa `register_chatcommand`:
|
||||
|
||||
```lua
|
||||
core.register_chatcommand("foo", {
|
||||
minetest.register_chatcommand("foo", {
|
||||
privs = {
|
||||
interact = true,
|
||||
},
|
||||
@ -160,7 +160,7 @@ Una guida più completa ai pattern è probabilmente quella su [lua-users.org](ht
|
||||
Per intercettare un messaggio, si usa `register_on_chat_message`:
|
||||
|
||||
```lua
|
||||
core.register_on_chat_message(function(name, message)
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
print(name .. " ha detto " .. message)
|
||||
return false
|
||||
end)
|
||||
@ -175,10 +175,10 @@ Dovresti assicurarti, poi, che il messaggio potrebbe essere un comando che invia
|
||||
o che l'utente potrebbere non avere `shout`.
|
||||
|
||||
```lua
|
||||
core.register_on_chat_message(function(name, message)
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
if message:sub(1, 1) == "/" then
|
||||
print(name .. " ha eseguito un comando")
|
||||
elseif core.check_player_privs(name, { shout = true }) then
|
||||
elseif minetest.check_player_privs(name, { shout = true }) then
|
||||
print(name .. " ha detto " .. message)
|
||||
else
|
||||
print(name .. " ha provato a dire " .. message ..
|
||||
|
@ -123,7 +123,7 @@ function indovina.prendi_formspec(nome)
|
||||
local formspec = {
|
||||
"formspec_version[4]",
|
||||
"size[6,3.476]",
|
||||
"label[0.375,0.5;", core.formspec_escape(testo), "]",
|
||||
"label[0.375,0.5;", minetest.formspec_escape(testo), "]",
|
||||
"field[0.375,1.25;5.25,0.8;numero;Numero;]",
|
||||
"button[1.5,2.3;3,0.8;indovina;Indovina]"
|
||||
}
|
||||
@ -143,10 +143,10 @@ Il metodo principale per farlo è usare `show_formspec`:
|
||||
|
||||
```lua
|
||||
function indovina.mostra_a(nome)
|
||||
core.show_formspec(nome, "indovina:gioco", indovina.prendi_formspec(nome))
|
||||
minetest.show_formspec(nome, "indovina:gioco", indovina.prendi_formspec(nome))
|
||||
end
|
||||
|
||||
core.register_chatcommand("gioco", {
|
||||
minetest.register_chatcommand("gioco", {
|
||||
func = function(name)
|
||||
indovina.mostra_a(name)
|
||||
end,
|
||||
@ -180,19 +180,19 @@ Per far sì che i formspec siano utili, le informazioni devono essere ritornate
|
||||
Il metodo per fare ciò è chiamato Campo di Compilazione (*formspec field submission*), e per `show_formspec` quel campo viene ottenuto usando un callback globale:
|
||||
|
||||
```lua
|
||||
core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "indovina:gioco" then
|
||||
return
|
||||
end
|
||||
|
||||
if fields.indovina then
|
||||
local p_name = player:get_player_name()
|
||||
core.chat_send_all(p_name .. " ha tentato di indovinare con il numero " .. fields.numero)
|
||||
minetest.chat_send_all(p_name .. " ha tentato di indovinare con il numero " .. fields.numero)
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
La funzione data in `core.register_on_player_receive_fields` è chiamata ogni volta che un utente invia un modulo.
|
||||
La funzione data in `minetest.register_on_player_receive_fields` è chiamata ogni volta che un utente invia un modulo.
|
||||
La maggior parte dei callback necessiteranno di controllare il nome fornito alla funzione, e uscire se non è quello esatto; tuttavia, alcuni potrebbero necessitare di operare su più moduli, se non addirittura su tutti.
|
||||
|
||||
Il parametro `fields` è una tabella di tutti i valori inviati dall'utente, indicizzati per stringhe.
|
||||
@ -223,7 +223,7 @@ local function prendi_contesto(nome)
|
||||
return contesto
|
||||
end
|
||||
|
||||
core.register_on_leaveplayer(function(player)
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
_contexts[player:get_player_name()] = nil
|
||||
end)
|
||||
```
|
||||
@ -236,7 +236,7 @@ function indovina.mostra_a(nome)
|
||||
contesto.soluzione = contesto.soluzione or math.random(1, 10)
|
||||
|
||||
local formspec = indovina.prendi_formspec(nome, contesto)
|
||||
core.show_formspec(nome, "indovina:gioco", formspec)
|
||||
minetest.show_formspec(nome, "indovina:gioco", formspec)
|
||||
end
|
||||
```
|
||||
|
||||
@ -282,11 +282,11 @@ Ci sono tre diversi modi per far sì che un formspec sia consegnato al client:
|
||||
|
||||
### Formspec nei nodi
|
||||
|
||||
`core.show_formspec` non è l'unico modo per mostrare un formspec; essi possono infatti essere aggiunti anche ai [metadati di un nodo](../map/storage.html).
|
||||
`minetest.show_formspec` non è l'unico modo per mostrare un formspec; essi possono infatti essere aggiunti anche ai [metadati di un nodo](../map/storage.html).
|
||||
Per esempio, questo è usato con le casse per permettere tempi più veloci d'apertura - non si ha bisogno di aspettare che il server invii il formspec della cassa al giocatore.
|
||||
|
||||
```lua
|
||||
core.register_node("miamod:tastodestro", {
|
||||
minetest.register_node("miamod:tastodestro", {
|
||||
description = "Premimi col tasto destro del mouse!",
|
||||
tiles = {"miamod_tastodestro.png"},
|
||||
groups = {cracky = 1},
|
||||
@ -295,7 +295,7 @@ core.register_node("miamod:tastodestro", {
|
||||
-- Il codice che segue imposta il formspec della cassa.
|
||||
-- I metadati sono un modo per immagazzinare dati nel nodo.
|
||||
|
||||
local meta = core.get_meta(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"formspec_version[4]" ..
|
||||
"size[5,5]"..
|
||||
@ -312,7 +312,7 @@ core.register_node("miamod:tastodestro", {
|
||||
I formspec impostati in questo modo non innescano lo stesso callback.
|
||||
Per far in modo di ricevere il modulo di input per i formspec nei nodi, bisogna includere una voce `on_receive_fields` al registrare il nodo.
|
||||
|
||||
Questo stile di callback viene innescato al premere invio in un campo, che è possibile grazie a `core.show_formspec`; tuttavia, questi tipi di moduli possono essere mostrati solo
|
||||
Questo stile di callback viene innescato al premere invio in un campo, che è possibile grazie a `minetest.show_formspec`; tuttavia, questi tipi di moduli possono essere mostrati solo
|
||||
tramite il premere col tasto destro del mouse su un nodo. Non è possibile farlo programmaticamente.
|
||||
|
||||
### Inventario del giocatore
|
||||
|
@ -79,7 +79,7 @@ Questo permette all'intero pannello di essere ancorato sulla destra della finest
|
||||
Puoi creare un elemento HUD una volta ottenuto il riferimento al giocatore al quale assegnarla:
|
||||
|
||||
```lua
|
||||
local giocatore = core.get_player_by_name("tizio")
|
||||
local giocatore = minetest.get_player_by_name("tizio")
|
||||
local idx = giocatore:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = {x = 0.5, y = 0.5},
|
||||
@ -268,9 +268,9 @@ function punteggio.aggiorna_hud(giocatore)
|
||||
end
|
||||
end
|
||||
|
||||
core.register_on_joinplayer(punteggio.aggiorna_hud)
|
||||
minetest.register_on_joinplayer(punteggio.aggiorna_hud)
|
||||
|
||||
core.register_on_leaveplayer(function(player)
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
hud_salvate[player:get_player_name()] = nil
|
||||
end)
|
||||
```
|
||||
|
@ -23,9 +23,9 @@ Per esempio, un valore di 2 sulla gravità, renderà la gravità di un utente du
|
||||
Segue l'esempio di un comando di antigravità:
|
||||
|
||||
```lua
|
||||
core.register_chatcommand("antigrav", {
|
||||
minetest.register_chatcommand("antigrav", {
|
||||
func = function(name, param)
|
||||
local giocatore = core.get_player_by_name(name)
|
||||
local giocatore = minetest.get_player_by_name(name)
|
||||
giocatore:set_physics_override({
|
||||
gravity = 0.1, -- imposta la gravità al 10% del suo valore originale
|
||||
-- (0.1 * 9.81)
|
||||
|
@ -46,7 +46,7 @@ I privilegi non sono fatti per indicare classi o status.
|
||||
Usa `register_privilege` per dichiarare un nuovo privilegio:
|
||||
|
||||
```lua
|
||||
core.register_privilege("voto", {
|
||||
minetest.register_privilege("voto", {
|
||||
description = "Può votare nei sondaggi",
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
@ -59,7 +59,7 @@ core.register_privilege("voto", {
|
||||
Per controllare velocemente se un giocatore ha tutti i privilegi necessari o meno:
|
||||
|
||||
```lua
|
||||
local celo, manca = core.check_player_privs(player_or_name, {
|
||||
local celo, manca = minetest.check_player_privs(player_or_name, {
|
||||
interact = true,
|
||||
voto = true })
|
||||
```
|
||||
@ -68,7 +68,7 @@ In quest'esempio, `celo` è true se il giocatore ha sia `interact` che `voto`.
|
||||
Se `celo` è false, allora `manca` conterrà una tabella con i privilegi mancanti.
|
||||
|
||||
```lua
|
||||
local celo, manca = core.check_player_privs(name, {
|
||||
local celo, manca = minetest.check_player_privs(name, {
|
||||
interact = true,
|
||||
voto = true })
|
||||
|
||||
@ -82,7 +82,7 @@ end
|
||||
Se non hai bisogno di controllare i privilegi mancanti, puoi inserire `check_player_privs` direttamente nel costrutto if:
|
||||
|
||||
```lua
|
||||
if not core.check_player_privs(name, { interact=true }) then
|
||||
if not minetest.check_player_privs(name, { interact=true }) then
|
||||
return false, "Hai bisogno del privilegio 'interact' per eseguire quest'azione!"
|
||||
end
|
||||
```
|
||||
@ -92,11 +92,11 @@ end
|
||||
Si può accedere o modificare i privilegi di un giocatore anche se quest'ultimo non risulta online.
|
||||
|
||||
```lua
|
||||
local privs = core.get_player_privs(name)
|
||||
local privs = minetest.get_player_privs(name)
|
||||
print(dump(privs))
|
||||
|
||||
privs.voto = true
|
||||
core.set_player_privs(name, privs)
|
||||
minetest.set_player_privs(name, privs)
|
||||
```
|
||||
|
||||
I privilegi sono sempre specificati come una tabella chiave-valore, con il loro nome come chiave e true/false come valore.
|
||||
|
@ -82,7 +82,7 @@ E hai ragione! L'API di Minetest è molto incentrata sull'Osservatore, per far i
|
||||
|
||||
## Modello-Vista-Controllo
|
||||
|
||||
Nel prossimo capitolo discuteremo di come testare automaticamente il codice, e uno dei problemi che riscontreremo sarà come separare il più possibile la logica (calcoli, cosa bisognerebbe fare) dalle chiamate alle API (`core.*`, altre mod).
|
||||
Nel prossimo capitolo discuteremo di come testare automaticamente il codice, e uno dei problemi che riscontreremo sarà come separare il più possibile la logica (calcoli, cosa bisognerebbe fare) dalle chiamate alle API (`minetest.*`, altre mod).
|
||||
|
||||
Un modo per fare ciò è pensare a:
|
||||
|
||||
@ -148,14 +148,14 @@ function terreno.mostra_formspec_crea(nome)
|
||||
]]
|
||||
end
|
||||
|
||||
core.register_chatcommand("/land", {
|
||||
minetest.register_chatcommand("/land", {
|
||||
privs = { terreno = true },
|
||||
func = function(name)
|
||||
land.gestore_richiesta_crea(name)
|
||||
end,
|
||||
})
|
||||
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
terreno.gestore_invio_crea(player:get_player_name(),
|
||||
fields.nome_area)
|
||||
@ -191,7 +191,7 @@ Al contrario, un approccio più comune e leggermente meno rigido è quello API-V
|
||||
In un mondo ideale, si avrebbero le 3 aree MVC perfettamente separate... ma siamo nel mondo reale.
|
||||
Un buon compromesso è ridurre la mod in due parti:
|
||||
|
||||
* **API** - modello + controllo. Non ci dovrebbe essere nessun uso di `core.` nella API.
|
||||
* **API** - modello + controllo. Non ci dovrebbe essere nessun uso di `minetest.` nella API.
|
||||
* **Vista** - la vista, esattamente come quella spiegata sopra.
|
||||
È buona norma strutturare questa parte in file separati per ogni tipo di evento.
|
||||
|
||||
|
@ -40,11 +40,11 @@ Per esempio, il seguente codice presenta una vulnerabilità che permette ai gioc
|
||||
|
||||
```lua
|
||||
local function show_formspec(name)
|
||||
if not core.check_player_privs(name, { privs = true }) then
|
||||
if not minetest.check_player_privs(name, { privs = true }) then
|
||||
return false
|
||||
end
|
||||
|
||||
core.show_formspec(name, "modman:modman", [[
|
||||
minetest.show_formspec(name, "modman:modman", [[
|
||||
size[3,2]
|
||||
field[0,0;3,1;target;Nome;]
|
||||
button_exit[0,1;3,1;sub;Promuovi]
|
||||
@ -52,14 +52,14 @@ local function show_formspec(name)
|
||||
return true
|
||||
})
|
||||
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
-- MALE! Manca il controllo dei privilegi!
|
||||
|
||||
local privs = core.get_player_privs(fields.target)
|
||||
local privs = minetest.get_player_privs(fields.target)
|
||||
privs.kick = true
|
||||
privs.ban = true
|
||||
core.set_player_privs(fields.target, privs)
|
||||
minetest.set_player_privs(fields.target, privs)
|
||||
return true
|
||||
end)
|
||||
```
|
||||
@ -67,9 +67,9 @@ end)
|
||||
Aggiungi un controllo dei privilegi per ovviare:
|
||||
|
||||
```lua
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
if not core.check_player_privs(name, { privs = true }) then
|
||||
if not minetest.check_player_privs(name, { privs = true }) then
|
||||
return false
|
||||
end
|
||||
|
||||
@ -105,7 +105,7 @@ inv:set_stack("main", 1, pila)
|
||||
Il comportamento dei callback è leggermente più complicato.
|
||||
|
||||
```lua
|
||||
core.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
itemstack:get_meta():set_string("description", "Un po' smangiucchiato")
|
||||
-- Quasi corretto! I dati saranno persi se un altro callback annulla questa chiamata
|
||||
@ -117,7 +117,7 @@ Se nessun callback cancella l'operazione, la pila sarà impostata e la descrizio
|
||||
È meglio quindi fare così:
|
||||
|
||||
```lua
|
||||
core.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
minetest.register_on_item_eat(function(hp_change, replace_with_item,
|
||||
itemstack, user, pointed_thing)
|
||||
itemstack:get_meta():set_string("description", "Un po' smangiucchiato")
|
||||
user:get_inventory():set_stack("main", user:get_wield_index(),
|
||||
|
@ -19,6 +19,7 @@ Dopo aver letto questo libro, se mastichi l'inglese dai un occhio a ciò che seg
|
||||
### Programmazione in Lua
|
||||
|
||||
* [Programmazione in Lua (PIL)](http://www.lua.org/pil/).
|
||||
* [Corso accelerato su Lua](http://luatut.com/crash_course.html).
|
||||
|
||||
### Modellazione 3D
|
||||
|
||||
|
@ -33,7 +33,7 @@ Qualsiasi utente può inviare qualsiasi formspec con i valori che preferisce qua
|
||||
Segue del codice trovato realmente in una mod:
|
||||
|
||||
```lua
|
||||
core.register_on_player_receive_fields(function(player,
|
||||
minetest.register_on_player_receive_fields(function(player,
|
||||
formname, fields)
|
||||
for key, field in pairs(fields) do
|
||||
local x,y,z = string.match(key,
|
||||
@ -71,7 +71,7 @@ Minetest permette alle mod di richiedere ambienti senza limiti, dando loro acces
|
||||
Riesci a individuare la vulnerabilità in questo pezzo di codice??
|
||||
|
||||
```lua
|
||||
local ie = core.request_insecure_environment()
|
||||
local ie = minetest.request_insecure_environment()
|
||||
ie.os.execute(("path/to/prog %d"):format(3))
|
||||
```
|
||||
|
||||
|
@ -42,12 +42,12 @@ Minetest ti permette di localizzare i tuoi contenuti in tante lingue diverse, ch
|
||||
### Testo formattato
|
||||
|
||||
Il server ha bisogno di dire ai client come tradurre il testo.
|
||||
Questo accade grazie alla funzione `core.get_translator(dominiotestuale)`, che abbrevieremo con `S()`:
|
||||
Questo accade grazie alla funzione `minetest.get_translator(dominiotestuale)`, che abbrevieremo con `S()`:
|
||||
|
||||
```lua
|
||||
local S = core.get_translator("miamod")
|
||||
local S = minetest.get_translator("miamod")
|
||||
|
||||
core.register_craftitem("miamod:oggetto", {
|
||||
minetest.register_craftitem("miamod:oggetto", {
|
||||
description = S("My Item"),
|
||||
})
|
||||
```
|
||||
@ -57,7 +57,7 @@ Piuttosto che avere tutte le traduzioni di una lingua salvate nello stesso file,
|
||||
È buona norma assegnare al dominio testuale lo stesso nome della mod, onde evitare conflitti tra mod diverse.
|
||||
|
||||
Il testo formattato può essere usato nella maggior parte dei casi dove è richiesto un testo fatto per gli esseri umani - come i formspec, i campi di definizioni di un oggetto, `infotext` ecc.
|
||||
Nel caso dei formspec, tieni presente che dovrai usare la funzione di escape `core.formspec_escape` per una corretta visualizzazione.
|
||||
Nel caso dei formspec, tieni presente che dovrai usare la funzione di escape `minetest.formspec_escape` per una corretta visualizzazione.
|
||||
|
||||
Quando il client incontra del testo formattato, come quello passato in `description`, ne andrà a cercare il corrispettivo nel file di traduzione della lingua del giocatore. Se la ricerca non avrà avuto esito positivo, ritornerà quello in inglese.
|
||||
|
||||
@ -95,8 +95,8 @@ Non è raro dover inserire una variabile dentro una stringa da tradurre.
|
||||
Al contrario, dovresti usare il seguente sistema di formattazione:
|
||||
|
||||
```lua
|
||||
core.register_on_joinplayer(function(player)
|
||||
core.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name()))
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
minetest.chat_send_all(S("Everyone, say hi to @1!", player:get_player_name()))
|
||||
end)
|
||||
```
|
||||
|
||||
@ -130,13 +130,13 @@ local list = {
|
||||
S("Potato")
|
||||
}
|
||||
|
||||
core.register_chatcommand("find", {
|
||||
minetest.register_chatcommand("find", {
|
||||
func = function(name, param)
|
||||
local info = core.get_player_information(name)
|
||||
local info = minetest.get_player_information(name)
|
||||
local lingua = info and info.language or "en"
|
||||
|
||||
for _, riga in ipairs(lista) do
|
||||
local trad = core.get_translated_string(language, riga)
|
||||
local trad = minetest.get_translated_string(language, riga)
|
||||
if trad:contains(query) then
|
||||
return riga
|
||||
end
|
||||
|
@ -52,7 +52,7 @@ Quello che fa è cercare i file Lua con il nome che termina in `_spec`, eseguend
|
||||
```lua
|
||||
miamod = {}
|
||||
|
||||
dofile(core.get_modpath("miamod") .. "/api.lua")
|
||||
dofile(minetest.get_modpath("miamod") .. "/api.lua")
|
||||
```
|
||||
|
||||
|
||||
@ -112,7 +112,7 @@ _G.minetest = {}
|
||||
|
||||
-- Definisce la funzione simulata
|
||||
local chiamate_chat_send_all = {}
|
||||
function core.chat_send_all(name, message)
|
||||
function minetest.chat_send_all(name, message)
|
||||
table.insert(chiamate_chat_send_all, { nome = name, messaggio = message })
|
||||
end
|
||||
|
||||
|
@ -13,7 +13,7 @@ layout: compress
|
||||
<html>
|
||||
{% endif %}
|
||||
<head>
|
||||
<title>{% if page.homepage %}{% else %}{{ page.title }} - {% endif %}Luanti / Minetest Modding Book</title>
|
||||
<title>{% if page.homepage %}{% else %}{{ page.title }} - {% endif %}Minetest Modding Book</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="author" content="rubenwardy">
|
||||
@ -22,7 +22,7 @@ layout: compress
|
||||
<meta name="og:url" content="https://rubenwardy.com/minetest_modding_book{{ page.url }}">
|
||||
<meta name="og:title" content="{{ page.title | escape }}">
|
||||
<meta name="og:author" content="rubenwardy">
|
||||
<meta name="og:site_name" content="Luanti Modding Book (formerly Minetest)">
|
||||
<meta name="og:site_name" content="Minetest Modding Book">
|
||||
{% if page.description %}
|
||||
<meta name="og:description" content="{{ page.description | escape | strip }}">
|
||||
<meta name="description" content="{{ page.description | escape | strip }}">
|
||||
|
@ -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 %}
|
||||
|
14
cat.html
Normal file
14
cat.html
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: none
|
||||
---
|
||||
|
||||
{% if site.calc_word_count %}
|
||||
{% assign chapters = site.en | sort: "idx" %}
|
||||
{% for chapter in chapters %}
|
||||
{% unless chapter.homepage %}
|
||||
{{ chapter.content }}
|
||||
{% endunless %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
Cat disabled.
|
||||
{% endif %}
|
@ -8,7 +8,7 @@ noindex: true
|
||||
|
||||
<main>
|
||||
<article>
|
||||
<h1>Luanti Modding Book (formerly Minetest)</h1>
|
||||
<h1>Minetest Modding Book</h1>
|
||||
|
||||
<h2>Thanks for sharing your feedback!</h2>
|
||||
|
||||
|
@ -4,7 +4,7 @@ layout: none
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Luanti Modding Book (formerly Minetest)</title>
|
||||
<title>Minetest Modding Book</title>
|
||||
<meta name="og:description" content="An easy guide to learn how to create mods for Minetest">
|
||||
<meta name="description" content="An easy guide to learn how to create mods for Minetest">
|
||||
<link rel="canonical" href="https://rubenwardy.com/minetest_modding_book/">
|
||||
@ -28,11 +28,11 @@ layout: none
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Luanti Modding Book (formerly Minetest)</h1>
|
||||
<h1>Minetest Modding Book</h1>
|
||||
<p>An easy guide to learn how to create mods for Minetest.</p>
|
||||
<p>Detecting and redirecting to the correct translation.</p>
|
||||
<p>
|
||||
<a href="en/index.html">View Luanti Modding Book in English</a>
|
||||
<a href="en/index.html">View Minetest Modding Book in English</a>
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
|
@ -6,7 +6,7 @@ root: .
|
||||
|
||||
<main>
|
||||
<article>
|
||||
<h1>Luanti Modding Book (formerly Minetest)</h1>
|
||||
<h1>Minetest Modding Book</h1>
|
||||
|
||||
<h2>Choose a Language</h2>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user