Nodes, Items, and Crafting: Rewrite chapter
This commit is contained in:
parent
c1a2528c72
commit
c0045d484a
@ -119,7 +119,7 @@ It's good practice to make sure a variable is only ever nil or a single non-nil
|
||||
|
||||
| Number | A whole or decimal number. | `local A = 4` |
|
||||
| String | A piece of text | `local D = "one two three" |
|
||||
| Boolean | True or False | `local is_true = false`, `local E = (1 == 1)`` |
|
||||
| Boolean | True or False | `local is_true = false`, `local E = (1 == 1)` |
|
||||
| Table | Lists | Explained below |
|
||||
| Function | Can run. May require inputs and may return a value | `local result = func(1, 2, 3)` |
|
||||
|
||||
|
@ -12,26 +12,51 @@ redirect_from: /en/chapters/nodes_items_crafting.html
|
||||
Registering new nodes and craftitems, and creating craft recipes, are
|
||||
basic requirements for many mods.
|
||||
|
||||
* [Item Strings](#item-strings)
|
||||
* [Overriding](#overriding)
|
||||
* [What are Nodes and Items?](#what-are-nodes-and-items)
|
||||
* [Registering Items](#registering-items)
|
||||
* [Item Names and Aliases](#item-names-and-aliases)
|
||||
* [Textures](#textures)
|
||||
* [Registering a Craftitem](#registering-a-craftitem)
|
||||
* [Foods](#foods)
|
||||
* [Foods, extended](#foods-extended)
|
||||
* [Registering a Basic Node](#registering-a-basic-node)
|
||||
* [Actions and Callbacks](#actions-and-callbacks)
|
||||
* [on_use](#on_use)
|
||||
* [Crafting](#crafting)
|
||||
* [Shaped](#shaped)
|
||||
* [Shapeless](#shapeless)
|
||||
* [Cooking](#cooking)
|
||||
* [Fuel](#fuel)
|
||||
* [Groups](#groups)
|
||||
* [Dig Types](#dig-types)
|
||||
* [Tools, Capabilities and Dig Types](#tools-capabilities-and-dig-types)
|
||||
|
||||
## Item Strings
|
||||
## What are Nodes and Items?
|
||||
|
||||
A string in programming terms is a piece of text.
|
||||
Each in-game item, whether a node, craftitem, tool, or entity, has an item string.
|
||||
This is sometimes referred to as its registered name or just its name. It takes the format:
|
||||
Nodes, Craftitems, and Tools are all Items.
|
||||
An item is something that could be found in an inventory -
|
||||
even though it may not be possible through normal game play.
|
||||
|
||||
A node is an item which is placable or can be found in the world.
|
||||
Every position in the world must be occupied with one and only one node -
|
||||
seemingly blank positions are usually air nodes.
|
||||
|
||||
A craftitem isn't placable but is only found in inventories or as a dropped item
|
||||
in the world.
|
||||
|
||||
A tool has the ability to wear and typically has non-default digging capabilities.
|
||||
In the future, it's likely that craftitems and tools will merge into one type of
|
||||
item, as the distinction between them is rather artificial.
|
||||
|
||||
## Registering Items
|
||||
|
||||
Item definitions consist of an *item name* and a *definition table*.
|
||||
The definition table contains attributes which affect the behaviour of the item.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craftitem("modname:itemname", {
|
||||
description = "My Special Item",
|
||||
inventory_image = "modname_itemname.png"
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
### Item Names and Aliases
|
||||
|
||||
Every item has an item name used to refer to it, which should be in the
|
||||
following format:
|
||||
|
||||
modname:itemname
|
||||
|
||||
@ -39,117 +64,50 @@ The modname is the name of the mod in which the item is registered, and the
|
||||
item name is the name of the item itself.
|
||||
The item name should be relevant to what the item is and can't already be registered.
|
||||
|
||||
### Overriding
|
||||
Items can also have *aliases* pointing to their name.
|
||||
An *alias* is a pseudo-item name which results in the engine treating any
|
||||
occurrences of the alias as if it were the item name.
|
||||
There are two main common uses of this:
|
||||
|
||||
Overriding allows you to:
|
||||
* Renaming removed items to something else.
|
||||
There may be unknown nodes in the world and in inventories if an item is
|
||||
removed from a mod without any corrective code.
|
||||
It's important to avoid aliasing to an unobtainable node if the remove node
|
||||
could be obtained.
|
||||
* Adding a shortcut. `/giveme dirt` is easier than `/giveme default:dirt`.
|
||||
|
||||
* Redefine an existing item.
|
||||
* Use a different modname.
|
||||
Registering an alias is pretty simple.
|
||||
A good way to remember is `from → to` where *from* is the alias and *to*
|
||||
is the target.
|
||||
|
||||
To override an item, prefix the item string with a colon, ``:``.
|
||||
For example, declaring an item as ``:default:dirt`` will override
|
||||
default:dirt in the default mod.
|
||||
{% highlight lua %}
|
||||
minetest.register_alias("dirt", "default:dirt")
|
||||
{% endhighlight %}
|
||||
|
||||
## Textures
|
||||
Mods need to make sure to resolve aliases before dealing directly with item names,
|
||||
as the engine won't do this.
|
||||
This is pretty simple though:
|
||||
|
||||
{% highlight lua %}
|
||||
itemname = minetest.registered_aliases[itemname] or itemname
|
||||
{% endhighlight %}
|
||||
|
||||
### Textures
|
||||
|
||||
Textures should be placed in the textures/ folder with names in the format
|
||||
`modname_itemname.png`.\\
|
||||
JPEG textures are supported, but they do not support transparency and are generally
|
||||
bad quality at low resolutions.
|
||||
It is often better to use the PNG format.
|
||||
|
||||
Textures in Minetest are usually 16 by 16 pixels.
|
||||
They can be any resolution, but it is recommended that they are in the order of 2,
|
||||
for example 16, 32, 64, or 128,
|
||||
because other resolutions may not be supported correctly on older devices.
|
||||
|
||||
Textures should be placed in the textures/ folder with names in the format
|
||||
``modname_itemname.png``.\\
|
||||
JPEG textures are supported, but do not support transparency and are generally
|
||||
bad quality at low resolutions. It is often better to use the PNG format.
|
||||
|
||||
## Registering a Craftitem
|
||||
|
||||
Craftitems are the simplest items in Minetest. They cannot be placed in the world.
|
||||
They are used in recipes to create other items, or can be used by the player.
|
||||
Examples include food items which can be eaten and metal ingots which can be
|
||||
crafted into tools or placeable nodes.
|
||||
|
||||
Item definitions usually include a unique
|
||||
[item string](#item-strings) and a definition table. The definition table
|
||||
contains attributes which affect the behaviour of the item. For example:
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craftitem("mymod:diamond_fragments", {
|
||||
description = "Alien Diamond Fragments",
|
||||
inventory_image = "mymod_diamond_fragments.png"
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
### Foods
|
||||
|
||||
Foods are items which restore health. To create a food item you need to define
|
||||
the on_use property of the item:
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craftitem("mymod:mudpie", {
|
||||
description = "Alien Mud Pie",
|
||||
inventory_image = "myfood_mudpie.png",
|
||||
on_use = minetest.item_eat(20)
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
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.
|
||||
Hitpoints don't have to be integers (whole numbers); they can be decimals.
|
||||
|
||||
Sometimes you may want a food item to be replaced with another item after it is eaten,
|
||||
for example smaller pieces of cake or bones after eating meat. To do this, use:
|
||||
|
||||
minetest.item_eat(hp, replace_with_item)
|
||||
|
||||
In this example replace_with_item must be an item string.
|
||||
|
||||
### Foods, extended
|
||||
|
||||
How about if you want to do more than just eat the item,
|
||||
such as send a message to the player?
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craftitem("mymod:mudpie", {
|
||||
description = "Alien Mud Pie",
|
||||
inventory_image = "myfood_mudpie.png",
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local hp_change = 20
|
||||
local replace_with_item = nil
|
||||
|
||||
minetest.chat_send_player(user:get_player_name(), "You ate an alien mud pie!")
|
||||
|
||||
-- Support for hunger mods using minetest.register_on_item_eat
|
||||
for _ , callback in pairs(minetest.registered_on_item_eats) do
|
||||
local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
if result then
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
if itemstack:take_item() ~= nil then
|
||||
user:set_hp(user:get_hp() + hp_change)
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
If you are creating a hunger mod, or if you are affecting foods outside of your
|
||||
mod, you should consider using minetest.register_on_item_eat
|
||||
for example 16, 32, 64, or 128.
|
||||
This is because other resolutions may not be supported correctly on older devices,
|
||||
resulting in lowered performance.
|
||||
|
||||
## Registering a basic node
|
||||
|
||||
In Minetest, a node is an item that you can place.
|
||||
Most nodes are 1m x 1m x 1m cubes; however, the shape doesn't
|
||||
have to be a cube - as we will explore later.
|
||||
|
||||
Let's get onto it. A node's definition table is very similar to a craftitem's
|
||||
definition table; however, you need to set the textures for the faces of the cube.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_node("mymod:diamond", {
|
||||
description = "Alien Diamond",
|
||||
@ -159,29 +117,25 @@ minetest.register_node("mymod:diamond", {
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
Let's ignore ``groups`` for now, and take a look at the tiles.
|
||||
The ``tiles`` property is a table of texture names the node will use.
|
||||
The `tiles` property is a table of texture names the node will use.
|
||||
When there is only one texture, this texture is used on every side.
|
||||
To give a different texture per-side, supply the names of 6 textures in this order:
|
||||
|
||||
What if you would like a different texture for each side?
|
||||
Well, you give a table of 6 texture names, in this order:\\
|
||||
up (+Y), down (-Y), right (+X), left (-X), back (+Z), front (-Z).
|
||||
(+Y, -Y, +X, -X, +Z, -Z)
|
||||
|
||||
Remember: +Y is upwards in Minetest, along with most video games.
|
||||
A plus direction means that it is facing positive co-ordinates,
|
||||
a negative direction means that it is facing negative co-ordinates.
|
||||
Remember: Just like with most 3D graphics, +Y is upwards in Minetest.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_node("mymod:diamond", {
|
||||
description = "Alien Diamond",
|
||||
tiles = {
|
||||
"mymod_diamond_up.png",
|
||||
"mymod_diamond_down.png",
|
||||
"mymod_diamond_right.png",
|
||||
"mymod_diamond_left.png",
|
||||
"mymod_diamond_back.png",
|
||||
"mymod_diamond_front.png"
|
||||
"mymod_diamond_up.png", -- y+
|
||||
"mymod_diamond_down.png", -- y-
|
||||
"mymod_diamond_right.png", -- x+
|
||||
"mymod_diamond_left.png", -- x-
|
||||
"mymod_diamond_back.png", -- z+
|
||||
"mymod_diamond_front.png", -- z-
|
||||
},
|
||||
is_ground_content = true,
|
||||
groups = {cracky = 3},
|
||||
@ -191,29 +145,76 @@ minetest.register_node("mymod:diamond", {
|
||||
{% endhighlight %}
|
||||
|
||||
The is_ground_content attribute allows caves to be generated over the stone.
|
||||
This is essential for any node which may be placed during map generation underground.
|
||||
Caves are cut out of the world after all the other nodes in an area have generated.
|
||||
|
||||
## Actions and Callbacks
|
||||
|
||||
Minetest heavily uses a callback-based modding design.
|
||||
Callbacks can be placed in the item definition table to allow response to various
|
||||
different user events.
|
||||
|
||||
### on_use
|
||||
|
||||
By default, the use callback is triggered when a player left-clicks with an item.
|
||||
Having a use callback prevents the item being used to dig nodes.
|
||||
One common use of the use callback is for food:
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craftitem("mymod:mudpie", {
|
||||
description = "Alien Mud Pie",
|
||||
inventory_image = "myfood_mudpie.png",
|
||||
on_use = minetest.item_eat(20),
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
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.
|
||||
Hitpoints don't have to be integers (whole numbers); they can be decimals.
|
||||
|
||||
minetest.item_eat() is a function which returns a function, setting it
|
||||
as the on_use callback.
|
||||
This means the code above is roughly similar to this:
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craftitem("mymod:mudpie", {
|
||||
description = "Alien Mud Pie",
|
||||
inventory_image = "myfood_mudpie.png",
|
||||
on_use = function(...)
|
||||
return minetest.do_item_eat(20, nil, ...)
|
||||
end,
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
By, understanding how item_eat works by simply returning a function, it's
|
||||
possible to modify it to do more complex behaviour such as play a custom sound.
|
||||
|
||||
## Crafting
|
||||
|
||||
There are several types of crafting, identified by the ``type`` property.
|
||||
There are several types of crafting recipe available, indicated by the `type`
|
||||
property.
|
||||
|
||||
* shaped - Ingredients must be in the correct position.
|
||||
* shapeless - It doesn't matter where the ingredients are,
|
||||
just that there is the right amount.
|
||||
* cooking - Recipes for the furnace to use.
|
||||
* fuel - Defines items which can be burned in furnaces.
|
||||
* tool_repair - Used to allow the repairing of tools.
|
||||
* tool_repair - Defines items which can be tool repaired.
|
||||
|
||||
Craft recipes do not use Item Strings to uniquely identify themselves.
|
||||
Craft recipes are not items, so they do not use Item Names to uniquely
|
||||
identify themselves.
|
||||
|
||||
### Shaped
|
||||
|
||||
Shaped recipes are the normal recipes - the ingredients have to be in the
|
||||
right place.
|
||||
For example, when you are making a pickaxe the ingredients have to be in the
|
||||
right place for it to work.
|
||||
Shaped recipes are when the ingredients need to be in the right shape or
|
||||
pattern to work. In the below example, the fragments need to be in a
|
||||
chair-like pattern for the craft to work.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craft({
|
||||
type = "shaped",
|
||||
output = "mymod:diamond_chair 99",
|
||||
recipe = {
|
||||
{"mymod:diamond_fragments", "", ""},
|
||||
@ -223,21 +224,15 @@ minetest.register_craft({
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
This is pretty self-explanatory. You don't need to define the type, as
|
||||
shaped crafts are default. The 99 after the itemname in output makes the
|
||||
craft create 99 chairs rather than one.
|
||||
|
||||
If you notice, there is a blank column at the far end.
|
||||
This means that the craft must always be exactly that.
|
||||
In most cases, such as the door recipe, you don't care if the ingredients
|
||||
are always in an exact place, you just want them correct relative to each
|
||||
other. In order to do this, delete any empty rows and columns.
|
||||
In the above case, there is an empty last column, which, when removed,
|
||||
allows the recipe to be crafted if it was all moved one place to the right.
|
||||
One thing to note is the blank column on the right-hand side.
|
||||
This means that there *must* be an empty column to the right of the shape, otherwise
|
||||
this won't work.
|
||||
If this empty column shouldn't be required, then the empty strings can be left
|
||||
out like so:
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craft({
|
||||
output = "mymod:diamond_chair",
|
||||
output = "mymod:diamond_chair 99",
|
||||
recipe = {
|
||||
{"mymod:diamond_fragments", "" },
|
||||
{"mymod:diamond_fragments", "mymod:diamond_fragments"},
|
||||
@ -246,31 +241,26 @@ minetest.register_craft({
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
The type field isn't actually needed for shapeless crafts, as shapeless is the
|
||||
default craft type.
|
||||
|
||||
### Shapeless
|
||||
|
||||
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.
|
||||
For example, when you craft a bronze ingot, the steel and the copper do not
|
||||
need to be in any specific place for it to work.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mymod:diamond",
|
||||
output = "mymod:diamond 3",
|
||||
recipe = {"mymod:diamond_fragments", "mymod:diamond_fragments", "mymod:diamond_fragments"}
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
When you are crafting the diamond, the three diamond fragments can be anywhere
|
||||
in the grid.\\
|
||||
Note: You can still use options like the number after the result, as mentioned
|
||||
earlier.
|
||||
|
||||
### Cooking
|
||||
### Cooking and Fuel
|
||||
|
||||
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.
|
||||
For example, you use a cooking recipe to turn ores into bars.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_craft({
|
||||
@ -281,17 +271,16 @@ minetest.register_craft({
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
As you can see from this example, the only real difference in the code
|
||||
is that the recipe is just a single item, compared to being in a table
|
||||
(between braces). They also have an optional "cooktime" parameter which
|
||||
defines how long the item takes to cook. If this is not set, it defaults to 3.
|
||||
The only real difference in the code is that the recipe is just a single item,
|
||||
compared to being in a table (between braces).
|
||||
They also have an optional "cooktime" parameter which
|
||||
defines how long the item takes to cook.
|
||||
If this is not set, it defaults to 3.
|
||||
|
||||
The recipe above works when the coal block is in the input slot,
|
||||
with some form of a fuel below it.
|
||||
It creates diamond fragments after 10 seconds!
|
||||
|
||||
#### Fuel
|
||||
|
||||
This type is an accompaniment to the cooking type, as it defines
|
||||
what can be burned in furnaces and other cooking tools from mods.
|
||||
|
||||
@ -310,30 +299,73 @@ So, the diamond is good as fuel for 300 seconds!
|
||||
## Groups
|
||||
|
||||
Items can be members of many groups and groups can have many members.
|
||||
Groups are usually identified using `group:group_name`
|
||||
There are several reasons you use groups.
|
||||
|
||||
Groups can be used in crafting recipes to allow interchangeability
|
||||
of ingredients. For example, you may use group:wood to allow any wood
|
||||
item to be used in the recipe.
|
||||
|
||||
### Dig types
|
||||
|
||||
Let's look at our above ``mymod:diamond`` definition. You'll notice this line:
|
||||
Groups are defined using the `groups` property in the definition table,
|
||||
and have an associated value.
|
||||
|
||||
{% highlight lua %}
|
||||
groups = {cracky = 3}
|
||||
groups = {cracky = 3, wood = 1}
|
||||
{% endhighlight %}
|
||||
|
||||
Cracky is a digtype. Dig types specify what type of the material the node is
|
||||
physically, and what tools are best to destroy it.
|
||||
There are several reasons you use groups.
|
||||
Firstly, groups are used to describe properties such as dig types and flammability.
|
||||
Secondly, groups can be used in a craft recipe instead of an item name to allow
|
||||
any item in group to be used.
|
||||
|
||||
| Group | Description |
|
||||
|-------------------------|----------------------------------------------------------------------------------------------|
|
||||
| crumbly | dirt, sand |
|
||||
| cracky | tough but crackable stuff like stone. |
|
||||
| snappy | something that can be cut using fine tools; e.g. leaves, smallplants, wire, sheets of metal |
|
||||
| choppy | something that can be cut using force; e.g. trees, wooden planks |
|
||||
| fleshy | Living things like animals and the player. This could imply some blood effects when hitting. |
|
||||
| explody | Especially prone to explosions |
|
||||
| oddly_breakable_by_hand | Torches, etc, quick to dig |
|
||||
{% highlight lua %}
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mymod:diamond_thing 3",
|
||||
recipe = {"group:wood", "mymod:diamond"}
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
## Tools, Capabilities and Dig Types
|
||||
|
||||
Dig types are groups which are used to define how strong a node is when dug
|
||||
with different tools.
|
||||
A dig type group having a higher associated value means the node is easier
|
||||
and quicker to cut.
|
||||
It's possible to combine multiple dig types to allow the more efficient use
|
||||
of multiple types of tools.
|
||||
A node with no dig types cannot be dug by any tools.
|
||||
|
||||
|
||||
| Group | Best Tool | Description |
|
||||
|--------|-----------|-------------|
|
||||
| crumbly | spade | Dirt, sand |
|
||||
| cracky | pickaxe | Tough (but brittle) stuff like stone |
|
||||
| snappy | *any* | Can be cut using fine tools;<br>e.g. leaves, smallplants, wire, sheets of metal |
|
||||
| choppy | axe | Can be cut using a sharp force; e.g. trees, wooden planks |
|
||||
| fleshy | sword | Living things like animals and the player.<br>This could imply some blood effects when hitting. |
|
||||
| explody | ? | Especially prone to explosions |
|
||||
| oddly_breakable_by_hand | *any* | Torches and such - very quick to dig |
|
||||
|
||||
|
||||
Every tool has a tool capability.
|
||||
A capability includes a list of supported dig types, and associated properties
|
||||
for each type such as dig times and the amount of wear.
|
||||
Tools can also have a maximum supported hardness for each type, which makes
|
||||
it possible to prevent weaker tools from digging harder nodes.
|
||||
It's very common for tools to include all dig types in their capabilities,
|
||||
with the less suitable ones having very inefficient properties.
|
||||
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.
|
||||
|
||||
{% highlight lua %}
|
||||
minetest.register_tool("mymod:tool", {
|
||||
description = "My Tool",
|
||||
inventory_image = "mymod_tool.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},
|
||||
},
|
||||
})
|
||||
{% endhighlight %}
|
||||
|
||||
Groupcaps is the list of supported dig types for digging nodes.
|
||||
Damage groups are for controlling how tools damage objects, which will be
|
||||
discussed later in the objects chapter.
|
||||
|
Loading…
Reference in New Issue
Block a user