more mods
No files matched your search
@@ -0,0 +1,14 @@
|
||||
allow_defined_top = true
|
||||
|
||||
read_globals = {
|
||||
"minetest",
|
||||
"vector", "ItemStack",
|
||||
"default",
|
||||
"stairs", "doors", "xpanes",
|
||||
"xdecor",
|
||||
table = {fields = {"copy"}},
|
||||
string = {fields = {"split"}},
|
||||
"unpack",
|
||||
"stairsplus",
|
||||
"mesecon"
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
# API for X-Decor-libre
|
||||
|
||||
X-Decor-libre is mostly self-contained but it allows for limited extension with
|
||||
a simple API. Not that extensibility is not the main goal of this mod.
|
||||
|
||||
The function documentation can be found in the respective source code files
|
||||
under the header "--[[ API FUNCTIONS ]]".
|
||||
|
||||
These are the features:
|
||||
|
||||
## Add custom tool enchantments
|
||||
|
||||
You can register tools to be able to be enchanted at the enchanting table.
|
||||
|
||||
See `src/enchanting.lua` for details.
|
||||
|
||||
## Add custom hammers
|
||||
|
||||
You can add a custom hammer for repairing tools at the workbench,
|
||||
using custom stats.
|
||||
|
||||
See `src/workbench.lua` for details.
|
||||
|
||||
## Add cut nodes
|
||||
|
||||
You can register "cut" node variants of an existing node which can
|
||||
be created at the workbench.
|
||||
This will add thin stairs, half stairs, panels, microcubes, etc.
|
||||
|
||||
See `src/workbench.lua` for details.
|
||||
|
||||
## Cauldron compatibility
|
||||
|
||||
The cauldron needs to interact with various items and nodes to
|
||||
work properly. It uses ingredients and bowls for soup, and
|
||||
fire nodes to get heated.
|
||||
|
||||
If your mod adds fire or hot nodes, bowls or food, this section
|
||||
is relevant for you.
|
||||
|
||||
### Soup ingredients
|
||||
|
||||
The cauldron soup accepts a variety of food items as ingredients for the soup.
|
||||
It uses a heuristic to determine which items it accepts as ingredient.
|
||||
|
||||
This heuristic may sometimes fail and recognize strange items as ingredient.
|
||||
To explicitly mark any item as a soup ingredient for xdecor, add the group
|
||||
`xdecor_soup_ingredient = 1` to it. To explicitly tell xdecor that an item
|
||||
is NOT a soup ingredient, use `xdecor_soup_ingredient = -1` instead.
|
||||
The `-1` should only be used if actually necessary.
|
||||
|
||||
#### Information about the heuristic
|
||||
|
||||
Any item without a valid `xdecor_soup_ingredient` group will be checked
|
||||
by against a heuristic to determine if it counts as a soup
|
||||
ingredient or not. Items that do have this group with a valid
|
||||
value are not subject to the heuristic.
|
||||
|
||||
First, the heuristic checks if the item is 'eatable'. This basically
|
||||
checks if the function `core.item_eat` or (`minetest.item_eat`) is called.
|
||||
If the item is eatable, it counts as ingredient.
|
||||
|
||||
If not, then the heuristic looks at the part of the technical itemname after
|
||||
the colon for certain keywords like 'apple', 'meat', 'potato' etc.
|
||||
(for the full list, see `ingredients_list` in `src/cooking.lua`).
|
||||
If a keyword was found, the item counts as an ingredient, otherwise not.
|
||||
|
||||
There is also a small blacklist that disqualifies a few items
|
||||
from Minetest Game.
|
||||
|
||||
### Heater nodes
|
||||
|
||||
Cauldrons need a fire below to get heated. All nodes with the group `fire`
|
||||
or `xdecor_cauldron_heater=1` will heat up the cauldron. You can use the latter
|
||||
group if adding the `fire` group would create problems.
|
||||
|
||||
### Bowls
|
||||
|
||||
Players can use the `xdecor:bowl` item to collect soup from a cauldron.
|
||||
But other mods also have their own bowls which may not be recognized by
|
||||
X-Decor-libre.
|
||||
|
||||
The following items are recognized as bowls that can collect soup from
|
||||
the cauldron:
|
||||
|
||||
* Items with group `xdecor_soup_bowl=1` (recommended)
|
||||
* `farming:bowl` (hardcoded)
|
||||
* `x_farming:bowl` (hardcoded)
|
||||
|
||||
If you want to make your bowl compatible with the cauldron, add the group
|
||||
`xdecor_soup_bowl=1` to it. **Only use this for empty bowls!**
|
||||
|
||||
**Please test your bowl!**
|
||||
|
||||
When any valid bowl is used at the cauldron, it turns into `xdecor:bowl_soup`.
|
||||
If the soup is eaten, the item will become the original bowl again, even
|
||||
if it was a custom bowl.
|
||||
For custom bowls (not from X-Decor-libre), the `xdecor:bowl_soup` item
|
||||
remembers the name of the original bowl in the metadata under
|
||||
`original_bowl`. If `original_bowl` is the empty string, or contains the
|
||||
name of an unknown item, `xdecor:bowl` is the assumed default.
|
||||
(However, an unknown item in `original_bowl` will trigger a warning.)
|
||||
|
||||
|
||||
## Custom Chess bot (experimental)
|
||||
|
||||
You can replace the (very weak) Chess computer player with a custom one.
|
||||
See `src/chess.lua` for details (`realchess.set_chessbot`).
|
||||
|
||||
Note this feature is relatively experimental, but it has survived
|
||||
first initial tests.
|
||||
|
||||
## Groups
|
||||
|
||||
This mod introduces the following groups:
|
||||
|
||||
* `potted_flower=1`: Potted flower
|
||||
* `food_bowl=1`: Empty bowl
|
||||
* `lever=X`: Lever (1 = off, 2 = on)
|
||||
* `pressure_plate=X`: Pressure plate (1 = off, 2 = on)
|
||||
* `cauldron=X`: Cauldron
|
||||
* `1`: Empty cauldron
|
||||
* `2`: Cauldron with cold liquid
|
||||
* `3`: Cauldron with boiling liquid
|
||||
* `xdecor_cut=1`: For nodes cut by the workbench (not the normal slab, normal stair, inner or outer stair).
|
||||
Cut nodes will also receive one of the following groups to specify the shape:
|
||||
* `xdecor_cut_microslab=1`: Microslab
|
||||
* `xdecor_cut_nanoslab=1`: Nanoslab
|
||||
* `xdecor_cut_panel=1`: Panel
|
||||
* `xdecor_cut_doublepanel=1`: Double panel
|
||||
* `xdecor_cut_micropanel=1`: Micropanel
|
||||
* `xdecor_cut_cube=1`: Cube
|
||||
* `xdecor_cut_halfstair=1`: Half stair
|
||||
* `xdecor_cut_thinstair=1`: Thin stair
|
||||
* `not_cuttable=1`: Prevent this node from being cut by the workbench
|
||||
* `repair_hammer=1`: A hammer that repairs tools (do not add this group manually, see `src/workbench.lua`)
|
||||
* `chess_piece=X`: Chess piece for Chess (see `src/chess.lua` for details)
|
||||
* `enchanted_tool=1`: Enchanted tool
|
||||
* `enchantment_durable=1`: Enchanted tool with Durability enchantment
|
||||
* `enchantment_fast=1`: Enchanted tool with Efficiency enchantment
|
||||
* `enchantment_sharp=1`: Enchanted tool with Sharpness enchantment
|
||||
|
||||
These groups are for cauldron features (please read the “Cauldron compatibility”
|
||||
section above before using them):
|
||||
|
||||
* `xdecor_soup_ingredient=1`: Add this item as a possible soup ingredient
|
||||
* `xdecor_soup_ingredient=-1`: Exclude this item as a possible soup ingredient
|
||||
* `xdecor_cauldron_heater=1`: If placed below cauldron, it will heat it up
|
||||
* `xdecor_soup_bowl=1`: A bowl that can collect soup from the cauldron
|
||||
|
||||
These groups are reserved for internal use:
|
||||
|
||||
* `xdecor_actuator=X`: A node that can turned on and off and toggles neighboring doors. 1 = off, 2 = on. Used by lever and pressure plate
|
||||
* `sittable=1`: Player can sit down on this node (also needs to call `xdecor.sit` to function)
|
||||
@@ -0,0 +1,550 @@
|
||||
# Chess
|
||||
|
||||
## Introduction
|
||||
|
||||
You can play Chess in X-Decor-libre!
|
||||
|
||||
While the game of Chess is well-known and widespread and its
|
||||
rules are well-documented all over the Internet and elsewhere,
|
||||
the devil still lies in the detail.
|
||||
|
||||
In X-Decor-libre, the game of Chess is closely modeled after
|
||||
the FIDE Laws of Chess from January 2023. However, for a
|
||||
computer version of Chess, there are still some details
|
||||
that might need explanation.
|
||||
|
||||
|
||||
|
||||
## Objective
|
||||
|
||||
Chess is played between two players on a chessboard. One player plays
|
||||
with white pieces while the other one plays with black pieces.
|
||||
The goal of the game is to put the king of the opponent under attack
|
||||
in such a way they have no legal move. This is known as ‘checkmate’.
|
||||
It is not allowed to put one’s king in danger, to leave him in danger
|
||||
or to capture the opponent’s king.
|
||||
|
||||
## How to play
|
||||
|
||||
You need a chessboard to play. Craft yourself a chessboard like this:
|
||||
|
||||
BWB
|
||||
sss
|
||||
|
||||
* B = Black Dye
|
||||
* W = White Dye
|
||||
* s = Wooden Slab (from apple tree)
|
||||
|
||||
Place the chessboard and examine it. You will see a close-up of the chessboard.
|
||||
|
||||
### The Chess interface
|
||||
|
||||
On the screen that pops up, you can choose to play against the
|
||||
computer (Singleplayer) or another player on the server (Multiplayer).
|
||||
You may also use the multiplayer option to play against yourself.
|
||||
The computer player is quite weak.
|
||||
|
||||
Click on the corresponding button to start the game.
|
||||
|
||||
Once the game has started, you see the following things:
|
||||
|
||||
To the left, the large chessboard consisting of 8×8 dark and white squares.
|
||||
The pieces are put on the chessboard. If there is no active game,
|
||||
the chessboard is empty.
|
||||
|
||||
During a game, the interface has the following meaning:
|
||||
|
||||
Above and below the chessboard, plaques show the name of the players.
|
||||
Above the chessboard is the player playing Black and below it
|
||||
the player playing White.
|
||||
An arrow left of the plaque shows whose turn it is. The name plaques
|
||||
may also show the “game status”, such as victory, checkmate (=loss),
|
||||
draw, being “in check”, etc.
|
||||
|
||||
On the right side, a list of moves that have been made is shown.
|
||||
It is written in a figurine long algebraic notation (see appendix).
|
||||
|
||||
The two boxes below the list of moves is where all the captured pieces
|
||||
go. This has no gameplay significance but it may serve as a visual
|
||||
aid to see how badly hurt the player’s “armies” are.
|
||||
|
||||
The top right corner is used for starting a new game. Press
|
||||
“New Game” to start a new game. This ends the current game.
|
||||
|
||||
The bottom right corner right corner is used for special
|
||||
player actions, such as resigning or claiming a draw.
|
||||
|
||||
Note that during a game, the buttons only work for the two players
|
||||
playing Chess. They don’t work for anyone else.
|
||||
|
||||
## The rules of Chess
|
||||
|
||||
### Starting a game
|
||||
|
||||
Select Singleplayer or Multiplayer. In Singleplayer, you choose
|
||||
the color you play as by clicking the corresponding button.
|
||||
|
||||
White always plays first.
|
||||
|
||||
In multiplayer, anyone can make the first move.
|
||||
The player making the first move as White will play as White,
|
||||
the player making the first move as Black will play as Black.
|
||||
After that, the players are “locked” to their colors and
|
||||
nobody else can play as White or Black.
|
||||
|
||||
### The chessboard
|
||||
|
||||
The chessboard is a board of 8×8 squares alternating between light
|
||||
and dark squares. Each square is either empty or holds exactly one
|
||||
chess piece.
|
||||
|
||||
### The Chess pieces and how they move
|
||||
|
||||
Each player starts with the same pieces on opposing sides of the board,
|
||||
only their color is different.
|
||||
|
||||
There are 4 types of moves you can make:
|
||||
|
||||
* Normal move: You pick up the piece and place it to an empty square
|
||||
* Capturing move: You pick up the piece and place it on top of an opposing piece
|
||||
Your piece will land on that square and the opponent’s piece is removed
|
||||
* En passant: Special capturing move of the pawn (see below)
|
||||
* Castling: Special king+rook move (see below)
|
||||
|
||||
It is not possible to place your piece on your own pieces.
|
||||
It is not possible to capture a king or your own pieces.
|
||||
Any square on which a piece could capture another piece in theory
|
||||
(even if it is actually empty) is considered to be “attacked”.
|
||||
|
||||
For most pieces, the rules for making a normal move and
|
||||
a capturing move are identical. Only for the pawn it is different
|
||||
(read below).
|
||||
|
||||
If the square of the king is attacked, he and the player playing him
|
||||
is considered to be in “check”.
|
||||
While a player is in check, any move which would leave their own
|
||||
king under attack is not allowed.
|
||||
|
||||
#### How to actually move
|
||||
|
||||
Each move can be made by clicking on the piece to pick it up and then
|
||||
clicking again on the destination. The destination is either an empty
|
||||
square or a square occupied by an opponent’s piece (which will be captured).
|
||||
You can also do the same via drag-and-drop.
|
||||
|
||||
Once you made a valid move by placing the piece to its destination, it is
|
||||
final and cannot be taken back. This ends your move and it’s your
|
||||
opponent’s turn (exception: promotion, see below).
|
||||
|
||||
If you pick up a piece and put it back, nothing happens, it is still
|
||||
your turn and you can still do your move normally. Also, if you try
|
||||
to make an invalid move, nothing happens as well.
|
||||
|
||||
(Nerd info: For the purposes of the FIDE Laws of Chess, pieces are never
|
||||
considered “touched” here. Thus, article 4 of the FIDE Laws of Chess has
|
||||
no effect.)
|
||||
|
||||
#### Rook
|
||||
|
||||
The rook looks like a tower and can move to any of square that lies
|
||||
in a straight horizontal or vertical line from it.
|
||||
It cannot move beyond pieces that are in the way.
|
||||
|
||||
The rook may be involved in Castling, see “King” below.
|
||||
|
||||
#### Bishop
|
||||
|
||||
The bishop can move to any square on a diagonal line from it.
|
||||
It cannot move beyond pieces that are in the way.
|
||||
|
||||
#### Queen
|
||||
|
||||
The queen combines the powers of the rook and bishop and can
|
||||
move to any square in a straight horizontal, vertical
|
||||
or diagonal line from it.
|
||||
It cannot move beyond pieces that are in the way.
|
||||
|
||||
#### Knight
|
||||
|
||||
The knight looks like a horse and can move to any square closest to
|
||||
it that is not in its same horizontal line (also known as “rank”),
|
||||
vertical line (also known as “file”), or diagonal of the board.
|
||||
To illustrate this:
|
||||
|
||||
..X.X..
|
||||
.X...X.
|
||||
...n...
|
||||
.X...X.
|
||||
..X.X..
|
||||
|
||||
In this diagram, “n” represents the knight and the Xes are all the
|
||||
possible squares it can theoretically reach. The dots are empty
|
||||
squares.
|
||||
|
||||
Unlike the other pieces, pieces are never “in the way” of the knight.
|
||||
You might say the knight can “jump over” other pieces.
|
||||
|
||||
#### King
|
||||
|
||||
The king can move exactly one square in any direction: horizontally,
|
||||
vertically or diagonally. However, the king can never move to any square that
|
||||
is attacked by an opponent’s piece.
|
||||
|
||||
The king also has a special move called “Castling”.
|
||||
|
||||
##### Castling
|
||||
|
||||
Castling is a special move in which two pieces move at once.
|
||||
Both the king and a rook move horizontally from their starting positions.
|
||||
The king will move two squares horizontally and a rook will be
|
||||
moved next to him.
|
||||
|
||||
Each player has two possible castling moves available, involving each
|
||||
of the 2 starting rooks.
|
||||
|
||||
Castling has several conditions:
|
||||
|
||||
- The king must not have moved yet
|
||||
- The rook you wish to castle with must not have moved yet
|
||||
- All of the squares between king and rook must be empty
|
||||
- The king must not be under attack
|
||||
- The king’s destination as well the square it crosses must not be under attack
|
||||
- You can castle only horizontally
|
||||
|
||||
If all the conditions are met, here’s how you castle:
|
||||
|
||||
Place the king two squares towards the rook you want to castle with.
|
||||
This square is where the king will end up. The rook will then
|
||||
automatically move towards the king and “jump” to the square
|
||||
behind the king, from the rook’s viewpoint.
|
||||
|
||||
**Remember**: You *must* move the king (not the rook) if you want
|
||||
to castle. If you move the rook instead, this is considered
|
||||
to be a regular move of the rook alone.
|
||||
|
||||
#### Pawn
|
||||
|
||||
The pawn has various ways to move.
|
||||
The pawn has a “walking direction”, it walks and captures towards
|
||||
the opponent’s side (i.e. the side on which the opponent’s
|
||||
pieces have started).
|
||||
|
||||
The pawn’s basic moves are:
|
||||
|
||||
1. Single step: The pawn moves one step vertically towards the
|
||||
opponent’s side.
|
||||
2. Double step: Like a single step, but it moves two squares instead.
|
||||
This is only possible from the pawn’s start position.
|
||||
|
||||
A pawn can never move backwards.
|
||||
|
||||
In both cases, the destination square must be empty as well as any crossed square.
|
||||
The pawn cannot capture by doing a single or double step, however.
|
||||
|
||||
The capturing move of the pawn is different. To capture, the pawn has to
|
||||
move one step diagonally towards the opponent’s side, either left or right.
|
||||
|
||||
To illustrate, in the following diagram, the X’es represent the
|
||||
squares attacked by a white pawn (w) and a black pawn (b):
|
||||
|
||||
.X.X..b..
|
||||
..w..X.X.
|
||||
|
||||
##### En passant capture
|
||||
|
||||
An en passant capture is a pawn move that is available if a pawn
|
||||
of the current player stands on a square left or right from an
|
||||
opposing pawn that has made a double step in the previous move.
|
||||
|
||||
In this situation, the first pawn may move as if the second pawn
|
||||
had made a single step instead. This will be considered as a
|
||||
capturing move and the opposing pawn will be removed from the board.
|
||||
|
||||
|
||||
Consider this example: Here, “w” represents a white pawn in its starting
|
||||
position, “b” a black pawn and “.” an empty square. White moves upwards
|
||||
and Black downwards. Consider this starting position:
|
||||
|
||||
b.
|
||||
..
|
||||
.w
|
||||
|
||||
Now, White does a double step:
|
||||
|
||||
bw
|
||||
..
|
||||
..
|
||||
|
||||
Black decides to do an en passant capture. For this, the black pawn moves one
|
||||
diagonal step towards the square just crossed by the opponent. The white
|
||||
pawn is captured and removed.
|
||||
|
||||
..
|
||||
.b
|
||||
..
|
||||
|
||||
Remember! An ‘en passant’ capture is only possible in the move directly after
|
||||
a pawn’s double step. So if the chance for a particular en passant capture
|
||||
is not taken, it will be gone from that point on.
|
||||
|
||||
##### Promotion
|
||||
|
||||
When a pawn reaches the other end of the chessboard (from its viewpoint),
|
||||
it will be promoted. A promotion is considered to be part of the move.
|
||||
|
||||
When promotion happens, the boxes where normally the captured pieces go
|
||||
will turn into a prompt. The current player must choose a new
|
||||
piece to replace the pawn with:
|
||||
A queen, rook, bishop or knight of the same color.
|
||||
Just click the corresponding button. These buttons only work for the
|
||||
current player. Promotion is mandatory and no other moves are possible
|
||||
until it is completed.
|
||||
|
||||
Once a piece was selected, the pawn will be replaced, which
|
||||
immediately activates its powers. This ends the move.
|
||||
|
||||
### The end of the game
|
||||
|
||||
There are various ways for the game of Chess to end. A game always
|
||||
ends in victory of one player, or in a draw.
|
||||
|
||||
#### Checkmate
|
||||
Checkmating your opponent is the primary goal of Chess.
|
||||
The player who has checkmated the opponent king wins the game and ends it.
|
||||
|
||||
You are checkmated when it’s your turn, your own king is in check
|
||||
(i.e. under attack) and you have no valid move available.
|
||||
This immediately ends the game and your opponent wins.
|
||||
|
||||
#### Stalemate
|
||||
If it’s a player’s turn, but they have no possible move and their
|
||||
king is not in check, the game immediately ends in a draw.
|
||||
This is called a “stalemate”.
|
||||
|
||||
#### Resign
|
||||
During the game, the possibility of resigning arises. Resigning
|
||||
basically means “giving up” and this leads to an instant loss
|
||||
and the victory of your opponent.
|
||||
Resigning is available after one’s name has been recorded on
|
||||
the name plaque. Resigning is possible even when it’s not your turn.
|
||||
|
||||
To resign, click the skull icon in the bottom right.
|
||||
|
||||
#### Dead position
|
||||
If during the game, on the board there are only the following pieces left,
|
||||
the game ends in a draw:
|
||||
|
||||
* king versus king
|
||||
* king versus king and bishop
|
||||
* king versus king and knight
|
||||
* king and bishop versus king and bishop, and both bishops stand on squares of the same color
|
||||
|
||||
This is called a “dead position”. For example, a board with only a white
|
||||
and a black king is a draw.
|
||||
|
||||
(Nerd info: In Chess, any position from which neither player can give
|
||||
checkmate is known as a dead position. But in X-Decor-libre, only the
|
||||
situations listed above lead to an instant draw.
|
||||
|
||||
This deviates from the FIDE Laws of Chess, article 5.2.2,
|
||||
which states that *any* dead position must end the game immediately
|
||||
in a draw.
|
||||
|
||||
X-Decor-libre deviates from the FIDE Laws of Chess here because of
|
||||
performance; calculating if any given position is ‘dead’ is
|
||||
computationally expensive.
|
||||
|
||||
Dead positions are still guaranteed to end the game in X-Decor-libre
|
||||
eventually due to the 75-move rule.)
|
||||
|
||||
#### 75-move rule
|
||||
If in the last 75 consecutive moves of each player, no piece was captured
|
||||
and no pawn was moved, the game ends in a draw.
|
||||
|
||||
Exception: If the last move has lead to a checkmate. In this case, checkmate
|
||||
takes precedence.
|
||||
|
||||
#### 50-move rule
|
||||
If in the last 50 consecutive moves of each player, no piece was
|
||||
captured and no pawn was moved, the player whose turn it is can invoke
|
||||
the 50-move rule to draw the game instantly.
|
||||
|
||||
When it’s your turn, and you believe your *next* move will satisfy
|
||||
the condition of the 50-move rule, you may also invoke this rule
|
||||
to draw the game, but in this case, you still have to make the move.
|
||||
If this move satisfies the 50-move rule, the game is drawn.
|
||||
But if not, this counts as a normal move, your turn ends and the
|
||||
game continues as normal.
|
||||
|
||||
A button on the bottom right will appear when this rule is available.
|
||||
The button is not shown when there are too few such moves for this
|
||||
draw claim to be successful.
|
||||
|
||||
The icon represents a barricade, as if the game of Chess itself
|
||||
has been blocked. This one will instantly draw the game.
|
||||
If you still would have to make the game-drawing move, the
|
||||
icon represents half a barricade.
|
||||
Note the tooltip.
|
||||
|
||||
Note the latter icon is no guarantee you can actually draw the
|
||||
game in the next move, only that such a draw claim is plausible.
|
||||
|
||||
#### Fivefold repetition rule
|
||||
If the current position on the chessboard has appeared at least 5 times,
|
||||
the game ends in a draw.
|
||||
|
||||
Two positions are considered to be the same if the chessboard has the
|
||||
same pieces of the same color on the same squares, it is the same
|
||||
player’s turn, the castling rights of both players are the same and
|
||||
the vulnerability of pawns to en passant captures (if any) is also
|
||||
the same.
|
||||
|
||||
A pawn is considered “vulnerable” to an en passant capture immediately
|
||||
after a double step turn, no matter if it is actually in danger of
|
||||
being captured that way.
|
||||
|
||||
#### Threefold repetition rule
|
||||
If the current position has appeared at least 3 times in the game,
|
||||
the current player can invoke the threefold repetition rule to draw
|
||||
the game instantly.
|
||||
|
||||
This rule can also be invoked when you think your *next* move will
|
||||
lead to the 3rd (or more) repeated position in the game. This
|
||||
works similar as for the 50-move rule.
|
||||
|
||||
Like for the 50-move rule, a button appears on the bottom right
|
||||
once this rule can be invoked.
|
||||
|
||||
If the 3 same positions have already occurred, the icon will
|
||||
represent 3 chess squares stacked on top of each other.
|
||||
If the game-drawing move still has to be made, the top
|
||||
square is a “ghost square”.
|
||||
|
||||
#### No agreeing to draw
|
||||
|
||||
Unlike in other Chess programs, the players cannot agree to draw.
|
||||
|
||||
#### Game result
|
||||
|
||||
Once the game has ended, the game result is shown on the name plaques of the
|
||||
players as well in chat (to the players only). From this point on, everyone
|
||||
(even spectators) can start a new game with “New Game”.
|
||||
|
||||
|
||||
## Resetting the chessboard
|
||||
|
||||
While a game of Chess is ongoing, the chessboard can’t be dug and the game
|
||||
can’t be stopped by other players. But to prevent two players blocking a
|
||||
chessboard forever, there is a 5-minute timer. If no player makes a move
|
||||
for 5 minutes, then the chessboard can be reset and dug by anyone.
|
||||
|
||||
Exception: Players with the `protection_bypass` privilege can always
|
||||
dig the chessboard.
|
||||
|
||||
|
||||
## Appendix
|
||||
|
||||
### The Chess Notation
|
||||
|
||||
The chessboard interface shows a list of all moves on the right side.
|
||||
|
||||
The list of moves is written in a special notation called “algebraic notation”.
|
||||
There are many variants of it, so this section explains what it means in X-Decor-libre.
|
||||
|
||||
This mod uses a longform figurine algebraic notation. “figurine” means that
|
||||
icons are used for the chess pieces. “longform” means the start
|
||||
and end coordinates are shown in full.
|
||||
|
||||
Square coordinates are important in any Chess notation. In algebraic notation,
|
||||
each square is assigned coordinates with a letter from a to h,
|
||||
followed by a number from 1 to 8.
|
||||
Provided that the player playing White is on the “bottom” side of the chessboard,
|
||||
the squares are numbered from the bottom left square in ascending order.
|
||||
The horizontal lines (“ranks”) are numbered 1 to 8, starting from the bottom.
|
||||
The vertical lines (“files”) are numbered a to h, starting from the left.
|
||||
So from White’s viewpoint, the bottom-left square is a1. The square above it
|
||||
is a2, then a3, a4, ... a8. The square right of a1 is b1, then c1, d1, ... h1.
|
||||
The top-right square is h8.
|
||||
|
||||
(Note that on a real chessboard, all of the coordinates are flipped from Black’s viewpoint
|
||||
because the board is rotated 180° from their view. In X-Decor-libre, this does not
|
||||
matter because the board is always aligned the same way.)
|
||||
|
||||
In the list of moves, each line shows 3 things: Move number, white’s move, black’s move (if made).
|
||||
The move number is a simple counter that increases after each move of *both* players, starting by 1.
|
||||
|
||||
In the notation, a move by a single player is called a “halfmove”. The two moves
|
||||
of each White and then Black are called a “fullmove”.
|
||||
|
||||
#### Normal moves
|
||||
|
||||
Normally, a halfmove is written like this, in this order:
|
||||
|
||||
1. Symbol of moved piece (called “figurine”)
|
||||
2. Start coordinates, a dash or cross, destination coordinates
|
||||
3. “e.p.”, if it was an en passant capture -OR- symbol of piece to which a pawn was promoted to
|
||||
|
||||
For number 1, the symbol is only shown if the piece is not a pawn.
|
||||
For number 2, the syntax for normal moves is like: “a1–a2”. This means the piece was moved from a1 to a2.
|
||||
The dash means it was a normal move.
|
||||
For capturing moves, the dash is replaced with a cross “×”. If it was an en passant capture, then
|
||||
“ e.p” is appended, like so: “a5×b4 e.p.”.
|
||||
If a pawn was promoted, the symbol of the new piece is appended.
|
||||
The figurines are always of the color of the player.
|
||||
|
||||
Both halfmoves on a line are separated by spacing.
|
||||
|
||||
#### Castling
|
||||
|
||||
When a player castles, it is notated the following way:
|
||||
|
||||
* “0–0” for castling with the rook on file h (“kingside castling”)
|
||||
* “0–0–0” for castling with the rook on file a (“queenside castling”)
|
||||
|
||||
#### Game completion
|
||||
|
||||
If the game came to an end, the game result is written in a final separate line as:
|
||||
|
||||
* “1–0” if White won
|
||||
* “0–1” if Black won
|
||||
* “½–½” in case of a draw
|
||||
|
||||
#### Example
|
||||
|
||||
1. d2–d4 e7–e6
|
||||
2. ♔e1–d2 ♛d8–h4
|
||||
3. d4–d5 e6×d5
|
||||
...
|
||||
8. d8×d8♖ ♞b8–c6
|
||||
9. e2–e4 d4×e3 e.p.
|
||||
|
||||
Explanation of the moves:
|
||||
|
||||
* 1.: First fullmove: White moves pawn from d2 to d4, Black moves pawn from e7 to e6
|
||||
* 2.: Second fullmove: White moves king from e1 to d2, Black moves queen from d8 to h4
|
||||
* 3.: Third fullmove: White moves pawn from d4 to d5, Black moves pawn from d6 to d5 and captures
|
||||
* 8.: Eighth fullmove: White moves pawn from d7 to d8, captures a piece and promotes it to rook, Black moves knight from b8 to c6
|
||||
* 9.: Ninth fullmove: White moves pawn from e2 to e4, black moves pawn from d4 to e3 and captures en passant
|
||||
|
||||
#### Other symbols
|
||||
|
||||
Other symbols are not used. So there are no special symbols for check and checkmate and no comments for moves considered good or bad.
|
||||
|
||||
|
||||
|
||||
## Notworthy differences from The FIDE Laws of Chess (2023)
|
||||
|
||||
X-Decor-libre tries to be as faithful to the FIDE Laws of Chess
|
||||
of 2023 as much as possible, but there are a few differences,
|
||||
mostly for technical reasons:
|
||||
|
||||
* Players cannot agree to draw (Articles 5.2.3 and 9.1.1)
|
||||
* Only a few select obvious dead positions (like King vs King)
|
||||
lead to an instant draw (Article 5.2.2)
|
||||
* Resigning always leads to an instant loss, even if the
|
||||
opponent cannot give checkmate under any series of legal moves
|
||||
(Article 5.1.2)
|
||||
* Pieces are never considered “touched”, Article 4 does not apply
|
||||
* There is no direct time limit, but the chessboard will unlock
|
||||
if nobody made a move in 5 minutes. This allows everyone to reset
|
||||
or dig the chessboard, which aborts the game without a result.
|
||||
@@ -0,0 +1,70 @@
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ Copyright (c) 2015-2021 kilbith <jeanpatrick.guerrero@gmail.com> │
|
||||
│ │
|
||||
│ Code: Modified BSD License │
|
||||
│ Textures: CC0 (credits: Gambit, kilbith, Cisoun, Wuzzy) │
|
||||
│ │
|
||||
│ Textures (radio and speaker) by │
|
||||
│ MCL <temp1@cubesoftware.xyz> (CC BY 4.0 Int'l) │
|
||||
│ │
|
||||
│ Textures (Chess icons for the Chess notation) │
|
||||
│ originally by Wikimedia user Cbnurnett, │
|
||||
│ scaled down and edited by Wuzzy (CC BY-SA 3.0 Unported) │
|
||||
│ │
|
||||
│ Textures (hanging candle) by │
|
||||
│ Wuzzy (CC0) │
|
||||
│ │
|
||||
│ Textures (rooster) by │
|
||||
│ sirrobzeroone (CC0) │
|
||||
│ │
|
||||
│ Sounds: │
|
||||
│ - xdecor_boiling_water.ogg - by Audionautics - CC BY 3.0 │
|
||||
│ freesound.org/people/Audionautics/sounds/133901/ │
|
||||
│ - xdecor_bouncy.ogg - by Blender Foundation - CC BY 3.0 │
|
||||
│ opengameart.org/content/funny-comic-cartoon-bounce-sound │
|
||||
│ - xdecor_enchanting.ogg - by Kostas17, edit by Wuzzy - CC BY 3.0 │
|
||||
│ freesound.org/people/Kostas17/sounds/542825/ │
|
||||
│ │
|
||||
│ Translations: │
|
||||
│ - German: Wuzzy │
|
||||
│ - Italian: Zughy │
|
||||
│ - Spanish: josemunozrwx │
|
||||
│ - French: Louis Royer │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
-- Modified BSD License --
|
||||
|
||||
Copyright (c) 2015-2021 kilbith <jeanpatrick.guerrero@gmail.com>
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the names of contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-- End of Modified BSD License --
|
||||
|
||||
## References for other licenses:
|
||||
|
||||
* CC BY 3.0: http://creativecommons.org/licenses/by/3.0/
|
||||
* CC BY 4.0: http://creativecommons.org/licenses/by/4.0/
|
||||
* CC0: https://creativecommons.org/publicdomain/zero/1.0/
|
||||
@@ -0,0 +1,93 @@
|
||||
## X-Decor-libre [`xdecor`] ##
|
||||
|
||||
[](https://content.luanti.org/packages/Wuzzy/xdecor/)
|
||||
|
||||
X-Decor-libre is a libre Luanti mod which adds various decorative blocks
|
||||
as well as simple gimmicks.
|
||||
|
||||
This is a libre version (free software, free media) of the X-Decor mod for Luanti.
|
||||
It is the same as X-Decor, except with all the non-free files replaced and with
|
||||
bugfixes. There are no major new features.
|
||||
|
||||
## Version
|
||||
|
||||
This is version `libre19`.
|
||||
|
||||
## New blocks
|
||||
|
||||
This mod adds many decoration blocks: Flower pot, weathervane, radio, speaker,
|
||||
wooden tile, new bricks, lamps, candles, new doors, packed ice, and more.
|
||||
|
||||
This mod also adds 7 new block shapes for many Minetest Game blocks. They can
|
||||
be created by using the workbench. This includes panels, mini blocks and flat
|
||||
stairs.
|
||||
|
||||
## Special blocks
|
||||
|
||||
Most blocks in this mod are purely decorative, but there are also many special
|
||||
blocks with special features:
|
||||
|
||||
* Workbench: Storage, crafting, cutting and repairing
|
||||
* Storage: 16 item slots for item storage
|
||||
* Craft: 3×3 crafting grid
|
||||
* Cut: Put a full cube-shaped block to create new shapes
|
||||
* Repair: Put a damaged tool and a hammer and wait for it to be repaired
|
||||
* Enchanting table: Upgrade your tools with mese crystals
|
||||
* Ender Chest: Interdimensional inventory that is the same no matter
|
||||
where you put the ender chest
|
||||
* Mailbox: Lets you receive items from other players
|
||||
* Item Frame: You can place an item into it to show it off
|
||||
* Cushion: Reduces fall damage
|
||||
* Cushion Block: Reduces fall damage even more
|
||||
* Trampoline: Jump on it to bounce off. Very low fall damage
|
||||
* Cauldron: For storing water and cooking soups
|
||||
* Recipe: Pour water in, light a fire below it and throw
|
||||
in some food items. Collect the soup with a bowl
|
||||
* Lever: Pull the lever to activate doors next to it
|
||||
* Pressure Plate: Step on it to activate doors next to it
|
||||
* Chessboard: Play Chess against a player or the computer (see `CHESS_README.md`)
|
||||
|
||||
## Supported mods
|
||||
|
||||
This mod interacts with the following mods to add new features:
|
||||
|
||||
* `tt`: Tooltips will explain the special items and blocks
|
||||
* `mesecons`: Doors react to mesecon signals
|
||||
* `unified_inventory`: Add honey in crafting guide
|
||||
* `toolranks`: Compatibility for enchanted tools
|
||||
* `moreblocks`: Compatibility changes (other recipes, etc.)
|
||||
|
||||
## For developers and translators
|
||||
|
||||
X-Decor-libre can be extended in a limited fashion. See `API.md` for details.
|
||||
|
||||
This mod can also be translated. The recommended way to translate this mod is
|
||||
online on Codeberg Translate:
|
||||
|
||||
<https://translate.codeberg.org/projects/xdecor/>
|
||||
|
||||
For technical details, see the text file `TRANSLATING.md`.
|
||||
|
||||
## Information about the original mod
|
||||
|
||||
### Compatibility
|
||||
|
||||
X-Decor-libre is highly compatible with X-Decor. Read
|
||||
“`historic/upgrade.md`” for details on how to upgrade from X-Decor
|
||||
to X-Decor-libre.
|
||||
|
||||
### More information
|
||||
|
||||
For more information about the original X-Decor mod (from which this one
|
||||
is derived) and a comparison between both mods, see the text files
|
||||
in the directory named “`historic`”.
|
||||
|
||||
## Technical information
|
||||
|
||||
X-Decor-libre is a fork of X-Decor, from <https://github.com/minetest-mods/xdecor>,
|
||||
forked at Git commit ID 8b614b3513f2719d5975c883180c011cb7428c8d.
|
||||
|
||||
Note the technical mod name of X-Decor-libre is the same as for X-Decor: `xdecor`.
|
||||
This is because this mod is meant to be a drop-in-replacement.
|
||||
|
||||
The original readme of X-Decor can be found at `historic/OLD_README.md`.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Translating X-Decor-libre
|
||||
|
||||
This mod wants to be translated in many languages.
|
||||
|
||||
The recommended way to translate this mod is online on Codeberg Translate:
|
||||
|
||||
<https://translate.codeberg.org/projects/xdecor/>
|
||||
|
||||
This is the easiest way to help this mod get translated.
|
||||
|
||||
## Manual translation
|
||||
|
||||
The rest of this document describes how translation works under the hood.
|
||||
You don’t need to understand this.
|
||||
|
||||
The translations are provided in the gettext PO format (supported since
|
||||
Luanti 5.10.0); they can be edited with programs like Poedit.
|
||||
|
||||
Many strings are commented, so read the comments to understand the context
|
||||
of each string.
|
||||
|
||||
There are two files to translate:
|
||||
|
||||
* `xdecor.pot`: Main translation file
|
||||
* `_xdecor_cut_nodes.pot`: Descriptions of nodes that were cut by the workbench
|
||||
|
||||
To create a complete translation, you need to have:
|
||||
|
||||
* `xdecor.<CODE>.po`
|
||||
* `_xdecor_cut_nodes.<CODE>.po`
|
||||
|
||||
where `<CODE>` is the language code like `de` for German.
|
||||
|
||||
## Special symbols
|
||||
|
||||
The strings may contain some symbols with special meaning:
|
||||
|
||||
`@1`, `@2`, `@3`, etc. are placeholders, they will be replaced with some other
|
||||
text. The translation *MUST* include all placeholders of the original string,
|
||||
but you can change the order as you wish.
|
||||
|
||||
`@n` represents a new line.
|
||||
|
||||
Refer to `lua_api.md` of the Luanti documentation for more information about
|
||||
the translation system.
|
||||
@@ -0,0 +1,6 @@
|
||||
The `extra_mods` directory contains extra mods for X-Decor-libre.
|
||||
|
||||
X-Decor-libre itself should NOT execute any of the code in this directory directly.
|
||||
|
||||
This directory is meant for special mods that you, the user
|
||||
are supposed to install manually (if you need them).
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Modified BSD License --
|
||||
|
||||
Copyright (c) 2025 Wuzzy <Wuzzy@disroot.org>
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the names of contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-- End of Modified BSD License --
|
||||
@@ -0,0 +1,85 @@
|
||||
# X-Decor-libre Autocut [`xdecor_autocut`]
|
||||
|
||||
## Version
|
||||
|
||||
This is version `1.0.0` of `xdecor_autocut`, compatible
|
||||
with version `libre14` or later of X-Decor-libre.
|
||||
|
||||
## Description
|
||||
|
||||
This is a companion mod for X-Decor-libre [`xdecor`].
|
||||
|
||||
It automatically adds many more “cut” variants for most
|
||||
cube-shaped nodes in the game so the player can cut more
|
||||
nodes at the workbench. This does not guarantee that
|
||||
all added cut nodes will look pretty, however,
|
||||
as the nodes will be added automatically and are
|
||||
not carefully curated.
|
||||
|
||||
By default, X-Decor-libre only adds cut node variants for
|
||||
nodes from itself and the `default` mod.
|
||||
|
||||
With this mod, nodes from other mods will be available for
|
||||
cutting as well.
|
||||
|
||||
This has been the original behavior of X-Decor-libre in the past,
|
||||
but it has been removed in version `libre6` due to
|
||||
hard-to-debug compatibility issues like naming collisions.
|
||||
This mod basically replicates this old behavior, but a bit
|
||||
more stable.
|
||||
|
||||
## Usage
|
||||
|
||||
To use this mod, simply copy the mod folder to your
|
||||
`mods` directory of your Luanti user directory and
|
||||
then enable it for your world like any other world.
|
||||
|
||||
### Note for server operators
|
||||
|
||||
You should probably not use this mod except for
|
||||
compatibility with old versions of X-Decor-libre
|
||||
or X-Decor.
|
||||
|
||||
If you are just now adding X-Decor-libre or
|
||||
start with a fresh world, you’re probably better
|
||||
off in writing a small mod to *explicitly* make
|
||||
each node you want to be made cut cuttable, to
|
||||
prevent to pollute your server with garbage nodes.
|
||||
See `src/workbench.lua` for details.
|
||||
|
||||
This mod mainly exists because if will be useful if
|
||||
your server used an older version of X-Decor-libre
|
||||
or X-Decor that aggressively added many cut nodes
|
||||
which have become unknown nodes in newer versions,
|
||||
because since X only nodes from X-Decor-libre
|
||||
and the `default` mod are cuttable, for safety
|
||||
reasons.
|
||||
If your world contains some cut nodes from any
|
||||
other mod, and they have become unknown after
|
||||
you upgraded of X-Decor-libre, this mod is for you.
|
||||
|
||||
Please note that this aggressive automatic
|
||||
adding of cut nodes is not guaranteed to give you
|
||||
the best quality. Some nodes that you probably
|
||||
do not want to be cut will be cuttable, some nodes
|
||||
might look ugly in their cut form, especially
|
||||
custom glass nodes. It is recommended you check your
|
||||
logs for `[xdecor_autocut]` messages to see which
|
||||
nodes have been made cuttable.
|
||||
|
||||
However, this mod should still be relatively safe to
|
||||
use, because it now checks for naming collisions.
|
||||
If it detects that any of the required node names
|
||||
for cut nodes is taken, it will refuse to add any
|
||||
cut node variants and print a message in the log.
|
||||
|
||||
### Note for modders
|
||||
|
||||
You can prevent any node from being having cut node
|
||||
variants added to it by adding the group
|
||||
`not_cuttable=1` to it.
|
||||
|
||||
## License
|
||||
|
||||
This mod is free software, distributed under the Modified
|
||||
BSD License (see `LICENSE.txt`).
|
||||
@@ -0,0 +1,22 @@
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local nodes = {}
|
||||
-- Iterate through all nodes, select nodes allowed to be cut
|
||||
-- Only the regular, solid blocks without metas or explosivity can be cut
|
||||
for nodename, def in pairs(minetest.registered_nodes) do
|
||||
if xdecor.stairs_valid_def(def) then
|
||||
if not xdecor.is_cut_registered(nodename) then
|
||||
if xdecor.can_cut(nodename) then
|
||||
table.insert(nodes, nodename)
|
||||
else
|
||||
-- Not all of the available node names are available
|
||||
-- for X-Decor-libre to add: Skip this node
|
||||
minetest.log("action", "[xdecor_autocut] Skipped adding cut nodes for "..nodename.." because at least one of the required nodenames is already taken")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for n=1, #nodes do
|
||||
xdecor.register_cut(nodes[n])
|
||||
minetest.log("action", "[xdecor_autocut] Registered cut nodes for: "..nodes[n])
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,3 @@
|
||||
name = xdecor_autocut
|
||||
description = Automatically add cut nodes variants for many nodes for X-Decor-libre’s workbench
|
||||
depends = xdecor
|
||||
@@ -0,0 +1,154 @@
|
||||
local mod_player_api = minetest.get_modpath("player_api") ~= nil
|
||||
|
||||
local sitting = {}
|
||||
local seats_occupied = {}
|
||||
|
||||
local function bottom_face(pointed_thing)
|
||||
if not pointed_thing then
|
||||
return
|
||||
end
|
||||
return pointed_thing.above.y < pointed_thing.under.y
|
||||
end
|
||||
|
||||
local function stand_up(player_name)
|
||||
if not mod_player_api then
|
||||
return
|
||||
end
|
||||
local player = minetest.get_player_by_name(player_name)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
player_api.player_attached[player_name] = false
|
||||
|
||||
local old_anim = player_api.get_animation(player)
|
||||
if old_anim and old_anim.animation == "sit" then
|
||||
player_api.set_animation(player, "stand")
|
||||
end
|
||||
|
||||
local hash = minetest.hash_node_position(sitting[player_name])
|
||||
seats_occupied[hash] = nil
|
||||
sitting[player_name] = nil
|
||||
|
||||
minetest.log("action", "[xdecor] "..player_name.." stands up at "..minetest.pos_to_string(player:get_pos(), 0))
|
||||
end
|
||||
|
||||
--[[ Used when player interacts with "sittable" node to sit down
|
||||
or stand up when interacting with that node again. Should
|
||||
be used in `on_rightclick` handler
|
||||
* `pos`: Position where to sit down player (MUST only use integers for coordinates!)
|
||||
* `node`: Node table of node to sit on
|
||||
* `clicker`: Player who interacted with node (from `on_rightclick`)
|
||||
* `pointed_thing`: From `on_rightclick` ]]
|
||||
function xdecor.sit(pos, node, clicker, pointed_thing)
|
||||
if not mod_player_api then
|
||||
return
|
||||
end
|
||||
-- Can't sit down if bottom face was pointed at
|
||||
if bottom_face(pointed_thing) then
|
||||
return
|
||||
end
|
||||
local player_name = clicker:get_player_name()
|
||||
local objs = minetest.get_objects_inside_radius(pos, 0.1)
|
||||
local vel = clicker:get_velocity()
|
||||
local ctrl = clicker:get_player_control()
|
||||
|
||||
-- Stand up if sitting
|
||||
if sitting[player_name] then
|
||||
stand_up(player_name)
|
||||
|
||||
-- Sit down if not sitting and not attached
|
||||
elseif not sitting[player_name] and not player_api.player_attached[player_name] and node.param2 <= 3 and
|
||||
not ctrl.sneak and vector.equals(vel, vector.new()) then
|
||||
|
||||
-- Can't sit down on note already occupied by player
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if seats_occupied[hash] then
|
||||
return
|
||||
end
|
||||
|
||||
player_api.player_attached[player_name] = true
|
||||
player_api.set_animation(clicker, "sit")
|
||||
sitting[player_name] = table.copy(pos)
|
||||
seats_occupied[hash] = player_name
|
||||
clicker:set_pos(pos)
|
||||
|
||||
if node.param2 == 0 then
|
||||
clicker:set_look_horizontal(0)
|
||||
elseif node.param2 == 1 then
|
||||
clicker:set_look_horizontal(3*(math.pi/2))
|
||||
elseif node.param2 == 2 then
|
||||
clicker:set_look_horizontal(math.pi)
|
||||
elseif node.param2 == 3 then
|
||||
clicker:set_look_horizontal(math.pi/2)
|
||||
end
|
||||
|
||||
minetest.log("action", "[xdecor] "..player_name.." sits down at "..minetest.pos_to_string(pos, 0))
|
||||
end
|
||||
end
|
||||
|
||||
-- Called when `digger` (a player object) wants to
|
||||
-- dig a node at pos. Returns true if it's allowed,
|
||||
-- false otherwise. This checks if the node at pos
|
||||
-- is an occupied sittable node.
|
||||
-- Can be used for the `can_dig` node function.
|
||||
function xdecor.sit_dig(pos, digger)
|
||||
if not mod_player_api then
|
||||
return true
|
||||
end
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if seats_occupied[hash] then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- To be called when a seat (sittable node) got destroyed
|
||||
-- to clean up state. Precisely, this should be used
|
||||
-- as the `after_destruct` handler.
|
||||
function xdecor.sit_destruct(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local occupier = seats_occupied[hash]
|
||||
if occupier then
|
||||
stand_up(occupier)
|
||||
seats_occupied[hash] = nil
|
||||
sitting[occupier] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Automatically cause players to stand up if they pressed a control
|
||||
-- or moved away from the seat
|
||||
minetest.register_globalstep(function(dtime)
|
||||
local to_stand_up = {}
|
||||
for player_name, sitting_pos in pairs(sitting) do
|
||||
local player = minetest.get_player_by_name(player_name)
|
||||
if player then
|
||||
local ctrl = player:get_player_control()
|
||||
if ctrl.up or ctrl.down or ctrl.left or ctrl.right or ctrl.sneak or ctrl.jump then
|
||||
table.insert(to_stand_up, player_name)
|
||||
elseif vector.distance(player:get_pos(), sitting_pos) > 0.55 then
|
||||
table.insert(to_stand_up, player_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
for s=1, #to_stand_up do
|
||||
stand_up(to_stand_up[s])
|
||||
end
|
||||
end)
|
||||
|
||||
-- Force player to stand on death (to the seat is released)
|
||||
minetest.register_on_dieplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
if sitting[player_name] then
|
||||
stand_up(player_name)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
if sitting[player_name] then
|
||||
local hash = minetest.hash_node_position(sitting[player_name])
|
||||
seats_occupied[hash] = nil
|
||||
sitting[player_name] = nil
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,170 @@
|
||||
-- Tile definitions for cut nodes of glass nodes:
|
||||
-- * Woodframed Glass (this mod)
|
||||
-- * Glass (Minetest Game)
|
||||
-- * Obsidian Glass (Minetest Game)
|
||||
-- This is done so the glass nodes still look nice
|
||||
-- when cut.
|
||||
-- If we would only use the base glass tile, most
|
||||
-- cut nodes look horrible because there are no
|
||||
-- clear contours.
|
||||
|
||||
local template_suffixes_glass = {
|
||||
stair = {
|
||||
"_split.png",
|
||||
".png",
|
||||
"_stairside.png^[transformFX",
|
||||
"_stairside.png",
|
||||
".png",
|
||||
"_split.png",
|
||||
},
|
||||
stair_inner = {
|
||||
"_stairside.png^[transformR270",
|
||||
".png",
|
||||
"_stairside.png^[transformFX",
|
||||
".png",
|
||||
".png",
|
||||
"_stairside.png",
|
||||
},
|
||||
stair_outer = {
|
||||
"_stairside.png^[transformR90",
|
||||
".png",
|
||||
"_outer_stairside.png",
|
||||
"_stairside.png",
|
||||
"_stairside.png^[transformR90",
|
||||
"_outer_stairside.png",
|
||||
},
|
||||
halfstair = {
|
||||
"_cube.png",
|
||||
".png",
|
||||
"_stairside.png^[transformFX",
|
||||
"_stairside.png",
|
||||
"_split.png^[transformR90",
|
||||
"_cube.png",
|
||||
},
|
||||
slab = {
|
||||
".png",
|
||||
".png",
|
||||
"_split.png",
|
||||
},
|
||||
cube = { "_cube.png" },
|
||||
thinstair = { "_split.png" },
|
||||
micropanel = { "_split.png" },
|
||||
panel = {
|
||||
"_split.png",
|
||||
"_split.png",
|
||||
"_cube.png",
|
||||
"_cube.png",
|
||||
"_split.png",
|
||||
},
|
||||
}
|
||||
|
||||
-- Template for "grass-covered" and similar nodes.
|
||||
-- This is defined in a way so that the cut nodes
|
||||
-- still have a natural-looking grass cover.
|
||||
-- !TOP and !BOTTOM are special and will be
|
||||
-- replaced via function argument.
|
||||
|
||||
local template_suffixes_grasscover = {
|
||||
stair = {
|
||||
"!TOP",
|
||||
"!BOTTOM",
|
||||
"_stairside.png^[transformFX",
|
||||
"_stairside.png",
|
||||
".png",
|
||||
"_split.png",
|
||||
},
|
||||
stair_inner = {
|
||||
"!TOP",
|
||||
"!BOTTOM",
|
||||
"_stairside.png^[transformFX",
|
||||
".png",
|
||||
".png",
|
||||
"_stairside.png",
|
||||
},
|
||||
stair_outer = {
|
||||
"!TOP",
|
||||
"!BOTTOM",
|
||||
"_outer_stairside.png",
|
||||
"_stairside.png",
|
||||
"_stairside.png^[transformR90",
|
||||
"_outer_stairside.png",
|
||||
},
|
||||
halfstair = {
|
||||
"!TOP",
|
||||
"!BOTTOM",
|
||||
"_stairside.png^[transformFX",
|
||||
"_stairside.png",
|
||||
".png",
|
||||
"_cube.png",
|
||||
},
|
||||
slab = {
|
||||
"!TOP",
|
||||
"!BOTTOM",
|
||||
"_split.png",
|
||||
},
|
||||
cube = { "!TOP", "!BOTTOM", "_cube.png" },
|
||||
thinstair = { "!TOP", "!BOTTOM", "_cube.png" },
|
||||
micropanel = { "!TOP", "!BOTTOM", "!TOP" },
|
||||
nanoslab = { "!TOP", "!BOTTOM", "!TOP" },
|
||||
microslab = { "!TOP", "!BOTTOM", "!TOP" },
|
||||
panel = {
|
||||
"!TOP",
|
||||
"!BOTTOM",
|
||||
"_cube.png",
|
||||
"_cube.png",
|
||||
"_split.png",
|
||||
},
|
||||
}
|
||||
|
||||
local generate_tilenames_glass = function(prefix, default_texture)
|
||||
if not default_texture then
|
||||
default_texture = prefix
|
||||
end
|
||||
local cuts = {}
|
||||
for t, tiles in pairs(template_suffixes_glass) do
|
||||
cuts[t] = {}
|
||||
for i=1, #tiles do
|
||||
if tiles[i] == ".png" then
|
||||
cuts[t][i] = default_texture .. tiles[i]
|
||||
else
|
||||
cuts[t][i] = prefix .. tiles[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
return cuts
|
||||
end
|
||||
|
||||
local generate_tilenames_grasscover = function(prefix, default_texture, base, top, bottom)
|
||||
if not default_texture then
|
||||
default_texture = prefix
|
||||
end
|
||||
local cuts = {}
|
||||
for t, tiles in pairs(template_suffixes_grasscover) do
|
||||
cuts[t] = {}
|
||||
for i=1, #tiles do
|
||||
if tiles[i] == "!TOP" then
|
||||
cuts[t][i] = {name=top, align_style="world"}
|
||||
elseif tiles[i] == "!BOTTOM" then
|
||||
cuts[t][i] = {name=bottom, align_style="world"}
|
||||
else
|
||||
if tiles[i] == ".png" then
|
||||
cuts[t][i] = default_texture .. tiles[i]
|
||||
else
|
||||
cuts[t][i] = prefix .. tiles[i]
|
||||
end
|
||||
if base then
|
||||
cuts[t][i] = base .. "^" .. cuts[t][i]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return cuts
|
||||
end
|
||||
|
||||
xdecor.glasscuts = {
|
||||
["xdecor:woodframed_glass"] = generate_tilenames_glass("xdecor_woodframed_glass"),
|
||||
["default:glass"] = generate_tilenames_glass("stairs_glass", "default_glass"),
|
||||
["default:obsidian_glass"] = generate_tilenames_glass("stairs_obsidian_glass", "default_obsidian_glass"),
|
||||
["default:permafrost_with_moss"] = generate_tilenames_grasscover("xdecor_permafrost_moss", "default_moss_side", "default_permafrost.png", "default_moss.png", "default_permafrost.png"),
|
||||
["default:permafrost_with_stones"] = generate_tilenames_grasscover("xdecor_permafrost_stones", "default_stones_side", "default_permafrost.png", "default_permafrost.png^default_stones.png", "default_permafrost.png"),
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
-- Returns the greatest numeric key in a table.
|
||||
function xdecor.maxn(T)
|
||||
local n = 0
|
||||
for k in pairs(T) do
|
||||
if k > n then
|
||||
n = k
|
||||
end
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
-- Returns the length of an hash table.
|
||||
function xdecor.tablelen(T)
|
||||
local n = 0
|
||||
|
||||
for _ in pairs(T) do
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
-- Deep copy of a table. Borrowed from mesecons mod (https://github.com/Jeija/minetest-mod-mesecons).
|
||||
function xdecor.tablecopy(T)
|
||||
if type(T) ~= "table" then
|
||||
return T -- No need to copy.
|
||||
end
|
||||
|
||||
local new = {}
|
||||
|
||||
for k, v in pairs(T) do
|
||||
if type(v) == "table" then
|
||||
new[k] = xdecor.tablecopy(v)
|
||||
else
|
||||
new[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return new
|
||||
end
|
||||
|
||||
function xdecor.stairs_valid_def(def)
|
||||
return (def.drawtype == "normal" or def.drawtype:sub(1,5) == "glass") and
|
||||
(def.groups.cracky or def.groups.choppy) and
|
||||
not def.on_construct and
|
||||
not def.after_place_node and
|
||||
not def.on_rightclick and
|
||||
not def.on_blast and
|
||||
not def.allow_metadata_inventory_take and
|
||||
not (def.groups.not_in_creative_inventory == 1) and
|
||||
not (def.groups.not_cuttable == 1) and
|
||||
not def.groups.wool and
|
||||
(def.tiles and type(def.tiles[1]) == "string" and not
|
||||
def.tiles[1]:find("default_mineral")) and
|
||||
not def.mesecons and
|
||||
def.description and
|
||||
def.description ~= "" and
|
||||
def.light_source == 0
|
||||
end
|
||||
|
||||
function xdecor.get_inventory_drops(pos, listnames)
|
||||
local drops = {}
|
||||
for l=1, #listnames do
|
||||
default.get_inventory_drops(pos, listnames[l], drops)
|
||||
end
|
||||
return drops
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
xdecor.box = {
|
||||
slab_y = function(height, shift)
|
||||
return {
|
||||
-0.5,
|
||||
-0.5 + (shift or 0),
|
||||
-0.5,
|
||||
0.5,
|
||||
-0.5 + height + (shift or 0),
|
||||
0.5
|
||||
}
|
||||
end,
|
||||
slab_z = function(depth)
|
||||
return {-0.5, -0.5, -0.5 + depth, 0.5, 0.5, 0.5}
|
||||
end,
|
||||
bar_y = function(radius)
|
||||
return {-radius, -0.5, -radius, radius, 0.5, radius}
|
||||
end,
|
||||
cuboid = function(radius_x, radius_y, radius_z)
|
||||
return {-radius_x, -radius_y, -radius_z, radius_x, radius_y, radius_z}
|
||||
end
|
||||
}
|
||||
|
||||
xdecor.nodebox = {
|
||||
regular = {type = "regular"},
|
||||
null = {
|
||||
type = "fixed", fixed = {0,0,0,0,0,0}
|
||||
}
|
||||
}
|
||||
|
||||
xdecor.pixelbox = function(size, boxes)
|
||||
local fixed = {}
|
||||
for _, box in ipairs(boxes) do
|
||||
-- `unpack` has been changed to `table.unpack` in newest Lua versions.
|
||||
local x, y, z, w, h, l = unpack(box)
|
||||
fixed[#fixed + 1] = {
|
||||
(x / size) - 0.5,
|
||||
(y / size) - 0.5,
|
||||
(z / size) - 0.5,
|
||||
((x + w) / size) - 0.5,
|
||||
((y + h) / size) - 0.5,
|
||||
((z + l) / size) - 0.5
|
||||
}
|
||||
end
|
||||
|
||||
return {type = "fixed", fixed = fixed}
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
|
||||
mt.__index = function(table, key)
|
||||
local ref = xdecor.box[key]
|
||||
local ref_type = type(ref)
|
||||
|
||||
if ref_type == "function" then
|
||||
return function(...)
|
||||
return {type = "fixed", fixed = ref(...)}
|
||||
end
|
||||
elseif ref_type == "table" then
|
||||
return {type = "fixed", fixed = ref}
|
||||
elseif ref_type == "nil" then
|
||||
error(key .. "could not be found among nodebox presets and functions")
|
||||
end
|
||||
|
||||
error("unexpected datatype " .. tostring(type(ref)) .. " while looking for " .. key)
|
||||
end
|
||||
|
||||
setmetatable(xdecor.nodebox, mt)
|
||||
@@ -0,0 +1,183 @@
|
||||
local S = minetest.get_translator("xdecor")
|
||||
|
||||
xdecor.xbg = default.gui_bg .. default.gui_bg_img .. default.gui_slots
|
||||
local default_inventory_size = 32
|
||||
|
||||
local default_inventory_formspecs = {
|
||||
["8"] = [[ size[8,6]
|
||||
list[context;main;0,0;8,1;]
|
||||
list[current_player;main;0,2;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;main] ]] ..
|
||||
default.get_hotbar_bg(0,2),
|
||||
|
||||
["16"] = [[ size[8,7]
|
||||
list[context;main;0,0;8,2;]
|
||||
list[current_player;main;0,3;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;main] ]] ..
|
||||
default.get_hotbar_bg(0,3),
|
||||
|
||||
["24"] = [[ size[8,8]
|
||||
list[context;main;0,0;8,3;]
|
||||
list[current_player;main;0,4;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;main]" ]] ..
|
||||
default.get_hotbar_bg(0,4),
|
||||
|
||||
["32"] = [[ size[8,9]
|
||||
list[context;main;0,0.3;8,4;]
|
||||
list[current_player;main;0,4.85;8,1;]
|
||||
list[current_player;main;0,6.08;8,3;8]
|
||||
listring[current_player;main]
|
||||
listring[context;main] ]] ..
|
||||
default.get_hotbar_bg(0,4.85)
|
||||
}
|
||||
|
||||
local function get_formspec_by_size(size)
|
||||
local formspec = default_inventory_formspecs[tostring(size)]
|
||||
return formspec or default_inventory_formspecs
|
||||
end
|
||||
|
||||
local default_can_dig = function(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end
|
||||
|
||||
local function xdecor_stairs_alternative(nodename, def)
|
||||
local mod, name = nodename:match("(.*):(.*)")
|
||||
|
||||
for groupname, value in pairs(def.groups) do
|
||||
if groupname ~= "cracky" and groupname ~= "choppy" and
|
||||
groupname ~= "flammable" and groupname ~= "crumbly" and
|
||||
groupname ~= "snappy" then
|
||||
def.groups.groupname = nil
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
stairsplus:register_all(
|
||||
mod,
|
||||
name,
|
||||
nodename,
|
||||
{
|
||||
description = def.description,
|
||||
tiles = def.tiles,
|
||||
groups = def.groups,
|
||||
sounds = def.sounds,
|
||||
}
|
||||
)
|
||||
elseif minetest.get_modpath("stairs") then
|
||||
local custom_tiles = xdecor.glasscuts[nodename]
|
||||
if custom_tiles and (custom_tiles.slab or custom_tiles.stair) then
|
||||
if custom_tiles.stair then
|
||||
stairs.register_stair(name, nodename,
|
||||
def.groups, custom_tiles.stair, S("@1 Stair", def.description),
|
||||
def.sounds)
|
||||
stairs.register_stair_inner(name, nodename,
|
||||
def.groups, custom_tiles.stair_inner, "", def.sounds, nil, S("Inner @1 Stair", def.description))
|
||||
stairs.register_stair_outer(name, nodename,
|
||||
def.groups, custom_tiles.stair_outer, "", def.sounds, nil, S("Outer @1 Stair", def.description))
|
||||
end
|
||||
if custom_tiles.slab then
|
||||
stairs.register_slab(name, nodename,
|
||||
def.groups, custom_tiles.slab, S("@1 Slab", def.description),
|
||||
def.sounds)
|
||||
end
|
||||
else
|
||||
stairs.register_stair_and_slab(name,nodename,
|
||||
def.groups,
|
||||
def.tiles,
|
||||
S("@1 Stair", def.description),
|
||||
S("@1 Slab", def.description),
|
||||
def.sounds, nil,
|
||||
S("Inner @1 Stair", def.description),
|
||||
S("Outer @1 Stair", def.description)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function xdecor.register(name, def)
|
||||
def.drawtype = def.drawtype or (def.mesh and "mesh") or (def.node_box and "nodebox")
|
||||
def.sounds = def.sounds or default.node_sound_defaults()
|
||||
|
||||
if not (def.drawtype == "normal" or def.drawtype == "signlike" or
|
||||
def.drawtype == "plantlike" or def.drawtype == "glasslike_framed" or
|
||||
def.drawtype == "glasslike_framed_optional") then
|
||||
def.paramtype2 = def.paramtype2 or "facedir"
|
||||
end
|
||||
|
||||
if def.sunlight_propagates ~= false and
|
||||
(def.drawtype == "plantlike" or def.drawtype == "torchlike" or
|
||||
def.drawtype == "signlike" or def.drawtype == "fencelike") then
|
||||
def.sunlight_propagates = true
|
||||
end
|
||||
|
||||
if not def.paramtype and
|
||||
(def.light_source or def.sunlight_propagates or
|
||||
def.drawtype == "nodebox" or def.drawtype == "mesh") then
|
||||
def.paramtype = "light"
|
||||
end
|
||||
|
||||
local infotext = def.infotext
|
||||
local inventory = def.inventory
|
||||
def.inventory = nil
|
||||
|
||||
if inventory then
|
||||
def.on_construct = def.on_construct or function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if infotext then meta:set_string("infotext", infotext) end
|
||||
|
||||
local size = inventory.size or default_inventory_size
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
inv:set_size("main", size)
|
||||
meta:set_string("formspec",
|
||||
(inventory.formspec or get_formspec_by_size(size)) .. xdecor.xbg)
|
||||
end
|
||||
|
||||
def.can_dig = def.can_dig or default_can_dig
|
||||
|
||||
elseif infotext and not def.on_construct then
|
||||
def.on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", infotext)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("xdecor:" .. name, def)
|
||||
|
||||
local workbench = minetest.settings:get_bool("enable_xdecor_workbench")
|
||||
|
||||
if workbench == false and
|
||||
(minetest.get_modpath("moreblocks") or minetest.get_modpath("stairs")) then
|
||||
if xdecor.stairs_valid_def(def) then
|
||||
xdecor_stairs_alternative("xdecor:"..name, def)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Registers aliases for a node that had a name collision
|
||||
-- with a node from the moreblocks mod
|
||||
function xdecor.register_legacy_aliases(original_basename, new_basename)
|
||||
minetest.register_alias("xdecor:"..original_basename, "xdecor:"..new_basename)
|
||||
minetest.register_alias("xdecor:"..original_basename.."_panel", "xdecor:"..new_basename.."_panel")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_doublepanel", "xdecor:"..new_basename.."_doublepanel")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_micropanel", "xdecor:"..new_basename.."_micropanel")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_halfstair", "xdecor:"..new_basename.."_halfstair")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_thinstair", "xdecor:"..new_basename.."_thinstair")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_cube", "xdecor:"..new_basename.."_cube")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_microslab", "xdecor:"..new_basename.."_microslab")
|
||||
minetest.register_alias("xdecor:"..original_basename.."_nanoslab", "xdecor:"..new_basename.."_nanoslab")
|
||||
if not minetest.get_modpath("moreblocks") then
|
||||
minetest.register_alias("stairs:slab_"..original_basename, "stairs:slab_"..new_basename)
|
||||
minetest.register_alias("stairs:stair_"..original_basename, "stairs:stair_"..new_basename)
|
||||
minetest.register_alias("stairs:stair_outer_"..original_basename, "stairs:stair_outer_"..new_basename)
|
||||
minetest.register_alias("stairs:stair_inner_"..original_basename, "stairs:stair_inner"..new_basename)
|
||||
end
|
||||
if minetest.get_modpath("stairsplus") and minetest.global_exists("stairsplus") and stairsplus.api then
|
||||
stairsplus.api.register_alias_all("xdecor:"..original_basename, "xdecor:"..new_basename)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Note: This is the original readme of the xdecor mod, added here for documentation/historic reasons.
|
||||
|
||||
## X-Decor ##
|
||||
|
||||
[](https://content.minetest.net/packages/jp/xdecor/)
|
||||
|
||||
A decoration mod meant to be simple and well-featured.
|
||||
It adds a bunch of cute cubes, various mechanisms and stuff for [cutting](https://forum.minetest.net/viewtopic.php?f=11&t=14085), [enchanting](https://forum.minetest.net/viewtopic.php?f=11&t=14087), cooking, etc.
|
||||
This mod is a lightweight alternative to HomeDecor and MoreBlocks.
|
||||
|
||||
### Requirements ###
|
||||
This mod requires at least version 5.1 of Minetest.
|
||||
|
||||
### Credits ###
|
||||
|
||||
Special thanks to Gambit for the textures from the PixelBOX pack for Minetest.
|
||||
Thanks to all contributors who keep this mod alive.
|
||||
|
||||

|
||||
@@ -0,0 +1,49 @@
|
||||
# Upgrading from X-Decor to X-Decor-libre
|
||||
|
||||
Here’s how you can upgrade from X-Decor to X-Decor-libre.
|
||||
|
||||
## Drop-in-replacement
|
||||
|
||||
If you used the original X-Decor before, this mod is a safe **drop-in-replacement**
|
||||
for X-Decor **provided you used no other mods that added new blocks**. X-Decor-libre
|
||||
is fully backwards-compatible in that case.
|
||||
|
||||
If other mods existed, X-Decor-libre is still a drop-in-replacement if
|
||||
the world contains no cut nodes that have been cut from nodes from
|
||||
a mod other than `default` or `xdecor`. (“classic” stairs, slabs, inner
|
||||
and outer slabs that already existed without X-Decor-libre are safe)
|
||||
|
||||
If this is the case, then you can just replace your X-Decor mod in your world
|
||||
with X-Decor-libre and you’re good to go.
|
||||
|
||||
If this for any reason still didn’t work, this is a bug, please report it at
|
||||
<https://codeberg.org/Wuzzy/xdecor-libre/issues>.
|
||||
|
||||
## Manual intervention required
|
||||
|
||||
If you used X-Decor before and you or any player in the world have cut any node
|
||||
at the workbench that is not from `xdecor` or the `default` mod, then those
|
||||
cut nodes will become **unknown nodes** after you upgrade. In that case,
|
||||
X-Decor-libre is **no longer a drop-in-replacement**, so a bit of manual
|
||||
intervention is required to make the upgrade to X-Decor-libre work.
|
||||
|
||||
(This was a change in version `libre6` that was done to remove clutter and
|
||||
prevent more and more potential naming collisions to build up. Versions
|
||||
before `libre6` added cut nodes too aggressively, potentially even overwriting
|
||||
existing nodes.)
|
||||
|
||||
Cut nodes end with `_panel`, `_cube`, `_thinstair`, `_microslab`, `_micropanel`,
|
||||
`_nanoslab`, `_halfstair` (but remember nodes with such names might come
|
||||
from other unrelated mods as well).
|
||||
|
||||
To fix the unknown nodes, you can add the `xdecor_autocut` mod which you find
|
||||
in the `extra_mods` directory. This will basically bring back the old
|
||||
behavior of X-Decor and old X-Decor-libre versions (before `libre6`), but
|
||||
is a bit more careful with adding nodes. Read `extra_mods/xdecor_autocut/README.md`
|
||||
for details.
|
||||
|
||||
Alternatively, you may decide to manually remove all unknown cut nodes from
|
||||
the world. This is more realistic if not many such nodes have been created
|
||||
so far.
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
### X-Decor-libre vs X-Decor
|
||||
|
||||
The original X-Decor mod is a popular mod in Luanti but it is (as the
|
||||
time of writing this text) non-free software, there are various files
|
||||
under proprietary licenses.
|
||||
|
||||
The purpose of this repository is to provide the community a fully-free fork of
|
||||
X-Decor with clearly documented licenses and to fix bugs.
|
||||
|
||||
#### New feature policy
|
||||
|
||||
X-Decor-libre is very careful with adding new features, especially new
|
||||
blocks and items, if they didn't exist in X-Decor before.
|
||||
|
||||
X-Decor-libre wants to preserve the features of the original mod
|
||||
as much as possible, but bug-free and under fully free licensing.
|
||||
|
||||
No major new additions are planned.
|
||||
|
||||
New blocks should be added only after very careful consideration
|
||||
to prevent bloat. Basically, only if they are a variant of an
|
||||
existing block and they feel an actual gap. Completely brand-new
|
||||
blocks should not be added.
|
||||
|
||||
The green chrysanthemum and black tulip have been added to
|
||||
complete the full set of flowers in Minetest Game 5.0.0 and
|
||||
later versions.
|
||||
|
||||
No major new gameplay features or changes are planned, except
|
||||
for maintenance and bugfixing.
|
||||
|
||||
#### List of changes
|
||||
|
||||
Compared to the original X-Decor mod, this is a list of changes
|
||||
that X-Decor-libre provides:
|
||||
|
||||
Important compatibilty-relevant change:
|
||||
|
||||
* Mod no longer registers cut nodes for mods other than `xdecor`
|
||||
and `default` (see `historic/upgrade.md` for details) (since
|
||||
version `libre6`)
|
||||
|
||||
Added blocks:
|
||||
|
||||
* Potted Green Chrysanthemum
|
||||
* Potted Black Tulip
|
||||
|
||||
Fixed bugs (as of 01/07/2023):
|
||||
|
||||
* Changed packed ice recipe to avoid recipe collision with Ethereal
|
||||
* Changed prison door recipe colliding with Minetest Game's Iron Bar Door
|
||||
* Beehives no longer show that the bees are busy when they're not
|
||||
* Fixed incorrect/incomplete node sounds
|
||||
* Fix poorly placed buttons in enchantment screen
|
||||
* Fix broken texture of cut Permafrost with Moss nodes
|
||||
* Fix awkward lantern rotation
|
||||
* Lanterns can no longer attach to sides
|
||||
* Fix item stacking issues of curtains
|
||||
* Cauldrons no longer turn river water to normal water
|
||||
* Fix boiling water in cauldrons not reliably cooling down
|
||||
* Fix boiling water sound not playing when rejoining
|
||||
* Fix cauldron with soup boiling forever
|
||||
* Fix cauldrons being heated up by fireflies
|
||||
* Fix rope and painting not compatible with itemframe
|
||||
* Fix itemframe, lever being offset when put into itemframe
|
||||
* Fix storage formspecs not closing if exploded
|
||||
* Show short item description in itemframe instead of itemstring
|
||||
* Minor typo fixes
|
||||
* Fix bad rope placement prediction
|
||||
* Fixed the broken Chess game
|
||||
|
||||
Maintenance updates:
|
||||
|
||||
* HUGE rework of Chess to make it actually be like real Chess (more or less)
|
||||
* New supported Chess rules (based on the FIDE Laws of Chess)
|
||||
* En passant
|
||||
* Choose your pawn promotion
|
||||
* Fixed incomplete enforcement of castling rule
|
||||
* 50-turn rule and 75-turn rule
|
||||
* Threefold repetition rule and fivefold repetition rule
|
||||
* Announce the winner or loser, or a drawn game
|
||||
* Many technical improvements for Chess
|
||||
* Renamed blocks:
|
||||
* "Empty Shelf" to "Plain Shelf"
|
||||
* "Slide Door" to "Paper Door"
|
||||
* "Rooster" to "Weathercock"
|
||||
* "Stone Tile" to "Polished Stone Block"
|
||||
* "Desert Stone Tile" to "Polished Desert Stone Block"
|
||||
* "Iron Light Box" to "Steel Lattice Light Box"
|
||||
* "Wooden Light Box" to "Wooden Cross Light Box"
|
||||
* "Wooden Light Box 2" to "Wooden Rhombus Light Box"
|
||||
* "Potted Rose" to "Potted Red Rose"
|
||||
* "Potted Tulip" to "Potted Orange Tulip"
|
||||
* "Potted Geranium" to "Potted Blue Geranium"
|
||||
* Added fuel recipes for wooden-based things
|
||||
* Changed a few confusing recipes to make more sense
|
||||
* Improved textures for cut glass, obsidian glass, wood-framed glass,
|
||||
permafrost with moss and permafrost with stones
|
||||
* Improved side texture of wood frame and rusty bar
|
||||
* Add honey and cushion block to creative inventory
|
||||
* Doors now count as nodes in creative inventory
|
||||
* Cobwebs are no longer considered (fake) liquids
|
||||
* Storage blocks now drop their inventory when exploded
|
||||
* Made several strings translatable
|
||||
* Translation updates
|
||||
* Add description to every setting
|
||||
* Add tooltip extensions for some interactive items (uses `tt` mod)
|
||||
* Add crafting guide support for `unified_inventory` mod (honey)
|
||||
* Rope no longer extends infinitely in Creative Mode
|
||||
* Added manual for Chess in `CHESS_README.md`
|
||||
|
||||
Note: This list may not be perfect, because a lot of other changes
|
||||
have been made since it was written.
|
||||
|
||||
#### List of replaced files
|
||||
|
||||
This is the list of non-free files in the original X-Decor mod
|
||||
(as of commit 8b614b3513f2719d5975c883180c011cb7428c8d)
|
||||
that X-Decor-libre replaces:
|
||||
|
||||
* `textures/xdecor_candle_hanging.png`
|
||||
* `textures/xdecor_radio_back.png`
|
||||
* `textures/xdecor_radio_front.png`
|
||||
* `textures/xdecor_radio_side.png`
|
||||
* `textures/xdecor_radio_top.png`
|
||||
* `textures/xdecor_rooster.png`
|
||||
* `textures/xdecor_speaker_back.png`
|
||||
* `textures/xdecor_speaker_front.png`
|
||||
* `textures/xdecor_speaker_side.png`
|
||||
* `textures/xdecor_speaker_top.png`
|
||||
* `sounds/xdecor_enchanting.ogg`
|
||||
|
||||
(see `LICENSE.txt` file for licensing).
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
--local t = os.clock()
|
||||
|
||||
xdecor = {}
|
||||
local modpath = minetest.get_modpath("xdecor")
|
||||
|
||||
dofile(modpath .. "/handlers/glasscut.lua")
|
||||
dofile(modpath .. "/handlers/animations.lua")
|
||||
dofile(modpath .. "/handlers/helpers.lua")
|
||||
dofile(modpath .. "/handlers/nodeboxes.lua")
|
||||
dofile(modpath .. "/handlers/registration.lua")
|
||||
|
||||
dofile(modpath .. "/src/nodes.lua")
|
||||
dofile(modpath .. "/src/recipes.lua")
|
||||
|
||||
-- Load modules that can be enabled and disabled by settings
|
||||
local subpart = {
|
||||
"chess",
|
||||
"cooking",
|
||||
"enchanting",
|
||||
"hive",
|
||||
"itemframe",
|
||||
"mailbox",
|
||||
"mechanisms",
|
||||
"rope",
|
||||
-- Workbench MUST be loaded after all other subparts that register nodes
|
||||
-- last for the default 'cut node' registrations to work
|
||||
"workbench",
|
||||
}
|
||||
|
||||
for _, name in ipairs(subpart) do
|
||||
local enable = minetest.settings:get_bool("enable_xdecor_" .. name, true)
|
||||
if enable then
|
||||
dofile(modpath .. "/src/" .. name .. ".lua")
|
||||
end
|
||||
end
|
||||
|
||||
-- Special case: enchanted tools. This code is split from enchanting to
|
||||
-- deal with loading order.
|
||||
-- Enchanted tools registered last because they depend on previous
|
||||
-- subparts
|
||||
local enable_enchanting = minetest.settings:get_bool("enable_xdecor_enchanting", true)
|
||||
if enable_enchanting then
|
||||
dofile(modpath .. "/src/enchanted_tools.lua")
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
name = xdecor
|
||||
title = X-Decor-libre
|
||||
description = A libre decoration mod meant to be simple and well-featured.
|
||||
depends = default, bucket, doors, farming, stairs, xpanes
|
||||
optional_depends = flowers, player_api, fire, moreblocks, mesecons, unified_inventory, tt, toolranks, wool, farming
|
||||
min_minetest_version = 5.9
|
||||
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,31 @@
|
||||
# For enabling components of X-Decor-libre.
|
||||
|
||||
|
||||
|
||||
# This adds a chessboard on which you can play Chess against other players or the computer.
|
||||
enable_xdecor_chess (Enable Chess) bool true
|
||||
|
||||
# This enables the cauldron which you can use to store water and cook soup.
|
||||
enable_xdecor_cooking (Enable Cooking) bool true
|
||||
|
||||
# This adds the enchanting table which you can use to upgrade your tools with mese crystals.
|
||||
enable_xdecor_enchanting (Enable Enchanting) bool true
|
||||
|
||||
# This adds the artificial beehive which produces honey.
|
||||
enable_xdecor_hive (Enable Hive) bool true
|
||||
|
||||
# This adds itemframes which are used to show off items on a wall.
|
||||
enable_xdecor_itemframe (Enable Itemframe) bool true
|
||||
|
||||
# This enables the mailbox which allows players to receive gifts from other players.
|
||||
enable_xdecor_mailbox (Enable Mailbox) bool true
|
||||
|
||||
# This adds mechanisms like the lever and pressure plate which are used to trigger doors.
|
||||
enable_xdecor_mechanisms (Enable Mechanisms) bool true
|
||||
|
||||
# This adds ropes which can be used for climbing.
|
||||
enable_xdecor_rope (Enable Rope) bool true
|
||||
|
||||
# This adds the workbench which allows you to craft, store items, cut blocks and repair tools.
|
||||
# Due to block cutting, many new block shapes become available.
|
||||
enable_xdecor_workbench (Enable Workbench) bool true
|
||||
@@ -0,0 +1,785 @@
|
||||
-- This Lua file is not actually meant to be executed. Instead, it contains
|
||||
-- dummy function calls to NS_CUT() to allow translation scripts to detect the strings
|
||||
-- for the cut nodes this mod registers by default.
|
||||
-- This should contain cut nodes for this mod and Minetest Game.
|
||||
-- It MUST be updated before release when new nodes have arrived.
|
||||
|
||||
-- To update this list, do this:
|
||||
-- 1. Set `GENERATE_TRANSLATABLE_STRING_LIST` in `src/workbench.lua` to `true`
|
||||
-- 2. Start Minetest Game with this mod enabled, but not any other mod that registers cut nodes
|
||||
-- 3. Paste the relevant part of the console output into this file
|
||||
|
||||
-- Note: This needs Luanti 5.14.0 or later to work.
|
||||
|
||||
local NS_CUT = function(s) return end
|
||||
|
||||
-- LIST OF STRINGS BEGINS HERE --
|
||||
|
||||
NS_CUT("Barrel Stair")
|
||||
NS_CUT("Barrel Slab")
|
||||
NS_CUT("Inner Barrel Stair")
|
||||
NS_CUT("Outer Barrel Stair")
|
||||
NS_CUT("Barrel Nanoslab")
|
||||
NS_CUT("Barrel Micropanel")
|
||||
NS_CUT("Barrel Microslab")
|
||||
NS_CUT("Barrel Thin Stair")
|
||||
NS_CUT("Barrel Cube")
|
||||
NS_CUT("Barrel Panel")
|
||||
NS_CUT("Barrel Double Panel")
|
||||
NS_CUT("Barrel Half-Stair")
|
||||
NS_CUT("Cactus Brick Stair")
|
||||
NS_CUT("Cactus Brick Slab")
|
||||
NS_CUT("Inner Cactus Brick Stair")
|
||||
NS_CUT("Outer Cactus Brick Stair")
|
||||
NS_CUT("Cactus Brick Nanoslab")
|
||||
NS_CUT("Cactus Brick Micropanel")
|
||||
NS_CUT("Cactus Brick Microslab")
|
||||
NS_CUT("Cactus Brick Thin Stair")
|
||||
NS_CUT("Cactus Brick Cube")
|
||||
NS_CUT("Cactus Brick Panel")
|
||||
NS_CUT("Cactus Brick Double Panel")
|
||||
NS_CUT("Cactus Brick Half-Stair")
|
||||
NS_CUT("Coal Stone Tile Stair")
|
||||
NS_CUT("Coal Stone Tile Slab")
|
||||
NS_CUT("Inner Coal Stone Tile Stair")
|
||||
NS_CUT("Outer Coal Stone Tile Stair")
|
||||
NS_CUT("Coal Stone Tile Nanoslab")
|
||||
NS_CUT("Coal Stone Tile Micropanel")
|
||||
NS_CUT("Coal Stone Tile Microslab")
|
||||
NS_CUT("Coal Stone Tile Thin Stair")
|
||||
NS_CUT("Coal Stone Tile Cube")
|
||||
NS_CUT("Coal Stone Tile Panel")
|
||||
NS_CUT("Coal Stone Tile Double Panel")
|
||||
NS_CUT("Coal Stone Tile Half-Stair")
|
||||
NS_CUT("Polished Desert Stone Block Stair")
|
||||
NS_CUT("Polished Desert Stone Block Slab")
|
||||
NS_CUT("Inner Polished Desert Stone Block Stair")
|
||||
NS_CUT("Outer Polished Desert Stone Block Stair")
|
||||
NS_CUT("Polished Desert Stone Block Nanoslab")
|
||||
NS_CUT("Polished Desert Stone Block Micropanel")
|
||||
NS_CUT("Polished Desert Stone Block Microslab")
|
||||
NS_CUT("Polished Desert Stone Block Thin Stair")
|
||||
NS_CUT("Polished Desert Stone Block Cube")
|
||||
NS_CUT("Polished Desert Stone Block Panel")
|
||||
NS_CUT("Polished Desert Stone Block Double Panel")
|
||||
NS_CUT("Polished Desert Stone Block Half-Stair")
|
||||
NS_CUT("Hardened Clay Stair")
|
||||
NS_CUT("Hardened Clay Slab")
|
||||
NS_CUT("Inner Hardened Clay Stair")
|
||||
NS_CUT("Outer Hardened Clay Stair")
|
||||
NS_CUT("Hardened Clay Nanoslab")
|
||||
NS_CUT("Hardened Clay Micropanel")
|
||||
NS_CUT("Hardened Clay Microslab")
|
||||
NS_CUT("Hardened Clay Thin Stair")
|
||||
NS_CUT("Hardened Clay Cube")
|
||||
NS_CUT("Hardened Clay Panel")
|
||||
NS_CUT("Hardened Clay Double Panel")
|
||||
NS_CUT("Hardened Clay Half-Stair")
|
||||
NS_CUT("Moon Brick Stair")
|
||||
NS_CUT("Moon Brick Slab")
|
||||
NS_CUT("Inner Moon Brick Stair")
|
||||
NS_CUT("Outer Moon Brick Stair")
|
||||
NS_CUT("Moon Brick Nanoslab")
|
||||
NS_CUT("Moon Brick Micropanel")
|
||||
NS_CUT("Moon Brick Microslab")
|
||||
NS_CUT("Moon Brick Thin Stair")
|
||||
NS_CUT("Moon Brick Cube")
|
||||
NS_CUT("Moon Brick Panel")
|
||||
NS_CUT("Moon Brick Double Panel")
|
||||
NS_CUT("Moon Brick Half-Stair")
|
||||
NS_CUT("Packed Ice Stair")
|
||||
NS_CUT("Packed Ice Slab")
|
||||
NS_CUT("Inner Packed Ice Stair")
|
||||
NS_CUT("Outer Packed Ice Stair")
|
||||
NS_CUT("Packed Ice Nanoslab")
|
||||
NS_CUT("Packed Ice Micropanel")
|
||||
NS_CUT("Packed Ice Microslab")
|
||||
NS_CUT("Packed Ice Thin Stair")
|
||||
NS_CUT("Packed Ice Cube")
|
||||
NS_CUT("Packed Ice Panel")
|
||||
NS_CUT("Packed Ice Double Panel")
|
||||
NS_CUT("Packed Ice Half-Stair")
|
||||
NS_CUT("Runestone Stair")
|
||||
NS_CUT("Runestone Slab")
|
||||
NS_CUT("Inner Runestone Stair")
|
||||
NS_CUT("Outer Runestone Stair")
|
||||
NS_CUT("Runestone Nanoslab")
|
||||
NS_CUT("Runestone Micropanel")
|
||||
NS_CUT("Runestone Microslab")
|
||||
NS_CUT("Runestone Thin Stair")
|
||||
NS_CUT("Runestone Cube")
|
||||
NS_CUT("Runestone Panel")
|
||||
NS_CUT("Runestone Double Panel")
|
||||
NS_CUT("Runestone Half-Stair")
|
||||
NS_CUT("Polished Stone Block Stair")
|
||||
NS_CUT("Polished Stone Block Slab")
|
||||
NS_CUT("Inner Polished Stone Block Stair")
|
||||
NS_CUT("Outer Polished Stone Block Stair")
|
||||
NS_CUT("Polished Stone Block Nanoslab")
|
||||
NS_CUT("Polished Stone Block Micropanel")
|
||||
NS_CUT("Polished Stone Block Microslab")
|
||||
NS_CUT("Polished Stone Block Thin Stair")
|
||||
NS_CUT("Polished Stone Block Cube")
|
||||
NS_CUT("Polished Stone Block Panel")
|
||||
NS_CUT("Polished Stone Block Double Panel")
|
||||
NS_CUT("Polished Stone Block Half-Stair")
|
||||
NS_CUT("Wooden Tile Stair")
|
||||
NS_CUT("Wooden Tile Slab")
|
||||
NS_CUT("Inner Wooden Tile Stair")
|
||||
NS_CUT("Outer Wooden Tile Stair")
|
||||
NS_CUT("Wooden Tile Nanoslab")
|
||||
NS_CUT("Wooden Tile Micropanel")
|
||||
NS_CUT("Wooden Tile Microslab")
|
||||
NS_CUT("Wooden Tile Thin Stair")
|
||||
NS_CUT("Wooden Tile Cube")
|
||||
NS_CUT("Wooden Tile Panel")
|
||||
NS_CUT("Wooden Tile Double Panel")
|
||||
NS_CUT("Wooden Tile Half-Stair")
|
||||
NS_CUT("Wood Framed Glass Stair")
|
||||
NS_CUT("Inner Wood Framed Glass Stair")
|
||||
NS_CUT("Outer Wood Framed Glass Stair")
|
||||
NS_CUT("Wood Framed Glass Slab")
|
||||
NS_CUT("Wood Framed Glass Nanoslab")
|
||||
NS_CUT("Wood Framed Glass Micropanel")
|
||||
NS_CUT("Wood Framed Glass Microslab")
|
||||
NS_CUT("Wood Framed Glass Thin Stair")
|
||||
NS_CUT("Wood Framed Glass Cube")
|
||||
NS_CUT("Wood Framed Glass Panel")
|
||||
NS_CUT("Wood Framed Glass Double Panel")
|
||||
NS_CUT("Wood Framed Glass Half-Stair")
|
||||
NS_CUT("Acacia Tree Stair")
|
||||
NS_CUT("Acacia Tree Slab")
|
||||
NS_CUT("Inner Acacia Tree Stair")
|
||||
NS_CUT("Outer Acacia Tree Stair")
|
||||
NS_CUT("Acacia Tree Nanoslab")
|
||||
NS_CUT("Acacia Tree Micropanel")
|
||||
NS_CUT("Acacia Tree Microslab")
|
||||
NS_CUT("Acacia Tree Thin Stair")
|
||||
NS_CUT("Acacia Tree Cube")
|
||||
NS_CUT("Acacia Tree Panel")
|
||||
NS_CUT("Acacia Tree Double Panel")
|
||||
NS_CUT("Acacia Tree Half-Stair")
|
||||
NS_CUT("Acacia Wood Planks Nanoslab")
|
||||
NS_CUT("Acacia Wood Planks Micropanel")
|
||||
NS_CUT("Acacia Wood Planks Microslab")
|
||||
NS_CUT("Acacia Wood Planks Thin Stair")
|
||||
NS_CUT("Acacia Wood Planks Cube")
|
||||
NS_CUT("Acacia Wood Planks Panel")
|
||||
NS_CUT("Acacia Wood Planks Double Panel")
|
||||
NS_CUT("Acacia Wood Planks Half-Stair")
|
||||
NS_CUT("Aspen Tree Stair")
|
||||
NS_CUT("Aspen Tree Slab")
|
||||
NS_CUT("Inner Aspen Tree Stair")
|
||||
NS_CUT("Outer Aspen Tree Stair")
|
||||
NS_CUT("Aspen Tree Nanoslab")
|
||||
NS_CUT("Aspen Tree Micropanel")
|
||||
NS_CUT("Aspen Tree Microslab")
|
||||
NS_CUT("Aspen Tree Thin Stair")
|
||||
NS_CUT("Aspen Tree Cube")
|
||||
NS_CUT("Aspen Tree Panel")
|
||||
NS_CUT("Aspen Tree Double Panel")
|
||||
NS_CUT("Aspen Tree Half-Stair")
|
||||
NS_CUT("Aspen Wood Planks Nanoslab")
|
||||
NS_CUT("Aspen Wood Planks Micropanel")
|
||||
NS_CUT("Aspen Wood Planks Microslab")
|
||||
NS_CUT("Aspen Wood Planks Thin Stair")
|
||||
NS_CUT("Aspen Wood Planks Cube")
|
||||
NS_CUT("Aspen Wood Planks Panel")
|
||||
NS_CUT("Aspen Wood Planks Double Panel")
|
||||
NS_CUT("Aspen Wood Planks Half-Stair")
|
||||
NS_CUT("Brick Block Nanoslab")
|
||||
NS_CUT("Brick Block Micropanel")
|
||||
NS_CUT("Brick Block Microslab")
|
||||
NS_CUT("Brick Block Thin Stair")
|
||||
NS_CUT("Brick Block Cube")
|
||||
NS_CUT("Brick Block Panel")
|
||||
NS_CUT("Brick Block Double Panel")
|
||||
NS_CUT("Brick Block Half-Stair")
|
||||
NS_CUT("Bronze Block Nanoslab")
|
||||
NS_CUT("Bronze Block Micropanel")
|
||||
NS_CUT("Bronze Block Microslab")
|
||||
NS_CUT("Bronze Block Thin Stair")
|
||||
NS_CUT("Bronze Block Cube")
|
||||
NS_CUT("Bronze Block Panel")
|
||||
NS_CUT("Bronze Block Double Panel")
|
||||
NS_CUT("Bronze Block Half-Stair")
|
||||
NS_CUT("Cactus Stair")
|
||||
NS_CUT("Cactus Slab")
|
||||
NS_CUT("Inner Cactus Stair")
|
||||
NS_CUT("Outer Cactus Stair")
|
||||
NS_CUT("Cactus Nanoslab")
|
||||
NS_CUT("Cactus Micropanel")
|
||||
NS_CUT("Cactus Microslab")
|
||||
NS_CUT("Cactus Thin Stair")
|
||||
NS_CUT("Cactus Cube")
|
||||
NS_CUT("Cactus Panel")
|
||||
NS_CUT("Cactus Double Panel")
|
||||
NS_CUT("Cactus Half-Stair")
|
||||
NS_CUT("Coal Block Stair")
|
||||
NS_CUT("Coal Block Slab")
|
||||
NS_CUT("Inner Coal Block Stair")
|
||||
NS_CUT("Outer Coal Block Stair")
|
||||
NS_CUT("Coal Block Nanoslab")
|
||||
NS_CUT("Coal Block Micropanel")
|
||||
NS_CUT("Coal Block Microslab")
|
||||
NS_CUT("Coal Block Thin Stair")
|
||||
NS_CUT("Coal Block Cube")
|
||||
NS_CUT("Coal Block Panel")
|
||||
NS_CUT("Coal Block Double Panel")
|
||||
NS_CUT("Coal Block Half-Stair")
|
||||
NS_CUT("Cobblestone Nanoslab")
|
||||
NS_CUT("Cobblestone Micropanel")
|
||||
NS_CUT("Cobblestone Microslab")
|
||||
NS_CUT("Cobblestone Thin Stair")
|
||||
NS_CUT("Cobblestone Cube")
|
||||
NS_CUT("Cobblestone Panel")
|
||||
NS_CUT("Cobblestone Double Panel")
|
||||
NS_CUT("Cobblestone Half-Stair")
|
||||
NS_CUT("Copper Block Nanoslab")
|
||||
NS_CUT("Copper Block Micropanel")
|
||||
NS_CUT("Copper Block Microslab")
|
||||
NS_CUT("Copper Block Thin Stair")
|
||||
NS_CUT("Copper Block Cube")
|
||||
NS_CUT("Copper Block Panel")
|
||||
NS_CUT("Copper Block Double Panel")
|
||||
NS_CUT("Copper Block Half-Stair")
|
||||
NS_CUT("Brown Coral Stair")
|
||||
NS_CUT("Brown Coral Slab")
|
||||
NS_CUT("Inner Brown Coral Stair")
|
||||
NS_CUT("Outer Brown Coral Stair")
|
||||
NS_CUT("Brown Coral Nanoslab")
|
||||
NS_CUT("Brown Coral Micropanel")
|
||||
NS_CUT("Brown Coral Microslab")
|
||||
NS_CUT("Brown Coral Thin Stair")
|
||||
NS_CUT("Brown Coral Cube")
|
||||
NS_CUT("Brown Coral Panel")
|
||||
NS_CUT("Brown Coral Double Panel")
|
||||
NS_CUT("Brown Coral Half-Stair")
|
||||
NS_CUT("Orange Coral Stair")
|
||||
NS_CUT("Orange Coral Slab")
|
||||
NS_CUT("Inner Orange Coral Stair")
|
||||
NS_CUT("Outer Orange Coral Stair")
|
||||
NS_CUT("Orange Coral Nanoslab")
|
||||
NS_CUT("Orange Coral Micropanel")
|
||||
NS_CUT("Orange Coral Microslab")
|
||||
NS_CUT("Orange Coral Thin Stair")
|
||||
NS_CUT("Orange Coral Cube")
|
||||
NS_CUT("Orange Coral Panel")
|
||||
NS_CUT("Orange Coral Double Panel")
|
||||
NS_CUT("Orange Coral Half-Stair")
|
||||
NS_CUT("Coral Skeleton Stair")
|
||||
NS_CUT("Coral Skeleton Slab")
|
||||
NS_CUT("Inner Coral Skeleton Stair")
|
||||
NS_CUT("Outer Coral Skeleton Stair")
|
||||
NS_CUT("Coral Skeleton Nanoslab")
|
||||
NS_CUT("Coral Skeleton Micropanel")
|
||||
NS_CUT("Coral Skeleton Microslab")
|
||||
NS_CUT("Coral Skeleton Thin Stair")
|
||||
NS_CUT("Coral Skeleton Cube")
|
||||
NS_CUT("Coral Skeleton Panel")
|
||||
NS_CUT("Coral Skeleton Double Panel")
|
||||
NS_CUT("Coral Skeleton Half-Stair")
|
||||
NS_CUT("Desert Cobblestone Nanoslab")
|
||||
NS_CUT("Desert Cobblestone Micropanel")
|
||||
NS_CUT("Desert Cobblestone Microslab")
|
||||
NS_CUT("Desert Cobblestone Thin Stair")
|
||||
NS_CUT("Desert Cobblestone Cube")
|
||||
NS_CUT("Desert Cobblestone Panel")
|
||||
NS_CUT("Desert Cobblestone Double Panel")
|
||||
NS_CUT("Desert Cobblestone Half-Stair")
|
||||
NS_CUT("Desert Sandstone Nanoslab")
|
||||
NS_CUT("Desert Sandstone Micropanel")
|
||||
NS_CUT("Desert Sandstone Microslab")
|
||||
NS_CUT("Desert Sandstone Thin Stair")
|
||||
NS_CUT("Desert Sandstone Cube")
|
||||
NS_CUT("Desert Sandstone Panel")
|
||||
NS_CUT("Desert Sandstone Double Panel")
|
||||
NS_CUT("Desert Sandstone Half-Stair")
|
||||
NS_CUT("Desert Sandstone Block Nanoslab")
|
||||
NS_CUT("Desert Sandstone Block Micropanel")
|
||||
NS_CUT("Desert Sandstone Block Microslab")
|
||||
NS_CUT("Desert Sandstone Block Thin Stair")
|
||||
NS_CUT("Desert Sandstone Block Cube")
|
||||
NS_CUT("Desert Sandstone Block Panel")
|
||||
NS_CUT("Desert Sandstone Block Double Panel")
|
||||
NS_CUT("Desert Sandstone Block Half-Stair")
|
||||
NS_CUT("Desert Sandstone Brick Nanoslab")
|
||||
NS_CUT("Desert Sandstone Brick Micropanel")
|
||||
NS_CUT("Desert Sandstone Brick Microslab")
|
||||
NS_CUT("Desert Sandstone Brick Thin Stair")
|
||||
NS_CUT("Desert Sandstone Brick Cube")
|
||||
NS_CUT("Desert Sandstone Brick Panel")
|
||||
NS_CUT("Desert Sandstone Brick Double Panel")
|
||||
NS_CUT("Desert Sandstone Brick Half-Stair")
|
||||
NS_CUT("Desert Stone Nanoslab")
|
||||
NS_CUT("Desert Stone Micropanel")
|
||||
NS_CUT("Desert Stone Microslab")
|
||||
NS_CUT("Desert Stone Thin Stair")
|
||||
NS_CUT("Desert Stone Cube")
|
||||
NS_CUT("Desert Stone Panel")
|
||||
NS_CUT("Desert Stone Double Panel")
|
||||
NS_CUT("Desert Stone Half-Stair")
|
||||
NS_CUT("Desert Stone Block Nanoslab")
|
||||
NS_CUT("Desert Stone Block Micropanel")
|
||||
NS_CUT("Desert Stone Block Microslab")
|
||||
NS_CUT("Desert Stone Block Thin Stair")
|
||||
NS_CUT("Desert Stone Block Cube")
|
||||
NS_CUT("Desert Stone Block Panel")
|
||||
NS_CUT("Desert Stone Block Double Panel")
|
||||
NS_CUT("Desert Stone Block Half-Stair")
|
||||
NS_CUT("Desert Stone Brick Nanoslab")
|
||||
NS_CUT("Desert Stone Brick Micropanel")
|
||||
NS_CUT("Desert Stone Brick Microslab")
|
||||
NS_CUT("Desert Stone Brick Thin Stair")
|
||||
NS_CUT("Desert Stone Brick Cube")
|
||||
NS_CUT("Desert Stone Brick Panel")
|
||||
NS_CUT("Desert Stone Brick Double Panel")
|
||||
NS_CUT("Desert Stone Brick Half-Stair")
|
||||
NS_CUT("Diamond Block Stair")
|
||||
NS_CUT("Diamond Block Slab")
|
||||
NS_CUT("Inner Diamond Block Stair")
|
||||
NS_CUT("Outer Diamond Block Stair")
|
||||
NS_CUT("Diamond Block Nanoslab")
|
||||
NS_CUT("Diamond Block Micropanel")
|
||||
NS_CUT("Diamond Block Microslab")
|
||||
NS_CUT("Diamond Block Thin Stair")
|
||||
NS_CUT("Diamond Block Cube")
|
||||
NS_CUT("Diamond Block Panel")
|
||||
NS_CUT("Diamond Block Double Panel")
|
||||
NS_CUT("Diamond Block Half-Stair")
|
||||
NS_CUT("Glass Nanoslab")
|
||||
NS_CUT("Glass Micropanel")
|
||||
NS_CUT("Glass Microslab")
|
||||
NS_CUT("Glass Thin Stair")
|
||||
NS_CUT("Glass Cube")
|
||||
NS_CUT("Glass Panel")
|
||||
NS_CUT("Glass Double Panel")
|
||||
NS_CUT("Glass Half-Stair")
|
||||
NS_CUT("Gold Block Nanoslab")
|
||||
NS_CUT("Gold Block Micropanel")
|
||||
NS_CUT("Gold Block Microslab")
|
||||
NS_CUT("Gold Block Thin Stair")
|
||||
NS_CUT("Gold Block Cube")
|
||||
NS_CUT("Gold Block Panel")
|
||||
NS_CUT("Gold Block Double Panel")
|
||||
NS_CUT("Gold Block Half-Stair")
|
||||
NS_CUT("Ice Nanoslab")
|
||||
NS_CUT("Ice Micropanel")
|
||||
NS_CUT("Ice Microslab")
|
||||
NS_CUT("Ice Thin Stair")
|
||||
NS_CUT("Ice Cube")
|
||||
NS_CUT("Ice Panel")
|
||||
NS_CUT("Ice Double Panel")
|
||||
NS_CUT("Ice Half-Stair")
|
||||
NS_CUT("Jungle Tree Stair")
|
||||
NS_CUT("Jungle Tree Slab")
|
||||
NS_CUT("Inner Jungle Tree Stair")
|
||||
NS_CUT("Outer Jungle Tree Stair")
|
||||
NS_CUT("Jungle Tree Nanoslab")
|
||||
NS_CUT("Jungle Tree Micropanel")
|
||||
NS_CUT("Jungle Tree Microslab")
|
||||
NS_CUT("Jungle Tree Thin Stair")
|
||||
NS_CUT("Jungle Tree Cube")
|
||||
NS_CUT("Jungle Tree Panel")
|
||||
NS_CUT("Jungle Tree Double Panel")
|
||||
NS_CUT("Jungle Tree Half-Stair")
|
||||
NS_CUT("Jungle Wood Planks Nanoslab")
|
||||
NS_CUT("Jungle Wood Planks Micropanel")
|
||||
NS_CUT("Jungle Wood Planks Microslab")
|
||||
NS_CUT("Jungle Wood Planks Thin Stair")
|
||||
NS_CUT("Jungle Wood Planks Cube")
|
||||
NS_CUT("Jungle Wood Planks Panel")
|
||||
NS_CUT("Jungle Wood Planks Double Panel")
|
||||
NS_CUT("Jungle Wood Planks Half-Stair")
|
||||
NS_CUT("Mossy Cobblestone Nanoslab")
|
||||
NS_CUT("Mossy Cobblestone Micropanel")
|
||||
NS_CUT("Mossy Cobblestone Microslab")
|
||||
NS_CUT("Mossy Cobblestone Thin Stair")
|
||||
NS_CUT("Mossy Cobblestone Cube")
|
||||
NS_CUT("Mossy Cobblestone Panel")
|
||||
NS_CUT("Mossy Cobblestone Double Panel")
|
||||
NS_CUT("Mossy Cobblestone Half-Stair")
|
||||
NS_CUT("Obsidian Nanoslab")
|
||||
NS_CUT("Obsidian Micropanel")
|
||||
NS_CUT("Obsidian Microslab")
|
||||
NS_CUT("Obsidian Thin Stair")
|
||||
NS_CUT("Obsidian Cube")
|
||||
NS_CUT("Obsidian Panel")
|
||||
NS_CUT("Obsidian Double Panel")
|
||||
NS_CUT("Obsidian Half-Stair")
|
||||
NS_CUT("Obsidian Block Nanoslab")
|
||||
NS_CUT("Obsidian Block Micropanel")
|
||||
NS_CUT("Obsidian Block Microslab")
|
||||
NS_CUT("Obsidian Block Thin Stair")
|
||||
NS_CUT("Obsidian Block Cube")
|
||||
NS_CUT("Obsidian Block Panel")
|
||||
NS_CUT("Obsidian Block Double Panel")
|
||||
NS_CUT("Obsidian Block Half-Stair")
|
||||
NS_CUT("Obsidian Glass Nanoslab")
|
||||
NS_CUT("Obsidian Glass Micropanel")
|
||||
NS_CUT("Obsidian Glass Microslab")
|
||||
NS_CUT("Obsidian Glass Thin Stair")
|
||||
NS_CUT("Obsidian Glass Cube")
|
||||
NS_CUT("Obsidian Glass Panel")
|
||||
NS_CUT("Obsidian Glass Double Panel")
|
||||
NS_CUT("Obsidian Glass Half-Stair")
|
||||
NS_CUT("Obsidian Brick Nanoslab")
|
||||
NS_CUT("Obsidian Brick Micropanel")
|
||||
NS_CUT("Obsidian Brick Microslab")
|
||||
NS_CUT("Obsidian Brick Thin Stair")
|
||||
NS_CUT("Obsidian Brick Cube")
|
||||
NS_CUT("Obsidian Brick Panel")
|
||||
NS_CUT("Obsidian Brick Double Panel")
|
||||
NS_CUT("Obsidian Brick Half-Stair")
|
||||
NS_CUT("Permafrost Stair")
|
||||
NS_CUT("Permafrost Slab")
|
||||
NS_CUT("Inner Permafrost Stair")
|
||||
NS_CUT("Outer Permafrost Stair")
|
||||
NS_CUT("Permafrost Nanoslab")
|
||||
NS_CUT("Permafrost Micropanel")
|
||||
NS_CUT("Permafrost Microslab")
|
||||
NS_CUT("Permafrost Thin Stair")
|
||||
NS_CUT("Permafrost Cube")
|
||||
NS_CUT("Permafrost Panel")
|
||||
NS_CUT("Permafrost Double Panel")
|
||||
NS_CUT("Permafrost Half-Stair")
|
||||
NS_CUT("Permafrost with Moss Stair")
|
||||
NS_CUT("Inner Permafrost with Moss Stair")
|
||||
NS_CUT("Outer Permafrost with Moss Stair")
|
||||
NS_CUT("Permafrost with Moss Slab")
|
||||
NS_CUT("Permafrost with Moss Nanoslab")
|
||||
NS_CUT("Permafrost with Moss Micropanel")
|
||||
NS_CUT("Permafrost with Moss Microslab")
|
||||
NS_CUT("Permafrost with Moss Thin Stair")
|
||||
NS_CUT("Permafrost with Moss Cube")
|
||||
NS_CUT("Permafrost with Moss Panel")
|
||||
NS_CUT("Permafrost with Moss Double Panel")
|
||||
NS_CUT("Permafrost with Moss Half-Stair")
|
||||
NS_CUT("Permafrost with Stones Stair")
|
||||
NS_CUT("Inner Permafrost with Stones Stair")
|
||||
NS_CUT("Outer Permafrost with Stones Stair")
|
||||
NS_CUT("Permafrost with Stones Slab")
|
||||
NS_CUT("Permafrost with Stones Nanoslab")
|
||||
NS_CUT("Permafrost with Stones Micropanel")
|
||||
NS_CUT("Permafrost with Stones Microslab")
|
||||
NS_CUT("Permafrost with Stones Thin Stair")
|
||||
NS_CUT("Permafrost with Stones Cube")
|
||||
NS_CUT("Permafrost with Stones Panel")
|
||||
NS_CUT("Permafrost with Stones Double Panel")
|
||||
NS_CUT("Permafrost with Stones Half-Stair")
|
||||
NS_CUT("Pine Tree Stair")
|
||||
NS_CUT("Pine Tree Slab")
|
||||
NS_CUT("Inner Pine Tree Stair")
|
||||
NS_CUT("Outer Pine Tree Stair")
|
||||
NS_CUT("Pine Tree Nanoslab")
|
||||
NS_CUT("Pine Tree Micropanel")
|
||||
NS_CUT("Pine Tree Microslab")
|
||||
NS_CUT("Pine Tree Thin Stair")
|
||||
NS_CUT("Pine Tree Cube")
|
||||
NS_CUT("Pine Tree Panel")
|
||||
NS_CUT("Pine Tree Double Panel")
|
||||
NS_CUT("Pine Tree Half-Stair")
|
||||
NS_CUT("Pine Wood Planks Nanoslab")
|
||||
NS_CUT("Pine Wood Planks Micropanel")
|
||||
NS_CUT("Pine Wood Planks Microslab")
|
||||
NS_CUT("Pine Wood Planks Thin Stair")
|
||||
NS_CUT("Pine Wood Planks Cube")
|
||||
NS_CUT("Pine Wood Planks Panel")
|
||||
NS_CUT("Pine Wood Planks Double Panel")
|
||||
NS_CUT("Pine Wood Planks Half-Stair")
|
||||
NS_CUT("Sandstone Nanoslab")
|
||||
NS_CUT("Sandstone Micropanel")
|
||||
NS_CUT("Sandstone Microslab")
|
||||
NS_CUT("Sandstone Thin Stair")
|
||||
NS_CUT("Sandstone Cube")
|
||||
NS_CUT("Sandstone Panel")
|
||||
NS_CUT("Sandstone Double Panel")
|
||||
NS_CUT("Sandstone Half-Stair")
|
||||
NS_CUT("Sandstone Block Nanoslab")
|
||||
NS_CUT("Sandstone Block Micropanel")
|
||||
NS_CUT("Sandstone Block Microslab")
|
||||
NS_CUT("Sandstone Block Thin Stair")
|
||||
NS_CUT("Sandstone Block Cube")
|
||||
NS_CUT("Sandstone Block Panel")
|
||||
NS_CUT("Sandstone Block Double Panel")
|
||||
NS_CUT("Sandstone Block Half-Stair")
|
||||
NS_CUT("Sandstone Brick Nanoslab")
|
||||
NS_CUT("Sandstone Brick Micropanel")
|
||||
NS_CUT("Sandstone Brick Microslab")
|
||||
NS_CUT("Sandstone Brick Thin Stair")
|
||||
NS_CUT("Sandstone Brick Cube")
|
||||
NS_CUT("Sandstone Brick Panel")
|
||||
NS_CUT("Sandstone Brick Double Panel")
|
||||
NS_CUT("Sandstone Brick Half-Stair")
|
||||
NS_CUT("Silver Sandstone Nanoslab")
|
||||
NS_CUT("Silver Sandstone Micropanel")
|
||||
NS_CUT("Silver Sandstone Microslab")
|
||||
NS_CUT("Silver Sandstone Thin Stair")
|
||||
NS_CUT("Silver Sandstone Cube")
|
||||
NS_CUT("Silver Sandstone Panel")
|
||||
NS_CUT("Silver Sandstone Double Panel")
|
||||
NS_CUT("Silver Sandstone Half-Stair")
|
||||
NS_CUT("Silver Sandstone Block Nanoslab")
|
||||
NS_CUT("Silver Sandstone Block Micropanel")
|
||||
NS_CUT("Silver Sandstone Block Microslab")
|
||||
NS_CUT("Silver Sandstone Block Thin Stair")
|
||||
NS_CUT("Silver Sandstone Block Cube")
|
||||
NS_CUT("Silver Sandstone Block Panel")
|
||||
NS_CUT("Silver Sandstone Block Double Panel")
|
||||
NS_CUT("Silver Sandstone Block Half-Stair")
|
||||
NS_CUT("Silver Sandstone Brick Nanoslab")
|
||||
NS_CUT("Silver Sandstone Brick Micropanel")
|
||||
NS_CUT("Silver Sandstone Brick Microslab")
|
||||
NS_CUT("Silver Sandstone Brick Thin Stair")
|
||||
NS_CUT("Silver Sandstone Brick Cube")
|
||||
NS_CUT("Silver Sandstone Brick Panel")
|
||||
NS_CUT("Silver Sandstone Brick Double Panel")
|
||||
NS_CUT("Silver Sandstone Brick Half-Stair")
|
||||
NS_CUT("Steel Block Nanoslab")
|
||||
NS_CUT("Steel Block Micropanel")
|
||||
NS_CUT("Steel Block Microslab")
|
||||
NS_CUT("Steel Block Thin Stair")
|
||||
NS_CUT("Steel Block Cube")
|
||||
NS_CUT("Steel Block Panel")
|
||||
NS_CUT("Steel Block Double Panel")
|
||||
NS_CUT("Steel Block Half-Stair")
|
||||
NS_CUT("Stone Nanoslab")
|
||||
NS_CUT("Stone Micropanel")
|
||||
NS_CUT("Stone Microslab")
|
||||
NS_CUT("Stone Thin Stair")
|
||||
NS_CUT("Stone Cube")
|
||||
NS_CUT("Stone Panel")
|
||||
NS_CUT("Stone Double Panel")
|
||||
NS_CUT("Stone Half-Stair")
|
||||
NS_CUT("Stone Block Nanoslab")
|
||||
NS_CUT("Stone Block Micropanel")
|
||||
NS_CUT("Stone Block Microslab")
|
||||
NS_CUT("Stone Block Thin Stair")
|
||||
NS_CUT("Stone Block Cube")
|
||||
NS_CUT("Stone Block Panel")
|
||||
NS_CUT("Stone Block Double Panel")
|
||||
NS_CUT("Stone Block Half-Stair")
|
||||
NS_CUT("Stone Brick Nanoslab")
|
||||
NS_CUT("Stone Brick Micropanel")
|
||||
NS_CUT("Stone Brick Microslab")
|
||||
NS_CUT("Stone Brick Thin Stair")
|
||||
NS_CUT("Stone Brick Cube")
|
||||
NS_CUT("Stone Brick Panel")
|
||||
NS_CUT("Stone Brick Double Panel")
|
||||
NS_CUT("Stone Brick Half-Stair")
|
||||
NS_CUT("Tin Block Nanoslab")
|
||||
NS_CUT("Tin Block Micropanel")
|
||||
NS_CUT("Tin Block Microslab")
|
||||
NS_CUT("Tin Block Thin Stair")
|
||||
NS_CUT("Tin Block Cube")
|
||||
NS_CUT("Tin Block Panel")
|
||||
NS_CUT("Tin Block Double Panel")
|
||||
NS_CUT("Tin Block Half-Stair")
|
||||
NS_CUT("Apple Tree Stair")
|
||||
NS_CUT("Apple Tree Slab")
|
||||
NS_CUT("Inner Apple Tree Stair")
|
||||
NS_CUT("Outer Apple Tree Stair")
|
||||
NS_CUT("Apple Tree Nanoslab")
|
||||
NS_CUT("Apple Tree Micropanel")
|
||||
NS_CUT("Apple Tree Microslab")
|
||||
NS_CUT("Apple Tree Thin Stair")
|
||||
NS_CUT("Apple Tree Cube")
|
||||
NS_CUT("Apple Tree Panel")
|
||||
NS_CUT("Apple Tree Double Panel")
|
||||
NS_CUT("Apple Tree Half-Stair")
|
||||
NS_CUT("Apple Wood Planks Nanoslab")
|
||||
NS_CUT("Apple Wood Planks Micropanel")
|
||||
NS_CUT("Apple Wood Planks Microslab")
|
||||
NS_CUT("Apple Wood Planks Thin Stair")
|
||||
NS_CUT("Apple Wood Planks Cube")
|
||||
NS_CUT("Apple Wood Planks Panel")
|
||||
NS_CUT("Apple Wood Planks Double Panel")
|
||||
NS_CUT("Apple Wood Planks Half-Stair")
|
||||
NS_CUT("Straw Nanoslab")
|
||||
NS_CUT("Straw Micropanel")
|
||||
NS_CUT("Straw Microslab")
|
||||
NS_CUT("Straw Thin Stair")
|
||||
NS_CUT("Straw Cube")
|
||||
NS_CUT("Straw Panel")
|
||||
NS_CUT("Straw Double Panel")
|
||||
NS_CUT("Straw Half-Stair")
|
||||
NS_CUT("Black Wool Stair")
|
||||
NS_CUT("Black Wool Slab")
|
||||
NS_CUT("Inner Black Wool Stair")
|
||||
NS_CUT("Outer Black Wool Stair")
|
||||
NS_CUT("Black Wool Nanoslab")
|
||||
NS_CUT("Black Wool Micropanel")
|
||||
NS_CUT("Black Wool Microslab")
|
||||
NS_CUT("Black Wool Thin Stair")
|
||||
NS_CUT("Black Wool Cube")
|
||||
NS_CUT("Black Wool Panel")
|
||||
NS_CUT("Black Wool Double Panel")
|
||||
NS_CUT("Black Wool Half-Stair")
|
||||
NS_CUT("Blue Wool Stair")
|
||||
NS_CUT("Blue Wool Slab")
|
||||
NS_CUT("Inner Blue Wool Stair")
|
||||
NS_CUT("Outer Blue Wool Stair")
|
||||
NS_CUT("Blue Wool Nanoslab")
|
||||
NS_CUT("Blue Wool Micropanel")
|
||||
NS_CUT("Blue Wool Microslab")
|
||||
NS_CUT("Blue Wool Thin Stair")
|
||||
NS_CUT("Blue Wool Cube")
|
||||
NS_CUT("Blue Wool Panel")
|
||||
NS_CUT("Blue Wool Double Panel")
|
||||
NS_CUT("Blue Wool Half-Stair")
|
||||
NS_CUT("Brown Wool Stair")
|
||||
NS_CUT("Brown Wool Slab")
|
||||
NS_CUT("Inner Brown Wool Stair")
|
||||
NS_CUT("Outer Brown Wool Stair")
|
||||
NS_CUT("Brown Wool Nanoslab")
|
||||
NS_CUT("Brown Wool Micropanel")
|
||||
NS_CUT("Brown Wool Microslab")
|
||||
NS_CUT("Brown Wool Thin Stair")
|
||||
NS_CUT("Brown Wool Cube")
|
||||
NS_CUT("Brown Wool Panel")
|
||||
NS_CUT("Brown Wool Double Panel")
|
||||
NS_CUT("Brown Wool Half-Stair")
|
||||
NS_CUT("Cyan Wool Stair")
|
||||
NS_CUT("Cyan Wool Slab")
|
||||
NS_CUT("Inner Cyan Wool Stair")
|
||||
NS_CUT("Outer Cyan Wool Stair")
|
||||
NS_CUT("Cyan Wool Nanoslab")
|
||||
NS_CUT("Cyan Wool Micropanel")
|
||||
NS_CUT("Cyan Wool Microslab")
|
||||
NS_CUT("Cyan Wool Thin Stair")
|
||||
NS_CUT("Cyan Wool Cube")
|
||||
NS_CUT("Cyan Wool Panel")
|
||||
NS_CUT("Cyan Wool Double Panel")
|
||||
NS_CUT("Cyan Wool Half-Stair")
|
||||
NS_CUT("Dark Green Wool Stair")
|
||||
NS_CUT("Dark Green Wool Slab")
|
||||
NS_CUT("Inner Dark Green Wool Stair")
|
||||
NS_CUT("Outer Dark Green Wool Stair")
|
||||
NS_CUT("Dark Green Wool Nanoslab")
|
||||
NS_CUT("Dark Green Wool Micropanel")
|
||||
NS_CUT("Dark Green Wool Microslab")
|
||||
NS_CUT("Dark Green Wool Thin Stair")
|
||||
NS_CUT("Dark Green Wool Cube")
|
||||
NS_CUT("Dark Green Wool Panel")
|
||||
NS_CUT("Dark Green Wool Double Panel")
|
||||
NS_CUT("Dark Green Wool Half-Stair")
|
||||
NS_CUT("Dark Grey Wool Stair")
|
||||
NS_CUT("Dark Grey Wool Slab")
|
||||
NS_CUT("Inner Dark Grey Wool Stair")
|
||||
NS_CUT("Outer Dark Grey Wool Stair")
|
||||
NS_CUT("Dark Grey Wool Nanoslab")
|
||||
NS_CUT("Dark Grey Wool Micropanel")
|
||||
NS_CUT("Dark Grey Wool Microslab")
|
||||
NS_CUT("Dark Grey Wool Thin Stair")
|
||||
NS_CUT("Dark Grey Wool Cube")
|
||||
NS_CUT("Dark Grey Wool Panel")
|
||||
NS_CUT("Dark Grey Wool Double Panel")
|
||||
NS_CUT("Dark Grey Wool Half-Stair")
|
||||
NS_CUT("Green Wool Stair")
|
||||
NS_CUT("Green Wool Slab")
|
||||
NS_CUT("Inner Green Wool Stair")
|
||||
NS_CUT("Outer Green Wool Stair")
|
||||
NS_CUT("Green Wool Nanoslab")
|
||||
NS_CUT("Green Wool Micropanel")
|
||||
NS_CUT("Green Wool Microslab")
|
||||
NS_CUT("Green Wool Thin Stair")
|
||||
NS_CUT("Green Wool Cube")
|
||||
NS_CUT("Green Wool Panel")
|
||||
NS_CUT("Green Wool Double Panel")
|
||||
NS_CUT("Green Wool Half-Stair")
|
||||
NS_CUT("Grey Wool Stair")
|
||||
NS_CUT("Grey Wool Slab")
|
||||
NS_CUT("Inner Grey Wool Stair")
|
||||
NS_CUT("Outer Grey Wool Stair")
|
||||
NS_CUT("Grey Wool Nanoslab")
|
||||
NS_CUT("Grey Wool Micropanel")
|
||||
NS_CUT("Grey Wool Microslab")
|
||||
NS_CUT("Grey Wool Thin Stair")
|
||||
NS_CUT("Grey Wool Cube")
|
||||
NS_CUT("Grey Wool Panel")
|
||||
NS_CUT("Grey Wool Double Panel")
|
||||
NS_CUT("Grey Wool Half-Stair")
|
||||
NS_CUT("Magenta Wool Stair")
|
||||
NS_CUT("Magenta Wool Slab")
|
||||
NS_CUT("Inner Magenta Wool Stair")
|
||||
NS_CUT("Outer Magenta Wool Stair")
|
||||
NS_CUT("Magenta Wool Nanoslab")
|
||||
NS_CUT("Magenta Wool Micropanel")
|
||||
NS_CUT("Magenta Wool Microslab")
|
||||
NS_CUT("Magenta Wool Thin Stair")
|
||||
NS_CUT("Magenta Wool Cube")
|
||||
NS_CUT("Magenta Wool Panel")
|
||||
NS_CUT("Magenta Wool Double Panel")
|
||||
NS_CUT("Magenta Wool Half-Stair")
|
||||
NS_CUT("Orange Wool Stair")
|
||||
NS_CUT("Orange Wool Slab")
|
||||
NS_CUT("Inner Orange Wool Stair")
|
||||
NS_CUT("Outer Orange Wool Stair")
|
||||
NS_CUT("Orange Wool Nanoslab")
|
||||
NS_CUT("Orange Wool Micropanel")
|
||||
NS_CUT("Orange Wool Microslab")
|
||||
NS_CUT("Orange Wool Thin Stair")
|
||||
NS_CUT("Orange Wool Cube")
|
||||
NS_CUT("Orange Wool Panel")
|
||||
NS_CUT("Orange Wool Double Panel")
|
||||
NS_CUT("Orange Wool Half-Stair")
|
||||
NS_CUT("Pink Wool Stair")
|
||||
NS_CUT("Pink Wool Slab")
|
||||
NS_CUT("Inner Pink Wool Stair")
|
||||
NS_CUT("Outer Pink Wool Stair")
|
||||
NS_CUT("Pink Wool Nanoslab")
|
||||
NS_CUT("Pink Wool Micropanel")
|
||||
NS_CUT("Pink Wool Microslab")
|
||||
NS_CUT("Pink Wool Thin Stair")
|
||||
NS_CUT("Pink Wool Cube")
|
||||
NS_CUT("Pink Wool Panel")
|
||||
NS_CUT("Pink Wool Double Panel")
|
||||
NS_CUT("Pink Wool Half-Stair")
|
||||
NS_CUT("Red Wool Stair")
|
||||
NS_CUT("Red Wool Slab")
|
||||
NS_CUT("Inner Red Wool Stair")
|
||||
NS_CUT("Outer Red Wool Stair")
|
||||
NS_CUT("Red Wool Nanoslab")
|
||||
NS_CUT("Red Wool Micropanel")
|
||||
NS_CUT("Red Wool Microslab")
|
||||
NS_CUT("Red Wool Thin Stair")
|
||||
NS_CUT("Red Wool Cube")
|
||||
NS_CUT("Red Wool Panel")
|
||||
NS_CUT("Red Wool Double Panel")
|
||||
NS_CUT("Red Wool Half-Stair")
|
||||
NS_CUT("Violet Wool Stair")
|
||||
NS_CUT("Violet Wool Slab")
|
||||
NS_CUT("Inner Violet Wool Stair")
|
||||
NS_CUT("Outer Violet Wool Stair")
|
||||
NS_CUT("Violet Wool Nanoslab")
|
||||
NS_CUT("Violet Wool Micropanel")
|
||||
NS_CUT("Violet Wool Microslab")
|
||||
NS_CUT("Violet Wool Thin Stair")
|
||||
NS_CUT("Violet Wool Cube")
|
||||
NS_CUT("Violet Wool Panel")
|
||||
NS_CUT("Violet Wool Double Panel")
|
||||
NS_CUT("Violet Wool Half-Stair")
|
||||
NS_CUT("White Wool Stair")
|
||||
NS_CUT("White Wool Slab")
|
||||
NS_CUT("Inner White Wool Stair")
|
||||
NS_CUT("Outer White Wool Stair")
|
||||
NS_CUT("White Wool Nanoslab")
|
||||
NS_CUT("White Wool Micropanel")
|
||||
NS_CUT("White Wool Microslab")
|
||||
NS_CUT("White Wool Thin Stair")
|
||||
NS_CUT("White Wool Cube")
|
||||
NS_CUT("White Wool Panel")
|
||||
NS_CUT("White Wool Double Panel")
|
||||
NS_CUT("White Wool Half-Stair")
|
||||
NS_CUT("Yellow Wool Stair")
|
||||
NS_CUT("Yellow Wool Slab")
|
||||
NS_CUT("Inner Yellow Wool Stair")
|
||||
NS_CUT("Outer Yellow Wool Stair")
|
||||
NS_CUT("Yellow Wool Nanoslab")
|
||||
NS_CUT("Yellow Wool Micropanel")
|
||||
NS_CUT("Yellow Wool Microslab")
|
||||
NS_CUT("Yellow Wool Thin Stair")
|
||||
NS_CUT("Yellow Wool Cube")
|
||||
NS_CUT("Yellow Wool Panel")
|
||||
NS_CUT("Yellow Wool Double Panel")
|
||||
NS_CUT("Yellow Wool Half-Stair")
|
||||
@@ -0,0 +1,154 @@
|
||||
local chessbot = {}
|
||||
|
||||
local active_jobs = {}
|
||||
|
||||
chessbot.cancel_job = function(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if active_jobs[hash] then
|
||||
active_jobs[hash]:cancel()
|
||||
active_jobs[hash] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local realchess = xdecor.chess
|
||||
|
||||
local get_game_state = function(meta)
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
local color
|
||||
if lastMove == "black" or lastMove == "" then
|
||||
color = "white"
|
||||
else
|
||||
color = "black"
|
||||
end
|
||||
return {
|
||||
botColor = color,
|
||||
prevDoublePawnStepTo = meta:get_int("prevDoublePawnStepTo"),
|
||||
castlingWhiteL = meta:get_int("castlingWhiteL"),
|
||||
castlingWhiteR = meta:get_int("castlingWhiteR"),
|
||||
castlingBlackL = meta:get_int("castlingBlackL"),
|
||||
castlingBlackR = meta:get_int("castlingBlackR"),
|
||||
halfmoveClock = meta:get_int("halfmoveClock"),
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Delay in seconds for a bot moving a piece (excluding choosing a promotion)
|
||||
local BOT_DELAY_MOVE = 1.0
|
||||
-- Delay in seconds for a bot promoting a piece
|
||||
local BOT_DELAY_PROMOTE = 1.0
|
||||
|
||||
chessbot.perform_move = function(pos, choice_from, choice_to, meta)
|
||||
local game_state = get_game_state(meta)
|
||||
local currentBotColor = game_state.botColor
|
||||
|
||||
-- Bot resigns if no move chosen
|
||||
if not choice_from or not choice_to then
|
||||
realchess.resign(pos, meta, currentBotColor)
|
||||
return
|
||||
end
|
||||
|
||||
local botName
|
||||
if currentBotColor == "white" then
|
||||
botName = meta:get_string("playerWhite")
|
||||
else
|
||||
botName = meta:get_string("playerBlack")
|
||||
end
|
||||
|
||||
local gameResult = meta:get_string("gameResult")
|
||||
if gameResult ~= "" then
|
||||
return
|
||||
end
|
||||
local botColor = meta:get_string("botColor")
|
||||
if botColor == "" then
|
||||
minetest.log("error", "[xdecor] Chess: chessbot.perform_move: botColor in meta string was empty!")
|
||||
return
|
||||
end
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
local lastMoveTime = meta:get_int("lastMoveTime")
|
||||
if lastMoveTime > 0 or lastMove == "" then
|
||||
-- Set the bot name if not set already
|
||||
if currentBotColor == "black" and meta:get_string("playerBlack") == "" then
|
||||
meta:set_string("playerBlack", botName)
|
||||
elseif currentBotColor == "white" and meta:get_string("playerWhite") == "" then
|
||||
meta:set_string("playerWhite", botName)
|
||||
end
|
||||
|
||||
-- Make a move
|
||||
local moveOK = realchess.move(pos, meta, "board", choice_from, "board", choice_to, botName)
|
||||
if not moveOK then
|
||||
minetest.log("error", "[xdecor] Chess: Bot tried to make an invalid move from "..
|
||||
realchess.index_to_notation(choice_from).." to "..realchess.index_to_notation(choice_to))
|
||||
end
|
||||
-- Bot resigns if it tried to make an invalid move
|
||||
if not moveOK then
|
||||
realchess.resign(pos, meta, currentBotColor)
|
||||
end
|
||||
else
|
||||
minetest.log("error", "[xdecor] Chess: chessbot.perform_move: No last move!")
|
||||
end
|
||||
end
|
||||
|
||||
function chessbot.perform_promote(pos, meta, promoteTo)
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
local color
|
||||
if lastMove == "black" or lastMove == "" then
|
||||
color = "white"
|
||||
else
|
||||
color = "black"
|
||||
end
|
||||
if not promoteTo then
|
||||
minetest.log("error", "[xdecor] Chess: Bot failed to pick a pawn promotion")
|
||||
realchess.resign(pos, meta, color)
|
||||
return
|
||||
elseif promoteTo ~= "queen" and promoteTo ~= "rook" and promoteTo ~= "knight" and promoteTo ~= "bishop" then
|
||||
minetest.log("error", "[xdecor] Chess: Bot picked an invalid pawn promotion: "..tostring(promoteTo))
|
||||
realchess.resign(pos, meta, color)
|
||||
return
|
||||
end
|
||||
realchess.promote_pawn(pos, meta, color, promoteTo)
|
||||
end
|
||||
|
||||
function chessbot.move(pos, inv, meta)
|
||||
local board_t = realchess.board_inv_to_table(inv)
|
||||
local game_state = get_game_state(meta)
|
||||
|
||||
local t1 = minetest.get_us_time()
|
||||
local choice_from, choice_to = chessbot.choose_move(board_t, game_state)
|
||||
local t2 = minetest.get_us_time()
|
||||
minetest.log("verbose", "[xdecor] Chessbot at "..minetest.pos_to_string(pos).." took "..(t2-t1).. " µs to pick a move")
|
||||
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if active_jobs[hash] then
|
||||
chessbot.cancel_job(pos)
|
||||
minetest.log("error", "[xdecor] chessbot.move called although the chessbot already had an active job for "..minetest.pos_to_string(pos).."!")
|
||||
end
|
||||
local job = minetest.after(BOT_DELAY_MOVE, function()
|
||||
active_jobs[hash] = nil
|
||||
chessbot.perform_move(pos, choice_from, choice_to, meta)
|
||||
end)
|
||||
active_jobs[hash] = job
|
||||
end
|
||||
|
||||
function chessbot.promote(pos, inv, meta, pawnIndex)
|
||||
local board_t = realchess.board_inv_to_table(inv)
|
||||
local game_state = get_game_state(meta)
|
||||
|
||||
local t1 = minetest.get_us_time()
|
||||
local promoteTo = chessbot.choose_promote(board_t, game_state, pawnIndex)
|
||||
local t2 = minetest.get_us_time()
|
||||
minetest.log("verbose", "[xdecor] Chessbot at "..minetest.pos_to_string(pos).." took "..(t2-t1).. " µs to pick a pawn promotion")
|
||||
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if active_jobs[hash] then
|
||||
chessbot.cancel_job(pos)
|
||||
minetest.log("error", "[xdecor] chessbot.move called although the chessbot already had an active job for "..minetest.pos_to_string(pos).."!")
|
||||
end
|
||||
local job = minetest.after(BOT_DELAY_PROMOTE, function()
|
||||
active_jobs[hash] = nil
|
||||
chessbot.perform_promote(pos, meta, promoteTo)
|
||||
end)
|
||||
active_jobs[hash] = job
|
||||
end
|
||||
|
||||
return chessbot
|
||||
@@ -0,0 +1,54 @@
|
||||
-- This is the random chessbot, it picks moves completely at random.
|
||||
-- Not used by this mod by default; can be used for debugging
|
||||
-- by manually adjusting the code.
|
||||
|
||||
local NS = function(s) return s end
|
||||
|
||||
local realchess = xdecor.chess
|
||||
|
||||
local function choose_move(board_t, game_state)
|
||||
local currentBotColor = game_state["botColor"]
|
||||
local prevDoublePawnStepTo = game_state["prevDoublePawnStepTo"]
|
||||
local castlingRights = {
|
||||
castlingWhiteR = game_state["castlingWhiteR"],
|
||||
castlingWhiteL = game_state["castlingWhiteL"],
|
||||
castlingBlackR = game_state["castlingBlackR"],
|
||||
castlingBlackL = game_state["castlingBlackL"],
|
||||
}
|
||||
|
||||
local moves = realchess.get_theoretical_moves_for(board_t, currentBotColor, prevDoublePawnStepTo, castlingRights)
|
||||
local safe_moves, safe_moves_count = realchess.get_king_safe_moves(moves, board_t, currentBotColor)
|
||||
if safe_moves_count == 0 then
|
||||
return nil
|
||||
end
|
||||
local flat_moves = {}
|
||||
for from, tos in pairs(safe_moves) do
|
||||
for to, _ in pairs(tos) do
|
||||
table.insert(flat_moves, { from = from, to = to })
|
||||
end
|
||||
end
|
||||
local r = math.random(1, #flat_moves)
|
||||
local move = flat_moves[r]
|
||||
return move.from, move.to
|
||||
end
|
||||
|
||||
local function choose_promote(board_t, game_state, pawnIndex)
|
||||
local r = math.random(1,4)
|
||||
if r == 1 then
|
||||
return "queen"
|
||||
elseif r == 2 then
|
||||
return "rook"
|
||||
elseif r == 3 then
|
||||
return "bishop"
|
||||
else
|
||||
return "knight"
|
||||
end
|
||||
end
|
||||
|
||||
-- Set the weak chessbot
|
||||
realchess.set_chessbot({
|
||||
id = "xdecor:random",
|
||||
name = NS("Random Computer"),
|
||||
choose_move = choose_move,
|
||||
choose_promote = choose_promote,
|
||||
})
|
||||
@@ -0,0 +1,128 @@
|
||||
-- This is the weak chessbot, the built-in chessbot for
|
||||
-- this mod. It is really bad at playing Chess but at
|
||||
-- least it can play at all!
|
||||
|
||||
local NS = function(s) return s end
|
||||
|
||||
local realchess = xdecor.chess
|
||||
|
||||
-- How valuable the chessbot thinks each piece is
|
||||
-- (higher = more valuable)
|
||||
local piece_values = {
|
||||
p = 10, -- pawn
|
||||
n = 30, -- knight
|
||||
b = 30, -- bishop
|
||||
r = 50, -- rook
|
||||
q = 90, -- queen
|
||||
k = 900, -- king
|
||||
}
|
||||
for k,v in pairs(piece_values) do
|
||||
local upper = string.upper(k)
|
||||
piece_values[upper] = v
|
||||
end
|
||||
|
||||
-- Pick a move from the list of all possible moves
|
||||
-- on this chessboard
|
||||
local function best_move(moves, board_t)
|
||||
--[[ This is a VERY simple algorithm that will greedily
|
||||
capture pieces as soon the opprtunity arises
|
||||
and otherwise takes random moves.
|
||||
This makes the bot very weak, as it lacks any kind of
|
||||
foresight, but the algorithm is blazingly fast. ]]
|
||||
|
||||
--[[ The algorithm:
|
||||
Look at all moves and rate each of them with a number
|
||||
(higher = better). Pick the move with the highest rating.
|
||||
If it's a tie, pick randomly from the tied moves.
|
||||
Non-capturing moves are rated 0.
|
||||
Capturing moves are rated by which piece is captured
|
||||
(in piece_values) ]]
|
||||
|
||||
local max_value, choices = 0, {}
|
||||
|
||||
for from, move in pairs(moves) do
|
||||
for m=1, #move do
|
||||
local to = move[m][1]
|
||||
local promote_to = move[m][2]
|
||||
|
||||
-- No promotion or promotion to queen
|
||||
if promote_to == nil or promote_to == 5 then
|
||||
-- Move rating. rating 0 is for non-capturing moves.
|
||||
-- higher ratings are for capturing moves.
|
||||
local val = 0
|
||||
local to_piece_name = board_t[to]
|
||||
|
||||
-- If destination is a piece that we capture, rate this move
|
||||
-- according to a table.
|
||||
if to_piece_name ~= "." then
|
||||
for piece_type, piece_value in pairs(piece_values) do
|
||||
if to_piece_name == piece_type then
|
||||
val = piece_value
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Update the list of best moves (choices).
|
||||
if val > max_value then
|
||||
max_value = val
|
||||
choices = {{
|
||||
from = from,
|
||||
to = to
|
||||
}}
|
||||
elseif val == max_value then
|
||||
choices[#choices + 1] = {
|
||||
from = from,
|
||||
to = to
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #choices == 0 then
|
||||
return nil
|
||||
end
|
||||
local random = math.random(1, #choices)
|
||||
local choice_from, choice_to = choices[random].from, choices[random].to
|
||||
|
||||
return tonumber(choice_from), choice_to
|
||||
end
|
||||
|
||||
local function choose_move(board_t, game_state)
|
||||
local currentBotColor = game_state["botColor"]
|
||||
local prevDoublePawnStepTo = game_state["prevDoublePawnStepTo"]
|
||||
local castlingRights = {
|
||||
castlingWhiteR = game_state["castlingWhiteR"],
|
||||
castlingWhiteL = game_state["castlingWhiteL"],
|
||||
castlingBlackR = game_state["castlingBlackR"],
|
||||
castlingBlackL = game_state["castlingBlackL"],
|
||||
}
|
||||
|
||||
local moves = realchess.get_theoretical_moves_for(board_t, currentBotColor, prevDoublePawnStepTo, castlingRights)
|
||||
local safe_moves, safe_moves_count = realchess.get_king_safe_moves(moves, board_t, currentBotColor)
|
||||
if safe_moves_count == 0 then
|
||||
-- No safe move: stalemate or checkmate
|
||||
return
|
||||
end
|
||||
local choice_from, choice_to = best_move(safe_moves, board_t)
|
||||
if choice_from == nil then
|
||||
-- No best move: stalemate or checkmate
|
||||
return
|
||||
end
|
||||
|
||||
return choice_from, choice_to
|
||||
end
|
||||
|
||||
local function choose_promote(board_t, game_state, pawnIndex)
|
||||
-- Bot always promotes to queen
|
||||
return "queen"
|
||||
end
|
||||
|
||||
-- Set the weak chessbot
|
||||
realchess.set_chessbot({
|
||||
id = "xdecor:weak",
|
||||
name = NS("Weak Computer"),
|
||||
choose_move = choose_move,
|
||||
choose_promote = choose_promote,
|
||||
})
|
||||
@@ -0,0 +1,590 @@
|
||||
local cauldron, sounds = {}, {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
|
||||
-- Set to true to print soup ingredients, heater nodes and bowls to console
|
||||
local DEBUG_RECOGNIZED_ITEMS = false
|
||||
|
||||
--~ cauldron hint
|
||||
local hint_fire = S("Light a fire below to heat it up")
|
||||
--~ cauldron hint
|
||||
local hint_eat = S("Use a bowl to eat the soup")
|
||||
--~ cauldron hint
|
||||
local hint_recipe = S("Drop foods inside to make a soup")
|
||||
|
||||
local infotexts = {
|
||||
["xdecor:cauldron_empty"] = S("Cauldron (empty)"),
|
||||
["xdecor:cauldron_idle"] = S("Cauldron (cold water)").."\n"..hint_fire,
|
||||
["xdecor:cauldron_idle_river_water"] = S("Cauldron (cold river water)").."\n"..hint_fire,
|
||||
["xdecor:cauldron_idle_soup"] = S("Cauldron (cold soup)").."\n"..hint_eat,
|
||||
["xdecor:cauldron_boiling"] = S("Cauldron (boiling water)").."\n"..hint_recipe,
|
||||
["xdecor:cauldron_boiling_river_water"] = S("Cauldron (boiling river water)").."\n"..hint_recipe,
|
||||
["xdecor:cauldron_soup"] = S("Cauldron (boiling soup)").."\n"..hint_eat,
|
||||
}
|
||||
|
||||
local function set_infotext(meta, node)
|
||||
if infotexts[node.name] then
|
||||
meta:set_string("infotext", infotexts[node.name])
|
||||
end
|
||||
end
|
||||
|
||||
-- HACKY list of soup ingredients.
|
||||
-- For items without a valid 'xdecor_soup_ingredient' group present, the cauldron
|
||||
-- will check part of the itemname after the ':' contains any of those words.
|
||||
-- If it does, the item counts as an ingredient, UNLESS the item is in the
|
||||
-- non_ingredients blacklist below.
|
||||
local ingredients_list = {
|
||||
"apple", "mushroom", "honey", "pumpkin", "egg", "bread", "meat",
|
||||
"chicken", "carrot", "potato", "melon", "rhubarb", "cucumber",
|
||||
"corn", "beans", "berries", "grapes", "tomato", "wheat"
|
||||
}
|
||||
|
||||
-- Blacklist of items that cannot be soup ingredients, in case they
|
||||
-- would otherwise match the ingredients_list above.
|
||||
-- Note the group 'xdecor_soup_ingredient' still takes precedence.
|
||||
local non_ingredients = {
|
||||
-- Minetest Game: default
|
||||
"default:apple_mark", "default:blueberry_bush_leaves_with_berries",
|
||||
-- Minetest Game: farming
|
||||
"farming:seed_wheat",
|
||||
"farming:wheat_1", "farming:wheat_2", "farming:wheat_3", "farming:wheat_4",
|
||||
"farming:wheat_5", "farming:wheat_6", "farming:wheat_7", "farming:wheat_8",
|
||||
}
|
||||
local non_ingredients_keyed = table.key_value_swap(non_ingredients)
|
||||
|
||||
cauldron.cbox = {
|
||||
{0, 0, 0, 16, 16, 0},
|
||||
{0, 0, 16, 16, 16, 0},
|
||||
{0, 0, 0, 0, 16, 16},
|
||||
{16, 0, 0, 0, 16, 16},
|
||||
{0, 0, 0, 16, 8, 16}
|
||||
}
|
||||
|
||||
-- Returns true is given item is a node that can heat fire and can heat up the cauldron
|
||||
local function is_heater(itemstring)
|
||||
-- Dedicated group to add custom cauldron-heating nodes
|
||||
return minetest.get_item_group(itemstring, "xdecor_cauldron_heater") == 1 or
|
||||
-- Also, all fire nodes count as heaters
|
||||
minetest.get_item_group(itemstring, "fire") ~= 0
|
||||
end
|
||||
|
||||
-- Returns true if given item is a bowl that is compatible with taking soup from
|
||||
-- the cauldron
|
||||
local function is_bowl(itemstring)
|
||||
-- Recommended: The item has this group
|
||||
return minetest.get_item_group(itemstring, "xdecor_soup_bowl") == 1
|
||||
-- Two items are hardcoded
|
||||
or itemstring == "farming:bowl" or itemstring == "x_farming:bowl"
|
||||
end
|
||||
|
||||
-- Returns true if the node at pos is above heater
|
||||
local function is_heated(pos)
|
||||
local below_node = {x = pos.x, y = pos.y - 1, z = pos.z}
|
||||
local nn = minetest.get_node(below_node).name
|
||||
-- Check heater status
|
||||
if is_heater(nn) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function cauldron.stop_sound(pos)
|
||||
local spos = minetest.hash_node_position(pos)
|
||||
if sounds[spos] then
|
||||
minetest.sound_stop(sounds[spos])
|
||||
sounds[spos] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function cauldron.start_sound(pos)
|
||||
local spos = minetest.hash_node_position(pos)
|
||||
-- Stop sound if one already exists.
|
||||
-- Only 1 sound per position at maximum allowed.
|
||||
if sounds[spos] then
|
||||
cauldron.stop_sound(pos)
|
||||
end
|
||||
sounds[spos] = minetest.sound_play("xdecor_boiling_water", {
|
||||
pos = pos,
|
||||
max_hear_distance = 5,
|
||||
gain = 0.8,
|
||||
loop = true
|
||||
})
|
||||
end
|
||||
|
||||
function cauldron.idle_construct(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
set_infotext(meta, node)
|
||||
timer:start(10.0)
|
||||
cauldron.stop_sound(pos)
|
||||
end
|
||||
|
||||
function cauldron.boiling_construct(pos)
|
||||
cauldron.start_sound(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
set_infotext(meta, node)
|
||||
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(5.0)
|
||||
end
|
||||
|
||||
|
||||
function cauldron.filling(pos, node, clicker, itemstack)
|
||||
local inv = clicker:get_inventory()
|
||||
local wield_item = clicker:get_wielded_item():get_name()
|
||||
|
||||
do
|
||||
if wield_item == "bucket:bucket_empty" and node.name:sub(-6) ~= "_empty" then
|
||||
local bucket_item
|
||||
if node.name:sub(-11) == "river_water" then
|
||||
bucket_item = "bucket:bucket_river_water 1"
|
||||
else
|
||||
bucket_item = "bucket:bucket_water 1"
|
||||
end
|
||||
if itemstack:get_count() > 1 then
|
||||
if inv:room_for_item("main", bucket_item) then
|
||||
itemstack:take_item()
|
||||
inv:add_item("main", bucket_item)
|
||||
else
|
||||
-- No space: Drop bucket on ground
|
||||
itemstack:take_item()
|
||||
minetest.add_item(clicker:get_pos(), bucket_item)
|
||||
end
|
||||
else
|
||||
itemstack:replace(bucket_item)
|
||||
end
|
||||
minetest.set_node(pos, {name = "xdecor:cauldron_empty", param2 = node.param2})
|
||||
|
||||
elseif minetest.get_item_group(wield_item, "water_bucket") == 1 and node.name:sub(-6) == "_empty" then
|
||||
local newnode
|
||||
if wield_item == "bucket:bucket_river_water" then
|
||||
newnode = "xdecor:cauldron_idle_river_water"
|
||||
else
|
||||
newnode = "xdecor:cauldron_idle"
|
||||
end
|
||||
minetest.set_node(pos, {name = newnode, param2 = node.param2})
|
||||
itemstack:replace("bucket:bucket_empty")
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
|
||||
function cauldron.idle_timer(pos)
|
||||
if not is_heated(pos) then
|
||||
return true
|
||||
end
|
||||
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name:sub(-4) == "soup" then
|
||||
node.name = "xdecor:cauldron_soup"
|
||||
elseif node.name:sub(-11) == "river_water" then
|
||||
node.name = "xdecor:cauldron_boiling_river_water"
|
||||
else
|
||||
node.name = "xdecor:cauldron_boiling"
|
||||
end
|
||||
minetest.set_node(pos, node)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Ugly hack to determine if an item has the function `minetest.item_eat` in its definition.
|
||||
local function eatable(itemstring)
|
||||
local item = itemstring:match("[%w_:]+")
|
||||
local on_use_def = minetest.registered_items[item].on_use
|
||||
if not on_use_def then return end
|
||||
|
||||
return string.format("%q", string.dump(on_use_def)):find("item_eat")
|
||||
end
|
||||
|
||||
-- Checks if the given item can be used as ingredient for the soup
|
||||
local function is_ingredient(itemstring)
|
||||
-- This group takes precedence over everything else, allowing
|
||||
-- mods to explicitly mark items as soup ingredient or not.
|
||||
local gval = minetest.get_item_group(itemstring, "xdecor_soup_ingredient")
|
||||
-- 1: This item is a soup ingredient
|
||||
if gval == 1 then
|
||||
return true
|
||||
-- -1: This item is NOT a soup ingredient
|
||||
-- This should be used in case the heuristic below fails.
|
||||
elseif gval == -1 then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Otherwise, we determine on whether this item is a soup ingredient
|
||||
-- based on its itemstring (admittedly kinda hacky ...)
|
||||
|
||||
-- But first check if our item is in the blacklist
|
||||
if non_ingredients_keyed[itemstring] then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Eatable items count as ingredient by default
|
||||
if eatable(itemstring) then
|
||||
return true
|
||||
end
|
||||
-- We check if the part of the itemstring after the colon
|
||||
-- contains one of the words in ingredients_list.
|
||||
-- If yes, this is an ingredient. Otherwise it isn't.
|
||||
local basename = itemstring:match(":([%w_]+)")
|
||||
if not basename then
|
||||
return false
|
||||
end
|
||||
for _, ingredient in ipairs(ingredients_list) do
|
||||
if basename:find(ingredient) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function cauldron.boiling_timer(pos)
|
||||
-- Cool down cauldron if there is no fire
|
||||
local node = minetest.get_node(pos)
|
||||
if not is_heated(pos) then
|
||||
local newnode
|
||||
if node.name:sub(-4) == "soup" then
|
||||
newnode = "xdecor:cauldron_idle_soup"
|
||||
elseif node.name:sub(-11) == "river_water" then
|
||||
newnode = "xdecor:cauldron_idle_river_water"
|
||||
else
|
||||
newnode = "xdecor:cauldron_idle"
|
||||
end
|
||||
minetest.set_node(pos, {name = newnode, param2 = node.param2})
|
||||
return true
|
||||
end
|
||||
|
||||
if node.name:sub(-4) == "soup" then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Cooking:
|
||||
|
||||
-- Count the ingredients in the cauldron
|
||||
local objs = minetest.get_objects_inside_radius(pos, 0.5)
|
||||
|
||||
if not next(objs) then
|
||||
return true
|
||||
end
|
||||
|
||||
local ingredients = {}
|
||||
for _, obj in pairs(objs) do
|
||||
if obj and not obj:is_player() and obj:get_luaentity().itemstring then
|
||||
local itemstring = obj:get_luaentity().itemstring
|
||||
local item = ItemStack(itemstring)
|
||||
local itemname = item:get_name()
|
||||
|
||||
if is_ingredient(itemname) then
|
||||
local basename = itemstring:match(":([%w_]+)")
|
||||
table.insert(ingredients, basename)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove ingredients and turn liquid into soup
|
||||
if #ingredients >= 2 then
|
||||
for _, obj in pairs(objs) do
|
||||
obj:remove()
|
||||
end
|
||||
|
||||
minetest.set_node(pos, {name = "xdecor:cauldron_soup", param2 = node.param2})
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function cauldron.take_soup(pos, node, clicker, itemstack)
|
||||
local inv = clicker:get_inventory()
|
||||
local wield_item = clicker:get_wielded_item()
|
||||
local item_name = wield_item:get_name()
|
||||
|
||||
if is_bowl(item_name) then
|
||||
local soup_bowl = ItemStack("xdecor:bowl_soup 1")
|
||||
-- For bowls from other mods, remember the original bowl name
|
||||
-- to restore it after eating the soup.
|
||||
if item_name ~= "xdecor:bowl" then
|
||||
local imeta = soup_bowl:get_meta()
|
||||
imeta:set_string("original_bowl", item_name)
|
||||
end
|
||||
-- Add item to inventory, if possible
|
||||
if wield_item:get_count() > 1 then
|
||||
if inv:room_for_item("main", soup_bowl) then
|
||||
itemstack:take_item()
|
||||
inv:add_item("main", soup_bowl)
|
||||
else
|
||||
-- No space: Drop soup bowl on ground
|
||||
itemstack:take_item()
|
||||
minetest.add_item(clicker:get_pos(), soup_bowl)
|
||||
end
|
||||
else
|
||||
itemstack:replace(soup_bowl)
|
||||
end
|
||||
|
||||
minetest.set_node(pos, {name = "xdecor:cauldron_empty", param2 = node.param2})
|
||||
|
||||
minetest.log("action", "[xdecor] "..clicker:get_player_name().." fills their bowl ("..item_name..") with soup from the cauldron at "..minetest.pos_to_string(pos))
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
xdecor.register("cauldron_empty", {
|
||||
description = S("Cauldron"),
|
||||
--~ Cauldron tooltip
|
||||
_tt_help = S("For storing water and cooking soup"),
|
||||
groups = {cracky=2, oddly_breakable_by_hand=1,cauldron=1},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {"xdecor_cauldron_top_empty.png", "xdecor_cauldron_bottom.png", "xdecor_cauldron_sides.png"},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_rightclick = cauldron.filling,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
set_infotext(meta, node)
|
||||
cauldron.stop_sound(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
xdecor.register("cauldron_idle", {
|
||||
description = S("Cauldron with Water (cold)"),
|
||||
groups = {cracky=2, oddly_breakable_by_hand=1, not_in_creative_inventory=1,cauldron=2},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {"xdecor_cauldron_top_idle.png", "xdecor_cauldron_bottom.png", "xdecor_cauldron_sides.png"},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
drop = "xdecor:cauldron_empty",
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_rightclick = cauldron.filling,
|
||||
on_construct = cauldron.idle_construct,
|
||||
on_timer = cauldron.idle_timer,
|
||||
})
|
||||
|
||||
xdecor.register("cauldron_idle_river_water", {
|
||||
description = S("Cauldron with River Water (cold)"),
|
||||
groups = {cracky=2, oddly_breakable_by_hand=1, not_in_creative_inventory=1,cauldron=2},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {"xdecor_cauldron_top_idle_river_water.png", "xdecor_cauldron_bottom.png", "xdecor_cauldron_sides.png"},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
drop = "xdecor:cauldron_empty",
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_rightclick = cauldron.filling,
|
||||
on_construct = cauldron.idle_construct,
|
||||
on_timer = cauldron.idle_timer,
|
||||
})
|
||||
|
||||
xdecor.register("cauldron_idle_soup", {
|
||||
description = S("Cauldron with Soup (cold)"),
|
||||
groups = {cracky = 2, oddly_breakable_by_hand = 1, not_in_creative_inventory = 1,cauldron=2},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
drop = "xdecor:cauldron_empty",
|
||||
tiles = {"xdecor_cauldron_top_idle_soup.png", "xdecor_cauldron_bottom.png", "xdecor_cauldron_sides.png"},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
set_infotext(meta, node)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(10.0)
|
||||
cauldron.stop_sound(pos)
|
||||
end,
|
||||
on_timer = cauldron.idle_timer,
|
||||
on_rightclick = cauldron.take_soup,
|
||||
})
|
||||
|
||||
xdecor.register("cauldron_boiling", {
|
||||
description = S("Cauldron with Water (boiling)"),
|
||||
groups = {cracky=2, oddly_breakable_by_hand=1, not_in_creative_inventory=1,cauldron=3},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
drop = "xdecor:cauldron_empty",
|
||||
damage_per_second = 2,
|
||||
tiles = {
|
||||
{
|
||||
name = "xdecor_cauldron_top_anim_boiling_water.png",
|
||||
animation = {type = "vertical_frames", length = 3.0}
|
||||
},
|
||||
"xdecor_cauldron_bottom.png",
|
||||
"xdecor_cauldron_sides.png"
|
||||
},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_rightclick = cauldron.filling,
|
||||
on_construct = cauldron.boiling_construct,
|
||||
on_timer = cauldron.boiling_timer,
|
||||
on_destruct = function(pos)
|
||||
cauldron.stop_sound(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
xdecor.register("cauldron_boiling_river_water", {
|
||||
description = S("Cauldron with River Water (boiling)"),
|
||||
groups = {cracky=2, oddly_breakable_by_hand=1, not_in_creative_inventory=1,cauldron=3},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
drop = "xdecor:cauldron_empty",
|
||||
damage_per_second = 2,
|
||||
tiles = {
|
||||
{
|
||||
name = "xdecor_cauldron_top_anim_boiling_river_water.png",
|
||||
animation = {type = "vertical_frames", length = 3.0}
|
||||
},
|
||||
"xdecor_cauldron_bottom.png",
|
||||
"xdecor_cauldron_sides.png"
|
||||
},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_rightclick = cauldron.filling,
|
||||
on_construct = cauldron.boiling_construct,
|
||||
on_timer = cauldron.boiling_timer,
|
||||
on_destruct = function(pos)
|
||||
cauldron.stop_sound(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
|
||||
xdecor.register("cauldron_soup", {
|
||||
description = S("Cauldron with Soup (boiling)"),
|
||||
groups = {cracky = 2, oddly_breakable_by_hand = 1, not_in_creative_inventory = 1,cauldron=3},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
drop = "xdecor:cauldron_empty",
|
||||
damage_per_second = 2,
|
||||
tiles = {
|
||||
{
|
||||
name = "xdecor_cauldron_top_anim_soup.png",
|
||||
animation = {type = "vertical_frames", length = 3.0}
|
||||
},
|
||||
"xdecor_cauldron_bottom.png",
|
||||
"xdecor_cauldron_sides.png"
|
||||
},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
collision_box = xdecor.pixelbox(16, cauldron.cbox),
|
||||
on_construct = function(pos)
|
||||
cauldron.start_sound(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
set_infotext(meta, node)
|
||||
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(5.0)
|
||||
end,
|
||||
on_timer = cauldron.boiling_timer,
|
||||
on_rightclick = cauldron.take_soup,
|
||||
on_destruct = function(pos)
|
||||
cauldron.stop_sound(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Craft items
|
||||
|
||||
minetest.register_craftitem("xdecor:bowl", {
|
||||
--~ Item that can hold soup
|
||||
description = S("Bowl"),
|
||||
inventory_image = "xdecor_bowl.png",
|
||||
wield_image = "xdecor_bowl.png",
|
||||
groups = {food_bowl = 1, xdecor_soup_bowl = 1, flammable = 2},
|
||||
})
|
||||
|
||||
minetest.register_craftitem("xdecor:bowl_soup", {
|
||||
description = S("Bowl of soup"),
|
||||
inventory_image = "xdecor_bowl_soup.png",
|
||||
wield_image = "xdecor_bowl_soup.png",
|
||||
groups = {
|
||||
-- The soup itself is NOT an ingredient for the soup
|
||||
xdecor_soup_ingredient = -1,
|
||||
},
|
||||
stack_max = 1,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
-- Eat the soup and replace item with the original bowl item
|
||||
-- (`xdecor:bowl` by default)
|
||||
local imeta = itemstack:get_meta()
|
||||
local empty_bowl = imeta:get_string("original_bowl")
|
||||
if empty_bowl == "" then
|
||||
empty_bowl = "xdecor:bowl"
|
||||
end
|
||||
if not minetest.registered_items[empty_bowl] then
|
||||
minetest.log("warning", "[xdecor] original_bowl of xdecor:bowl_soup was '"..empty_bowl.."', an unknown item. Falling back to xdecor:bowl")
|
||||
empty_bowl = "xdecor:bowl"
|
||||
end
|
||||
return minetest.do_item_eat(30, empty_bowl, itemstack, user, pointed_thing)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:bowl 3",
|
||||
recipe = {
|
||||
{"group:wood", "", "group:wood"},
|
||||
{"", "group:wood", ""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cauldron_empty",
|
||||
recipe = {
|
||||
{"default:iron_lump", "", "default:iron_lump"},
|
||||
{"default:iron_lump", "", "default:iron_lump"},
|
||||
{"default:iron_lump", "default:iron_lump", "default:iron_lump"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Restart boiling cauldron sounds",
|
||||
name = "xdecor:restart_boiling_cauldron_sounds",
|
||||
nodenames = {"xdecor:cauldron_boiling", "xdecor:cauldron_boiling_river_water", "xdecor:cauldron_soup"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
cauldron.start_sound(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Update cauldron infotexts",
|
||||
name = "xdecor:update_cauldron_infotexts",
|
||||
nodenames = {"group:cauldron"},
|
||||
run_at_every_load = false,
|
||||
action = function(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
set_infotext(meta, node)
|
||||
end,
|
||||
})
|
||||
|
||||
if DEBUG_RECOGNIZED_ITEMS then
|
||||
-- Print all soup ingredients, heater nodes and bowls
|
||||
-- in console
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local ingredients = {}
|
||||
local heaters = {}
|
||||
local bowls = {}
|
||||
for k,v in pairs(minetest.registered_items) do
|
||||
if is_ingredient(k) then
|
||||
table.insert(ingredients, k)
|
||||
end
|
||||
if is_heater(k) then
|
||||
table.insert(heaters, k)
|
||||
end
|
||||
if is_bowl(k) then
|
||||
table.insert(bowls, k)
|
||||
end
|
||||
end
|
||||
table.sort(ingredients)
|
||||
table.sort(heaters)
|
||||
table.sort(bowls)
|
||||
local str_i = table.concat(ingredients, ", ")
|
||||
local str_h = table.concat(heaters, ", ")
|
||||
local str_b = table.concat(bowls, ", ")
|
||||
minetest.log("action", "[xdecor] List of ingredients for soup: "..str_i)
|
||||
minetest.log("action", "[xdecor] List of nodes that can heat cauldron: "..str_h)
|
||||
minetest.log("action", "[xdecor] List of bowls able to take soup from cauldron: "..str_b)
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,124 @@
|
||||
--[[
|
||||
List of nodes that can be cut in the workbench.
|
||||
In previous version, this used to be automatically
|
||||
generated at runtime but it has been decided to turn
|
||||
this to a hardcoded list for higher predictability
|
||||
and to prevent future game/mod updates to accidentally
|
||||
add more unintentional node cuts.
|
||||
|
||||
Many of the "weird" cuts in the list (like the barrel)
|
||||
are needed for compatibility reasons with older versions.
|
||||
|
||||
This list was originally generated with this code:
|
||||
```
|
||||
|
||||
-- Nodes allowed to be cut:
|
||||
-- Only the regular, solid blocks without metas or explosivity
|
||||
-- from the xdecor or default mods.
|
||||
|
||||
for nodename, def in pairs(minetest.registered_nodes) do
|
||||
local nodenamesplit = string.split(nodename, ":")
|
||||
local modname = nodenamesplit[1]
|
||||
if (modname == "xdecor" or modname == "default") and xdecor.stairs_valid_def(def) then
|
||||
cuttable_nodes[#cuttable_nodes + 1] = nodename
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(cuttable_nodes)
|
||||
for i = 1, #cuttable_nodes do
|
||||
print(" \""..cuttable_nodes[i].."\",")
|
||||
end
|
||||
```
|
||||
]]
|
||||
|
||||
local cuttable_nodes = {
|
||||
-- xdecor
|
||||
"xdecor:barrel",
|
||||
"xdecor:cactusbrick",
|
||||
"xdecor:coalstone_tile",
|
||||
"xdecor:desertstone_tile",
|
||||
"xdecor:hard_clay",
|
||||
"xdecor:moonbrick",
|
||||
"xdecor:packed_ice",
|
||||
"xdecor:stone_rune",
|
||||
"xdecor:stone_tile_x",
|
||||
"xdecor:wood_tile_x",
|
||||
"xdecor:woodframed_glass",
|
||||
|
||||
-- 'default' from Minetest Game (as of Minetest Game commit ac2bc0f52118230eb4dd50567c30a47e9bd3a31e)
|
||||
"default:acacia_tree",
|
||||
"default:acacia_wood",
|
||||
"default:aspen_tree",
|
||||
"default:aspen_wood",
|
||||
"default:brick",
|
||||
"default:bronzeblock",
|
||||
"default:cactus",
|
||||
"default:coalblock",
|
||||
"default:cobble",
|
||||
"default:copperblock",
|
||||
"default:coral_brown",
|
||||
"default:coral_orange",
|
||||
"default:coral_skeleton",
|
||||
"default:desert_cobble",
|
||||
"default:desert_sandstone",
|
||||
"default:desert_sandstone_block",
|
||||
"default:desert_sandstone_brick",
|
||||
"default:desert_stone",
|
||||
"default:desert_stone_block",
|
||||
"default:desert_stonebrick",
|
||||
"default:diamondblock",
|
||||
"default:glass",
|
||||
"default:goldblock",
|
||||
"default:ice",
|
||||
"default:jungletree",
|
||||
"default:junglewood",
|
||||
"default:mossycobble",
|
||||
"default:obsidian",
|
||||
"default:obsidian_block",
|
||||
"default:obsidian_glass",
|
||||
"default:obsidianbrick",
|
||||
"default:permafrost",
|
||||
"default:permafrost_with_moss",
|
||||
"default:permafrost_with_stones",
|
||||
"default:pine_tree",
|
||||
"default:pine_wood",
|
||||
"default:sandstone",
|
||||
"default:sandstone_block",
|
||||
"default:sandstonebrick",
|
||||
"default:silver_sandstone",
|
||||
"default:silver_sandstone_block",
|
||||
"default:silver_sandstone_brick",
|
||||
"default:steelblock",
|
||||
"default:stone",
|
||||
"default:stone_block",
|
||||
"default:stonebrick",
|
||||
"default:tinblock",
|
||||
"default:tree",
|
||||
"default:wood",
|
||||
}
|
||||
|
||||
-- 'farming' from Minetest Game (as of Minetest Game commit ac2bc0f52118230eb4dd50567c30a47e9bd3a31e)
|
||||
if minetest.get_modpath("farming") ~= nil then
|
||||
table.insert(cuttable_nodes, "farming:straw")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("wool") ~= nil then
|
||||
-- 'wool' from Minetest Game (as of Minetest Game commit ac2bc0f52118230eb4dd50567c30a47e9bd3a31e)
|
||||
table.insert(cuttable_nodes, "wool:black")
|
||||
table.insert(cuttable_nodes, "wool:blue")
|
||||
table.insert(cuttable_nodes, "wool:brown")
|
||||
table.insert(cuttable_nodes, "wool:cyan")
|
||||
table.insert(cuttable_nodes, "wool:dark_green")
|
||||
table.insert(cuttable_nodes, "wool:dark_grey")
|
||||
table.insert(cuttable_nodes, "wool:green")
|
||||
table.insert(cuttable_nodes, "wool:grey")
|
||||
table.insert(cuttable_nodes, "wool:magenta")
|
||||
table.insert(cuttable_nodes, "wool:orange")
|
||||
table.insert(cuttable_nodes, "wool:pink")
|
||||
table.insert(cuttable_nodes, "wool:red")
|
||||
table.insert(cuttable_nodes, "wool:violet")
|
||||
table.insert(cuttable_nodes, "wool:white")
|
||||
table.insert(cuttable_nodes, "wool:yellow")
|
||||
end
|
||||
|
||||
return cuttable_nodes
|
||||
@@ -0,0 +1,115 @@
|
||||
-- Register enchanted tools.
|
||||
|
||||
local S = minetest.get_translator("xdecor")
|
||||
|
||||
-- Number of uses for the (normal) steel hoe from Minetest Game (as of 01/12/20224)
|
||||
-- This is technically redundant because we cannot access that number
|
||||
-- directly, but it's unlikely to change in future because Minetest Game is
|
||||
-- unlikely to change.
|
||||
local STEEL_HOE_USES = 500
|
||||
|
||||
-- Modifier of the steel hoe uses for the enchanted steel hoe
|
||||
local STEEL_HOE_USES_MODIFIER = 2.2
|
||||
|
||||
-- Modifier of the bug net uses for the enchanted bug net
|
||||
local BUG_NET_USES_MODIFIER = 4
|
||||
|
||||
-- Multiplies by much faster the fast hammer repairs
|
||||
local HAMMER_FAST_MODIFIER = 1.3
|
||||
|
||||
-- Reduces the wear taken by the hammer for a single repair step
|
||||
-- (absolute value)
|
||||
local HAMMER_DURABLE_MODIFIER = 100
|
||||
|
||||
-- Register enchantments for default tools from Minetest Game
|
||||
local materials = {"steel", "bronze", "mese", "diamond"}
|
||||
local tooltypes = {
|
||||
{ "axe", { "durable", "fast" }, "choppy" },
|
||||
{ "pick", { "durable", "fast" }, "cracky" },
|
||||
{ "shovel", { "durable", "fast" }, "crumbly" },
|
||||
{ "sword", { "sharp" }, nil },
|
||||
}
|
||||
for t=1, #tooltypes do
|
||||
for m=1, #materials do
|
||||
local tooltype = tooltypes[t][1]
|
||||
local enchants = tooltypes[t][2]
|
||||
local dig_group = tooltypes[t][3]
|
||||
local material = materials[m]
|
||||
xdecor.register_enchantable_tool("default:"..tooltype.."_"..material, {
|
||||
enchants = enchants,
|
||||
dig_group = dig_group,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Register enchantment for bug net
|
||||
xdecor.register_enchantable_tool("fireflies:bug_net", {
|
||||
enchants = { "durable" },
|
||||
dig_group = "catchable",
|
||||
bonuses = {
|
||||
uses = BUG_NET_USES_MODIFIER,
|
||||
}
|
||||
})
|
||||
|
||||
-- Register enchanted steel hoe (more durability)
|
||||
if farming.register_hoe then
|
||||
local percent = math.round((STEEL_HOE_USES_MODIFIER - 1) * 100)
|
||||
local hitem = ItemStack("farming:hoe_steel")
|
||||
local hdesc = hitem:get_short_description() or "farming:hoe_steel"
|
||||
local ehdesc, ehsdesc = xdecor.enchant_description(hdesc, "durable", percent)
|
||||
farming.register_hoe(":farming:enchanted_hoe_steel_durable", {
|
||||
description = ehdesc,
|
||||
short_description = ehsdesc,
|
||||
inventory_image = xdecor.enchant_texture("farming_tool_steelhoe.png"),
|
||||
max_uses = STEEL_HOE_USES * STEEL_HOE_USES_MODIFIER,
|
||||
groups = {hoe = 1, not_in_creative_inventory = 1}
|
||||
})
|
||||
|
||||
xdecor.register_custom_enchantable_tool("farming:hoe_steel", {
|
||||
durable = "farming:enchanted_hoe_steel_durable",
|
||||
})
|
||||
end
|
||||
|
||||
-- Register enchanted hammer (more durbility and efficiency)
|
||||
local hammerdef = minetest.registered_items["xdecor:hammer"]
|
||||
if hammerdef then
|
||||
local hitem = ItemStack("xdecor:hammer")
|
||||
local hdesc = hitem:get_short_description() or "xdecor:hammer"
|
||||
local repair = hammerdef._xdecor_hammer_repair
|
||||
local repair_cost = hammerdef._xdecor_hammer_repair_cost
|
||||
|
||||
-- Durable hammer (reduces wear taken by each repair step)
|
||||
local d_repair_cost_modified = repair_cost - HAMMER_DURABLE_MODIFIER
|
||||
local d_percent = math.round(100 - d_repair_cost_modified/repair_cost * 100)
|
||||
local d_ehdesc, d_ehsdesc = xdecor.enchant_description(hdesc, "durable", d_percent)
|
||||
|
||||
xdecor.register_hammer("xdecor:enchanted_hammer_durable", {
|
||||
description = d_ehdesc,
|
||||
short_description = d_ehsdesc,
|
||||
image = xdecor.enchant_texture("xdecor_hammer.png"),
|
||||
repair_cost = d_repair_cost_modified,
|
||||
groups = {repair_hammer = 1, not_in_creative_inventory = 1}
|
||||
})
|
||||
|
||||
-- Fast hammer (increases both repair amount and repair cost per
|
||||
-- repair step by an equal amount)
|
||||
local f_repair_modified = math.round(repair * HAMMER_FAST_MODIFIER)
|
||||
local repair_diff = f_repair_modified - repair
|
||||
local f_repair_cost_modified = repair_cost + repair_diff
|
||||
local f_percent = math.round(HAMMER_FAST_MODIFIER * 100 - 100)
|
||||
local f_ehdesc, f_ehsdesc = xdecor.enchant_description(hdesc, "fast", f_percent)
|
||||
|
||||
xdecor.register_hammer("xdecor:enchanted_hammer_fast", {
|
||||
description = f_ehdesc,
|
||||
short_description = f_ehsdesc,
|
||||
image = xdecor.enchant_texture("xdecor_hammer.png"),
|
||||
repair = f_repair_modified,
|
||||
repair_cost = f_repair_cost_modified,
|
||||
groups = {repair_hammer = 1, not_in_creative_inventory = 1}
|
||||
})
|
||||
|
||||
xdecor.register_custom_enchantable_tool("xdecor:hammer", {
|
||||
durable = "xdecor:enchanted_hammer_durable",
|
||||
fast = "xdecor:enchanted_hammer_fast",
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,520 @@
|
||||
local enchanting = {}
|
||||
|
||||
screwdriver = screwdriver or {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
local NS = function(s) return s end
|
||||
local FS = function(...) return minetest.formspec_escape(S(...)) end
|
||||
local ceil, abs, random = math.ceil, math.abs, math.random
|
||||
local reg_tools = minetest.registered_tools
|
||||
local reg_enchantable_tools = {}
|
||||
local available_tool_enchants = {}
|
||||
|
||||
-- Cost in Mese crystal(s) for enchanting.
|
||||
local MESE_COST = 1
|
||||
|
||||
-- Default strenth of the enchantments
|
||||
local DEFAULT_ENCHANTING_USES = 1.2 -- Durability
|
||||
local DEFAULT_ENCHANTING_TIMES = 0.1 -- Efficiency
|
||||
local DEFAULT_ENCHANTING_DAMAGES = 1 -- Sharpness
|
||||
|
||||
local function to_percent(orig_value, final_value)
|
||||
return abs(ceil(((final_value - orig_value) / orig_value) * 100))
|
||||
end
|
||||
|
||||
function enchanting:get_tooltip_raw(enchant, percent)
|
||||
local specs = {
|
||||
durable = "#00baff",
|
||||
fast = "#74ff49",
|
||||
sharp = "#ffff00",
|
||||
}
|
||||
local enchant_loc = {
|
||||
--~ Enchantment
|
||||
fast = S("Efficiency"),
|
||||
--~ Enchantment
|
||||
durable = S("Durability"),
|
||||
--~ Enchantment
|
||||
sharp = S("Sharpness"),
|
||||
}
|
||||
|
||||
if minetest.colorize then
|
||||
--~ Tooltip for enchantment, in format "<enchantment name> (+<bonus>%)", e.g. "Efficiency (+5%)"
|
||||
return minetest.colorize(specs[enchant], S("@1 (+@2%)", enchant_loc[enchant], percent))
|
||||
else
|
||||
return S("@1 (+@2%)", enchant_loc[enchant], percent)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function enchanting:get_tooltip(enchant, orig_caps, fleshy, bonus_defs)
|
||||
local bonus = {durable = 0, efficiency = 0, damages = 0}
|
||||
|
||||
if orig_caps then
|
||||
bonus.durable = to_percent(orig_caps.uses, orig_caps.uses * bonus_defs.uses)
|
||||
local sum_caps_times = 0
|
||||
for i=1, #orig_caps.times do
|
||||
sum_caps_times = sum_caps_times + orig_caps.times[i]
|
||||
end
|
||||
local average_caps_time = sum_caps_times / #orig_caps.times
|
||||
bonus.efficiency = to_percent(average_caps_time, average_caps_time -
|
||||
bonus_defs.times)
|
||||
end
|
||||
|
||||
if fleshy then
|
||||
bonus.damages = to_percent(fleshy, fleshy + bonus_defs.damages)
|
||||
end
|
||||
|
||||
local specs = {
|
||||
durable = bonus.durable,
|
||||
fast = bonus.efficiency,
|
||||
sharp = bonus.damages,
|
||||
}
|
||||
local percent = specs[enchant]
|
||||
return enchanting:get_tooltip_raw(enchant, percent)
|
||||
end
|
||||
|
||||
local enchant_buttons = {
|
||||
fast = "image_button[3.6,0.67;4.75,0.85;bg_btn.png;fast;"..FS("Efficiency").."]",
|
||||
durable = "image_button[3.6,1.65;4.75,1.05;bg_btn.png;durable;"..FS("Durability").."]",
|
||||
sharp = "image_button[3.6,2.8;4.75,0.85;bg_btn.png;sharp;"..FS("Sharpness").."]",
|
||||
}
|
||||
|
||||
function enchanting.formspec(pos, enchants)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local formspec = [[
|
||||
size[9,8.6;]
|
||||
no_prepend[]
|
||||
bgcolor[#080808BB;true]
|
||||
listcolors[#00000069;#5A5A5A;#141318;#30434C;#FFF]
|
||||
background9[0,0;9,9;ench_ui.png;6]
|
||||
list[context;tool;0.9,2.9;1,1;]
|
||||
list[context;mese;2,2.9;1,1;]
|
||||
list[current_player;main;0.55,4.5;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;tool]
|
||||
listring[current_player;main]
|
||||
listring[context;mese]
|
||||
image[2,2.9;1,1;mese_layout.png]
|
||||
]]
|
||||
--~ Sharpness enchantment
|
||||
.."tooltip[sharp;"..FS("Your weapon inflicts more damage").."]"
|
||||
--~ Durability enchantment
|
||||
.."tooltip[durable;"..FS("Your tool lasts longer").."]"
|
||||
--~ Efficiency enchantment
|
||||
.."tooltip[fast;"..FS("Your tool digs faster").."]"
|
||||
..default.gui_slots .. default.get_hotbar_bg(0.55, 4.5)
|
||||
|
||||
if enchants then
|
||||
for e=1, #enchants do
|
||||
formspec = formspec .. enchant_buttons[enchants[e]]
|
||||
end
|
||||
end
|
||||
meta:set_string("formspec", formspec)
|
||||
end
|
||||
|
||||
function enchanting.on_put(pos, listname, _, stack)
|
||||
if listname == "tool" then
|
||||
local stackname = stack:get_name()
|
||||
local enchants = available_tool_enchants[stackname]
|
||||
if enchants then
|
||||
enchanting.formspec(pos, enchants)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function enchanting.fields(pos, _, fields, sender)
|
||||
if not next(fields) or fields.quit then return end
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local tool = inv:get_stack("tool", 1)
|
||||
local mese = inv:get_stack("mese", 1)
|
||||
local orig_wear = tool:get_wear()
|
||||
local mod, name = tool:get_name():match("(.*):(.*)")
|
||||
local enchanted_tool = (mod or "") .. ":enchanted_" .. (name or "") .. "_" .. next(fields)
|
||||
|
||||
if mese:get_count() >= MESE_COST and reg_tools[enchanted_tool] then
|
||||
minetest.sound_play("xdecor_enchanting", {
|
||||
to_player = sender:get_player_name(),
|
||||
gain = 0.8
|
||||
})
|
||||
|
||||
tool:replace(enchanted_tool)
|
||||
tool:add_wear(orig_wear)
|
||||
mese:take_item(MESE_COST)
|
||||
inv:set_stack("mese", 1, mese)
|
||||
inv:set_stack("tool", 1, tool)
|
||||
end
|
||||
end
|
||||
|
||||
function enchanting.dig(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
return inv:is_empty("tool") and inv:is_empty("mese")
|
||||
end
|
||||
|
||||
function enchanting.blast(pos)
|
||||
local drops = xdecor.get_inventory_drops(pos, {"tool", "mese"})
|
||||
minetest.remove_node(pos)
|
||||
return drops
|
||||
end
|
||||
|
||||
local function allowed(tool)
|
||||
if not tool then
|
||||
return false
|
||||
end
|
||||
if reg_enchantable_tools[tool] then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function enchanting.put(_, listname, _, stack)
|
||||
local stackname = stack:get_name()
|
||||
if listname == "mese" and (stackname == "default:mese_crystal" or
|
||||
stackname == "imese:industrial_mese_crystal") then
|
||||
return stack:get_count()
|
||||
elseif listname == "tool" and allowed(stackname) then
|
||||
return 1
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function enchanting.on_take(pos, listname)
|
||||
if listname == "tool" then
|
||||
enchanting.formspec(pos)
|
||||
end
|
||||
end
|
||||
|
||||
function enchanting.construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", S("Enchantment Table"))
|
||||
enchanting.formspec(pos)
|
||||
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("tool", 1)
|
||||
inv:set_size("mese", 1)
|
||||
|
||||
minetest.add_entity({x = pos.x, y = pos.y + 0.85, z = pos.z}, "xdecor:book_open")
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(0.5)
|
||||
end
|
||||
|
||||
function enchanting.destruct(pos)
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0.9)) do
|
||||
if obj and obj:get_luaentity() and
|
||||
obj:get_luaentity().name == "xdecor:book_open" then
|
||||
obj:remove()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function enchanting.timer(pos)
|
||||
local minp = {x = pos.x - 2, y = pos.y, z = pos.z - 2}
|
||||
local maxp = {x = pos.x + 2, y = pos.y + 1, z = pos.z + 2}
|
||||
|
||||
local bookshelves = minetest.find_nodes_in_area(minp, maxp, "default:bookshelf")
|
||||
if #bookshelves == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
local bookshelf_pos = bookshelves[random(1, #bookshelves)]
|
||||
local x = pos.x - bookshelf_pos.x
|
||||
local y = bookshelf_pos.y - pos.y
|
||||
local z = pos.z - bookshelf_pos.z
|
||||
|
||||
if tostring(x .. z):find(2) then
|
||||
minetest.add_particle({
|
||||
pos = bookshelf_pos,
|
||||
velocity = {x = x, y = 2 - y, z = z},
|
||||
acceleration = {x = 0, y = -2.2, z = 0},
|
||||
expirationtime = 1,
|
||||
size = 1.5,
|
||||
glow = 5,
|
||||
texture = "xdecor_glyph" .. random(1,18) .. ".png"
|
||||
})
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
xdecor.register("enchantment_table", {
|
||||
description = S("Enchantment Table"),
|
||||
--~ Enchantment table tooltip
|
||||
_tt_help = S("Enchant your tools with mese crystals"),
|
||||
tiles = {
|
||||
"xdecor_enchantment_top.png", "xdecor_enchantment_bottom.png",
|
||||
"xdecor_enchantment_side.png", "xdecor_enchantment_side.png",
|
||||
"xdecor_enchantment_side.png", "xdecor_enchantment_side.png"
|
||||
},
|
||||
groups = {cracky = 1, level = 1},
|
||||
is_ground_content = false,
|
||||
light_source = 6,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
can_dig = enchanting.dig,
|
||||
on_blast = enchanting.blast,
|
||||
on_timer = enchanting.timer,
|
||||
on_construct = enchanting.construct,
|
||||
on_destruct = enchanting.destruct,
|
||||
on_receive_fields = enchanting.fields,
|
||||
on_metadata_inventory_put = enchanting.on_put,
|
||||
on_metadata_inventory_take = enchanting.on_take,
|
||||
allow_metadata_inventory_put = enchanting.put,
|
||||
allow_metadata_inventory_move = function()
|
||||
return 0
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity("xdecor:book_open", {
|
||||
initial_properties = {
|
||||
visual = "sprite",
|
||||
visual_size = {x=0.75, y=0.75},
|
||||
collisionbox = {0,0,0,0,0,0},
|
||||
pointable = false,
|
||||
physical = false,
|
||||
textures = {"xdecor_book_open.png"},
|
||||
static_save = false,
|
||||
},
|
||||
|
||||
on_activate = function(self)
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
end,
|
||||
|
||||
-- Minetest Game support: Prevent entity being pushed or damaged by TNT explosion
|
||||
on_blast = function()
|
||||
return false, false, {}
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "recreate book entity",
|
||||
name = "xdecor:create_book_entity",
|
||||
nodenames = {"xdecor:enchantment_table"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
local objs = minetest.get_objects_inside_radius(pos, 0.9)
|
||||
|
||||
for _, obj in ipairs(objs) do
|
||||
local e = obj:get_luaentity()
|
||||
if e and e.name == "xdecor:book_open" then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
minetest.add_entity({x = pos.x, y = pos.y + 0.85, z = pos.z}, "xdecor:book_open")
|
||||
end,
|
||||
})
|
||||
|
||||
function enchanting:enchant_texture(img)
|
||||
if img == nil or img == "" or type(img) ~= "string" then
|
||||
return "no_texture.png"
|
||||
else
|
||||
return "("..img.. ")^[colorize:violet:50"
|
||||
end
|
||||
end
|
||||
|
||||
function enchanting:register_tool(original_tool_name, def)
|
||||
local original_tool = reg_tools[original_tool_name]
|
||||
if not original_tool then
|
||||
minetest.log("error", "[xdecor] Called enchanting:register_tool for non-existing tool: "..original_tool_name)
|
||||
return
|
||||
end
|
||||
local original_toolcaps = original_tool.tool_capabilities
|
||||
if not original_toolcaps then
|
||||
minetest.log("error", "[xdecor] Called enchanting:register_tool for tool without tool_capabilities: "..original_tool_name)
|
||||
return
|
||||
end
|
||||
local original_damage_groups = original_toolcaps.damage_groups
|
||||
local original_groupcaps = original_toolcaps.groupcaps
|
||||
local original_basename = original_tool_name:match(".*:(.*)")
|
||||
local toolitem = ItemStack(original_tool_name)
|
||||
local original_desc = toolitem:get_short_description() or original_tool_name
|
||||
local groups
|
||||
if def.groups then
|
||||
groups = table.copy(def.groups)
|
||||
elseif original_tool.groups then
|
||||
groups = table.copy(original_tool.groups)
|
||||
else
|
||||
groups = {}
|
||||
end
|
||||
groups.not_in_creative_inventory = 1
|
||||
groups.enchanted_tool = 1
|
||||
for _, enchant in ipairs(def.enchants) do
|
||||
local groupcaps = table.copy(original_groupcaps)
|
||||
local full_punch_interval = original_toolcaps.full_punch_interval
|
||||
local max_drop_level = original_toolcaps.max_drop_level
|
||||
local dig_group = def.dig_group
|
||||
local fleshy
|
||||
|
||||
if not def.bonuses then
|
||||
def.bonuses = {}
|
||||
end
|
||||
local bonus_defs = {
|
||||
uses = def.bonuses.uses or DEFAULT_ENCHANTING_USES,
|
||||
times = def.bonuses.times or DEFAULT_ENCHANTING_TIMES,
|
||||
damages = def.bonuses.damages or DEFAULT_ENCHANTING_DAMAGES,
|
||||
}
|
||||
|
||||
if enchant == "durable" then
|
||||
groupcaps[dig_group].uses = ceil(original_groupcaps[dig_group].uses *
|
||||
bonus_defs.uses)
|
||||
groups.enchantment_durable = 1
|
||||
elseif enchant == "fast" then
|
||||
for i, time in pairs(original_groupcaps[dig_group].times) do
|
||||
groupcaps[dig_group].times[i] = time - bonus_defs.times
|
||||
end
|
||||
groups.enchantment_fast = 1
|
||||
elseif enchant == "sharp" then
|
||||
fleshy = original_damage_groups.fleshy
|
||||
fleshy = fleshy + bonus_defs.damages
|
||||
groups.enchantment_sharp = 1
|
||||
else
|
||||
minetest.log("error", "[xdecor] Called enchanting:register_tool with unsupported enchant: "..tostring(enchant))
|
||||
return
|
||||
end
|
||||
|
||||
local arg1 = original_desc
|
||||
local arg2 = self:get_tooltip(enchant, original_groupcaps[dig_group], fleshy, bonus_defs)
|
||||
local enchantedTool = original_tool.mod_origin .. ":enchanted_" .. original_basename .. "_" .. enchant
|
||||
|
||||
local invimg = original_tool.inventory_image
|
||||
invimg = enchanting:enchant_texture(invimg)
|
||||
local wieldimg = original_tool.wield_image
|
||||
if wieldimg == nil or wieldimg == "" then
|
||||
wieldimg = invimg
|
||||
end
|
||||
minetest.register_tool(":" .. enchantedTool, {
|
||||
--~ Enchanted tool description, e.g. "Enchanted Diamond Sword". @1 is the original tool name, @2 is the enchantment text, e.g. "Durability (+20%)"
|
||||
description = S("Enchanted @1\n@2", arg1, arg2),
|
||||
--~ Enchanted tool description, e.g. "Enchanted Diamond Sword"
|
||||
short_description = S("Enchanted @1", arg1),
|
||||
inventory_image = invimg,
|
||||
wield_image = wieldimg,
|
||||
groups = groups,
|
||||
tool_capabilities = {
|
||||
groupcaps = groupcaps, damage_groups = {fleshy = fleshy},
|
||||
full_punch_interval = full_punch_interval,
|
||||
max_drop_level = max_drop_level
|
||||
},
|
||||
pointabilities = original_tool.pointabilities,
|
||||
})
|
||||
if minetest.get_modpath("toolranks") then
|
||||
toolranks.add_tool(enchantedTool)
|
||||
end
|
||||
end
|
||||
available_tool_enchants[original_tool_name] = table.copy(def.enchants)
|
||||
reg_enchantable_tools[original_tool_name] = true
|
||||
end
|
||||
|
||||
function enchanting:register_custom_tool(original_tool_name, enchanted_tools)
|
||||
if not available_tool_enchants[original_tool_name] then
|
||||
available_tool_enchants[original_tool_name] = {}
|
||||
end
|
||||
for enchant, v in pairs(enchanted_tools) do
|
||||
table.insert(available_tool_enchants[original_tool_name], enchant)
|
||||
end
|
||||
reg_enchantable_tools[original_tool_name] = true
|
||||
end
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:enchantment_table",
|
||||
recipe = {
|
||||
{"", "default:book", ""},
|
||||
{"default:diamond", "default:obsidian", "default:diamond"},
|
||||
{"default:obsidian", "default:obsidian", "default:obsidian"}
|
||||
}
|
||||
})
|
||||
|
||||
--[[ API FUNCTIONS ]]
|
||||
|
||||
--[[
|
||||
Register one or more enchantments for an already defined tool.
|
||||
This will register a new tool for each enchantment. The new tools will
|
||||
have the following changes over the original:
|
||||
* New description and short_description
|
||||
* Apply a purple glow on wield_image and inventory_image using
|
||||
"(<original_texture_string>)^[colorize:purple"
|
||||
* Change tool_capabilities and damage_groups, depending on
|
||||
enchantments.
|
||||
* Have groups set to { not_in_creative_inventory = 1 }
|
||||
|
||||
The new tools will follow this naming scheme:
|
||||
|
||||
<original_mod>:enchanted_<original_basename>_<enchantment>
|
||||
|
||||
e.g. example:sword_diamond with the enchantment "sharp" will
|
||||
have "example:enchanted_sword_diamond_sharp" added.
|
||||
|
||||
You must make sure this name is available before calling this
|
||||
function.
|
||||
|
||||
Arguments:
|
||||
* toolname: Itemstring of original tool to enchant
|
||||
* def: Definition table with the following fields:
|
||||
* enchants: a list of strings, one for each enchantment to add.
|
||||
there must be at least one enchantment.
|
||||
Available enchantments:
|
||||
* "durable": Durability (tool lasts longer)
|
||||
* "fast": Efficiency (tool digs faster)
|
||||
* "sharp": Sharpness (more damage using the damage group "fleshy")
|
||||
* dig_group: Must be specified if Durability or Efficiency is used.
|
||||
This defines the tool's digging group that enchantment will improve.
|
||||
* bonuses: optional table to customize the enchantment "strengths":
|
||||
* uses: multiplies number of uses (Durability) (default: 1.2)
|
||||
* times: subtracts from digging time; higher = faster (Efficiency) (default: 0.1)
|
||||
* damages: adds to damage (Sharpness) (default: 1)
|
||||
* groups: optional table specifying all item groups. If specified,
|
||||
this should at least contain `not_in_creative_inventory=1`.
|
||||
If unspecified (recommended), the enchanted tools will inherit all
|
||||
groups from the original tool, plus they receive `not_in_creative_inventory=1`
|
||||
]]
|
||||
xdecor.register_enchantable_tool = function(toolname, def)
|
||||
enchanting:register_tool(toolname, def)
|
||||
end
|
||||
|
||||
--[[ Registers a custom tool enchantment.
|
||||
Here, you are fully free to design the tool yourself.
|
||||
|
||||
The enchanted tools should follow these guidelines:
|
||||
|
||||
1) Use xdecor.enchant_description to generate the description and short_description
|
||||
2) Use xdecor.enchant_texture to generate the inventory_image and wield_image
|
||||
3) Set groups to { not_in_creative_inventory = 1 }
|
||||
|
||||
Arguments:
|
||||
* toolname: Itemstring of original tool to enchant
|
||||
* enchanted_tools: Table of enchanted tools.
|
||||
* The keys are enchantment names from "enchants" in xdecor.register_enchantable_tool
|
||||
* The values are the itemstrings of the enchanted tools for those
|
||||
enchantments
|
||||
]]
|
||||
xdecor.register_custom_enchantable_tool = function(toolname, enchanted_tools)
|
||||
enchanting:register_custom_tool(toolname, enchanted_tools)
|
||||
end
|
||||
|
||||
-- Takes a texture (string) and applies an "enchanting" modifier on it.
|
||||
-- Useful when you want to register custom tool enchantments.
|
||||
xdecor.enchant_texture = function(texture)
|
||||
return enchanting:enchant_texture(texture)
|
||||
end
|
||||
|
||||
--[[
|
||||
Takes a description of a normal tool and modifies it for the enchanted tool variant.
|
||||
Arguments:
|
||||
* description: Original description to modify
|
||||
* enchant: Enchantment type. One of the enchantment names from "enchants" in xdecor.register_enchantable_tool
|
||||
* percent: Percentage to display
|
||||
|
||||
Returns: <description>, <short_description>
|
||||
|
||||
-- Useful when you want to register custom tool enchantments.
|
||||
]]
|
||||
xdecor.enchant_description = function(description, enchant, percent)
|
||||
local append = enchanting:get_tooltip_raw(enchant, percent)
|
||||
local desc = S("Enchanted @1\n@2", description, append)
|
||||
local short_desc S("Enchanted @1", description)
|
||||
return desc, short_desc
|
||||
end
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
local hive = {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
local FS = function(...) return minetest.formspec_escape(S(...)) end
|
||||
local HONEY_MAX = 16
|
||||
local NEEDED_FLOWERS = 3
|
||||
local TIMER_MIN = 64
|
||||
local TIMER_MAX = 128
|
||||
|
||||
local text_busy = FS("The bees are busy making honey.")
|
||||
local text_noflowers = FS("The bees are looking for flowers.")
|
||||
local text_fewflowers = FS("The bees want to pollinate more flowers.")
|
||||
local text_idle = FS("The bees are idle.")
|
||||
local text_sleep = FS("The bees are resting.")
|
||||
|
||||
function hive.set_formspec(meta, status)
|
||||
local statustext
|
||||
if status == "busy" then
|
||||
statustext = text_busy
|
||||
elseif status == "noflowers" then
|
||||
statustext = text_noflowers
|
||||
elseif status == "fewflowers" then
|
||||
statustext = text_fewflowers
|
||||
elseif status == "idle" then
|
||||
statustext = text_idle
|
||||
elseif status == "sleep" then
|
||||
statustext = text_sleep
|
||||
end
|
||||
|
||||
local formspec = "size[8,6;]"
|
||||
.."label[0.5,0;"..statustext.."]"
|
||||
..[[ image[6,1;1,1;hive_bee.png]
|
||||
image[5,1;1,1;hive_layout.png]
|
||||
list[context;honey;5,1;1,1;]
|
||||
list[current_player;main;0,2.35;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;honey] ]] ..
|
||||
xdecor.xbg .. default.get_hotbar_bg(0,2.35)
|
||||
meta:set_string("formspec", formspec)
|
||||
end
|
||||
|
||||
local function count_flowers(pos)
|
||||
local radius = 4
|
||||
local minp = vector.add(pos, -radius)
|
||||
local maxp = vector.add(pos, radius)
|
||||
local flowers = minetest.find_nodes_in_area_under_air(minp, maxp, "group:flower")
|
||||
return #flowers
|
||||
end
|
||||
|
||||
local function is_sleeptime()
|
||||
local time = (minetest.get_timeofday() or 0) * 24000
|
||||
if time < 5500 or time > 18500 then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function hive.construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
local status = "idle"
|
||||
local flowers = count_flowers(pos)
|
||||
if is_sleeptime() then
|
||||
status = "sleep"
|
||||
elseif flowers >= NEEDED_FLOWERS then
|
||||
status = "busy"
|
||||
elseif flowers > 0 then
|
||||
status = "fewflowers"
|
||||
else
|
||||
status = "noflowers"
|
||||
end
|
||||
hive.set_formspec(meta, status)
|
||||
meta:set_string("infotext", S("Artificial Hive"))
|
||||
inv:set_size("honey", 1)
|
||||
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(math.random(TIMER_MIN, TIMER_MAX))
|
||||
end
|
||||
|
||||
function hive.timer(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if is_sleeptime() then
|
||||
hive.set_formspec(meta, "sleep")
|
||||
return true
|
||||
end
|
||||
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local honeystack = inv:get_stack("honey", 1)
|
||||
local honey = honeystack:get_count()
|
||||
|
||||
local flowers = count_flowers(pos)
|
||||
|
||||
if flowers >= NEEDED_FLOWERS and honey < HONEY_MAX then
|
||||
if honey == HONEY_MAX - 1 then
|
||||
hive.set_formspec(meta, "idle")
|
||||
else
|
||||
hive.set_formspec(meta, "busy")
|
||||
end
|
||||
inv:add_item("honey", "xdecor:honey")
|
||||
elseif honey == HONEY_MAX then
|
||||
hive.set_formspec(meta, "idle")
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:stop()
|
||||
return true
|
||||
end
|
||||
if flowers == 0 then
|
||||
hive.set_formspec(meta, "noflowers")
|
||||
elseif flowers < NEEDED_FLOWERS then
|
||||
hive.set_formspec(meta, "fewflowers")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function hive.blast(pos)
|
||||
local drops = xdecor.get_inventory_drops(pos, {"honey"})
|
||||
minetest.remove_node(pos)
|
||||
return drops
|
||||
end
|
||||
|
||||
xdecor.register("hive", {
|
||||
--~ Beehive
|
||||
description = S("Artificial Hive"),
|
||||
--~ Tooltip of artificial hive
|
||||
_tt_help = S("Bees live here and produce honey"),
|
||||
tiles = {"xdecor_hive_top.png", "xdecor_hive_top.png",
|
||||
"xdecor_hive_side.png", "xdecor_hive_side.png",
|
||||
"xdecor_hive_side.png", "xdecor_hive_front.png"},
|
||||
groups = {choppy=3, oddly_breakable_by_hand=2, flammable=1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = hive.construct,
|
||||
on_timer = hive.timer,
|
||||
on_blast = hive.blast,
|
||||
|
||||
can_dig = function(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
return inv:is_empty("honey")
|
||||
end,
|
||||
|
||||
on_punch = function(_, _, puncher)
|
||||
puncher:set_hp(puncher:get_hp() - 2)
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function()
|
||||
return 0
|
||||
end,
|
||||
|
||||
on_metadata_inventory_take = function(pos, list, index, stack)
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
local remainstack = inv:get_stack(list, index)
|
||||
-- Trigger if taking anything from full honey slot
|
||||
if remainstack:get_count() + stack:get_count() >= HONEY_MAX then
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(math.random(TIMER_MIN, TIMER_MAX))
|
||||
if not is_sleeptime() and count_flowers(pos) >= NEEDED_FLOWERS then
|
||||
local meta = minetest.get_meta(pos)
|
||||
hive.set_formspec(meta, "busy")
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- Craft items
|
||||
|
||||
minetest.register_craftitem("xdecor:honey", {
|
||||
description = S("Honey"),
|
||||
inventory_image = "xdecor_honey.png",
|
||||
wield_image = "xdecor_honey.png",
|
||||
on_use = minetest.item_eat(2),
|
||||
groups = {
|
||||
food_honey = 1,
|
||||
food_sugar = 1,
|
||||
flammable = 2,
|
||||
xdecor_soup_ingredient = 1,
|
||||
},
|
||||
})
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:hive",
|
||||
recipe = {
|
||||
{"group:stick", "group:stick", "group:stick"},
|
||||
{"default:paper", "default:paper", "default:paper"},
|
||||
{"group:stick", "group:stick", "group:stick"}
|
||||
}
|
||||
})
|
||||
|
||||
if minetest.get_modpath("unified_inventory") then
|
||||
unified_inventory.register_craft_type("xdecor:hive", {
|
||||
--~ Tooltip for honey in crafting guide of the Unified Inventory mod
|
||||
description = S("Made by bees"),
|
||||
icon = "hive_bee.png",
|
||||
width = 1,
|
||||
height = 1,
|
||||
uses_crafting_grid = false
|
||||
})
|
||||
|
||||
unified_inventory.register_craft({
|
||||
output = "xdecor:honey",
|
||||
type = "xdecor:hive",
|
||||
items = {"xdecor:hive"},
|
||||
width = 1
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,266 @@
|
||||
-- Item frames.
|
||||
|
||||
-- Hint:
|
||||
-- If your item appears behind or too far in front of the item frame, add
|
||||
-- _xdecor_itemframe_offset = <number>
|
||||
-- to your item definition to fix it.
|
||||
|
||||
local itemframe, tmp = {}, {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
screwdriver = screwdriver or {}
|
||||
|
||||
-- Description of item frame node
|
||||
local DESC_ITEM_FRAME = S("Item Frame")
|
||||
|
||||
local function remove_item(pos, node)
|
||||
local objs = minetest.get_objects_inside_radius(pos, 0.5)
|
||||
if not objs then return end
|
||||
|
||||
for _, obj in pairs(objs) do
|
||||
local ent = obj:get_luaentity()
|
||||
if obj and ent and ent.name == "xdecor:f_item" then
|
||||
obj:remove() break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local facedir = {
|
||||
[0] = {x = 0, y = 0, z = 1},
|
||||
{x = 1, y = 0, z = 0},
|
||||
{x = 0, y = 0, z = -1},
|
||||
{x = -1, y = 0, z = 0}
|
||||
}
|
||||
|
||||
local function update_item(pos, node)
|
||||
remove_item(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local itemstring = meta:get_string("item")
|
||||
local posad = facedir[node.param2]
|
||||
if not posad or itemstring == "" then return end
|
||||
|
||||
local itemdef = ItemStack(itemstring):get_definition()
|
||||
local offset_plus = 0
|
||||
if itemdef and itemdef._xdecor_itemframe_offset then
|
||||
offset_plus = itemdef._xdecor_itemframe_offset
|
||||
offset_plus = math.min(6, math.max(-6, offset_plus))
|
||||
end
|
||||
local offset = (6.5+offset_plus)/16
|
||||
|
||||
pos = vector.add(pos, vector.multiply(posad, offset))
|
||||
tmp.nodename = node.name
|
||||
tmp.texture = ItemStack(itemstring):get_name()
|
||||
|
||||
local entity = minetest.add_entity(pos, "xdecor:f_item")
|
||||
local yaw = (math.pi * 2) - node.param2 * (math.pi / 2)
|
||||
entity:set_yaw(yaw)
|
||||
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(15.0)
|
||||
end
|
||||
|
||||
local function drop_item(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local item = meta:get_string("item")
|
||||
if item == "" then return end
|
||||
|
||||
minetest.add_item(pos, item)
|
||||
meta:set_string("item", "")
|
||||
remove_item(pos, node)
|
||||
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:stop()
|
||||
end
|
||||
|
||||
function itemframe.set_infotext(meta)
|
||||
local itemstring = meta:get_string("item")
|
||||
local owner = meta:get_string("owner")
|
||||
if itemstring == "" then
|
||||
if owner ~= "" then
|
||||
meta:set_string("infotext",
|
||||
--~ Item frame infotext. @1 = item frame name, @2 = owner name (player)
|
||||
S("@1 (owned by @2)",
|
||||
--~ Block that can display an item
|
||||
DESC_ITEM_FRAME, owner))
|
||||
else
|
||||
meta:set_string("infotext", DESC_ITEM_FRAME)
|
||||
end
|
||||
else
|
||||
local itemstack = ItemStack(itemstring)
|
||||
local tooltip = itemstack:get_short_description()
|
||||
if tooltip == "" then
|
||||
tooltip = itemstack:get_name()
|
||||
end
|
||||
if itemstring == "" then
|
||||
tooltip = DESC_ITEM_FRAME
|
||||
end
|
||||
if owner ~= "" then
|
||||
meta:set_string("infotext", S("@1 (owned by @2)", tooltip, owner))
|
||||
else
|
||||
meta:set_string("infotext", tooltip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function itemframe.after_place(pos, placer, itemstack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = placer:get_player_name()
|
||||
meta:set_string("owner", name)
|
||||
itemframe.set_infotext(meta)
|
||||
end
|
||||
|
||||
function itemframe.timer(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local num = #minetest.get_objects_inside_radius(pos, 0.5)
|
||||
|
||||
if num == 0 and meta:get_string("item") ~= "" then
|
||||
update_item(pos, node)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function itemframe.rightclick(pos, node, clicker, itemstack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local player_name = clicker:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
local admin = minetest.check_player_privs(player_name, "protection_bypass")
|
||||
|
||||
if not admin and (player_name ~= owner or not itemstack) then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
drop_item(pos, node)
|
||||
local itemstring = itemstack:take_item():to_string()
|
||||
meta:set_string("item", itemstring)
|
||||
itemframe.set_infotext(meta)
|
||||
update_item(pos, node)
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function itemframe.punch(pos, node, puncher)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local player_name = puncher:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
local admin = minetest.check_player_privs(player_name, "protection_bypass")
|
||||
|
||||
if admin or player_name == owner then
|
||||
drop_item(pos, node)
|
||||
end
|
||||
end
|
||||
|
||||
function itemframe.dig(pos, player)
|
||||
if not player then return end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local player_name = player and player:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
local admin = minetest.check_player_privs(player_name, "protection_bypass")
|
||||
|
||||
return admin or player_name == owner
|
||||
end
|
||||
|
||||
function itemframe.blast(pos)
|
||||
return
|
||||
end
|
||||
|
||||
xdecor.register("itemframe", {
|
||||
description = DESC_ITEM_FRAME,
|
||||
--~ Item frame tooltip
|
||||
_tt_help = S("For presenting a single item"),
|
||||
groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_rotate = screwdriver.disallow,
|
||||
sunlight_propagates = true,
|
||||
inventory_image = "xdecor_itemframe.png",
|
||||
node_box = xdecor.nodebox.slab_z(0.9375),
|
||||
tiles = {
|
||||
"xdecor_wood.png", "xdecor_wood.png", "xdecor_wood.png",
|
||||
"xdecor_wood.png", "xdecor_wood.png", "xdecor_itemframe.png"
|
||||
},
|
||||
after_place_node = itemframe.after_place,
|
||||
on_timer = itemframe.timer,
|
||||
on_rightclick = itemframe.rightclick,
|
||||
on_punch = itemframe.punch,
|
||||
can_dig = itemframe.dig,
|
||||
on_blast = itemframe.blast,
|
||||
on_destruct = function(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
drop_item(pos, node)
|
||||
end,
|
||||
after_destruct = remove_item,
|
||||
_xdecor_itemframe_offset = -3.5,
|
||||
})
|
||||
|
||||
minetest.register_entity("xdecor:f_item", {
|
||||
initial_properties = {
|
||||
visual = "wielditem",
|
||||
visual_size = {x = 0.33, y = 0.33},
|
||||
collisionbox = {0,0,0,0,0,0},
|
||||
pointable = false,
|
||||
physical = false,
|
||||
textures = {"air"},
|
||||
},
|
||||
on_activate = function(self, staticdata)
|
||||
local pos = self.object:get_pos()
|
||||
if minetest.get_node(pos).name ~= "xdecor:itemframe" then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
if tmp.nodename and tmp.texture then
|
||||
self.nodename = tmp.nodename
|
||||
tmp.nodename = nil
|
||||
self.texture = tmp.texture
|
||||
tmp.texture = nil
|
||||
elseif staticdata and staticdata ~= "" then
|
||||
local data = staticdata:split(";")
|
||||
if data and data[1] and data[2] then
|
||||
self.nodename = data[1]
|
||||
self.texture = data[2]
|
||||
end
|
||||
end
|
||||
if self.texture then
|
||||
self.object:set_properties({
|
||||
textures = {self.texture}
|
||||
})
|
||||
end
|
||||
|
||||
self.object:set_armor_groups({immortal=1})
|
||||
end,
|
||||
get_staticdata = function(self)
|
||||
if self.nodename and self.texture then
|
||||
return self.nodename .. ";" .. self.texture
|
||||
end
|
||||
|
||||
return ""
|
||||
end,
|
||||
|
||||
-- Minetest Game support: Prevent entity being pushed or damaged by TNT explosion
|
||||
on_blast = function()
|
||||
return false, false, {}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:itemframe",
|
||||
recipe = {
|
||||
{"group:stick", "group:stick", "group:stick"},
|
||||
{"group:stick", "default:paper", "group:stick"},
|
||||
{"group:stick", "group:stick", "group:stick"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Update itemframe infotexts",
|
||||
name = "xdecor:update_itemframe_infotexts",
|
||||
nodenames = {"xdecor:itemframe"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
itemframe.set_infotext(meta)
|
||||
end,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
local mailbox = {}
|
||||
screwdriver = screwdriver or {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
local FS = function(...) return minetest.formspec_escape(S(...)) end
|
||||
|
||||
-- Max. length of the list of givers in mailbox formspec
|
||||
local GIVER_LIST_LENGTH = 7
|
||||
|
||||
local function get_img(img)
|
||||
if not img then return end
|
||||
local img_name = img:match("(.*)%.png")
|
||||
|
||||
if img_name then
|
||||
return img_name .. ".png"
|
||||
end
|
||||
end
|
||||
|
||||
local function img_col(stack)
|
||||
local def = minetest.registered_items[stack]
|
||||
if not def then
|
||||
return ""
|
||||
end
|
||||
|
||||
if def.inventory_image ~= "" then
|
||||
local img = get_img(def.inventory_image)
|
||||
if img then
|
||||
return img
|
||||
end
|
||||
end
|
||||
|
||||
if def.tiles then
|
||||
local tile, img = def.tiles[1]
|
||||
if type(tile) == "table" then
|
||||
img = get_img(tile.name)
|
||||
elseif type(tile) == "string" then
|
||||
img = get_img(tile)
|
||||
end
|
||||
|
||||
if img then
|
||||
return img
|
||||
end
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
function mailbox:formspec(pos, owner, is_owner)
|
||||
local spos = pos.x .. "," .. pos.y .. "," .. pos.z
|
||||
local meta = minetest.get_meta(pos)
|
||||
local giver, img = "", ""
|
||||
|
||||
if is_owner then
|
||||
for i = 1, GIVER_LIST_LENGTH do
|
||||
local giving = meta:get_string("giver" .. i)
|
||||
if giving ~= "" then
|
||||
local stack = meta:get_string("stack" .. i)
|
||||
local giver_name = giving:sub(1,12)
|
||||
local stack_name = stack:match("[%w_:]+")
|
||||
local stack_count = stack:match("%s(%d+)") or 1
|
||||
|
||||
-- List of donors. A line looks like this:
|
||||
-- <donor name> <item icon> × <item count>
|
||||
giver = giver .. "#FFFF00," .. giver_name .. "," .. i ..
|
||||
--~ Used in the mailbox donor list. Will be displayed as item icon followed by this string. @1 = item count
|
||||
",#FFFFFF," .. FS("× @1", stack_count) .. ","
|
||||
|
||||
img = img .. i .. "=" ..
|
||||
img_col(stack_name) .. "^\\[resize:16x16,"
|
||||
end
|
||||
end
|
||||
|
||||
return "size[9.5,9]"
|
||||
.."label[0,0;"..FS("Mailbox").."]"
|
||||
--~ Caption of the list of players who gave items in a mailbox
|
||||
.."label[6,0;"..FS("Last donators").."]"
|
||||
..[[ box[6,0.72;3.3,3.9;#555555]
|
||||
listring[current_player;main]
|
||||
list[current_player;main;0.75,5.25;8,4;]
|
||||
tableoptions[background=#00000000;highlight=#00000000;border=false] ]] ..
|
||||
"tablecolumns[color;text;image," .. img .. "0;color;text]" ..
|
||||
"table[6,0.75;3.3,4;givers;" .. giver .. "]" ..
|
||||
"list[nodemeta:" .. spos .. ";mailbox;0,0.75;6,4;]" ..
|
||||
"listring[nodemeta:" .. spos .. ";mailbox]" ..
|
||||
xdecor.xbg .. default.get_hotbar_bg(0.75, 5.25)
|
||||
end
|
||||
|
||||
return "size[8,5]" ..
|
||||
"list[current_player;main;0,1.25;8,4;]" ..
|
||||
--~ Mailbox menu title. @1 = player name
|
||||
"label[0,0;"..FS("Send your goods to\n@1",
|
||||
(minetest.colorize and
|
||||
minetest.colorize("#FFFF00", owner) or owner)) .. "]" ..
|
||||
"list[nodemeta:" .. spos .. ";drop;3.5,0;1,1;]" ..
|
||||
"listring[]" ..
|
||||
xdecor.xbg .. default.get_hotbar_bg(0, 1.25)
|
||||
end
|
||||
|
||||
function mailbox.dig(pos, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
local player_name = player and player:get_player_name()
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
return inv:is_empty("mailbox") and player_name == owner
|
||||
end
|
||||
|
||||
function mailbox.blast(pos)
|
||||
return
|
||||
end
|
||||
|
||||
function mailbox.after_place_node(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local player_name = placer:get_player_name()
|
||||
|
||||
meta:set_string("owner", player_name)
|
||||
--~ Mailbox infotext. @1 = owner name
|
||||
meta:set_string("infotext", S("@1's Mailbox", player_name))
|
||||
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("mailbox", 6 * 4)
|
||||
inv:set_size("drop", 1)
|
||||
end
|
||||
|
||||
function mailbox.rightclick(pos, node, clicker, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local player = clicker:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
|
||||
minetest.show_formspec(player, "xdecor:mailbox",
|
||||
mailbox:formspec(pos, owner, (player == owner)))
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function mailbox.put(pos, listname, _, stack, player)
|
||||
if listname == "drop" then
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if inv:room_for_item("mailbox", stack) then
|
||||
return -1
|
||||
else
|
||||
minetest.chat_send_player(player:get_player_name(),
|
||||
S("The mailbox is full."))
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function mailbox.on_put(pos, listname, _, stack, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
if listname == "drop" and inv:room_for_item("mailbox", stack) then
|
||||
inv:set_list("drop", {})
|
||||
inv:add_item("mailbox", stack)
|
||||
|
||||
for i = GIVER_LIST_LENGTH, 2, -1 do
|
||||
meta:set_string("giver" .. i, meta:get_string("giver" .. (i - 1)))
|
||||
meta:set_string("stack" .. i, meta:get_string("stack" .. (i - 1)))
|
||||
end
|
||||
|
||||
meta:set_string("giver1", player:get_player_name())
|
||||
meta:set_string("stack1", stack:to_string())
|
||||
end
|
||||
end
|
||||
|
||||
function mailbox.allow_take(pos, listname, index, stack, player)
|
||||
if listname == "drop" then
|
||||
return 0
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
if player:get_player_name() ~= meta:get_string("owner") then
|
||||
return 0
|
||||
end
|
||||
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
function mailbox.allow_move(pos)
|
||||
return 0
|
||||
end
|
||||
|
||||
xdecor.register("mailbox", {
|
||||
description = S("Mailbox"),
|
||||
--~ Mailbox tooltip
|
||||
_tt_help = S("Lets other players give you things"),
|
||||
tiles = {"xdecor_mailbox_top.png", "xdecor_mailbox_bottom.png",
|
||||
"xdecor_mailbox_side.png", "xdecor_mailbox_side.png",
|
||||
"xdecor_mailbox.png", "xdecor_mailbox.png"},
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
can_dig = mailbox.dig,
|
||||
on_blast = mailbox.blast,
|
||||
on_rightclick = mailbox.rightclick,
|
||||
allow_metadata_inventory_take = mailbox.allow_take,
|
||||
allow_metadata_inventory_move = mailbox.allow_move,
|
||||
on_metadata_inventory_put = mailbox.on_put,
|
||||
allow_metadata_inventory_put = mailbox.put,
|
||||
after_place_node = mailbox.after_place_node
|
||||
})
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:mailbox",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
{"dye:red", "default:paper", "dye:red"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,325 @@
|
||||
-- Thanks to sofar for helping with that code.
|
||||
|
||||
local plate = {}
|
||||
screwdriver = screwdriver or {}
|
||||
|
||||
local S = minetest.get_translator("xdecor")
|
||||
local ALPHA_OPAQUE = minetest.features.use_texture_alpha_string_modes and "opaque" or false
|
||||
|
||||
-- Number of seconds an actuator (pressure plate, lever) stays active.
|
||||
-- After this time, it will return to the disabled state again.
|
||||
local DISABLE_ACTUATOR_AFTER = 2.0
|
||||
|
||||
-- Effect area of pressure plates and levers. Doors within this area
|
||||
-- can be affected.
|
||||
local PRESSURE_PLATE_AREA_MIN = {x = -2, y = 0, z = -2}
|
||||
local PRESSURE_PLATE_AREA_MAX = {x = 2, y = 0, z = 2}
|
||||
local LEVER_AREA_MIN = {x = -2, y = -1, z = -2}
|
||||
local LEVER_AREA_MAX = {x = 2, y = 1, z = 2}
|
||||
|
||||
-- Pressure plates check for players within this radius
|
||||
local PRESSURE_PLATE_PLAYER_RADIUS = 0.8
|
||||
-- Interval in seconds that pressure plates check for players
|
||||
local PRESSURE_PLATE_CHECK_TIMER = 0.1
|
||||
|
||||
local function door_open(pos_door, player)
|
||||
local door = doors.get(pos_door)
|
||||
if not door then
|
||||
return
|
||||
end
|
||||
door:open(player)
|
||||
end
|
||||
|
||||
local function door_close(pos_door, player)
|
||||
local door = doors.get(pos_door)
|
||||
if not door then
|
||||
return
|
||||
end
|
||||
door:close(player)
|
||||
end
|
||||
|
||||
-- Returns true if the door node at pos is currently next to any
|
||||
-- active actuator node (lever, pressure plate)
|
||||
local function door_is_actuatored(pos_door)
|
||||
local minp = vector.add(LEVER_AREA_MIN, pos_door)
|
||||
local maxp = vector.add(LEVER_AREA_MAX, pos_door)
|
||||
local levers = minetest.find_nodes_in_area(minp, maxp, "group:lever")
|
||||
for l=1, #levers do
|
||||
local lnode = minetest.get_node(levers[l])
|
||||
if minetest.get_item_group(lnode.name, "xdecor_actuator") == 2 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
minp = vector.add(PRESSURE_PLATE_AREA_MIN, pos_door)
|
||||
maxp = vector.add(PRESSURE_PLATE_AREA_MAX, pos_door)
|
||||
local pressure_plates = minetest.find_nodes_in_area(minp, maxp, "group:pressure_plate")
|
||||
for p=1, #pressure_plates do
|
||||
local pnode = minetest.get_node(pressure_plates[p])
|
||||
if minetest.get_item_group(pnode.name, "xdecor_actuator") == 2 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function actuator_timeout(pos_actuator, actuator_area_min, actuator_area_max)
|
||||
local actuator = minetest.get_node(pos_actuator)
|
||||
|
||||
-- Get name of last player that triggered the actuator
|
||||
local meta = minetest.get_meta(pos_actuator)
|
||||
local last_triggerer_str = meta:get_string("last_triggerer")
|
||||
local last_triggerer_obj = minetest.get_player_by_name(last_triggerer_str)
|
||||
|
||||
-- Turn off actuator
|
||||
if minetest.get_item_group(actuator.name, "xdecor_actuator") == 2 then
|
||||
local def = minetest.registered_nodes[actuator.name]
|
||||
if def._xdecor_actuator_off then
|
||||
minetest.set_node(pos_actuator, { name = def._xdecor_actuator_off, param2 = actuator.param2 })
|
||||
end
|
||||
end
|
||||
|
||||
-- Close neighboring doors that are no longer next to any active actuator
|
||||
local minp = vector.add(actuator_area_min, pos_actuator)
|
||||
local maxp = vector.add(actuator_area_max, pos_actuator)
|
||||
local doors = minetest.find_nodes_in_area(minp, maxp, "group:door")
|
||||
for d=1, #doors do
|
||||
if not door_is_actuatored(doors[d]) then
|
||||
local dnode = minetest.get_node(doors[d])
|
||||
local ddef = minetest.registered_nodes[dnode.name]
|
||||
if (ddef.protected and last_triggerer_obj) or (not ddef.protected) then
|
||||
door_close(doors[d], last_triggerer_obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function actuator_activate(pos_actuator, actuator_area_min, actuator_area_max, player)
|
||||
local player_name = player:get_player_name()
|
||||
local actuator = minetest.get_node(pos_actuator)
|
||||
local ga = minetest.get_item_group(actuator.name, "xdecor_actuator")
|
||||
if ga == 2 then
|
||||
-- No-op if actuator is already active
|
||||
return
|
||||
elseif ga == 1 then
|
||||
local def = minetest.registered_nodes[actuator.name]
|
||||
-- Turn actuator on
|
||||
if def._xdecor_actuator_on then
|
||||
minetest.set_node(pos_actuator, { name = def._xdecor_actuator_on, param2 = actuator.param2 })
|
||||
|
||||
-- Store name of last player that triggered the actuator
|
||||
local meta = minetest.get_meta(pos_actuator)
|
||||
meta:set_string("last_triggerer", player_name)
|
||||
end
|
||||
end
|
||||
|
||||
-- Turn on neighboring doors
|
||||
local minp = vector.add(actuator_area_min, pos_actuator)
|
||||
local maxp = vector.add(actuator_area_max, pos_actuator)
|
||||
local doors = minetest.find_nodes_in_area(minp, maxp, "group:door")
|
||||
for i = 1, #doors do
|
||||
door_open(doors[i], player)
|
||||
end
|
||||
end
|
||||
|
||||
function plate.construct(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(PRESSURE_PLATE_CHECK_TIMER)
|
||||
end
|
||||
|
||||
function plate.has_player_standing_on(pos)
|
||||
local objs = minetest.get_objects_inside_radius(pos, PRESSURE_PLATE_PLAYER_RADIUS)
|
||||
for _, player in pairs(objs) do
|
||||
if player:is_player() then
|
||||
return true, player
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function plate.timer(pos)
|
||||
if not doors.get then
|
||||
return true
|
||||
end
|
||||
local ok, player = plate.has_player_standing_on(pos)
|
||||
if ok then
|
||||
actuator_activate(pos, PRESSURE_PLATE_AREA_MIN, PRESSURE_PLATE_AREA_MAX, player)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function plate.construct_on(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(DISABLE_ACTUATOR_AFTER)
|
||||
end
|
||||
|
||||
function plate.timer_on(pos)
|
||||
if plate.has_player_standing_on(pos) then
|
||||
-- If player is still standing on active pressure plate, restart timer
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(DISABLE_ACTUATOR_AFTER)
|
||||
return
|
||||
end
|
||||
|
||||
actuator_timeout(pos, PRESSURE_PLATE_AREA_MIN, PRESSURE_PLATE_AREA_MAX)
|
||||
end
|
||||
|
||||
function plate.register(material, desc, def)
|
||||
local groups
|
||||
if def.groups then
|
||||
groups = table.copy(def.groups)
|
||||
else
|
||||
groups = {}
|
||||
end
|
||||
groups.pressure_plate = 1
|
||||
groups.xdecor_actuator = 1
|
||||
xdecor.register("pressure_" .. material .. "_off", {
|
||||
description = def.description or (desc .. " Pressure Plate"),
|
||||
--~ Pressure plate tooltip
|
||||
_tt_help = S("Opens doors when stepped on"),
|
||||
tiles = {"xdecor_pressure_" .. material .. ".png"},
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
drawtype = "nodebox",
|
||||
node_box = xdecor.pixelbox(16, {{1, 0, 1, 14, 1, 14}}),
|
||||
groups = groups,
|
||||
is_ground_content = false,
|
||||
sounds = def.sounds,
|
||||
sunlight_propagates = true,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
on_construct = plate.construct,
|
||||
on_timer = plate.timer,
|
||||
_xdecor_actuator_off = "xdecor:pressure_"..material.."_off",
|
||||
_xdecor_actuator_on = "xdecor:pressure_"..material.."_on",
|
||||
})
|
||||
local groups_on = table.copy(groups)
|
||||
groups_on.xdecor_actuator = 2
|
||||
groups_on.pressure_plate = 2
|
||||
xdecor.register("pressure_" .. material .. "_on", {
|
||||
tiles = {"xdecor_pressure_" .. material .. ".png"},
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
drawtype = "nodebox",
|
||||
node_box = xdecor.pixelbox(16, {{1, 0, 1, 14, 0.4, 14}}),
|
||||
groups = groups_on,
|
||||
is_ground_content = false,
|
||||
sounds = def.sounds,
|
||||
drop = "xdecor:pressure_" .. material .. "_off",
|
||||
sunlight_propagates = true,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
on_construct = plate.construct_on,
|
||||
on_timer = plate.timer_on,
|
||||
_xdecor_actuator_off = "xdecor:pressure_"..material.."_off",
|
||||
_xdecor_actuator_on = "xdecor:pressure_"..material.."_on",
|
||||
})
|
||||
end
|
||||
|
||||
plate.register("wood", "Wooden", {
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 2},
|
||||
description = S("Wooden Pressure Plate"),
|
||||
})
|
||||
|
||||
plate.register("stone", "Stone", {
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 2},
|
||||
description = S("Stone Pressure Plate"),
|
||||
})
|
||||
|
||||
xdecor.register("lever_off", {
|
||||
description = S("Lever"),
|
||||
--~ Lever tooltip
|
||||
_tt_help = S("Opens doors when pulled"),
|
||||
tiles = {"xdecor_lever_off.png"},
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
drawtype = "nodebox",
|
||||
node_box = xdecor.pixelbox(16, {{2, 1, 15, 12, 14, 1}}),
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 2, lever = 1, xdecor_actuator = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
|
||||
on_rightclick = function(pos, node, clicker, itemstack)
|
||||
if not doors.get then
|
||||
return itemstack
|
||||
end
|
||||
actuator_activate(pos, LEVER_AREA_MIN, LEVER_AREA_MAX, clicker)
|
||||
return itemstack
|
||||
end,
|
||||
_xdecor_itemframe_offset = -3.5,
|
||||
_xdecor_actuator_off = "xdecor:lever_off",
|
||||
_xdecor_actuator_on = "xdecor:lever_on",
|
||||
})
|
||||
|
||||
xdecor.register("lever_on", {
|
||||
tiles = {"xdecor_lever_on.png"},
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
drawtype = "nodebox",
|
||||
node_box = xdecor.pixelbox(16, {{2, 1, 15, 12, 14, 1}}),
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 2, lever = 2, xdecor_actuator = 2, not_in_creative_inventory = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
on_rightclick = function(pos, node, clicker, itemstack)
|
||||
-- Prevent placing nodes on activated lever with the place key
|
||||
-- for consistent behavior with the lever in "off" state.
|
||||
-- The player may still place nodes using [Sneak].
|
||||
return itemstack
|
||||
end,
|
||||
on_construct = function(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(DISABLE_ACTUATOR_AFTER)
|
||||
end,
|
||||
on_timer = function(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
actuator_timeout(pos, LEVER_AREA_MIN, LEVER_AREA_MAX)
|
||||
end,
|
||||
drop = "xdecor:lever_off",
|
||||
_xdecor_itemframe_offset = -3.5,
|
||||
_xdecor_actuator_off = "xdecor:lever_off",
|
||||
_xdecor_actuator_on = "xdecor:lever_on",
|
||||
})
|
||||
|
||||
-- Make sure the node timers of active actuators are still
|
||||
-- active when these nodes load again. If not, start them
|
||||
-- again to trigger their timer action, which is expected
|
||||
-- to turn off the actuator soon.
|
||||
minetest.register_lbm({
|
||||
label = "Restart actuator timers (X-Decor-libre)",
|
||||
name = "xdecor:restart_actuator_timers",
|
||||
nodenames = { "group:xdecor_actuator" },
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
local g = minetest.get_item_group(node.name, "xdecor_actuator")
|
||||
if g ~= 2 then
|
||||
return
|
||||
end
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
if not timer:is_started() then
|
||||
timer:start(DISABLE_ACTUATOR_AFTER)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:pressure_stone_off",
|
||||
type = "shapeless",
|
||||
recipe = {"group:stone", "group:stone"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:pressure_wood_off",
|
||||
type = "shapeless",
|
||||
recipe = {"group:wood", "group:wood"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:lever_off",
|
||||
recipe = {
|
||||
{"group:stick"},
|
||||
{"group:stone"}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,977 @@
|
||||
screwdriver = screwdriver or {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
local ALPHA_CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or true
|
||||
local ALPHA_OPAQUE = minetest.features.use_texture_alpha_string_modes and "opaque" or false
|
||||
|
||||
local function register_pane(name, desc, def)
|
||||
xpanes.register_pane(name, {
|
||||
description = desc,
|
||||
tiles = {"xdecor_" .. name .. ".png"},
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
textures = def.textures or {"xdecor_" .. name .. ".png", "" ,"xdecor_" .. name .. ".png"},
|
||||
inventory_image = "xdecor_" .. name .. ".png",
|
||||
wield_image = "xdecor_" .. name .. ".png",
|
||||
groups = def.groups,
|
||||
sounds = def.sounds or default.node_sound_defaults(),
|
||||
recipe = def.recipe
|
||||
})
|
||||
end
|
||||
|
||||
register_pane("bamboo_frame", S("Bamboo Frame"), {
|
||||
groups = {choppy = 3, oddly_breakable_by_hand = 2, pane = 1, flammable = 2},
|
||||
recipe = {
|
||||
{"default:papyrus", "default:papyrus", "default:papyrus"},
|
||||
{"default:papyrus", "farming:cotton", "default:papyrus"},
|
||||
{"default:papyrus", "default:papyrus", "default:papyrus"}
|
||||
},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
register_pane("chainlink", S("Chainlink"), {
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 2, pane = 1},
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", "default:steel_ingot"},
|
||||
{"", "default:steel_ingot", ""},
|
||||
{"default:steel_ingot", "", "default:steel_ingot"}
|
||||
}
|
||||
})
|
||||
|
||||
register_pane("rusty_bar", S("Rusty Iron Bars"), {
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
textures = {"xdecor_rusty_bar.png", "", "xdecor_rusty_bar_top.png"},
|
||||
groups = {cracky = 2, pane = 1},
|
||||
recipe = {
|
||||
{"", "default:dirt", ""},
|
||||
{"default:iron_lump", "default:iron_lump", "default:iron_lump"},
|
||||
{"default:iron_lump", "default:iron_lump", "default:iron_lump"},
|
||||
},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
})
|
||||
|
||||
register_pane("wood_frame", S("Wood Frame"), {
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
textures = {"xdecor_wood_frame.png", "", "xdecor_wood_frame_top.png"},
|
||||
groups = {choppy = 2, pane = 1, flammable = 2},
|
||||
recipe = {
|
||||
{"group:wood", "group:stick", "group:wood"},
|
||||
{"group:stick", "group:stick", "group:stick"},
|
||||
{"group:wood", "group:stick", "group:wood"}
|
||||
}
|
||||
})
|
||||
|
||||
xdecor.register("baricade", {
|
||||
description = S("Barricade"),
|
||||
drawtype = "plantlike",
|
||||
paramtype2 = "facedir",
|
||||
inventory_image = "xdecor_baricade.png",
|
||||
tiles = {"xdecor_baricade.png"},
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 1, flammable = 2},
|
||||
is_ground_content = false,
|
||||
damage_per_second = 4,
|
||||
selection_box = xdecor.nodebox.slab_y(0.3),
|
||||
collision_box = xdecor.pixelbox(2, {{0, 0, 1, 2, 2, 0}})
|
||||
})
|
||||
|
||||
xdecor.register("barrel", {
|
||||
description = S("Barrel"),
|
||||
tiles = {"xdecor_barrel_top.png", "xdecor_barrel_top.png", "xdecor_barrel_sides.png"},
|
||||
on_place = minetest.rotate_node,
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 1, flammable = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults()
|
||||
})
|
||||
|
||||
local function blast_storage(pos)
|
||||
local drops = xdecor.get_inventory_drops(pos, {"main"})
|
||||
minetest.remove_node(pos)
|
||||
return drops
|
||||
end
|
||||
|
||||
local function register_storage(name, desc, def)
|
||||
xdecor.register(name, {
|
||||
description = desc,
|
||||
_tt_help = def._tt_help,
|
||||
inventory = {size = def.inv_size or 24},
|
||||
infotext = desc,
|
||||
tiles = def.tiles,
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
node_box = def.node_box,
|
||||
on_rotate = def.on_rotate,
|
||||
on_place = def.on_place,
|
||||
on_blast = blast_storage,
|
||||
groups = def.groups or {choppy = 2, oddly_breakable_by_hand = 1, flammable = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults()
|
||||
})
|
||||
end
|
||||
|
||||
register_storage("cabinet", S("Wooden Cabinet"), {
|
||||
--~ Tooltip for block that stores items
|
||||
_tt_help = S("24 inventory slots"),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {
|
||||
"xdecor_cabinet_sides.png", "xdecor_cabinet_sides.png",
|
||||
"xdecor_cabinet_sides.png", "xdecor_cabinet_sides.png",
|
||||
"xdecor_cabinet_sides.png", "xdecor_cabinet_front.png"
|
||||
}
|
||||
})
|
||||
|
||||
register_storage("cabinet_half", S("Half Wooden Cabinet"), {
|
||||
inv_size = 8,
|
||||
--~ Tooltip for block that stores items
|
||||
_tt_help = S("8 inventory slots"),
|
||||
node_box = xdecor.nodebox.slab_y(0.5, 0.5),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {
|
||||
"xdecor_cabinet_sides.png", "xdecor_cabinet_sides.png",
|
||||
"xdecor_half_cabinet_sides.png", "xdecor_half_cabinet_sides.png",
|
||||
"xdecor_half_cabinet_sides.png", "xdecor_half_cabinet_front.png"
|
||||
}
|
||||
})
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
minetest.register_alias("xdecor:empty_shelf", "moreblocks:empty_shelf")
|
||||
else
|
||||
-- Node renamed from "Empty Shelf" because it was misleading.
|
||||
-- (you can still put things in it, making it non-empty)
|
||||
register_storage("empty_shelf", S("Plain Shelf"), {
|
||||
_tt_help = S("24 inventory slots"),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {
|
||||
"default_wood.png", "default_wood.png", "default_wood.png",
|
||||
"default_wood.png", "default_wood.png^xdecor_empty_shelf.png"
|
||||
},
|
||||
})
|
||||
|
||||
-- Update infotext of old empty_shelf nodes to "Plain Shelf"
|
||||
minetest.register_lbm({
|
||||
label = "Update plain shelf infotext",
|
||||
name = "xdecor:empty_shelf_to_plain_shelf",
|
||||
nodenames = {"xdecor:empty_shelf"},
|
||||
run_at_every_load = false,
|
||||
action = function(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", S("Plain Shelf"))
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
register_storage("multishelf", S("Multi Shelf"), {
|
||||
_tt_help = S("24 inventory slots"),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {
|
||||
"default_wood.png", "default_wood.png", "default_wood.png",
|
||||
"default_wood.png", "default_wood.png^xdecor_multishelf.png"
|
||||
},
|
||||
})
|
||||
|
||||
xdecor.register("candle", {
|
||||
description = S("Candle"),
|
||||
light_source = 12,
|
||||
drawtype = "torchlike",
|
||||
inventory_image = "xdecor_candle_inv.png",
|
||||
wield_image = "xdecor_candle_wield.png",
|
||||
paramtype2 = "wallmounted",
|
||||
walkable = false,
|
||||
groups = {dig_immediate = 3, attached_node = 1},
|
||||
is_ground_content = false,
|
||||
tiles = {
|
||||
{
|
||||
name = "xdecor_candle_floor.png",
|
||||
animation = {type="vertical_frames", length = 1.5}
|
||||
},
|
||||
{
|
||||
name = "xdecor_candle_hanging.png",
|
||||
animation = {type="vertical_frames", length = 1.5}
|
||||
},
|
||||
{
|
||||
name = "xdecor_candle_wall.png",
|
||||
animation = {type="vertical_frames", length = 1.5}
|
||||
}
|
||||
},
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
wall_top = {-0.25, -0.3, -0.25, 0.25, 0.5, 0.25},
|
||||
wall_bottom = {-0.25, -0.5, -0.25, 0.25, 0.1, 0.25},
|
||||
wall_side = {-0.5, -0.35, -0.15, -0.15, 0.4, 0.15}
|
||||
}
|
||||
})
|
||||
|
||||
xdecor.register("chair", {
|
||||
description = S("Chair"),
|
||||
tiles = {"xdecor_wood.png"},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 2, sittable = 1},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
node_box = xdecor.pixelbox(16, {
|
||||
{3, 0, 11, 2, 16, 2},
|
||||
{11, 0, 11, 2, 16, 2},
|
||||
{5, 9, 11.5, 6, 6, 1},
|
||||
{3, 0, 3, 2, 6, 2},
|
||||
{11, 0, 3, 2, 6, 2},
|
||||
{3, 6, 3, 10, 2, 8}
|
||||
}),
|
||||
can_dig = xdecor.sit_dig,
|
||||
after_destruct = xdecor.sit_destruct,
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
xdecor.sit(pos, node, clicker, pointed_thing)
|
||||
return itemstack
|
||||
end,
|
||||
_xdecor_itemframe_offset = -1.5,
|
||||
})
|
||||
|
||||
xdecor.register("cobweb", {
|
||||
description = S("Cobweb"),
|
||||
drawtype = "plantlike",
|
||||
tiles = {"xdecor_cobweb.png"},
|
||||
inventory_image = "xdecor_cobweb.png",
|
||||
move_resistance = 8,
|
||||
walkable = false,
|
||||
selection_box = {type = "regular"},
|
||||
groups = {snappy = 3, flammable = 3},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_leaves_defaults()
|
||||
})
|
||||
|
||||
local curtain_colors = {
|
||||
red = { S("Red Curtain"), "wool_red.png", "wool:red" },
|
||||
}
|
||||
|
||||
local CURTAIN_OFFSET = 1/16
|
||||
|
||||
-- For preserve_metadata for curtains.
|
||||
-- Erases metadata from the drops
|
||||
-- because the item metadata should be empty
|
||||
-- to allow proper item stacking.
|
||||
local cleanup_curtain_meta = function(_,_,_,drops)
|
||||
for d=1, #drops do
|
||||
local meta = drops[d]:get_meta()
|
||||
meta:set_string("palette_index", "")
|
||||
end
|
||||
end
|
||||
|
||||
for c, info in pairs(curtain_colors) do
|
||||
local desc = info[1]
|
||||
local base_texture = info[2]
|
||||
local craft_item = info[3]
|
||||
xdecor.register("curtain_" .. c, {
|
||||
description = desc,
|
||||
walkable = false,
|
||||
tiles = {base_texture, "("..base_texture..")^[transformFY", base_texture},
|
||||
use_texture_alpha = ALPHA_CLIP,
|
||||
inventory_image = base_texture.."^xdecor_curtain_open_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = base_texture.."^xdecor_curtain_open_overlay.png^[makealpha:255,126,126",
|
||||
drawtype = "nodebox",
|
||||
paramtype2 = "wallmounted",
|
||||
node_box = {
|
||||
type = "wallmounted",
|
||||
wall_side = { -0.5, -0.5, -0.5, -0.5+CURTAIN_OFFSET, 0.5, 0.5 },
|
||||
wall_top = { -0.5, 0.5-CURTAIN_OFFSET, -0.5, 0.5, 0.5, 0.5 },
|
||||
wall_bottom = { -0.5, -0.5, -0.5, 0.5, -0.5+CURTAIN_OFFSET, 0.5 },
|
||||
},
|
||||
groups = {dig_immediate = 3, flammable = 3},
|
||||
is_ground_content = false,
|
||||
on_rightclick = function(pos, node, _, itemstack)
|
||||
minetest.set_node(pos, {name = "xdecor:curtain_open_" .. c, param2 = node.param2})
|
||||
return itemstack
|
||||
end,
|
||||
preserve_metadata = cleanup_curtain_meta,
|
||||
})
|
||||
|
||||
local open_tile = base_texture.."^xdecor_curtain_open_overlay.png^[makealpha:255,126,126"
|
||||
xdecor.register("curtain_open_" .. c, {
|
||||
tiles = {
|
||||
open_tile,
|
||||
"("..open_tile..")^[transformFY",
|
||||
base_texture,
|
||||
base_texture,
|
||||
base_texture.."^xdecor_curtain_open_overlay_top.png^[makealpha:255,126,126",
|
||||
base_texture.."^xdecor_curtain_open_overlay_bottom.png^[makealpha:255,126,126",
|
||||
},
|
||||
use_texture_alpha = ALPHA_CLIP,
|
||||
drawtype = "nodebox",
|
||||
paramtype2 = "wallmounted",
|
||||
node_box = {
|
||||
type = "wallmounted",
|
||||
wall_side = { -0.5, -0.5, -0.5, -0.5+CURTAIN_OFFSET, 0.5, 0.5 },
|
||||
wall_top = { -0.5, 0.5-CURTAIN_OFFSET, -0.5, 0.5, 0.5, 0.5 },
|
||||
wall_bottom = { -0.5, -0.5, -0.5, 0.5, -0.5+CURTAIN_OFFSET, 0.5 },
|
||||
},
|
||||
walkable = false,
|
||||
groups = {dig_immediate = 3, flammable = 3, not_in_creative_inventory = 1},
|
||||
is_ground_content = false,
|
||||
drop = "xdecor:curtain_" .. c,
|
||||
on_rightclick = function(pos, node, _, itemstack)
|
||||
minetest.set_node(pos, {name="xdecor:curtain_" .. c, param2 = node.param2})
|
||||
return itemstack
|
||||
end,
|
||||
preserve_metadata = cleanup_curtain_meta,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:curtain_" .. c .. " 4",
|
||||
recipe = {
|
||||
{"", craft_item, ""},
|
||||
{"", craft_item, ""}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
xdecor.register("cushion", {
|
||||
description = S("Cushion"),
|
||||
tiles = {"xdecor_cushion.png"},
|
||||
groups = {snappy = 3, flammable = 3, fall_damage_add_percent = -50, sittable = 1},
|
||||
is_ground_content = false,
|
||||
on_place = minetest.rotate_node,
|
||||
node_box = xdecor.nodebox.slab_y(0.5),
|
||||
can_dig = xdecor.sit_dig,
|
||||
after_destruct = xdecor.sit_destruct,
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
xdecor.sit(pos, node, clicker, pointed_thing)
|
||||
return itemstack
|
||||
end
|
||||
})
|
||||
|
||||
xdecor.register("cushion_block", {
|
||||
description = S("Cushion Block"),
|
||||
tiles = {"xdecor_cushion.png"},
|
||||
groups = {snappy = 3, flammable = 3, fall_damage_add_percent = -75},
|
||||
is_ground_content = false,
|
||||
})
|
||||
|
||||
local xdecor_doors = {
|
||||
japanese = {
|
||||
recipe = {
|
||||
{"group:wood", "default:paper"},
|
||||
{"default:paper", "group:wood"},
|
||||
{"group:wood", "default:paper"}
|
||||
},
|
||||
desc = S("Japanese Door"),
|
||||
},
|
||||
prison = {
|
||||
recipe = {
|
||||
{"xpanes:bar_flat", "xpanes:bar_flat",},
|
||||
{"xpanes:bar_flat", "default:steel_ingot",},
|
||||
{"xpanes:bar_flat", "xpanes:bar_flat"}
|
||||
},
|
||||
desc = S("Prison Door"),
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
sound_open = "xpanes_steel_bar_door_open",
|
||||
sound_close = "xpanes_steel_bar_door_close",
|
||||
gain_open = 0.18,
|
||||
gain_close = 0.16,
|
||||
protected = true,
|
||||
},
|
||||
rusty_prison = {
|
||||
recipe = {
|
||||
{"xpanes:rusty_bar_flat", "xpanes:rusty_bar_flat",},
|
||||
{"xpanes:rusty_bar_flat", "xpanes:rusty_bar_flat",},
|
||||
{"xpanes:rusty_bar_flat", "xpanes:rusty_bar_flat"}
|
||||
},
|
||||
desc = S("Rusty Prison Door"),
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
sound_open = "xpanes_steel_bar_door_open",
|
||||
sound_close = "xpanes_steel_bar_door_close",
|
||||
gain_open = 0.21,
|
||||
gain_close = 0.19,
|
||||
protected = true,
|
||||
},
|
||||
screen = {
|
||||
recipe = {
|
||||
{"group:wood", "group:wood"},
|
||||
{"xpanes:chainlink_flat", "xpanes:chainlink_flat"},
|
||||
{"group:wood", "group:wood"}
|
||||
},
|
||||
desc = S("Screen Door"),
|
||||
},
|
||||
slide = {
|
||||
recipe = {
|
||||
{"default:paper", "default:paper"},
|
||||
{"default:paper", "default:paper"},
|
||||
{"group:wood", "group:wood"}
|
||||
},
|
||||
desc = S("Paper Door"),
|
||||
},
|
||||
woodglass = {
|
||||
recipe = {
|
||||
{"default:glass", "default:glass"},
|
||||
{"group:wood", "group:wood"},
|
||||
{"group:wood", "group:wood"}
|
||||
},
|
||||
desc = S("Woodglass Door"),
|
||||
},
|
||||
}
|
||||
|
||||
local mesecons_register
|
||||
|
||||
if minetest.global_exists("mesecon") then
|
||||
mesecons_register = { effector = {
|
||||
action_on = function(pos, node)
|
||||
local door = doors.get(pos)
|
||||
if door then
|
||||
door:open()
|
||||
end
|
||||
end,
|
||||
action_off = function(pos, node)
|
||||
local door = doors.get(pos)
|
||||
if door then
|
||||
door:close()
|
||||
end
|
||||
end,
|
||||
rules = mesecon.rules.pplate
|
||||
}}
|
||||
end
|
||||
|
||||
for name, def in pairs(xdecor_doors) do
|
||||
if not doors.register then break end
|
||||
doors.register(name .. "_door", {
|
||||
tiles = {
|
||||
{name = "xdecor_" .. name .. "_door.png", backface_culling = true}
|
||||
},
|
||||
description = def.desc,
|
||||
inventory_image = "xdecor_" .. name .. "_door_inv.png",
|
||||
sounds = def.sounds,
|
||||
sound_open = def.sound_open,
|
||||
sound_close = def.sound_close,
|
||||
gain_open = def.gain_open,
|
||||
gain_close = def.gain_close,
|
||||
protected = def.protected,
|
||||
groups = {choppy = 2, cracky = 2, oddly_breakable_by_hand = 1, door = 1, node = 1},
|
||||
recipe = def.recipe,
|
||||
mesecons = mesecons_register,
|
||||
})
|
||||
end
|
||||
|
||||
xdecor.register("enderchest", {
|
||||
description = S("Ender Chest"),
|
||||
--~ Ender chest tooltip
|
||||
_tt_help = S("Interdimensional inventory"),
|
||||
tiles = {
|
||||
"xdecor_enderchest_top.png", "xdecor_enderchest_top.png",
|
||||
"xdecor_enderchest_side.png", "xdecor_enderchest_side.png",
|
||||
"xdecor_enderchest_side.png", "xdecor_enderchest_front.png"
|
||||
},
|
||||
groups = {cracky = 1, choppy = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec", [[ size[8,9]
|
||||
list[current_player;enderchest;0,0;8,4;]
|
||||
list[current_player;main;0,5;8,4;]
|
||||
listring[current_player;enderchest]
|
||||
listring[current_player;main] ]]
|
||||
.. xdecor.xbg .. default.get_hotbar_bg(0,5))
|
||||
|
||||
meta:set_string("infotext", S("Ender Chest"))
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local inv = player:get_inventory()
|
||||
inv:set_size("enderchest", 8*4)
|
||||
end)
|
||||
|
||||
xdecor.register("ivy", {
|
||||
description = S("Ivy"),
|
||||
drawtype = "signlike",
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
groups = {snappy = 3, attached_node = 1, plant = 1, flammable = 3},
|
||||
paramtype2 = "wallmounted",
|
||||
selection_box = {type="wallmounted"},
|
||||
tiles = {"xdecor_ivy.png"},
|
||||
inventory_image = "xdecor_ivy.png",
|
||||
wield_image = "xdecor_ivy.png",
|
||||
sounds = default.node_sound_leaves_defaults()
|
||||
})
|
||||
|
||||
xdecor.register("rooster", {
|
||||
description = S("Weathercock"),
|
||||
drawtype = "torchlike",
|
||||
inventory_image = "xdecor_rooster.png",
|
||||
walkable = false,
|
||||
groups = {snappy = 3, attached_node = 1},
|
||||
is_ground_content = false,
|
||||
tiles = {"xdecor_rooster.png"},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
})
|
||||
|
||||
-- Lantern which attaches to the floor.
|
||||
-- Has a hanging variant
|
||||
xdecor.register("lantern", {
|
||||
description = S("Lantern"),
|
||||
light_source = 13,
|
||||
drawtype = "plantlike",
|
||||
inventory_image = "xdecor_lantern_inv.png",
|
||||
wield_image = "xdecor_lantern_inv.png",
|
||||
walkable = false,
|
||||
groups = {snappy = 3, attached_node = 3},
|
||||
is_ground_content = false,
|
||||
tiles = {
|
||||
{
|
||||
name = "xdecor_lantern.png",
|
||||
animation = {type="vertical_frames", length = 1.5}
|
||||
}
|
||||
},
|
||||
selection_box = xdecor.pixelbox(16, {{4, 0, 4, 8, 16, 8}}),
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
-- Check protection
|
||||
if minetest.is_protected(pointed_thing.above, placer:get_player_name()) and
|
||||
not minetest.check_player_privs(placer, "protection_bypass") then
|
||||
minetest.record_protection_violation(pointed_thing.above, placer:get_player_name())
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Decide whether the lantern attaches the the floor
|
||||
-- (default) or the ceiling.
|
||||
local leftover, place_pos, nodename
|
||||
local up = vector.new(pointed_thing.above.x, pointed_thing.above.y+1, pointed_thing.above.z)
|
||||
local upnode = minetest.get_node(up)
|
||||
local updef = minetest.registered_nodes[upnode.name]
|
||||
local down = vector.new(pointed_thing.above.x, pointed_thing.above.y-1, pointed_thing.above.z)
|
||||
local downnode = minetest.get_node(down)
|
||||
local downdef = minetest.registered_nodes[downnode.name]
|
||||
local sound_play = false
|
||||
if pointed_thing.under.y > pointed_thing.above.y then
|
||||
nodename = "xdecor:lantern_hanging"
|
||||
if downdef and not downdef.walkable then
|
||||
sound_play = true
|
||||
end
|
||||
elseif downdef and not downdef.walkable and updef and updef.walkable then
|
||||
nodename = "xdecor:lantern_hanging"
|
||||
sound_play = true
|
||||
else
|
||||
nodename = "xdecor:lantern"
|
||||
end
|
||||
leftover, place_pos = minetest.item_place_node(ItemStack(nodename), placer, pointed_thing)
|
||||
if place_pos == nil then
|
||||
return
|
||||
end
|
||||
if leftover:get_count() == 0 and
|
||||
not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
if sound_play then
|
||||
minetest.sound_play(default.node_sound_metal_defaults().place, {pos=place_pos}, true)
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
-- Same as lantern, but attaches to ceiling
|
||||
xdecor.register("lantern_hanging", {
|
||||
description = S("Hanging Lantern"),
|
||||
light_source = 13,
|
||||
drawtype = "plantlike",
|
||||
inventory_image = "xdecor_lantern_inv.png^xdecor_lantern_hanging_overlay_inv.png",
|
||||
wield_image = "xdecor_lantern_inv.png",
|
||||
walkable = false,
|
||||
groups = {snappy = 3, attached_node = 4, not_in_creative_inventory = 1},
|
||||
is_ground_content = false,
|
||||
tiles = {
|
||||
{
|
||||
name = "xdecor_lantern.png",
|
||||
animation = {type="vertical_frames", length = 1.5}
|
||||
}
|
||||
},
|
||||
selection_box = xdecor.pixelbox(16, {{4, 0, 4, 8, 16, 8}}),
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
drop = "xdecor:lantern",
|
||||
})
|
||||
|
||||
-- Update legacy lantern (back when they were wallmounted)
|
||||
-- that are hanging to the proper node.
|
||||
minetest.register_lbm({
|
||||
label = "Update hanging lanterns",
|
||||
name = "xdecor:update_hanging_lanterns",
|
||||
nodenames = {"xdecor:lantern"},
|
||||
run_at_every_load = false,
|
||||
action = function(pos, node)
|
||||
if node.param2 == 0 then -- wallmounted 0 value means attached to the ceiling
|
||||
-- Only convert the node if it needs to hang
|
||||
-- (walkable node above, non-walkable node below)
|
||||
local up = vector.new(pos.x, pos.y+1, pos.z)
|
||||
local upnode = minetest.get_node(up)
|
||||
local updef = minetest.registered_nodes[upnode.name]
|
||||
local down = vector.new(pos.x, pos.y-1, pos.z)
|
||||
local downnode = minetest.get_node(down)
|
||||
local downdef = minetest.registered_nodes[downnode.name]
|
||||
if updef and updef.walkable and downdef and not downdef.walkable then
|
||||
minetest.swap_node(pos, {name="xdecor:lantern_hanging"})
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local xdecor_lightbox = {
|
||||
iron = S("Steel Lattice Light Box"),
|
||||
wooden = S("Wooden Cross Light Box"),
|
||||
wooden2 = S("Wooden Rhombus Light Box"),
|
||||
}
|
||||
|
||||
for l, desc in pairs(xdecor_lightbox) do
|
||||
xdecor.register(l .. "_lightbox", {
|
||||
description = desc,
|
||||
tiles = {"xdecor_" .. l .. "_lightbox.png"},
|
||||
groups = {cracky = 3, choppy = 3, oddly_breakable_by_hand = 2},
|
||||
is_ground_content = false,
|
||||
light_source = 13,
|
||||
sounds = default.node_sound_glass_defaults()
|
||||
})
|
||||
end
|
||||
|
||||
-- Potted flowers
|
||||
local xdecor_potted = {
|
||||
-- Based on the original Minetest Game flowers
|
||||
dandelion_white = S("Potted White Dandelion"),
|
||||
dandelion_yellow = S("Potted Yellow Dandelion"),
|
||||
geranium = S("Potted Blue Geranium"),
|
||||
rose = S("Potted Red Rose"),
|
||||
tulip = S("Potted Orange Tulip"),
|
||||
viola = S("Potted Viola"),
|
||||
-- Based on the flowers added in Minetest Game 5.0.0
|
||||
tulip_black = S("Potted Black Tulip"),
|
||||
chrysanthemum_green = S("Potted Green Chrysanthemum"),
|
||||
}
|
||||
|
||||
for f, desc in pairs(xdecor_potted) do
|
||||
xdecor.register("potted_" .. f, {
|
||||
description = desc,
|
||||
walkable = false,
|
||||
groups = {snappy = 3, flammable = 3, plant = 1, flower = 1, potted_flower = 1},
|
||||
is_ground_content = false,
|
||||
tiles = {"xdecor_" .. f .. "_pot.png"},
|
||||
inventory_image = "xdecor_" .. f .. "_pot.png",
|
||||
drawtype = "plantlike",
|
||||
sounds = default.node_sound_leaves_defaults({
|
||||
place = default.node_sound_stone_defaults().place,
|
||||
dug = default.node_sound_stone_defaults().dug,
|
||||
}),
|
||||
selection_box = xdecor.nodebox.slab_y(0.3)
|
||||
})
|
||||
|
||||
-- Register recipe only if the matching flower exists
|
||||
if minetest.registered_items["flowers:" .. f] then
|
||||
minetest.register_craft({
|
||||
output = "xdecor:potted_" .. f,
|
||||
recipe = {
|
||||
{"default:clay_brick", "flowers:" .. f, "default:clay_brick"},
|
||||
{"", "default:clay_brick", ""}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local painting_box = {
|
||||
type = "wallmounted",
|
||||
wall_top = {-0.4375, 0.4375, -0.3125, 0.4375, 0.5, 0.3125},
|
||||
wall_bottom = {-0.4375, -0.5, -0.3125, 0.4375, -0.4375, 0.3125},
|
||||
wall_side = {-0.5, -0.3125, -0.4375, -0.4375, 0.3125, 0.4375}
|
||||
}
|
||||
|
||||
xdecor.register("painting_1", {
|
||||
description = S("Painting"),
|
||||
tiles = {"xdecor_painting_1.png","xdecor_painting_1.png^[transformR180","xdecor_painting_1.png"},
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
inventory_image = "xdecor_painting_empty.png",
|
||||
wield_image = "xdecor_painting_empty.png",
|
||||
paramtype2 = "wallmounted",
|
||||
wallmounted_rotate_vertical = true,
|
||||
sunlight_propagates = true,
|
||||
groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 2, attached_node = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
node_box = painting_box,
|
||||
node_placement_prediction = "",
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
-- Check protection
|
||||
if minetest.is_protected(pointed_thing.above, placer:get_player_name()) and
|
||||
not minetest.check_player_privs(placer, "protection_bypass") then
|
||||
minetest.record_protection_violation(pointed_thing.above, placer:get_player_name())
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local num = math.random(4)
|
||||
local leftover, place_pos = minetest.item_place_node(
|
||||
ItemStack("xdecor:painting_" .. num), placer, pointed_thing)
|
||||
|
||||
if not place_pos then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if leftover:get_count() == 0 and
|
||||
not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
-- Play 'place' sound manually
|
||||
minetest.sound_play(default.node_sound_wood_defaults().place, {pos=place_pos}, true)
|
||||
|
||||
return itemstack
|
||||
end,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
for i = 2, 4 do
|
||||
xdecor.register("painting_" .. i, {
|
||||
tiles = {"xdecor_painting_"..i..".png","xdecor_painting_"..i..".png^[transformR180","xdecor_painting_"..i..".png"},
|
||||
use_texture_alpha = ALPHA_OPAQUE,
|
||||
paramtype2 = "wallmounted",
|
||||
wallmounted_rotate_vertical = true,
|
||||
drop = "xdecor:painting_1",
|
||||
sunlight_propagates = true,
|
||||
groups = {
|
||||
choppy = 3,
|
||||
oddly_breakable_by_hand = 2,
|
||||
flammable = 2,
|
||||
attached_node = 1,
|
||||
not_in_creative_inventory = 1
|
||||
},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
node_box = painting_box
|
||||
})
|
||||
end
|
||||
|
||||
xdecor.register("stonepath", {
|
||||
description = S("Garden Stone Path"),
|
||||
tiles = {"default_stone.png"},
|
||||
groups = {snappy = 3},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
node_box = xdecor.pixelbox(16, {
|
||||
{8, 0, 8, 6, .5, 6}, {1, 0, 1, 6, .5, 6},
|
||||
{1, 0, 10, 5, .5, 5}, {10, 0, 2, 4, .5, 4}
|
||||
}),
|
||||
selection_box = xdecor.nodebox.slab_y(0.05)
|
||||
})
|
||||
|
||||
local function register_hard_node(name, desc, def)
|
||||
def = def or {}
|
||||
xdecor.register(name, {
|
||||
description = desc,
|
||||
tiles = def.tiles or {"xdecor_" .. name .. ".png"},
|
||||
groups = def.groups or {cracky = 1},
|
||||
is_ground_content = false,
|
||||
sounds = def.sounds or default.node_sound_stone_defaults(),
|
||||
-- Use a default rotation to avoid annoying rotation
|
||||
-- on normal placement.
|
||||
-- The nodes can still be rotated by e.g. the screwdriver
|
||||
-- because xdecor.register uses 'facedir' by default.
|
||||
place_param2 = 0,
|
||||
})
|
||||
end
|
||||
|
||||
register_hard_node("cactusbrick", S("Cactus Brick"))
|
||||
register_hard_node("coalstone_tile", S("Coal Stone Tile"))
|
||||
register_hard_node("desertstone_tile", S("Polished Desert Stone Block"))
|
||||
register_hard_node("hard_clay", S("Hardened Clay"))
|
||||
register_hard_node("moonbrick", S("Moon Brick"))
|
||||
register_hard_node("stone_rune", S("Runestone"))
|
||||
|
||||
-- renamed from stone_tile to fix naming collision with moreblocks
|
||||
-- mod for the registrations under the 'stairs:' namespace
|
||||
register_hard_node("stone_tile_x", S("Polished Stone Block"), {
|
||||
tiles = {"xdecor_stone_tile.png"},
|
||||
})
|
||||
xdecor.register_legacy_aliases("stone_tile", "stone_tile_x")
|
||||
|
||||
register_hard_node("packed_ice", S("Packed Ice"), {
|
||||
groups = {cracky = 1, cools_lava = 1, slippery = 3},
|
||||
sounds = default.node_sound_glass_defaults()
|
||||
})
|
||||
|
||||
-- renamed from wood_tile to fix naming collision with moreblocks
|
||||
-- mod for the registrations under the 'stairs:' namespace
|
||||
register_hard_node("wood_tile_x", S("Wooden Tile"), {
|
||||
groups = {choppy = 1, wood = 1, flammable = 2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
tiles = {"xdecor_wood_tile.png"},
|
||||
})
|
||||
xdecor.register_legacy_aliases("wood_tile", "wood_tile_x")
|
||||
|
||||
xdecor.register("table", {
|
||||
--~ Furniture
|
||||
description = S("Table"),
|
||||
tiles = {"xdecor_wood.png"},
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 1, flammable = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
node_box = xdecor.pixelbox(16, {
|
||||
{0, 14, 0, 16, 2, 16}, {5.5, 0, 5.5, 5, 14, 6}
|
||||
})
|
||||
})
|
||||
|
||||
xdecor.register("tatami", {
|
||||
description = S("Tatami"),
|
||||
tiles = {"xdecor_tatami.png"},
|
||||
wield_image = "xdecor_tatami.png",
|
||||
groups = {snappy = 3, flammable = 3},
|
||||
is_ground_content = false,
|
||||
sunlight_propagates = true,
|
||||
node_box = xdecor.nodebox.slab_y(0.0625)
|
||||
})
|
||||
|
||||
xdecor.register("trampoline", {
|
||||
description = S("Trampoline"),
|
||||
tiles = {"xdecor_trampoline.png", "xdecor_trampoline_bottom.png", "xdecor_trampoline_sides.png"},
|
||||
use_texture_alpha = ALPHA_CLIP,
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 1, fall_damage_add_percent = -80, bouncy = 90},
|
||||
is_ground_content = false,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{ -0.5, -1/16, -0.5, 0.5, 0, 0.5 }, -- bouncy top
|
||||
{ -0.5, -0.5, -0.5, -3/16, 0, -3/16 }, -- leg 1
|
||||
{ 3/16, -0.5, -0.5, 0.5, 0, -3/16 }, -- leg 2
|
||||
{ -0.5, -0.5, 3/16, -3/16, 0, 0.5 }, -- leg 3
|
||||
{ 3/16, -0.5, 3/16, 0.5, 0, 0.5 }, -- leg 4
|
||||
{ -3/16, -5/16, -0.5, 3/16, -1/16, -7/16 }, -- connector 1
|
||||
{ -0.5, -5/16, -3/16, -7/16, -1/16, 3/16 }, -- connector 2
|
||||
{ -3/16, -5/16, 7/16, 3/16, -1/16, 0.5 }, -- connector 3
|
||||
{ 7/16, -5/16, -3/16, 0.5, -1/16, 3/16 }, -- connector 4
|
||||
},
|
||||
},
|
||||
selection_box = xdecor.nodebox.slab_y(0.5),
|
||||
collision_box = xdecor.nodebox.slab_y(0.5),
|
||||
sounds = default.node_sound_defaults({
|
||||
footstep = {
|
||||
name = "xdecor_bouncy",
|
||||
gain = 0.8
|
||||
},
|
||||
dig = default.node_sound_wood_defaults().dig,
|
||||
}),
|
||||
})
|
||||
|
||||
xdecor.register("tv", {
|
||||
description = S("Television"),
|
||||
light_source = 11,
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 2},
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {
|
||||
"xdecor_television_left.png^[transformR270",
|
||||
"xdecor_television_left.png^[transformR90",
|
||||
"xdecor_television_left.png^[transformFX",
|
||||
"xdecor_television_left.png", "xdecor_television_back.png",
|
||||
{
|
||||
name = "xdecor_television_front_animated.png",
|
||||
animation = {type = "vertical_frames", length = 80.0}
|
||||
}
|
||||
},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
})
|
||||
|
||||
xdecor.register("woodframed_glass", {
|
||||
description = S("Wood Framed Glass"),
|
||||
drawtype = "glasslike_framed",
|
||||
sunlight_propagates = true,
|
||||
tiles = {"xdecor_woodframed_glass.png", "xdecor_woodframed_glass_detail.png"},
|
||||
use_texture_alpha = ALPHA_CLIP,
|
||||
groups = {cracky = 2, oddly_breakable_by_hand = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
_xdecor_custom_noncube_tiles = {
|
||||
stair = {
|
||||
"xdecor_woodframed_glass_split.png",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_stairside_flip.png",
|
||||
"xdecor_woodframed_glass_stairside.png",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_split.png",
|
||||
},
|
||||
stair_inner = {
|
||||
"xdecor_woodframed_glass_stairside.png^[transformR270",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_stairside_flip.png",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_stairside.png",
|
||||
},
|
||||
stair_outer = {
|
||||
"xdecor_woodframed_glass_stairside.png^[transformR90",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_outer_stairside.png",
|
||||
"xdecor_woodframed_glass_stairside_flip.png",
|
||||
"xdecor_woodframed_glass_stairside.png^[transformR90",
|
||||
"xdecor_woodframed_glass_outer_stairside.png",
|
||||
},
|
||||
halfstair = {
|
||||
"xdecor_woodframed_glass_cube.png",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_stairside_flip.png",
|
||||
"xdecor_woodframed_glass_stairside.png",
|
||||
"xdecor_woodframed_glass_split.png^[transformR90",
|
||||
"xdecor_woodframed_glass_cube.png",
|
||||
},
|
||||
slab = {
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass.png",
|
||||
"xdecor_woodframed_glass_split.png",
|
||||
},
|
||||
cube = { "xdecor_woodframed_glass_cube.png" },
|
||||
thinstair = { "xdecor_woodframed_glass_split.png" },
|
||||
micropanel = { "xdecor_woodframed_glass_split.png" },
|
||||
panel = {
|
||||
"xdecor_woodframed_glass_split.png",
|
||||
"xdecor_woodframed_glass_split.png",
|
||||
"xdecor_woodframed_glass_cube.png",
|
||||
"xdecor_woodframed_glass_cube.png",
|
||||
"xdecor_woodframed_glass_split.png",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
local devices = {
|
||||
{ "radio", S("Radio"), default.node_sound_metal_defaults() },
|
||||
--~ as in "loudspeaker"
|
||||
{ "speaker", S("Speaker"), default.node_sound_metal_defaults() },
|
||||
}
|
||||
for _, v in pairs(devices) do
|
||||
xdecor.register(v[1], {
|
||||
description = v[2],
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
tiles = {
|
||||
"xdecor_" .. v[1] .. "_top.png",
|
||||
"xdecor_" .. v[1] .. "_side.png",
|
||||
"xdecor_" .. v[1] .. "_side.png",
|
||||
"xdecor_" .. v[1] .. "_side.png",
|
||||
"xdecor_" .. v[1] .. "_back.png",
|
||||
"xdecor_" .. v[1] .. "_front.png",
|
||||
},
|
||||
groups = {cracky = 2, not_cuttable = 1},
|
||||
is_ground_content = false,
|
||||
sounds = v[3],
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,405 @@
|
||||
minetest.register_craft({
|
||||
output = "xdecor:baricade",
|
||||
recipe = {
|
||||
{"group:stick", "", "group:stick"},
|
||||
{"", "default:steel_ingot", ""},
|
||||
{"group:stick", "", "group:stick"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:barrel",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"default:iron_lump", "", "default:iron_lump"},
|
||||
{"group:wood", "group:wood", "group:wood"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:candle",
|
||||
recipe = {
|
||||
{"default:torch"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cabinet",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"doors:trapdoor", "", "doors:trapdoor"},
|
||||
{"group:wood", "group:wood", "group:wood"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cabinet_half 2",
|
||||
recipe = {
|
||||
{"xdecor:cabinet"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cactusbrick",
|
||||
recipe = {
|
||||
{"default:brick", "default:cactus"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:chair",
|
||||
recipe = {
|
||||
{"group:stick", "", ""},
|
||||
{"group:stick", "group:stick", "group:stick"},
|
||||
{"group:stick", "", "group:stick"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:coalstone_tile 4",
|
||||
recipe = {
|
||||
{"default:coalblock", "default:stone"},
|
||||
{"default:stone", "default:coalblock"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cobweb",
|
||||
recipe = {
|
||||
{"farming:string", "", "farming:string"},
|
||||
{"", "farming:string", ""},
|
||||
{"farming:string", "", "farming:string"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cushion 3",
|
||||
recipe = {
|
||||
{"wool:red", "wool:red", "wool:red"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:cushion_block",
|
||||
recipe = {
|
||||
{"xdecor:cushion"},
|
||||
{"xdecor:cushion"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:desertstone_tile 4",
|
||||
recipe = {
|
||||
{"default:desert_stone_block", "default:desert_stone_block"},
|
||||
{"default:desert_stone_block", "default:desert_stone_block"},
|
||||
}
|
||||
})
|
||||
|
||||
if not minetest.get_modpath("moreblocks") then
|
||||
minetest.register_craft({
|
||||
output = "xdecor:empty_shelf",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"", "", ""},
|
||||
{"group:wood", "group:wood", "group:wood"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:enderchest",
|
||||
recipe = {
|
||||
{"", "default:obsidian", ""},
|
||||
{"default:obsidian", "default:chest", "default:obsidian"},
|
||||
{"", "default:obsidian", ""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:hard_clay",
|
||||
recipe = {
|
||||
{"default:clay", "default:clay"},
|
||||
{"default:clay", "default:clay"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:iron_lightbox",
|
||||
recipe = {
|
||||
{"xpanes:bar_flat", "default:torch", "xpanes:bar_flat"},
|
||||
{"xpanes:bar_flat", "default:glass", "xpanes:bar_flat"},
|
||||
{"xpanes:bar_flat", "default:torch", "xpanes:bar_flat"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:ivy 4",
|
||||
recipe = {
|
||||
{"group:leaves"},
|
||||
{"group:leaves"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:lantern",
|
||||
recipe = {
|
||||
{"default:iron_lump"},
|
||||
{"default:torch"},
|
||||
{"default:iron_lump"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:moonbrick",
|
||||
recipe = {
|
||||
{"default:brick", "default:stone"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:multishelf",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"group:vessel", "group:book", "group:vessel"},
|
||||
{"group:wood", "group:wood", "group:wood"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:packed_ice",
|
||||
recipe = {
|
||||
{"", "default:ice", ""},
|
||||
{"default:ice", "", "default:ice"},
|
||||
{"", "default:ice", ""},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:painting_1",
|
||||
recipe = {
|
||||
{"default:sign_wall_wood", "group:dye"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:radio",
|
||||
type = "shapeless",
|
||||
recipe = {"xdecor:speaker", "xdecor:speaker"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:rooster",
|
||||
recipe = {
|
||||
{"default:gold_ingot", "", "default:gold_ingot"},
|
||||
{"", "default:gold_ingot", ""},
|
||||
{"default:gold_ingot", "", "default:gold_ingot"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:speaker",
|
||||
recipe = {
|
||||
{"default:gold_ingot", "default:copper_ingot", "default:gold_ingot"},
|
||||
{"default:copper_ingot", "", "default:copper_ingot"},
|
||||
{"default:gold_ingot", "default:copper_ingot", "default:gold_ingot"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:stone_tile_x 4",
|
||||
recipe = {
|
||||
{"default:stone_block", "default:stone_block"},
|
||||
{"default:stone_block", "default:stone_block"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:stone_rune 4",
|
||||
recipe = {
|
||||
{"default:stone_block", "default:stone_block", "default:stone_block"},
|
||||
{"default:stone_block", "", "default:stone_block"},
|
||||
{"default:stone_block", "default:stone_block", "default:stone_block"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:stonepath 16",
|
||||
recipe = {
|
||||
{"stairs:slab_cobble", "", "stairs:slab_cobble"},
|
||||
{"", "stairs:slab_cobble", ""},
|
||||
{"stairs:slab_cobble", "", "stairs:slab_cobble"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:table",
|
||||
recipe = {
|
||||
{"stairs:slab_wood", "stairs:slab_wood", "stairs:slab_wood"},
|
||||
{"", "group:stick", ""},
|
||||
{"", "group:stick", ""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:tatami",
|
||||
recipe = {
|
||||
{"farming:wheat", "farming:wheat", "farming:wheat"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:trampoline",
|
||||
recipe = {
|
||||
{"farming:string", "farming:string", "farming:string"},
|
||||
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "", "default:steel_ingot"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:tv",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:copper_ingot", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:glass", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:copper_ingot", "default:steel_ingot"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:woodframed_glass",
|
||||
recipe = {
|
||||
{"group:stick", "group:stick", "group:stick"},
|
||||
{"group:stick", "default:glass", "group:stick"},
|
||||
{"group:stick", "group:stick", "group:stick"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:wood_tile_x 2",
|
||||
recipe = {
|
||||
{"", "group:wood", ""},
|
||||
{"group:wood", "", "group:wood"},
|
||||
{"", "group:wood", ""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:wooden_lightbox",
|
||||
recipe = {
|
||||
{"group:stick", "default:torch", "group:stick"},
|
||||
{"group:stick", "default:glass", "group:stick"},
|
||||
{"group:stick", "default:torch", "group:stick"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:wooden2_lightbox",
|
||||
recipe = {
|
||||
{"group:stick", "group:stick", "group:stick"},
|
||||
{"default:torch", "default:glass", "default:torch"},
|
||||
{"group:stick", "group:stick", "group:stick"}
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:empty_shelf",
|
||||
burntime = 30,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:multishelf",
|
||||
burntime = 30,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:cabinet",
|
||||
burntime = 30,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:barrel",
|
||||
burntime = 30,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:cabinet_half",
|
||||
burntime = 15,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:workbench",
|
||||
burntime = 15,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:table",
|
||||
burntime = 12,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "doors:woodglass_door",
|
||||
burntime = 13,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "doors:screen_door",
|
||||
burntime = 10,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "doors:slide_door",
|
||||
burntime = 8,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xpanes:wood_frame_flat",
|
||||
burntime = 5,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xpanes:bamboo_frame_flat",
|
||||
burntime = 3,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "doors:japanese_door",
|
||||
burntime = 8,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:chair",
|
||||
burntime = 6,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:baricade",
|
||||
burntime = 6,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:wood_tile_x",
|
||||
burntime = 10,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "realchess:chessboard",
|
||||
burntime = 4,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:painting_1",
|
||||
burntime = 3,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:tatami",
|
||||
burntime = 1,
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "xdecor:ivy",
|
||||
burntime = 1,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
local rope = {}
|
||||
local S = minetest.get_translator("xdecor")
|
||||
|
||||
-- Maximum length a rope can extend to
|
||||
local MAX_ROPES = 30
|
||||
|
||||
local ropesounds = default.node_sound_leaves_defaults()
|
||||
|
||||
-- Code by Mirko K. (modified by Temperest, Wulfsdad, kilbith and Wuzzy) (License: GPL).
|
||||
function rope.place(itemstack, placer, pointed_thing)
|
||||
local creative = minetest.is_creative_enabled(placer:get_player_name())
|
||||
local protection_bypass = minetest.check_player_privs(placer, "protection_bypass")
|
||||
local pname = placer:get_player_name()
|
||||
if pointed_thing.type == "node" then
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
local pos = pointed_thing.above
|
||||
-- Check protection
|
||||
if minetest.is_protected(pos, pname) and not protection_bypass then
|
||||
minetest.record_protection_violation(pos, pname)
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local oldnode = minetest.get_node(pos)
|
||||
local stackname = itemstack:get_name()
|
||||
-- Limit rope length to max. stack size or MAX_ROPES (whatever is smaller).
|
||||
-- Prevents the rope to extend infinitely in Creative Mode.
|
||||
local max_ropes = math.min(itemstack:get_stack_max(), MAX_ROPES)
|
||||
|
||||
-- Start placing ropes and extend it downwards until we hit an obstacle,
|
||||
-- run out of ropes or hit the maximum rope length.
|
||||
local start_pos = table.copy(pos)
|
||||
local ropes_to_place = 0
|
||||
local new_rope_nodes = {}
|
||||
while oldnode.name == "air" and (creative or (ropes_to_place < itemstack:get_count())) and ropes_to_place < max_ropes do
|
||||
-- Stop extending rope into protected area
|
||||
if minetest.is_protected(pos, pname) and not protection_bypass then
|
||||
break
|
||||
end
|
||||
|
||||
table.insert(new_rope_nodes, table.copy(pos))
|
||||
pos.y = pos.y - 1
|
||||
oldnode = minetest.get_node(pos)
|
||||
ropes_to_place = ropes_to_place + 1
|
||||
end
|
||||
local newnode = {name = stackname}
|
||||
if ropes_to_place == 1 then
|
||||
minetest.set_node(new_rope_nodes[1], newnode)
|
||||
else
|
||||
minetest.bulk_set_node(new_rope_nodes, newnode)
|
||||
end
|
||||
if not creative then
|
||||
itemstack:take_item(ropes_to_place)
|
||||
end
|
||||
|
||||
-- Play placement sound manually
|
||||
if ropes_to_place > 0 then
|
||||
minetest.sound_play(ropesounds.place, {pos=start_pos}, true)
|
||||
end
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
function rope.remove(pos, oldnode, digger, rope_name)
|
||||
local num = 0
|
||||
local below = {x = pos.x, y = pos.y, z = pos.z}
|
||||
|
||||
-- Remove all ropes below our rope
|
||||
local positions = {}
|
||||
while minetest.get_node(below).name == rope_name do
|
||||
local pname = ""
|
||||
local protection_bypass = false
|
||||
if digger and digger:is_player() then
|
||||
protection_bypass = minetest.check_player_privs(digger, "protection_bypass")
|
||||
pname = digger:get_player_name()
|
||||
end
|
||||
-- Stop removing ropes when reaching protected land
|
||||
if minetest.is_protected(below, pname) and not protection_bypass then
|
||||
break
|
||||
end
|
||||
table.insert(positions, table.copy(below))
|
||||
below.y = below.y - 1
|
||||
num = num + 1
|
||||
end
|
||||
if num == 0 then
|
||||
return
|
||||
end
|
||||
minetest.bulk_set_node(positions, {name="air", param2=0})
|
||||
|
||||
if digger and digger:is_player() then
|
||||
local digger_inv = digger:get_inventory()
|
||||
-- Give/drop rope items
|
||||
local creative = minetest.is_creative_enabled(digger:get_player_name())
|
||||
if not creative or not digger_inv:contains_item("main", rope_name) then
|
||||
if creative then
|
||||
num = 1
|
||||
end
|
||||
local item = rope_name.." "..num
|
||||
local leftover = digger_inv:add_item("main", rope_name.." "..num)
|
||||
if not leftover:is_empty() then
|
||||
minetest.add_item(pos, leftover)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.log("action", "[xdecor] Rope of length "..(num+1).." dug by "..digger:get_player_name().." starting at "..minetest.pos_to_string(pos))
|
||||
else
|
||||
-- No digger: Drop rope as item
|
||||
minetest.add_item(pos, "xdecor:rope")
|
||||
|
||||
minetest.log("action", "[xdecor] Rope of length "..(num+1).." removed starting at "..minetest.pos_to_string(pos))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
xdecor.register("rope", {
|
||||
description = S("Rope"),
|
||||
drawtype = "plantlike",
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
groups = {dig_immediate = 3, flammable = 3},
|
||||
is_ground_content = false,
|
||||
tiles = {"xdecor_rope.png"},
|
||||
inventory_image = "xdecor_rope_inv.png",
|
||||
wield_image = "xdecor_rope_inv.png",
|
||||
selection_box = xdecor.pixelbox(8, {{3, 0, 3, 2, 8, 2}}),
|
||||
node_placement_prediction = "",
|
||||
on_place = rope.place,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
pos = vector.new(pos.x, pos.y-1, pos.z)
|
||||
rope.remove(pos, oldnode, digger, "xdecor:rope")
|
||||
end,
|
||||
sounds = ropesounds,
|
||||
})
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:rope",
|
||||
recipe = {
|
||||
{"farming:string"},
|
||||
{"farming:string"},
|
||||
{"farming:string"}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,764 @@
|
||||
local workbench = {}
|
||||
local registered_cuttable_nodes = {}
|
||||
local special_cuts = {}
|
||||
|
||||
screwdriver = screwdriver or {}
|
||||
local min, ceil = math.min, math.ceil
|
||||
local S = minetest.get_translator("xdecor")
|
||||
local FS = function(...) return minetest.formspec_escape(S(...)) end
|
||||
local NS = function(s) return s end
|
||||
|
||||
-- Set to true to print all the raw English strings
|
||||
-- of registered cut nodes into console.
|
||||
-- (Requires Luanti 5.14.0 or later to work.)
|
||||
local GENERATE_TRANSLATABLE_STRING_LIST = false
|
||||
|
||||
if GENERATE_TRANSLATABLE_STRING_LIST and not minetest.strip_escapes then
|
||||
minetest.log("warning", "[xdecor] GENERATE_TRANSLATABLE_STRING_LIST is set to true but minetest.strip_escapes is missing! "..
|
||||
"This would malform the output, so GENERATE_TRANSLATABLE_STRING_LIST will be forced to false.")
|
||||
GENERATE_TRANSLATABLE_STRING_LIST = false
|
||||
end
|
||||
|
||||
local DEFAULT_HAMMER_REPAIR = 500
|
||||
local DEFAULT_HAMMER_REPAIR_COST = 700
|
||||
|
||||
|
||||
-- Nodeboxes definitions
|
||||
workbench.defs = {
|
||||
-- Name Yield Nodeboxes (X Y Z W H L) Description LegacyDescription
|
||||
{"nanoslab", 16, {{ 0, 0, 0, 8, 1, 8 }},
|
||||
--~ Part of a block name for a tiny slab with 1/16 height and 1/4 area. E.g. "Stone Nanoslab"
|
||||
NS("Nanoslab"),
|
||||
--~ Block name for a tiny slab with 1/16 height and 1/4 area. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Nanoslab")},
|
||||
{"micropanel", 16, {{ 0, 0, 0, 16, 1, 8 }},
|
||||
--~ Part of a block name for a tiny slab with 1/16 height and 1/2 area. E.g. "Stone Micropanel"
|
||||
NS("Micropanel"),
|
||||
--~ Block name for a tiny slab with 1/16 height and 1/2 area. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Micropanel")},
|
||||
{"microslab", 8, {{ 0, 0, 0, 16, 1, 16 }},
|
||||
--~ Part of a block name for a tiny slab with 1/16 height and full area. E.g. "Stone Microslab"
|
||||
NS("Microslab"),
|
||||
--~ Block name for a tiny slab with 1/16 height and full area. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Microslab")},
|
||||
{"thinstair", 8, {{ 0, 7, 0, 16, 1, 8 }, { 0, 15, 8, 16, 1, 8 }},
|
||||
--~ Part of a block name of a thin stair, a stair-like block where the "steps" are thinner. E.g. "Stone Thin Stair"
|
||||
NS("Thin Stair"),
|
||||
--~ Block name of a thin stair, a stair-like block where the "steps" are thinner. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Thin Stair")},
|
||||
{"cube", 4, {{ 0, 0, 0, 8, 8, 8 }},
|
||||
--~ Part of a block name of a tiny cube-shaped block with 1/2 the side length of a full block. E.g. "Stone Cube"
|
||||
NS("Cube"),
|
||||
--~ Block name of a tiny cube-shaped block with 1/2 the side length of a full block. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Cube")},
|
||||
{"panel", 4, {{ 0, 0, 0, 16, 8, 8 }},
|
||||
--~ Part of a block name of a block with 1/2 the height and 1/2 the length of a full block. It's like a slab that was cut in half. E.g. "Stone Panel"
|
||||
NS("Panel"),
|
||||
--~ Block name of a block with 1/2 the height and 1/2 the length of a full block. It's like a slab that was cut in half. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Panel")},
|
||||
{"slab", 2, nil,
|
||||
--~ Part of block name of a block with 1/2 the height of a full block. E.g. "Stone Slab"
|
||||
NS("Slab"),
|
||||
--~ Block name of a block with 1/2 the height of a full block. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Slab") },
|
||||
{"doublepanel", 2, {{ 0, 0, 0, 16, 8, 8 }, { 0, 8, 8, 16, 8, 8 }},
|
||||
--~ Block name of a stair-like block variant with a lower piece cut away. E.g. "Stone Double Panel"
|
||||
NS("Double Panel"),
|
||||
--~ Block name of a stair-like block variant with a lower piece cut away. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Double Panel")},
|
||||
{"halfstair", 2, {{ 0, 0, 0, 8, 8, 16 },
|
||||
{ 0, 8, 8, 8, 8, 8 }},
|
||||
--~ Part of a block name for a stair where 1/2 has been cut away sideways. Can be obtained by work bench. E.g. "Stone Half-Stair"
|
||||
NS("Half-Stair"),
|
||||
--~ Block name of a stair where 1/2 has been cut away sideways. Can be obtained by work bench. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Half-Stair")},
|
||||
{"stair_outer", 1, nil, nil},
|
||||
{"stair", 1, nil,
|
||||
--~ Block name of a 'traditional' stair-shaped block. E.g. "Stone Stair"
|
||||
NS("Stair"),
|
||||
--~ Block name of a 'traditional' stair-shaped block. (@1 = base block name, e.g. "Stone")
|
||||
NS("@1 Stair")},
|
||||
{"stair_inner", 1, nil, nil},
|
||||
}
|
||||
|
||||
local custom_repairable = {}
|
||||
function xdecor:register_repairable(item)
|
||||
custom_repairable[item] = true
|
||||
end
|
||||
|
||||
-- Tools allowed to be repaired
|
||||
function workbench:repairable(stack)
|
||||
-- Explicitly registered as repairable: Overrides everything else
|
||||
if custom_repairable[stack] then
|
||||
return true
|
||||
end
|
||||
-- no repair if non-tool
|
||||
if not minetest.registered_tools[stack] then
|
||||
return false
|
||||
end
|
||||
-- no repair if disable_repair group
|
||||
if minetest.get_item_group(stack, "disable_repair") == 1 then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Returns true if item can be cut into basic stairs and slabs
|
||||
function workbench:cuttable(itemname)
|
||||
local split = string.split(itemname, ":")
|
||||
if split and split[1] and split[2] then
|
||||
if minetest.registered_nodes["stairs:stair_"..split[2]] ~= nil or
|
||||
minetest.registered_nodes["stairs:slab_"..split[2]] ~= nil then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if registered_cuttable_nodes[itemname] == true then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Returns true if item can be cut into xdecor extended shapes (thinslab, panel, cube, etc.)
|
||||
function workbench:cuttable_extended(itemname)
|
||||
return registered_cuttable_nodes[itemname] == true
|
||||
end
|
||||
|
||||
-- method to allow other mods to check if an item is repairable
|
||||
function xdecor:is_repairable(stack)
|
||||
return workbench:repairable(stack)
|
||||
end
|
||||
|
||||
function workbench:get_output(inv, input, name)
|
||||
local output = {}
|
||||
local extended = workbench:cuttable_extended(input:get_name())
|
||||
for i = 1, #self.defs do
|
||||
local nbox = self.defs[i]
|
||||
local cuttype = nbox[1]
|
||||
local count = nbox[2] * input:get_count()
|
||||
local max_count = input:get_stack_max()
|
||||
if count > max_count then
|
||||
-- Limit count to maximum multiple to avoid waste
|
||||
count = nbox[2] * math.floor(max_count / nbox[2])
|
||||
end
|
||||
local was_cut = false
|
||||
if extended or nbox[3] == nil then
|
||||
local item = name .. "_" .. cuttype
|
||||
local name_after_colon = name:match(":(.*)")
|
||||
item = (nbox[3] and item)
|
||||
if not item and name_after_colon then
|
||||
item = "stairs:" .. cuttype .. "_" .. name_after_colon
|
||||
end
|
||||
if item and minetest.registered_items[item] then
|
||||
output[i] = item .. " " .. count
|
||||
was_cut = true
|
||||
end
|
||||
end
|
||||
if not was_cut and special_cuts[input:get_name()] ~= nil then
|
||||
local cut = special_cuts[input:get_name()][cuttype]
|
||||
if cut then
|
||||
output[i] = cut .. " " .. count
|
||||
was_cut = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
inv:set_list("forms", output)
|
||||
end
|
||||
|
||||
local main_fs = ""..
|
||||
--~ Label shown in work bench menu where you can cut a block to create smaller versions of it
|
||||
"label[0.9,1.23;"..FS("Cut").."]"
|
||||
--~ Label shown in work bench menu where you can repair an item
|
||||
.."label[0.9,2.23;"..FS("Repair").."]"
|
||||
..[[ box[-0.05,1;2.05,0.9;#555555]
|
||||
box[-0.05,2;2.05,0.9;#555555] ]]
|
||||
--~ Button in work bench menu for the crafting grid
|
||||
.."button[0,0;2,1;craft;"..FS("Crafting").."]"
|
||||
--~ Button in work bench menu for its inventory
|
||||
.."button[2,0;2,1;storage;"..FS("Storage").."]"
|
||||
..[[ image[3,1;1,1;gui_arrow.png]
|
||||
image[0,1;1,1;worktable_saw.png]
|
||||
image[0,2;1,1;worktable_anvil.png]
|
||||
image[3,2;1,1;hammer_layout.png]
|
||||
list[context;input;2,1;1,1;]
|
||||
list[context;tool;2,2;1,1;]
|
||||
list[context;hammer;3,2;1,1;]
|
||||
list[context;forms;4,0;4,3;]
|
||||
listring[current_player;main]
|
||||
listring[context;tool]
|
||||
listring[current_player;main]
|
||||
listring[context;hammer]
|
||||
listring[current_player;main]
|
||||
listring[context;forms]
|
||||
listring[current_player;main]
|
||||
listring[context;input]
|
||||
]]
|
||||
|
||||
local crafting_fs = "image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]"
|
||||
--~ Button to return to main page in work bench menu
|
||||
.."button[0,0;1.5,1;back;< "..FS("Back").."]"
|
||||
..[[ list[current_player;craft;2,0;3,3;]
|
||||
list[current_player;craftpreview;6,1;1,1;]
|
||||
listring[current_player;main]
|
||||
listring[current_player;craft]
|
||||
]]
|
||||
|
||||
local storage_fs = "list[context;storage;0,1;8,2;]"
|
||||
.."button[0,0;1.5,1;back;< "..FS("Back").."]"
|
||||
..[[listring[context;storage]
|
||||
listring[current_player;main]
|
||||
]]
|
||||
|
||||
local formspecs = {
|
||||
-- Main formspec
|
||||
main_fs,
|
||||
|
||||
-- Crafting formspec
|
||||
crafting_fs,
|
||||
|
||||
-- Storage formspec
|
||||
storage_fs,
|
||||
}
|
||||
|
||||
function workbench:set_formspec(meta, id)
|
||||
meta:set_string("formspec",
|
||||
"size[8,7;]list[current_player;main;0,3.25;8,4;]" ..
|
||||
formspecs[id] .. xdecor.xbg .. default.get_hotbar_bg(0,3.25))
|
||||
end
|
||||
|
||||
function workbench.construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
inv:set_size("tool", 1)
|
||||
inv:set_size("input", 1)
|
||||
inv:set_size("hammer", 1)
|
||||
inv:set_size("forms", 4*3)
|
||||
inv:set_size("storage", 8*2)
|
||||
|
||||
meta:set_string("infotext", S("Work Bench"))
|
||||
workbench:set_formspec(meta, 1)
|
||||
end
|
||||
|
||||
function workbench.fields(pos, _, fields)
|
||||
if fields.quit then return end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local id = fields.back and 1 or fields.craft and 2 or fields.storage and 3
|
||||
if not id then return end
|
||||
|
||||
workbench:set_formspec(meta, id)
|
||||
end
|
||||
|
||||
function workbench.dig(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
return inv:is_empty("input") and inv:is_empty("hammer") and
|
||||
inv:is_empty("tool") and inv:is_empty("storage")
|
||||
end
|
||||
|
||||
function workbench.blast(pos)
|
||||
local drops = xdecor.get_inventory_drops(pos, {"input", "hammer", "tool", "storage"})
|
||||
minetest.remove_node(pos)
|
||||
return drops
|
||||
end
|
||||
|
||||
function workbench.timer(pos)
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local tool = inv:get_stack("tool", 1)
|
||||
local hammer = inv:get_stack("hammer", 1)
|
||||
|
||||
if tool:is_empty() or hammer:is_empty() or tool:get_wear() == 0 then
|
||||
timer:stop()
|
||||
return
|
||||
end
|
||||
|
||||
local hammerdef = hammer:get_definition()
|
||||
|
||||
-- Tool's wearing range: 0-65535; 0 = new condition
|
||||
tool:add_wear(-hammerdef._xdecor_hammer_repair or DEFAULT_HAMMER_REPAIR)
|
||||
hammer:add_wear(hammerdef._xdecor_hammer_repair_cost or DEFAULT_HAMMER_REPAIR_COST)
|
||||
|
||||
inv:set_stack("tool", 1, tool)
|
||||
inv:set_stack("hammer", 1, hammer)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function workbench.allow_put(pos, listname, index, stack, player)
|
||||
local stackname = stack:get_name()
|
||||
if (listname == "tool" and workbench:repairable(stackname)) or
|
||||
(listname == "input" and workbench:cuttable(stackname)) or
|
||||
(listname == "hammer" and minetest.get_item_group(stackname, "repair_hammer") == 1) or
|
||||
listname == "storage" then
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function workbench.on_put(pos, listname, index, stack, player)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if listname == "input" then
|
||||
local input = inv:get_stack("input", 1)
|
||||
workbench:get_output(inv, input, stack:get_name())
|
||||
elseif listname == "tool" or listname == "hammer" then
|
||||
local timer = minetest.get_node_timer(pos)
|
||||
timer:start(3.0)
|
||||
end
|
||||
end
|
||||
|
||||
function workbench.allow_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if (to_list == "storage" and from_list ~= "forms") then
|
||||
return count
|
||||
elseif (to_list == "hammer" and (from_list == "tool" or from_list == "storage")) then
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
if minetest.get_item_group(stack:get_name(), "repair_hammer") == 1 then
|
||||
return count
|
||||
end
|
||||
elseif (to_list == "tool") then
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
if workbench:repairable(stack:get_name()) then
|
||||
return count
|
||||
end
|
||||
elseif (to_list == "input") then
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
if workbench:cuttable(stack:get_name()) then
|
||||
return count
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function workbench.on_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local from_stack = inv:get_stack(from_list, from_index)
|
||||
local to_stack = inv:get_stack(to_list, to_index)
|
||||
|
||||
workbench.on_take(pos, from_list, from_index, from_stack, player)
|
||||
workbench.on_put(pos, to_list, to_index, to_stack, player)
|
||||
end
|
||||
|
||||
function workbench.allow_take(pos, listname, index, stack, player)
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
function workbench.on_take(pos, listname, index, stack, player)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local input = inv:get_stack("input", 1)
|
||||
local inputname = input:get_name()
|
||||
local stackname = stack:get_name()
|
||||
|
||||
if listname == "input" then
|
||||
if stackname == inputname and workbench:cuttable(inputname) then
|
||||
workbench:get_output(inv, input, stackname)
|
||||
else
|
||||
inv:set_list("forms", {})
|
||||
end
|
||||
elseif listname == "forms" then
|
||||
local fromstack = inv:get_stack(listname, index)
|
||||
if not fromstack:is_empty() and fromstack:get_name() ~= stackname then
|
||||
local player_inv = player:get_inventory()
|
||||
if player_inv:room_for_item("main", fromstack) then
|
||||
player_inv:add_item("main", fromstack)
|
||||
end
|
||||
end
|
||||
|
||||
input:take_item(ceil(stack:get_count() / workbench.defs[index][2]))
|
||||
inv:set_stack("input", 1, input)
|
||||
workbench:get_output(inv, input, inputname)
|
||||
end
|
||||
end
|
||||
|
||||
xdecor.register("workbench", {
|
||||
description = S("Work Bench"),
|
||||
--~ Work bench tooltip
|
||||
_tt_help = S("For cutting blocks, repairing tools with a hammer, crafting and storing items"),
|
||||
groups = {cracky = 2, choppy = 2, oddly_breakable_by_hand = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
tiles = {
|
||||
"xdecor_workbench_top.png","xdecor_workbench_bottom.png",
|
||||
"xdecor_workbench_sides.png", "xdecor_workbench_sides.png",
|
||||
"xdecor_workbench_front.png", "xdecor_workbench_front.png"
|
||||
},
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
can_dig = workbench.dig,
|
||||
on_blast = workbench.blast,
|
||||
on_timer = workbench.timer,
|
||||
on_construct = workbench.construct,
|
||||
on_receive_fields = workbench.fields,
|
||||
on_metadata_inventory_put = workbench.on_put,
|
||||
on_metadata_inventory_take = workbench.on_take,
|
||||
on_metadata_inventory_move = workbench.on_move,
|
||||
allow_metadata_inventory_put = workbench.allow_put,
|
||||
allow_metadata_inventory_take = workbench.allow_take,
|
||||
allow_metadata_inventory_move = workbench.allow_move
|
||||
})
|
||||
|
||||
local function register_cut_raw(node, workbench_def, textdomain)
|
||||
local mod_name, item_name = node:match("^(.-):(.*)")
|
||||
local def = minetest.registered_nodes[node]
|
||||
|
||||
-- Special translation symbol
|
||||
local T
|
||||
if textdomain then
|
||||
T = minetest.get_translator(textdomain)
|
||||
else
|
||||
T = function(s) return s end
|
||||
end
|
||||
|
||||
if item_name and workbench_def[3] then
|
||||
local groups = {}
|
||||
local tiles
|
||||
|
||||
for k, v in pairs(def.groups) do
|
||||
if k ~= "wood" and k ~= "stone" and k ~= "level" then
|
||||
groups[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
if def.tiles then
|
||||
if #def.tiles > 1 and (def.drawtype:sub(1,5) ~= "glass") then
|
||||
tiles = def.tiles
|
||||
else
|
||||
tiles = {def.tiles[1]}
|
||||
end
|
||||
else
|
||||
tiles = {def.tile_images[1]}
|
||||
end
|
||||
|
||||
-- Erase `tileable_vertical=false` from tiles because it
|
||||
-- lead to buggy textures (e.g. with default:permafrost_with_moss)
|
||||
for t=1, #tiles do
|
||||
if type(tiles[t]) == "table" and tiles[t].tileable_vertical == false then
|
||||
tiles[t].tileable_vertical = nil
|
||||
end
|
||||
end
|
||||
|
||||
local custom_tiles = xdecor.glasscuts[node]
|
||||
if custom_tiles then
|
||||
if not custom_tiles.nanoslab then
|
||||
custom_tiles.nanoslab = custom_tiles.cube
|
||||
end
|
||||
if not custom_tiles.micropanel then
|
||||
custom_tiles.micropanel = custom_tiles.micropanel
|
||||
end
|
||||
if not custom_tiles.doublepanel then
|
||||
custom_tiles.doublepanel = custom_tiles.panel
|
||||
end
|
||||
end
|
||||
|
||||
local core_desc, modifier
|
||||
local desc_stair, desc_stair_inner, desc_stair_outer, desc_slab, desc_cut
|
||||
if minetest.strip_escapes then
|
||||
core_desc = minetest.strip_escapes(def.description)
|
||||
modifier = minetest.strip_escapes(workbench_def[4])
|
||||
|
||||
desc_stair = T(core_desc .. " Stair")
|
||||
desc_stair_inner = T("Inner " .. core_desc .. " Stair")
|
||||
desc_stair_outer = T("Outer " .. core_desc .. " Stair")
|
||||
desc_slab = T(core_desc .. " Slab")
|
||||
-- Base node description (e.g. "Stone") concatenated with a space,
|
||||
-- then a modifier (e.g. "Nanoslab"), e.g. "Stone Nanoslab".
|
||||
desc_cut = T(core_desc .. " " .. modifier)
|
||||
else
|
||||
core_desc = def.description
|
||||
modifier = workbench_def[4]
|
||||
|
||||
desc_stair = S("@1 Stair", core_desc)
|
||||
desc_stair_inner = S("Inner @1 Stair", core_desc)
|
||||
desc_stair_outer = S("Outer @1 Stair", core_desc)
|
||||
desc_slab = S("@1 Slab", core_desc)
|
||||
desc_cut = S(workbench_def[5], core_desc)
|
||||
end
|
||||
|
||||
if not minetest.registered_nodes["stairs:slab_" .. item_name] then
|
||||
if custom_tiles and (custom_tiles.slab or custom_tiles.stair) then
|
||||
if custom_tiles.stair then
|
||||
stairs.register_stair(item_name, node,
|
||||
groups, custom_tiles.stair, desc_stair,
|
||||
def.sounds)
|
||||
stairs.register_stair_inner(item_name, node,
|
||||
groups, custom_tiles.stair_inner, "", def.sounds, nil, desc_stair_inner)
|
||||
stairs.register_stair_outer(item_name, node,
|
||||
groups, custom_tiles.stair_outer, "", def.sounds, nil, desc_stair_outer)
|
||||
|
||||
if GENERATE_TRANSLATABLE_STRING_LIST then
|
||||
print(("S(%q)"):format(core_desc .. " Stair"))
|
||||
print(("S(%q)"):format("Inner " .. core_desc .. " Stair"))
|
||||
print(("S(%q)"):format("Outer " .. core_desc .. " Stair"))
|
||||
end
|
||||
end
|
||||
if custom_tiles.slab then
|
||||
stairs.register_slab(item_name, node,
|
||||
groups, custom_tiles.slab, desc_slab,
|
||||
def.sounds)
|
||||
|
||||
if GENERATE_TRANSLATABLE_STRING_LIST then
|
||||
print(("S(%q)"):format(core_desc .. " Slab"))
|
||||
end
|
||||
end
|
||||
else
|
||||
stairs.register_stair_and_slab(item_name, node,
|
||||
groups, tiles,
|
||||
desc_stair,
|
||||
desc_slab,
|
||||
def.sounds, nil,
|
||||
desc_stair_inner,
|
||||
desc_stair_outer)
|
||||
|
||||
if GENERATE_TRANSLATABLE_STRING_LIST then
|
||||
print(("S(%q)"):format(core_desc .. " Stair"))
|
||||
print(("S(%q)"):format(core_desc .. " Slab"))
|
||||
print(("S(%q)"):format("Inner " .. core_desc .. " Stair"))
|
||||
print(("S(%q)"):format("Outer " .. core_desc .. " Stair"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cutname = workbench_def[1]
|
||||
local tiles_special_cut
|
||||
if custom_tiles and custom_tiles[cutname] then
|
||||
tiles_special_cut = custom_tiles[cutname]
|
||||
else
|
||||
tiles_special_cut = tiles
|
||||
end
|
||||
|
||||
local cutnodename = node .. "_" .. cutname
|
||||
if minetest.registered_nodes[cutnodename] then
|
||||
minetest.log("error", "[xdecor] register_cut_raw: Refusing to register node "..cutnodename.." becaut it was already registered!")
|
||||
return false
|
||||
end
|
||||
|
||||
-- Add groups to identify node as a cut node
|
||||
local cutgroups = table.copy(groups)
|
||||
-- Marks it as a cut node
|
||||
cutgroups["xdecor_cut"] = 1
|
||||
-- Specifies the cut type (nanoslab, panel, etc.)
|
||||
cutgroups["xdecor_cut_"..workbench_def[1]] = 1
|
||||
|
||||
-- Also hide cut nodes from creative inv
|
||||
cutgroups.not_in_creative_inventory = 1
|
||||
|
||||
minetest.register_node(":" .. cutnodename, {
|
||||
description = desc_cut,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
sounds = def.sounds,
|
||||
tiles = tiles_special_cut,
|
||||
use_texture_alpha = def.use_texture_alpha,
|
||||
groups = cutgroups,
|
||||
is_ground_content = def.is_ground_content,
|
||||
node_box = xdecor.pixelbox(16, workbench_def[3]),
|
||||
sunlight_propagates = true,
|
||||
on_place = minetest.rotate_node
|
||||
})
|
||||
|
||||
if GENERATE_TRANSLATABLE_STRING_LIST then
|
||||
print(("S(%q)"):format(core_desc .. " " .. modifier))
|
||||
end
|
||||
|
||||
elseif item_name and mod_name then
|
||||
minetest.register_alias_force(
|
||||
("%s:%s_innerstair"):format(mod_name, item_name),
|
||||
("stairs:stair_inner_%s"):format(item_name)
|
||||
)
|
||||
minetest.register_alias_force(
|
||||
("%s:%s_outerstair"):format(mod_name, item_name),
|
||||
("stairs:stair_outer_%s"):format(item_name)
|
||||
)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function workbench:register_cut(nodename, textdomain)
|
||||
if registered_cuttable_nodes[nodename] then
|
||||
minetest.log("error", "[xdecor] Workbench: Tried to register cut for node "..nodename..", but it was already registered!")
|
||||
return false
|
||||
end
|
||||
local ok = true
|
||||
for _, d in ipairs(workbench.defs) do
|
||||
local ok = register_cut_raw(nodename, d, textdomain)
|
||||
if not ok then
|
||||
ok = false
|
||||
end
|
||||
end
|
||||
registered_cuttable_nodes[nodename] = true
|
||||
return ok
|
||||
end
|
||||
|
||||
function workbench:register_special_cut(nodename, cutlist)
|
||||
if registered_cuttable_nodes[nodename] or special_cuts[nodename] then
|
||||
minetest.log("error", "[xdecor] Workbench: Tried to register special cut for node "..nodename..", but it was already registered!")
|
||||
return false
|
||||
end
|
||||
registered_cuttable_nodes[nodename] = true
|
||||
special_cuts[nodename] = cutlist
|
||||
end
|
||||
|
||||
-- Workbench craft
|
||||
minetest.register_craft({
|
||||
output = "xdecor:workbench",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood"},
|
||||
{"group:wood", "group:wood"}
|
||||
}
|
||||
})
|
||||
|
||||
--[[ API FUNCTIONS ]]
|
||||
|
||||
--[[ Register a custom hammer (for repairing).
|
||||
A hammer repair items at the work bench. The workbench repeatedly
|
||||
checks if a hammer and a repairable tool are in the slots. The hammer
|
||||
will repair the tool in regular intervals. This is called a "step".
|
||||
In each step, the hammer reduces the wear of the repairable
|
||||
tool but increases its own wear, each by a fixed amount.
|
||||
|
||||
This function allows you to register a custom hammer with custom
|
||||
name, item image and wear stats.
|
||||
|
||||
Arguments:
|
||||
* name: Internal itemname
|
||||
* def: Definition table:
|
||||
* description: Item `description`
|
||||
* image: Inventory image and wield image
|
||||
* groups: Item groups (MUST contain at least `repair_hammer = 1`)
|
||||
* repair: How much item wear the hammer repairs per step
|
||||
* repair_cost: How much item wear the hammer takes itself per step
|
||||
|
||||
Note: Mind the implication of repair_cost! If repair_cost is lower than
|
||||
repair, this means practically infinite durability if you have two
|
||||
hammers that repair each other. If repair_cost is higher than repair,
|
||||
then hammers will break eventually.
|
||||
]]
|
||||
function xdecor.register_hammer(name, def)
|
||||
minetest.register_tool(name, {
|
||||
description = def.description,
|
||||
--~ Hammer tooltip
|
||||
_tt_help = S("Repairs tools at the work bench"),
|
||||
inventory_image = def.image,
|
||||
wield_image = def.image,
|
||||
on_use = function() do
|
||||
return end
|
||||
end,
|
||||
groups = def.groups,
|
||||
_xdecor_hammer_repair = def.repair or DEFAULT_HAMMER_REPAIR,
|
||||
_xdecor_hammer_repair_cost = def.repair_cost or DEFAULT_HAMMER_REPAIR_COST,
|
||||
})
|
||||
end
|
||||
|
||||
--[[ API FUNCTIONS ]]
|
||||
|
||||
--[[
|
||||
Registers various 'cut' node variants for the node with the given nodename,
|
||||
which will be available in the workbench.
|
||||
This must only be called once per node. Calling it again is an error.
|
||||
|
||||
The following nodes will be registered:
|
||||
|
||||
* <nodename>_nanoslab
|
||||
* <nodename>_micropanel
|
||||
* <nodename>_microslab
|
||||
* <nodename>_thinstair
|
||||
* <nodename>_cube
|
||||
* <nodename>_panel
|
||||
* <nodename>_doublepanel
|
||||
* <nodename>_halfstair
|
||||
|
||||
You MUST make sure these names are not already taken before
|
||||
calling this function. Call xdecor.can_cut to do this.
|
||||
Failing to do so is an error.
|
||||
|
||||
Additionally, a slab, stair, inner stair and outer stair
|
||||
will be registered by using the `stairs` mod if the slab
|
||||
node does not exist yet. Refer to the `stairs` mod documentation
|
||||
for details.
|
||||
|
||||
Parameters:
|
||||
* nodename: Name of node to be cut
|
||||
* textdomain: Translation textdomain for registered node
|
||||
descriptions (if not provided, node descriptions will
|
||||
be untranslatable)
|
||||
|
||||
Returns true if all nodes were registered successfully,
|
||||
returns false (and writes to error log) if any error occurred.
|
||||
]]
|
||||
xdecor.register_cut = function(nodename, textdomain)
|
||||
return workbench:register_cut(nodename, textdomain)
|
||||
end
|
||||
|
||||
-- Returns true if cut nodes have been registered for the given node
|
||||
xdecor.is_cut_registered = function(nodename)
|
||||
return registered_cuttable_nodes[nodename] == true
|
||||
end
|
||||
|
||||
--[[ Returns true if cut node variants can be registered
|
||||
for the node AND this node doesn't already have
|
||||
cut nodes registered. ]]
|
||||
xdecor.can_cut = function(nodename)
|
||||
-- Node already has cut variants: Fail
|
||||
if xdecor.is_cut_registered(nodename) then
|
||||
return false
|
||||
end
|
||||
-- Check group
|
||||
if minetest.get_item_group(nodename, "not_cuttable") == 1 then
|
||||
return false
|
||||
end
|
||||
for w=1, #workbench.defs do
|
||||
local wdef = workbench.defs[w]
|
||||
local cut = wdef[1]
|
||||
if cut ~= "stair" and cut ~= "slab" and cut ~= "stair_inner" and cut ~= "stair_outer" then
|
||||
if minetest.registered_nodes[nodename .. "_" ..cut] then
|
||||
-- There already exists a node with one of the required names: Fail
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
-- All tests passed: Success!
|
||||
return true
|
||||
end
|
||||
|
||||
--[[ END OF API FUNCTIONS ]]
|
||||
|
||||
|
||||
-- Register xdecor's built-in hammer
|
||||
xdecor.register_hammer("xdecor:hammer", {
|
||||
description = S("Hammer"),
|
||||
image = "xdecor_hammer.png",
|
||||
groups = { repair_hammer = 1 },
|
||||
repair = DEFAULT_HAMMER_REPAIR,
|
||||
repair_cost = DEFAULT_HAMMER_REPAIR_COST,
|
||||
})
|
||||
|
||||
-- Hammer recipes
|
||||
minetest.register_craft({
|
||||
output = "xdecor:hammer",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
|
||||
{"", "group:stick", ""}
|
||||
}
|
||||
})
|
||||
|
||||
-- Register default cuttable blocks (loaded from a list
|
||||
-- of hardcoded node names)
|
||||
local cuttable_nodes = dofile(minetest.get_modpath("xdecor").."/src/cuttable_node_list.lua")
|
||||
|
||||
for i = 1, #cuttable_nodes do
|
||||
local nodename = cuttable_nodes[i]
|
||||
if xdecor.can_cut(nodename) then
|
||||
workbench:register_cut(nodename, "_xdecor_cut_nodes")
|
||||
else
|
||||
minetest.log("action", "[xdecor] Tried to register cut for default node '"..nodename.."' but it was not allowed")
|
||||
end
|
||||
end
|
||||
|
||||
-- Special cuts for cushion block and cabinet
|
||||
workbench:register_special_cut("xdecor:cushion_block", { slab = "xdecor:cushion" })
|
||||
workbench:register_special_cut("xdecor:cabinet", { slab = "xdecor:cabinet_half" })
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1,20,400,8902,197281,4865609,119060324
|
||||
r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1,48,2039,97862,4085603,193690690
|
||||
4k3/8/8/8/8/8/8/4K2R w K - 0 1,15,66,1197,7059,133987,764643
|
||||
4k3/8/8/8/8/8/8/R3K3 w Q - 0 1,16,71,1287,7626,145232,846648
|
||||
4k2r/8/8/8/8/8/8/4K3 w k - 0 1,5,75,459,8290,47635,899442
|
||||
r3k3/8/8/8/8/8/8/4K3 w q - 0 1,5,80,493,8897,52710,1001523
|
||||
4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1,26,112,3189,17945,532933,2788982
|
||||
r3k2r/8/8/8/8/8/8/4K3 w kq - 0 1,5,130,782,22180,118882,3517770
|
||||
8/8/8/8/8/8/6k1/4K2R w K - 0 1,12,38,564,2219,37735,185867
|
||||
8/8/8/8/8/8/1k6/R3K3 w Q - 0 1,15,65,1018,4573,80619,413018
|
||||
4k2r/6K1/8/8/8/8/8/8 w k - 0 1,3,32,134,2073,10485,179869
|
||||
r3k3/1K6/8/8/8/8/8/8 w q - 0 1,4,49,243,3991,20780,367724
|
||||
r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1,26,568,13744,314346,7594526,179862938
|
||||
r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1,25,567,14095,328965,8153719,195629489
|
||||
r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1,25,548,13502,312835,7736373,184411439
|
||||
r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1,25,547,13579,316214,7878456,189224276
|
||||
1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1,26,583,14252,334705,8198901,198328929
|
||||
2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1,25,560,13592,317324,7710115,185959088
|
||||
r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1,25,560,13607,320792,7848606,190755813
|
||||
4k3/8/8/8/8/8/8/4K2R b K - 0 1,5,75,459,8290,47635,899442
|
||||
4k3/8/8/8/8/8/8/R3K3 b Q - 0 1,5,80,493,8897,52710,1001523
|
||||
4k2r/8/8/8/8/8/8/4K3 b k - 0 1,15,66,1197,7059,133987,764643
|
||||
r3k3/8/8/8/8/8/8/4K3 b q - 0 1,16,71,1287,7626,145232,846648
|
||||
4k3/8/8/8/8/8/8/R3K2R b KQ - 0 1,5,130,782,22180,118882,3517770
|
||||
r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1,26,112,3189,17945,532933,2788982
|
||||
8/8/8/8/8/8/6k1/4K2R b K - 0 1,3,32,134,2073,10485,179869
|
||||
8/8/8/8/8/8/1k6/R3K3 b Q - 0 1,4,49,243,3991,20780,367724
|
||||
4k2r/6K1/8/8/8/8/8/8 b k - 0 1,12,38,564,2219,37735,185867
|
||||
r3k3/1K6/8/8/8/8/8/8 b q - 0 1,15,65,1018,4573,80619,413018
|
||||
r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1,26,568,13744,314346,7594526,179862938
|
||||
r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1,26,583,14252,334705,8198901,198328929
|
||||
r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1,25,560,13592,317324,7710115,185959088
|
||||
r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1,25,560,13607,320792,7848606,190755813
|
||||
1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1,25,567,14095,328965,8153719,195629489
|
||||
2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1,25,548,13502,312835,7736373,184411439
|
||||
r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1,25,547,13579,316214,7878456,189224276
|
||||
8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1,14,195,2760,38675,570726,8107539
|
||||
8/1k6/8/5N2/8/4n3/8/2K5 w - - 0 1,11,156,1636,20534,223507,2594412
|
||||
8/8/4k3/3Nn3/3nN3/4K3/8/8 w - - 0 1,19,289,4442,73584,1198299,19870403
|
||||
K7/8/2n5/1n6/8/8/8/k6N w - - 0 1,3,51,345,5301,38348,588695
|
||||
k7/8/2N5/1N6/8/8/8/K6n w - - 0 1,17,54,835,5910,92250,688780
|
||||
8/1n4N1/2k5/8/8/5K2/1N4n1/8 b - - 0 1,15,193,2816,40039,582642,8503277
|
||||
8/1k6/8/5N2/8/4n3/8/2K5 b - - 0 1,16,180,2290,24640,288141,3147566
|
||||
8/8/3K4/3Nn3/3nN3/4k3/8/8 b - - 0 1,4,68,1118,16199,281190,4405103
|
||||
K7/8/2n5/1n6/8/8/8/k6N b - - 0 1,17,54,835,5910,92250,688780
|
||||
k7/8/2N5/1N6/8/8/8/K6n b - - 0 1,3,51,345,5301,38348,588695
|
||||
B6b/8/8/8/2K5/4k3/8/b6B w - - 0 1,17,278,4607,76778,1320507,22823890
|
||||
8/8/1B6/7b/7k/8/2B1b3/7K w - - 0 1,21,316,5744,93338,1713368,28861171
|
||||
k7/B7/1B6/1B6/8/8/8/K6b w - - 0 1,21,144,3242,32955,787524,7881673
|
||||
K7/b7/1b6/1b6/8/8/8/k6B w - - 0 1,7,143,1416,31787,310862,7382896
|
||||
B6b/8/8/8/2K5/5k2/8/b6B b - - 0 1,6,106,1829,31151,530585,9250746
|
||||
8/8/1B6/7b/7k/8/2B1b3/7K b - - 0 1,17,309,5133,93603,1591064,29027891
|
||||
k7/B7/1B6/1B6/8/8/8/K6b b - - 0 1,7,143,1416,31787,310862,7382896
|
||||
K7/b7/1b6/1b6/8/8/8/k6B b - - 0 1,21,144,3242,32955,787524,7881673
|
||||
7k/RR6/8/8/8/8/rr6/7K w - - 0 1,19,275,5300,104342,2161211,44956585
|
||||
R6r/8/8/2K5/5k2/8/8/r6R w - - 0 1,36,1027,29215,771461,20506480,525169084
|
||||
7k/RR6/8/8/8/8/rr6/7K b - - 0 1,19,275,5300,104342,2161211,44956585
|
||||
R6r/8/8/2K5/5k2/8/8/r6R b - - 0 1,36,1027,29227,771368,20521342,524966748
|
||||
6kq/8/8/8/8/8/8/7K w - - 0 1,2,36,143,3637,14893,391507
|
||||
6KQ/8/8/8/8/8/8/7k b - - 0 1,2,36,143,3637,14893,391507
|
||||
K7/8/8/3Q4/4q3/8/8/7k w - - 0 1,6,35,495,8349,166741,3370175
|
||||
6qk/8/8/8/8/8/8/7K b - - 0 1,22,43,1015,4167,105749,419369
|
||||
6KQ/8/8/8/8/8/8/7k b - - 0 1,2,36,143,3637,14893,391507
|
||||
K7/8/8/3Q4/4q3/8/8/7k b - - 0 1,6,35,495,8349,166741,3370175
|
||||
8/8/8/8/8/K7/P7/k7 w - - 0 1,3,7,43,199,1347,6249
|
||||
8/8/8/8/8/7K/7P/7k w - - 0 1,3,7,43,199,1347,6249
|
||||
K7/p7/k7/8/8/8/8/8 w - - 0 1,1,3,12,80,342,2343
|
||||
7K/7p/7k/8/8/8/8/8 w - - 0 1,1,3,12,80,342,2343
|
||||
8/2k1p3/3pP3/3P2K1/8/8/8/8 w - - 0 1,7,35,210,1091,7028,34834
|
||||
8/8/8/8/8/K7/P7/k7 b - - 0 1,1,3,12,80,342,2343
|
||||
8/8/8/8/8/7K/7P/7k b - - 0 1,1,3,12,80,342,2343
|
||||
K7/p7/k7/8/8/8/8/8 b - - 0 1,3,7,43,199,1347,6249
|
||||
7K/7p/7k/8/8/8/8/8 b - - 0 1,3,7,43,199,1347,6249
|
||||
8/2k1p3/3pP3/3P2K1/8/8/8/8 b - - 0 1,5,35,182,1091,5408,34822
|
||||
8/8/8/8/8/4k3/4P3/4K3 w - - 0 1,2,8,44,282,1814,11848
|
||||
4k3/4p3/4K3/8/8/8/8/8 b - - 0 1,2,8,44,282,1814,11848
|
||||
8/8/7k/7p/7P/7K/8/8 w - - 0 1,3,9,57,360,1969,10724
|
||||
8/8/k7/p7/P7/K7/8/8 w - - 0 1,3,9,57,360,1969,10724
|
||||
8/8/3k4/3p4/3P4/3K4/8/8 w - - 0 1,5,25,180,1294,8296,53138
|
||||
8/3k4/3p4/8/3P4/3K4/8/8 w - - 0 1,8,61,483,3213,23599,157093
|
||||
8/8/3k4/3p4/8/3P4/3K4/8 w - - 0 1,8,61,411,3213,21637,158065
|
||||
k7/8/3p4/8/3P4/8/8/7K w - - 0 1,4,15,90,534,3450,20960
|
||||
8/8/7k/7p/7P/7K/8/8 b - - 0 1,3,9,57,360,1969,10724
|
||||
8/8/k7/p7/P7/K7/8/8 b - - 0 1,3,9,57,360,1969,10724
|
||||
8/8/3k4/3p4/3P4/3K4/8/8 b - - 0 1,5,25,180,1294,8296,53138
|
||||
8/3k4/3p4/8/3P4/3K4/8/8 b - - 0 1,8,61,411,3213,21637,158065
|
||||
8/8/3k4/3p4/8/3P4/3K4/8 b - - 0 1,8,61,483,3213,23599,157093
|
||||
k7/8/3p4/8/3P4/8/8/7K b - - 0 1,4,15,89,537,3309,21104
|
||||
7k/3p4/8/8/3P4/8/8/K7 w - - 0 1,4,19,117,720,4661,32191
|
||||
7k/8/8/3p4/8/8/3P4/K7 w - - 0 1,5,19,116,716,4786,30980
|
||||
k7/8/8/7p/6P1/8/8/K7 w - - 0 1,5,22,139,877,6112,41874
|
||||
k7/8/7p/8/8/6P1/8/K7 w - - 0 1,4,16,101,637,4354,29679
|
||||
k7/8/8/6p1/7P/8/8/K7 w - - 0 1,5,22,139,877,6112,41874
|
||||
k7/8/6p1/8/8/7P/8/K7 w - - 0 1,4,16,101,637,4354,29679
|
||||
k7/8/8/3p4/4p3/8/8/7K w - - 0 1,3,15,84,573,3013,22886
|
||||
k7/8/3p4/8/8/4P3/8/7K w - - 0 1,4,16,101,637,4271,28662
|
||||
7k/3p4/8/8/3P4/8/8/K7 b - - 0 1,5,19,117,720,5014,32167
|
||||
7k/8/8/3p4/8/8/3P4/K7 b - - 0 1,4,19,117,712,4658,30749
|
||||
k7/8/8/7p/6P1/8/8/K7 b - - 0 1,5,22,139,877,6112,41874
|
||||
k7/8/7p/8/8/6P1/8/K7 b - - 0 1,4,16,101,637,4354,29679
|
||||
k7/8/8/6p1/7P/8/8/K7 b - - 0 1,5,22,139,877,6112,41874
|
||||
k7/8/6p1/8/8/7P/8/K7 b - - 0 1,4,16,101,637,4354,29679
|
||||
k7/8/8/3p4/4p3/8/8/7K b - - 0 1,5,15,102,569,4337,22579
|
||||
k7/8/3p4/8/8/4P3/8/7K b - - 0 1,4,16,101,637,4271,28662
|
||||
7k/8/8/p7/1P6/8/8/7K w - - 0 1,5,22,139,877,6112,41874
|
||||
7k/8/p7/8/8/1P6/8/7K w - - 0 1,4,16,101,637,4354,29679
|
||||
7k/8/8/1p6/P7/8/8/7K w - - 0 1,5,22,139,877,6112,41874
|
||||
7k/8/1p6/8/8/P7/8/7K w - - 0 1,4,16,101,637,4354,29679
|
||||
k7/7p/8/8/8/8/6P1/K7 w - - 0 1,5,25,161,1035,7574,55338
|
||||
k7/6p1/8/8/8/8/7P/K7 w - - 0 1,5,25,161,1035,7574,55338
|
||||
3k4/3pp3/8/8/8/8/3PP3/3K4 w - - 0 1,7,49,378,2902,24122,199002
|
||||
7k/8/8/p7/1P6/8/8/7K b - - 0 1,5,22,139,877,6112,41874
|
||||
7k/8/p7/8/8/1P6/8/7K b - - 0 1,4,16,101,637,4354,29679
|
||||
7k/8/8/1p6/P7/8/8/7K b - - 0 1,5,22,139,877,6112,41874
|
||||
7k/8/1p6/8/8/P7/8/7K b - - 0 1,4,16,101,637,4354,29679
|
||||
k7/7p/8/8/8/8/6P1/K7 b - - 0 1,5,25,161,1035,7574,55338
|
||||
k7/6p1/8/8/8/8/7P/K7 b - - 0 1,5,25,161,1035,7574,55338
|
||||
3k4/3pp3/8/8/8/8/3PP3/3K4 b - - 0 1,7,49,378,2902,24122,199002
|
||||
8/Pk6/8/8/8/8/6Kp/8 w - - 0 1,11,97,887,8048,90606,1030499
|
||||
n1n5/1Pk5/8/8/8/8/5Kp1/5N1N w - - 0 1,24,421,7421,124608,2193768,37665329
|
||||
8/PPPk4/8/8/8/8/4Kppp/8 w - - 0 1,18,270,4699,79355,1533145,28859283
|
||||
n1n5/PPPk4/8/8/8/8/4Kppp/5N1N w - - 0 1,24,496,9483,182838,3605103,71179139
|
||||
8/Pk6/8/8/8/8/6Kp/8 b - - 0 1,11,97,887,8048,90606,1030499
|
||||
n1n5/1Pk5/8/8/8/8/5Kp1/5N1N b - - 0 1,24,421,7421,124608,2193768,37665329
|
||||
8/PPPk4/8/8/8/8/4Kppp/8 b - - 0 1,18,270,4699,79355,1533145,28859283
|
||||
n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1,24,496,9483,182838,3605103,71179139
|
||||
|
|
After Width: | Height: | Size: 82 B |
|
After Width: | Height: | Size: 171 B |
|
After Width: | Height: | Size: 176 B |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 472 B |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 340 B |
|
After Width: | Height: | Size: 441 B |
|
After Width: | Height: | Size: 200 B |
|
After Width: | Height: | Size: 180 B |
|
After Width: | Height: | Size: 210 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 195 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 161 B |
|
After Width: | Height: | Size: 162 B |
|
After Width: | Height: | Size: 213 B |
|
After Width: | Height: | Size: 195 B |
|
After Width: | Height: | Size: 167 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 380 B |
|
After Width: | Height: | Size: 239 B |
|
After Width: | Height: | Size: 239 B |
|
After Width: | Height: | Size: 115 B |
|
After Width: | Height: | Size: 208 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 469 B |
|
After Width: | Height: | Size: 116 B |
|
After Width: | Height: | Size: 625 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 190 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 182 B |
|
After Width: | Height: | Size: 82 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 167 B |
|
After Width: | Height: | Size: 889 B |
|
After Width: | Height: | Size: 167 B |
|
After Width: | Height: | Size: 947 B |
|
After Width: | Height: | Size: 184 B |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 173 B |
|
After Width: | Height: | Size: 179 B |
|
After Width: | Height: | Size: 268 B |
|
After Width: | Height: | Size: 148 B |
|
After Width: | Height: | Size: 158 B |
|
After Width: | Height: | Size: 194 B |
|
After Width: | Height: | Size: 283 B |