Compare commits
5 Commits
e8e16eee5d
...
12e6cb7291
Author | SHA1 | Date |
---|---|---|
rubenwardy | 12e6cb7291 | |
rubenwardy | 8892bcd8e9 | |
rubenwardy | 3e265da9b5 | |
rubenwardy | c258e67f35 | |
rubenwardy | 6622a49cd1 |
|
@ -4,6 +4,7 @@
|
|||
],
|
||||
"spellright.documentTypes": [
|
||||
"latex",
|
||||
"plaintext"
|
||||
"plaintext",
|
||||
"markdown"
|
||||
]
|
||||
}
|
16
README.md
16
README.md
|
@ -3,7 +3,7 @@
|
|||
[![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/)
|
||||
|
||||
Book written by rubenwardy.
|
||||
Book written by rubenwardy.
|
||||
License: CC-BY-SA 3.0
|
||||
|
||||
## Finding your way around
|
||||
|
@ -23,15 +23,25 @@ License: CC-BY-SA 3.0
|
|||
I'm happy to fix the formatting of any chapters. It is
|
||||
the writing which is the hard bit, not the formatting.
|
||||
|
||||
### Chapter Guidelines
|
||||
### Chapter and Writing Guide
|
||||
|
||||
Grammar and such:
|
||||
|
||||
* British English, except when referring common code words like `color` and
|
||||
`initialize`.
|
||||
* Prefer pronounless text, but `you` if you must. Never `we` nor `I`.
|
||||
* Titles and subheadings should be in Title Case.
|
||||
* References to code (such as function names) should be formatted as \`inline-code`.
|
||||
* Italics used for emphasis, not necessarily for technical words.
|
||||
* Full stops and correct punctionation, except for lists without full sentences.
|
||||
|
||||
Formatting:
|
||||
|
||||
* Do not rely on anything that isn't printable to a physical book.
|
||||
* Any links must be invisible - ie: if they're removed, then the chapter must
|
||||
still make sense.
|
||||
* Table of contents for each chapter with anchor links.
|
||||
* Add `your turn`s to the end of a chapter when relevant.
|
||||
* Titles and subheadings should be in Title Case.
|
||||
|
||||
### Making a Chapter
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ basic requirements for many mods.
|
|||
|
||||
- [What are Nodes and Items?](#what-are-nodes-and-items)
|
||||
- [Registering Items](#registering-items)
|
||||
- [Item Names and Aliases](#item-names-and-aliases)
|
||||
- [Item Names](#item-names)
|
||||
- [Item Aliases](#item-aliases)
|
||||
- [Textures](#textures)
|
||||
- [Registering a basic node](#registering-a-basic-node)
|
||||
- [Actions and Callbacks](#actions-and-callbacks)
|
||||
|
@ -55,7 +56,7 @@ minetest.register_craftitem("modname:itemname", {
|
|||
})
|
||||
```
|
||||
|
||||
### Item Names and Aliases
|
||||
### Item Names
|
||||
|
||||
Every item has an item name used to refer to it, which should be in the
|
||||
following format:
|
||||
|
@ -66,6 +67,8 @@ 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.
|
||||
|
||||
### Item Aliases
|
||||
|
||||
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.
|
||||
|
@ -74,8 +77,6 @@ There are two main common uses of this:
|
|||
* 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`.
|
||||
|
||||
Registering an alias is pretty simple.
|
||||
|
|
|
@ -13,11 +13,11 @@ In this chapter, you will learn how to perform basic actions on the map.
|
|||
|
||||
- [Map Structure](#map-structure)
|
||||
- [Reading](#reading)
|
||||
- [Reading Nodes](#reading-nodes)
|
||||
- [Finding Nodes](#finding-nodes)
|
||||
- [Reading Nodes](#reading-nodes)
|
||||
- [Finding Nodes](#finding-nodes)
|
||||
- [Writing](#writing)
|
||||
- [Writing Nodes](#writing-nodes)
|
||||
- [Removing Nodes](#removing-nodes)
|
||||
- [Writing Nodes](#writing-nodes)
|
||||
- [Removing Nodes](#removing-nodes)
|
||||
- [Loading Blocks](#loading-blocks)
|
||||
- [Deleting Blocks](#deleting-blocks)
|
||||
|
||||
|
@ -74,6 +74,10 @@ For example, say we wanted to make a certain type of plant that grows
|
|||
better near mese; you would need to search for any nearby mese nodes,
|
||||
and adapt the growth rate accordingly.
|
||||
|
||||
`minetest.find_node_near` will return the first found node in a certain radius
|
||||
which matches the node names or groups given. In the following example,
|
||||
we look for a mese node within 5 nodes of the position:
|
||||
|
||||
```lua
|
||||
local grow_speed = 1
|
||||
local node_pos = minetest.find_node_near(pos, 5, { "default:mese" })
|
||||
|
@ -84,7 +88,7 @@ end
|
|||
```
|
||||
|
||||
Let's say, for example, that the growth rate increases the more mese there is
|
||||
nearby. You should then use a function which can find multiple nodes in area:
|
||||
nearby. You should then use a function that can find multiple nodes in the area:
|
||||
|
||||
```lua
|
||||
local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 })
|
||||
|
@ -94,9 +98,9 @@ local pos_list =
|
|||
local grow_speed = 1 + #pos_list
|
||||
```
|
||||
|
||||
The above code doesn't quite do what we want, as it checks based on area, whereas
|
||||
`find_node_near` checks based on range. In order to fix this, we will,
|
||||
unfortunately, need to manually check the range ourselves.
|
||||
The above code finds the number of nodes in a *cuboid volume*. This is different
|
||||
to `find_node_near`, which uses the distance to the position (ie: a *sphere*). In
|
||||
order to fix this, we will need to manually check the range ourselves:
|
||||
|
||||
```lua
|
||||
local pos1 = vector.subtract(pos, { x = 5, y = 5, z = 5 })
|
||||
|
@ -112,11 +116,11 @@ for i=1, #pos_list do
|
|||
end
|
||||
```
|
||||
|
||||
Now your code will correctly increase `grow_speed` based on mese nodes in range.
|
||||
Now the code will correctly increase `grow_speed` based on mese nodes in range.
|
||||
|
||||
Note how we compared the squared distance from the position, rather than square
|
||||
rooting it to obtain the actual distance. This is because computers find square
|
||||
roots computationally expensive, so you should avoid them as much as possible.
|
||||
roots computationally expensive, so they should avoided as much as possible.
|
||||
|
||||
There are more variations of the above two functions, such as
|
||||
`find_nodes_with_meta` and `find_nodes_in_area_under_air`, which work similarly
|
||||
|
@ -127,8 +131,8 @@ and are useful in other circumstances.
|
|||
### Writing Nodes
|
||||
|
||||
You can use `set_node` to write to the map. Each call to set_node will cause
|
||||
lighting to be recalculated, which means that set_node is fairly slow for large
|
||||
numbers of nodes.
|
||||
lighting to be recalculated and node callbacks to run, which means that set_node
|
||||
is fairly slow for large numbers of nodes.
|
||||
|
||||
```lua
|
||||
minetest.set_node({ x = 1, y = 3, z = 4 }, { name = "default:mese" })
|
||||
|
@ -140,7 +144,7 @@ print(node.name) --> default:mese
|
|||
set_node will remove any associated metadata or inventory from that position.
|
||||
This isn't desirable in all circumstances, especially if you're using multiple
|
||||
node definitions to represent one conceptual node. An example of this is the
|
||||
furnace node - whilst you think conceptually of it as one node, it's actually
|
||||
furnace node - whilst you conceptually think of it as one node, it's actually
|
||||
two.
|
||||
|
||||
You can set a node without deleting metadata or the inventory like so:
|
||||
|
@ -160,7 +164,7 @@ minetest.remove_node(pos)
|
|||
minetest.set_node(pos, { name = "air" })
|
||||
```
|
||||
|
||||
In fact, remove_node will call set_node with the name being air.
|
||||
In fact, remove_node is just a helper function that calls set_node with `"air"`.
|
||||
|
||||
## Loading Blocks
|
||||
|
||||
|
@ -205,7 +209,8 @@ local function emerge_callback(pos, action,
|
|||
end
|
||||
```
|
||||
|
||||
This is not the only way of loading blocks; using an LVM will also cause the
|
||||
This is not the only way of loading blocks; using an
|
||||
[Lua Voxel Manipulator (LVM)](../advmap/lvm.html) will also cause the
|
||||
encompassed blocks to be loaded synchronously.
|
||||
|
||||
## Deleting Blocks
|
||||
|
|
|
@ -20,12 +20,13 @@ own.
|
|||
- [Position and Velocity](#position-and-velocity)
|
||||
- [Object Properties](#object-properties)
|
||||
- [Entities](#entities)
|
||||
- [Health and Damage](#health-and-damage)
|
||||
- [Attachments](#attachments)
|
||||
- [Your Turn](#your-turn)
|
||||
|
||||
## What are Objects, Players, and Entities?
|
||||
|
||||
Players and Entities are both types of Objects. An Object is something that can move
|
||||
Players and Entities are both types of Objects. An object is something that can move
|
||||
independently of the node grid and has properties such as velocity and scale.
|
||||
Objects aren't items, and they have their own separate registration system.
|
||||
|
||||
|
@ -235,6 +236,89 @@ use a [chat command](../players/chat.html) to spawn entities:
|
|||
|
||||
/spawnentity mymod:entity
|
||||
|
||||
|
||||
## Health and Damage
|
||||
|
||||
### HitPoints (HP)
|
||||
|
||||
Each object has a HitPoints (HP) number, which represents the current health.
|
||||
Players have a maximum hp set using the `hp_max` object property.
|
||||
An object will die if its hp reaches 0.
|
||||
|
||||
```lua
|
||||
local hp = object:get_hp()
|
||||
object:set_hp(hp + 3)
|
||||
```
|
||||
|
||||
### Punch, Damage Groups, and Armor Groups
|
||||
|
||||
Damage is the reduction of an object's HP. An object can *punch* another object to
|
||||
inflict damage. A punch isn't necessarily an actual punch - it can be an
|
||||
explosion, a sword slash, or something else.
|
||||
|
||||
The total damage is calculated by multiplying the punch's damage groups with the
|
||||
target's vulnerabilities. This is then limited depending on how recent the last
|
||||
punch was. We will go over an example of this calculation in a bit.
|
||||
|
||||
Just like [node dig groups](../items/nodes_items_crafting.html#tools-capabilities-and-dig-types),
|
||||
these groups can take any name and do not need to be registered. However, it's
|
||||
common to use the same group names as with node digging.
|
||||
|
||||
How vulnerable an object is to particular types of damage depends on its
|
||||
`armor_groups` [object property](#object-properties). Despite its misleading
|
||||
name, `armor_groups` specify the percentage damage taken from particular damage
|
||||
groups, not the resistance. If a damage group is not listed in an object's armor
|
||||
groups, that object is completely invulnerable to it.
|
||||
|
||||
```lua
|
||||
target:set_properties({
|
||||
armor_groups = { fleshy = 90, crumbly = 50 },
|
||||
})
|
||||
```
|
||||
|
||||
In the above example, the object will take 90% of `fleshy` damage and 50% of
|
||||
`crumbly` damage.
|
||||
|
||||
When a player punches an object, the damage groups come from the item they are
|
||||
currently wielding. In other cases, mods decide which damage groups are used.
|
||||
|
||||
### Example Damage Calculation
|
||||
|
||||
Let's punch the `target` object:
|
||||
|
||||
```lua
|
||||
local tool_capabilities = {
|
||||
full_punch_interval = 0.8,
|
||||
damage_groups = { fleshy = 5, choppy = 10 },
|
||||
|
||||
-- This is only used for digging nodes, but is still required
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=2.5, [2]=1.20, [3]=0.35}, uses=30, maxlevel=2},
|
||||
},
|
||||
}
|
||||
|
||||
local time_since_last_punch = tool_capabilities.full_punch_interval
|
||||
target:punch(object, time_since_last_punch, tool_capabilities)
|
||||
```
|
||||
|
||||
Now, let's work out what the damage will be. The punch's damage groups are
|
||||
`fleshy=5` and `choppy=10`, and `target` will take 90% damage from fleshy and 0%
|
||||
from choppy.
|
||||
|
||||
First, we multiply the damage groups by the vulnerability and sum the result.
|
||||
We then multiply by a number between 0 or 1 depending on the `time_since_last_punch`.
|
||||
|
||||
```lua
|
||||
= (5*90/100 + 10*0/100) * limit(time_since_last_punch / full_punch_interval, 0, 1)
|
||||
= (5*90/100 + 10*0/100) * 1
|
||||
= 4.5
|
||||
```
|
||||
|
||||
As HP is an integer, the damage is rounded to 5 points.
|
||||
|
||||
|
||||
|
||||
## Attachments
|
||||
|
||||
Attached objects will move when the parent - the object they are attached to -
|
||||
|
@ -245,7 +329,7 @@ An object can have an unlimited number of children, but at most one parent.
|
|||
child:set_attach(parent, bone, position, rotation)
|
||||
```
|
||||
|
||||
An Object's `get_pos()` will always return the global position of the object, no
|
||||
An object's `get_pos()` will always return the global position of the object, no
|
||||
matter whether it is attached or not.
|
||||
`set_attach` takes a relative position, but not as you'd expect.
|
||||
The attachment position is relative to the parent's origin as scaled up by 10 times.
|
||||
|
|
|
@ -63,7 +63,7 @@ layout: base
|
|||
</ul>
|
||||
|
||||
<footer>
|
||||
© 2014-20
|
||||
© 2014-21
|
||||
{% if language == "en" %}
|
||||
| Helpful? Consider
|
||||
<a href="https://rubenwardy.com/donate/">donating</a>
|
||||
|
|
Loading…
Reference in New Issue