Formspecs: Rewrite chapter
This commit is contained in:
parent
4feb9b4251
commit
6e09b6f447
@ -7,12 +7,12 @@ root: ..
|
|||||||
idx: 0.1
|
idx: 0.1
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id="header">
|
<header>
|
||||||
<h1>Minetest Modding Book</h1>
|
<h1>Minetest Modding Book</h1>
|
||||||
|
|
||||||
<span>by <a href="https://rubenwardy.com" rel="author">rubenwardy</a></span>
|
<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>
|
<span>with editing by <a href="http://rc.minetest.tv/">Shara</a></span>
|
||||||
</div>
|
</header>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
|
@ -4,6 +4,13 @@ layout: default
|
|||||||
root: ../..
|
root: ../..
|
||||||
idx: 4.5
|
idx: 4.5
|
||||||
redirect_from: /en/chapters/formspecs.html
|
redirect_from: /en/chapters/formspecs.html
|
||||||
|
minetest510:
|
||||||
|
level: warning
|
||||||
|
title: Real coordinates will be in 5.1.0
|
||||||
|
classes: web-only
|
||||||
|
message: This chapter describes the use of a feature that hasn't been released yet.
|
||||||
|
You can still use this chapter and the code in Minetest 5.0, but elements will
|
||||||
|
be positioned differently to what is shown.
|
||||||
submit_vuln:
|
submit_vuln:
|
||||||
level: warning
|
level: warning
|
||||||
title: Malicious clients can submit anything at anytime
|
title: Malicious clients can submit anything at anytime
|
||||||
@ -24,250 +31,302 @@ submit_vuln:
|
|||||||
|
|
||||||
In this chapter we will learn how to create a formspec and display it to the user.
|
In this chapter we will learn how to create a formspec and display it to the user.
|
||||||
A formspec is the specification code for a form.
|
A formspec is the specification code for a form.
|
||||||
In Minetest, forms are windows such as the player inventory, which can contain labels,
|
In Minetest, forms are windows such as the player inventory and can contain a
|
||||||
buttons and fields to allow you to enter information.
|
variety of elements, such as labels, buttons and fields.
|
||||||
|
|
||||||
- [Formspec Syntax](#formspec-syntax)
|
|
||||||
- [Size[w, h]](#sizew-h)
|
|
||||||
- [Field[x, y; w, h; name; label; default]](#fieldx-y-w-h-name-label-default)
|
|
||||||
- [Other Elements](#other-elements)
|
|
||||||
- [Displaying Formspecs](#displaying-formspecs)
|
|
||||||
- [Example](#example)
|
|
||||||
- [Callbacks](#callbacks)
|
|
||||||
- [Fields](#fields)
|
|
||||||
- [Contexts](#contexts)
|
|
||||||
- [Node Meta Formspecs](#node-meta-formspecs)
|
|
||||||
|
|
||||||
Note that if you do not need to get user input, for example when you only need
|
Note that if you do not need to get user input, for example when you only need
|
||||||
to provide information to the player, you should consider using Heads Up Display
|
to provide information to the player, you should consider using
|
||||||
(HUD) elements instead of forms, because unexpected windows tend to disrupt gameplay.
|
[Heads Up Display (HUD)](hud.html) elements instead of forms, because
|
||||||
|
unexpected windows tend to disrupt gameplay.
|
||||||
|
|
||||||
## Formspec Syntax
|
- [Real or Legacy Coordinates](#real-or-legacy-coordinates)
|
||||||
|
- [Anatomy of a Formspec](#anatomy-of-a-formspec)
|
||||||
|
- [Elements](#elements)
|
||||||
|
- [Header](#header)
|
||||||
|
- [Guessing Game](#guessing-game)
|
||||||
|
- [Padding and Spacing](#padding-and-spacing)
|
||||||
|
- [Receiving Formspec Submissions](#receiving-formspec-submissions)
|
||||||
|
- [Contexts](#contexts)
|
||||||
|
- [Formspec Sources](#formspec-sources)
|
||||||
|
- [Node Meta Formspecs](#node-meta-formspecs)
|
||||||
|
- [Player Inventory Formspecs](#player-inventory-formspecs)
|
||||||
|
- [Your Turn](#your-turn)
|
||||||
|
|
||||||
Formspecs have an unusual syntax.
|
|
||||||
They consist of a series of tags which are in the following form:
|
|
||||||
|
|
||||||
element_type[param1;param2;...]
|
## Real or Legacy Coordinates
|
||||||
|
|
||||||
Firstly the element type is declared, and then the attributes are given
|
In older versions of Minetest, formspecs were inconsistent. The way that different
|
||||||
in square brackets.
|
elements were positioned varied in unexpected ways; it was hard to predict the
|
||||||
|
placement of elements and align them. Minetest 5.1.0 contains a feature
|
||||||
|
called real coordinates which aims to rectify this by introducing a consistent
|
||||||
|
coordinate system. The use of real coordinates is highly recommended, and so
|
||||||
|
this chapter will use them exclusively.
|
||||||
|
|
||||||
Elements are items such as text boxes or buttons, or can be metadata such
|
{% include notice.html notice=page.minetest510 %}
|
||||||
as size or background.
|
|
||||||
|
|
||||||
Here are two elements, of types foo and bar.
|
|
||||||
|
## Anatomy of a Formspec
|
||||||
|
|
||||||
|
### Elements
|
||||||
|
|
||||||
|
Formspec is a domain-specific language with an unusual format.
|
||||||
|
It consists of a number of elements with the following form:
|
||||||
|
|
||||||
|
type[param1;param2]
|
||||||
|
|
||||||
|
The element type is declared and then any parameters are given
|
||||||
|
in square brackets. Multiple elements can be joined together, or placed
|
||||||
|
on multiple lines, like so:
|
||||||
|
|
||||||
foo[param1]bar[param1]
|
foo[param1]bar[param1]
|
||||||
|
bo[param1]
|
||||||
|
|
||||||
### Size[w, h]
|
|
||||||
|
|
||||||
Nearly all forms have a size tag. This declares the size of the form window. Note that
|
Elements are items such as text boxes or buttons, or can be metadata such
|
||||||
**forms don't use pixels as co-ordinates; they use a grid based on inventories**.
|
as size or background. You should refer to
|
||||||
A size of (1, 1) means the form is big enough to host a 1x1 inventory.
|
[lua_api.txt](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt#L1019)
|
||||||
This means the size of the form is independent of screen resolution and it should work
|
for a list of all possible elements. Search for "Formspec" to locate the correct
|
||||||
just as well on large screens as small screens.
|
part of the document.
|
||||||
You can use decimals in sizes and co-ordinates.
|
|
||||||
|
|
||||||
size[5,2]
|
|
||||||
|
|
||||||
Co-ordinates and sizes only use one attribute.
|
### Header
|
||||||
The x and y values are separated by a comma, as you can see above.
|
|
||||||
|
|
||||||
### Field[x, y; w, h; name; label; default]
|
The header of a formspec contains information which must appear first. This
|
||||||
|
includes the size of the formspec, the position, the anchor, and whether the
|
||||||
|
game-wide theme should be applied.
|
||||||
|
|
||||||
This is a textbox element. Most other elements have a similar style of attributes.
|
The elements in the header must be defined in a specific order, otherwise you
|
||||||
The name attribute is used in callbacks to get the submitted information.
|
will see an error. This order is given in the above paragraph, and, as always,
|
||||||
The x and y attributes determine the position of the element, and
|
documented in [lua_api.txt](../../lua_api.html#sizewhfixed_size)
|
||||||
the w and h attributes provide the size.
|
|
||||||
|
|
||||||
field[1,1;3,1;firstname;Firstname;]
|
The size is in formspec slots - a unit of measurement which is roughly
|
||||||
|
around 64 pixels, but varies based on the screen density and scaling
|
||||||
|
settings of the client. Here's a formspec which is `2,2` in size:
|
||||||
|
|
||||||
It is perfectly valid to not define an attribute.
|
size[2,2]
|
||||||
|
real_coordinates[true]
|
||||||
|
|
||||||
### Other Elements
|
Notice how we explicitly need to enable the use of the real coordinate system.
|
||||||
|
Without this, the legacy system will instead be used to size the formspec, which will
|
||||||
|
result in a larger size. This element is a special case, as it is the only element
|
||||||
|
which may appear both in the header and the body of a formspec. When in the header,
|
||||||
|
it must appear immediately after the size.
|
||||||
|
|
||||||
You should refer to [lua_api.txt](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt#L1019)
|
The position and anchor elements are used to place the formspec on the screen.
|
||||||
for a list of all possible elements. Search for "Formspec" to locate the correct part of the document.
|
The position sets where on the screen the formspec will be, and defaults to
|
||||||
At the time of writing, formspec information begins on line 1765.
|
the center (`0.5,0.5`). The anchor sets where on the formspec the position is,
|
||||||
|
allowing you to line the formspec up with the edge of the screen. The formspec
|
||||||
|
can be placed to the left of the screen like so:
|
||||||
|
|
||||||
## Displaying Formspecs
|
size[2,2]
|
||||||
|
real_coordinates[true]
|
||||||
|
position[0,0.5]
|
||||||
|
anchor[0,0.5]
|
||||||
|
|
||||||
Here is a generalised way to show a formspec:
|
This sets the anchor to the left middle edge of the formspec box, and then the
|
||||||
|
position of that anchor to the left of the screen.
|
||||||
|
|
||||||
minetest.show_formspec(playername, formname, formspec)
|
|
||||||
|
|
||||||
Formnames should be itemnames; however, this is not enforced.
|
## Guessing Game
|
||||||
There is no need to override a formspec, because formspecs are not registered like
|
|
||||||
nodes and items are. The formspec code is sent to the player's client for them
|
|
||||||
to see, along with the formname.
|
|
||||||
Formnames are used in callbacks to identify which form has been submitted,
|
|
||||||
and to see if the callback is relevant.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
This example shows a formspec to a player when they use the /formspec command.
|
|
||||||
|
|
||||||
<figure class="right_image">
|
<figure class="right_image">
|
||||||
<img src="{{ page.root }}//static/formspec_name.png" alt="Name Formspec">
|
<img src="{{ page.root }}/static/formspec_guessing.png" alt="Guessing Formspec">
|
||||||
<figcaption>
|
<figcaption>
|
||||||
The formspec generated by<br />
|
The guessing game formspec.
|
||||||
the example's code
|
|
||||||
</figcaption>
|
</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
The best way to learn is to make something, so let's make a guessing game.
|
||||||
|
The principle is simple: the mod decides on a number, then the player makes
|
||||||
|
guesses on the number. The mod then says if the guess is higher or lower then
|
||||||
|
the actual number.
|
||||||
|
|
||||||
|
First, let's make a function to create the formspec code. It's good practice to
|
||||||
|
do this, as it makes it easier to reuse elsewhere.
|
||||||
|
|
||||||
|
<div style="clear: both;"></div>
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Show form when the /formspec command is used.
|
guessing = {}
|
||||||
minetest.register_chatcommand("formspec", {
|
|
||||||
func = function(name, param)
|
function guessing.get_formspec(name)
|
||||||
minetest.show_formspec(name, "mymod:form",
|
-- TODO: display whether the last guess was higher or lower
|
||||||
"size[4,3]" ..
|
local text = "I'm thinking of a number... Make a guess!"
|
||||||
"label[0,0;Hello, " .. name .. "]" ..
|
|
||||||
"field[1,1.5;3,1;name;Name;]" ..
|
local formspec = {
|
||||||
"button_exit[1,2;2,1;exit;Save]")
|
"size[6,3.476]",
|
||||||
end
|
"real_coordinates[true]",
|
||||||
|
"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]"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- table.concat is faster than string concatenation - `..`
|
||||||
|
return table.concat(formspec, "")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above code, we place a field, a label, and a button. A field allows text
|
||||||
|
entry, and a button is used to submit the form. You'll notice that the elements
|
||||||
|
are positioned carefully in order to add padding and spacing, this will be explained
|
||||||
|
later.
|
||||||
|
|
||||||
|
Next, we want to allow the player to show the formspec. The main way to do this
|
||||||
|
is using `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,
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: the .. is used to join two strings together. The following two lines are equivalent:
|
The show_formspec function accepts a player name, the formspec name, and the
|
||||||
|
formspec itself. The formspec name should be a valid itemname, ie: in the format
|
||||||
|
`modname:itemname`.
|
||||||
|
|
||||||
|
|
||||||
|
### Padding and Spacing
|
||||||
|
|
||||||
|
<figure class="right_image">
|
||||||
|
<img src="{{ page.root }}/static/formspec_padding_spacing.png" alt="Padding and spacing">
|
||||||
|
<figcaption>
|
||||||
|
The guessing game formspec.
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Padding is the gap between the edge of the formspec and its contents, or between unrelated
|
||||||
|
elements, shown in red. Spacing is the gap between related elements, shown in blue.
|
||||||
|
|
||||||
|
It is fairly standard to have a padding of `0.375` and a spacing of `0.25`.
|
||||||
|
|
||||||
|
<div style="clear: both;"></div>
|
||||||
|
|
||||||
|
|
||||||
|
### Receiving Formspec Submissions
|
||||||
|
|
||||||
|
When `show_formspec` is called, the formspec is sent to the client to be displayed.
|
||||||
|
For formspecs to be useful, information needs to be returned from the client to server.
|
||||||
|
The method for this is called formspec field submission, and for `show_formspec`, that
|
||||||
|
submission is received using a global callback:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
"foobar"
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
"foo" .. "bar"
|
if formname ~= "guessing:game" then
|
||||||
```
|
return
|
||||||
|
|
||||||
## Callbacks
|
|
||||||
|
|
||||||
It's possible to expand the previous example with a callback:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- Show form when the /formspec command is used.
|
|
||||||
minetest.register_chatcommand("formspec", {
|
|
||||||
func = function(name, param)
|
|
||||||
minetest.show_formspec(name, "mymod:form",
|
|
||||||
"size[4,3]" ..
|
|
||||||
"label[0,0;Hello, " .. name .. "]" ..
|
|
||||||
"field[1,1.5;3,1;name;Name;]" ..
|
|
||||||
"button_exit[1,2;2,1;exit;Save]")
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
-- Register callback
|
|
||||||
minetest.register_on_player_receive_fields(function(player,
|
|
||||||
formname, fields)
|
|
||||||
if formname ~= "mymod:form" then
|
|
||||||
-- Formname is not mymod:form,
|
|
||||||
-- exit callback.
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send message to player.
|
if fields.guess then
|
||||||
minetest.chat_send_player(player:get_player_name(),
|
local pname = player:get_player_name()
|
||||||
"You said: " .. fields.name .. "!")
|
minetest.chat_send_all(pname .. " guessed " .. fields.number)
|
||||||
|
end
|
||||||
-- Return true to stop other callbacks from
|
|
||||||
-- receiving this submission.
|
|
||||||
return true
|
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
The function given in minetest.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 check the formname given
|
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
|
to the function, and exit if it is not the right form; however, some callbacks
|
||||||
may need to work on multiple forms, or all forms - it depends on what you
|
may need to work on multiple forms, or on all forms.
|
||||||
want to do.
|
|
||||||
|
The `fields` parameter to the function is a table of the values submitted by the
|
||||||
|
user, indexed by strings. Named elements will appear in the field under their own
|
||||||
|
name, but only if they are relevent for the event that caused the submission.
|
||||||
|
For example, a button element will only appear in fields if that particular button
|
||||||
|
was pressed.
|
||||||
|
|
||||||
{% include notice.html notice=page.submit_vuln %}
|
{% include notice.html notice=page.submit_vuln %}
|
||||||
|
|
||||||
### Fields
|
So, now the formspec is sent to the client and the client sends information back.
|
||||||
|
The next step is to somehow generate and remember the target value, and to update
|
||||||
|
the formspec based on guesses. The way to do this is using a concept called
|
||||||
|
"contexts".
|
||||||
|
|
||||||
The `fields` parameter to the function is a table, index by string, of the values
|
|
||||||
submitted by the user. You can access values in the table via fields.name,
|
|
||||||
where 'name' is the name of the element.
|
|
||||||
|
|
||||||
As well as retrieving the values of each element, you can also get which button
|
### Contexts
|
||||||
was clicked. In this case, the button called 'exit' was clicked, so fields.exit
|
|
||||||
will be true.
|
|
||||||
|
|
||||||
Some elements can submit the form without the user clicking a button,
|
|
||||||
such as a checkbox. You can detect these cases by looking
|
|
||||||
for a clicked button.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- An example of what fields could contain,
|
|
||||||
-- using the above code
|
|
||||||
{
|
|
||||||
name = "Foo Bar",
|
|
||||||
exit = true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contexts
|
|
||||||
|
|
||||||
In many cases you want minetest.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
|
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.
|
what a chat command was called with, or what the dialog is about. In this case,
|
||||||
|
the target value that needs to be remembered.
|
||||||
|
|
||||||
For example, you might make a form to handle land protection information:
|
A context is a per-player table to store information, and the contexts for all
|
||||||
|
online players are stored in a file-local variable:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
--
|
local _contexts = {}
|
||||||
-- Step 1) set context when player requests the formspec
|
local function get_context(name)
|
||||||
--
|
local context = _contexts[name] or {}
|
||||||
|
_contexts[name] = context
|
||||||
|
return context
|
||||||
|
end
|
||||||
|
|
||||||
-- land_formspec_context[playername] gives the player's context.
|
minetest.register_on_leaveplayer(function(player)
|
||||||
local land_formspec_context = {}
|
_contexts[player:get_player_name()] = nil
|
||||||
|
|
||||||
minetest.register_chatcommand("land", {
|
|
||||||
func = function(name, param)
|
|
||||||
if param == "" then
|
|
||||||
minetest.chat_send_player(name,
|
|
||||||
"Incorrect parameters - supply a land ID")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Save information
|
|
||||||
land_formspec_context[name] = {id = param}
|
|
||||||
|
|
||||||
minetest.show_formspec(name, "mylandowner:edit",
|
|
||||||
"size[4,4]" ..
|
|
||||||
"field[1,1;3,1;plot;Plot Name;]" ..
|
|
||||||
"field[1,2;3,1;owner;Owner;]" ..
|
|
||||||
"button_exit[1,3;2,1;exit;Save]")
|
|
||||||
end
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Step 2) retrieve context when player submits the form
|
|
||||||
--
|
|
||||||
minetest.register_on_player_receive_fields(function(player,
|
|
||||||
formname, fields)
|
|
||||||
if formname ~= "mylandowner:edit" then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Load information
|
|
||||||
local context = land_formspec_context[player:get_player_name()]
|
|
||||||
|
|
||||||
if context then
|
|
||||||
minetest.chat_send_player(player:get_player_name(), "Id " ..
|
|
||||||
context.id .. " is now called " .. fields.plot ..
|
|
||||||
" and owned by " .. fields.owner)
|
|
||||||
|
|
||||||
-- Delete context if it is no longer going to be used
|
|
||||||
land_formspec_context[player:get_player_name()] = nil
|
|
||||||
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
-- Fail gracefully if the context does not exist.
|
|
||||||
minetest.chat_send_player(player:get_player_name(),
|
|
||||||
"Something went wrong, try again.")
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Node Meta Formspecs
|
Next, we need to modify the show code to update the context
|
||||||
|
before showing the formspec:
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
We also need to modify the formspec generation code to use the context:
|
||||||
|
|
||||||
|
```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 = "To high!"
|
||||||
|
else
|
||||||
|
text = "To low!"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it's good practice for get_formspec to only read the context, and not
|
||||||
|
update it at all. This can make the function simpler, and also easier to test.
|
||||||
|
|
||||||
|
And finally, we need to update the handler to update the context with the guess:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
if fields.guess then
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local context = get_context(name)
|
||||||
|
context.guess = tonumber(fields.number)
|
||||||
|
guessing.show_to(name)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Formspec Sources
|
||||||
|
|
||||||
|
There are three different ways that a formspec can be delivered to the client:
|
||||||
|
|
||||||
|
* [show_formspec](#guessing-game): the method used above, fields are received by register_on_player_receive_fields.
|
||||||
|
* [Node Meta Formspecs](#node-meta-formspecs): the node contains a formspec in its meta data, and the client
|
||||||
|
shows it *immediately* when the player rightclicks. Fields are received by a
|
||||||
|
method in the node definition called `on_receive_fields`.
|
||||||
|
* [Player Inventory Formspecs](#player-inventory-formspecs): the formspec is sent to the client at some point, and then
|
||||||
|
shown immediately when the player presses `i`. Fields are received by
|
||||||
|
register_on_player_receive_fields.
|
||||||
|
|
||||||
|
### Node Meta Formspecs
|
||||||
|
|
||||||
minetest.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](node_metadata.html). For example,
|
add formspecs to a [node's metadata](node_metadata.html). For example,
|
||||||
@ -305,3 +364,22 @@ This style of callback triggers when you press enter
|
|||||||
in a field, which is impossible with `minetest.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
|
however, this kind of form can only be shown by right-clicking on a
|
||||||
node. It cannot be triggered programmatically.
|
node. It cannot be triggered programmatically.
|
||||||
|
|
||||||
|
### Player Inventory Formspecs
|
||||||
|
|
||||||
|
The player inventory formspec is the one shown when the player presses i.
|
||||||
|
The global callback is used to receive events from this formspec, and the
|
||||||
|
formname is `""`.
|
||||||
|
|
||||||
|
There are a number of different mods which allow multiple mods to customise
|
||||||
|
the player inventory. The officially recommended mod is
|
||||||
|
[Simple Fast Inventory (sfinv)](sfinv.html), and is included in Minetest Game.
|
||||||
|
|
||||||
|
|
||||||
|
### Your Turn
|
||||||
|
|
||||||
|
* Extend the Guessing Game to keep track of each player's top score, where the
|
||||||
|
top score is how many guesses it took.
|
||||||
|
* Make a node called "Inbox" where users can open up a formspec and leave messages.
|
||||||
|
This node should store the placers' name as `owner` in the meta, and should use
|
||||||
|
`show_formspec` to show different formspecs to different players.
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{% assign notice=include %}
|
{% assign notice=include %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="notice notice-{{ notice.level }}">
|
<div class="notice notice-{{ notice.level }} {{ notice.classes }}">
|
||||||
{% if notice.level == "warning" %}
|
{% if notice.level == "warning" %}
|
||||||
<span>⚠</span>
|
<span>⚠</span>
|
||||||
{% else if notice.level == "tip" %}
|
{% else if notice.level == "tip" %}
|
||||||
|
@ -101,16 +101,16 @@ footer a:hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header {
|
header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 100px 0;
|
padding: 100px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header h1 {
|
header h1 {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header span {
|
header span {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
@ -119,6 +119,10 @@ footer a:hover {
|
|||||||
font-size: 200%;
|
font-size: 200%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.book-only {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-height: 568px) {
|
@media all and (max-height: 568px) {
|
||||||
nav {
|
nav {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
BIN
static/formspec_guessing.png
Normal file
BIN
static/formspec_guessing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
BIN
static/formspec_padding_spacing.png
Normal file
BIN
static/formspec_padding_spacing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Loading…
Reference in New Issue
Block a user