more mods

This commit is contained in:
tangent committed 2026-09-12 07:03:41 -06:00
1 parent cc0fa41e3b
commit bac6ac94a3
1742 files changed
+115814 -1

No files matched your search

+14
View File
@@ -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"
}
+154
View File
@@ -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)
+550
View File
@@ -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.
+70
View File
@@ -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/
+93
View File
@@ -0,0 +1,93 @@
## X-Decor-libre [`xdecor`] ##
[![ContentDB](https://content.luanti.org/packages/Wuzzy/xdecor/shields/downloads/)](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`.
+45
View File
@@ -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.
+6
View File
@@ -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
+154
View File
@@ -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)
+170
View File
@@ -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"),
}
+68
View File
@@ -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
+67
View File
@@ -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)
+183
View File
@@ -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
+19
View File
@@ -0,0 +1,19 @@
Note: This is the original readme of the xdecor mod, added here for documentation/historic reasons.
## X-Decor ##
[![ContentDB](https://content.minetest.net/packages/jp/xdecor/shields/downloads/)](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.
![Preview](http://i.imgur.com/AVoyCQy.png)
+49
View File
@@ -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).
+44
View File
@@ -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
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+6
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

+31
View File
@@ -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
Binary file not shown.
Binary file not shown.
Binary file not shown.
+785
View File
@@ -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")
File diff suppressed because it is too large. Load diff
+154
View File
@@ -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
+54
View File
@@ -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,
})
+128
View File
@@ -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,
})
+590
View File
@@ -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
+124
View File
@@ -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
+115
View File
@@ -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
+520
View File
@@ -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
+207
View File
@@ -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
+266
View File
@@ -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,
})
+214
View File
@@ -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"}
}
})
+325
View File
@@ -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"}
}
})
+977
View File
@@ -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
+405
View File
@@ -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,
})
+152
View File
@@ -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"}
}
})
+764
View File
@@ -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" })
+126
View File
@@ -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
1 rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1,20,400,8902,197281,4865609,119060324
2 r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1,48,2039,97862,4085603,193690690
3 4k3/8/8/8/8/8/8/4K2R w K - 0 1,15,66,1197,7059,133987,764643
4 4k3/8/8/8/8/8/8/R3K3 w Q - 0 1,16,71,1287,7626,145232,846648
5 4k2r/8/8/8/8/8/8/4K3 w k - 0 1,5,75,459,8290,47635,899442
6 r3k3/8/8/8/8/8/8/4K3 w q - 0 1,5,80,493,8897,52710,1001523
7 4k3/8/8/8/8/8/8/R3K2R w KQ - 0 1,26,112,3189,17945,532933,2788982
8 r3k2r/8/8/8/8/8/8/4K3 w kq - 0 1,5,130,782,22180,118882,3517770
9 8/8/8/8/8/8/6k1/4K2R w K - 0 1,12,38,564,2219,37735,185867
10 8/8/8/8/8/8/1k6/R3K3 w Q - 0 1,15,65,1018,4573,80619,413018
11 4k2r/6K1/8/8/8/8/8/8 w k - 0 1,3,32,134,2073,10485,179869
12 r3k3/1K6/8/8/8/8/8/8 w q - 0 1,4,49,243,3991,20780,367724
13 r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1,26,568,13744,314346,7594526,179862938
14 r3k2r/8/8/8/8/8/8/1R2K2R w Kkq - 0 1,25,567,14095,328965,8153719,195629489
15 r3k2r/8/8/8/8/8/8/2R1K2R w Kkq - 0 1,25,548,13502,312835,7736373,184411439
16 r3k2r/8/8/8/8/8/8/R3K1R1 w Qkq - 0 1,25,547,13579,316214,7878456,189224276
17 1r2k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1,26,583,14252,334705,8198901,198328929
18 2r1k2r/8/8/8/8/8/8/R3K2R w KQk - 0 1,25,560,13592,317324,7710115,185959088
19 r3k1r1/8/8/8/8/8/8/R3K2R w KQq - 0 1,25,560,13607,320792,7848606,190755813
20 4k3/8/8/8/8/8/8/4K2R b K - 0 1,5,75,459,8290,47635,899442
21 4k3/8/8/8/8/8/8/R3K3 b Q - 0 1,5,80,493,8897,52710,1001523
22 4k2r/8/8/8/8/8/8/4K3 b k - 0 1,15,66,1197,7059,133987,764643
23 r3k3/8/8/8/8/8/8/4K3 b q - 0 1,16,71,1287,7626,145232,846648
24 4k3/8/8/8/8/8/8/R3K2R b KQ - 0 1,5,130,782,22180,118882,3517770
25 r3k2r/8/8/8/8/8/8/4K3 b kq - 0 1,26,112,3189,17945,532933,2788982
26 8/8/8/8/8/8/6k1/4K2R b K - 0 1,3,32,134,2073,10485,179869
27 8/8/8/8/8/8/1k6/R3K3 b Q - 0 1,4,49,243,3991,20780,367724
28 4k2r/6K1/8/8/8/8/8/8 b k - 0 1,12,38,564,2219,37735,185867
29 r3k3/1K6/8/8/8/8/8/8 b q - 0 1,15,65,1018,4573,80619,413018
30 r3k2r/8/8/8/8/8/8/R3K2R b KQkq - 0 1,26,568,13744,314346,7594526,179862938
31 r3k2r/8/8/8/8/8/8/1R2K2R b Kkq - 0 1,26,583,14252,334705,8198901,198328929
32 r3k2r/8/8/8/8/8/8/2R1K2R b Kkq - 0 1,25,560,13592,317324,7710115,185959088
33 r3k2r/8/8/8/8/8/8/R3K1R1 b Qkq - 0 1,25,560,13607,320792,7848606,190755813
34 1r2k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1,25,567,14095,328965,8153719,195629489
35 2r1k2r/8/8/8/8/8/8/R3K2R b KQk - 0 1,25,548,13502,312835,7736373,184411439
36 r3k1r1/8/8/8/8/8/8/R3K2R b KQq - 0 1,25,547,13579,316214,7878456,189224276
37 8/1n4N1/2k5/8/8/5K2/1N4n1/8 w - - 0 1,14,195,2760,38675,570726,8107539
38 8/1k6/8/5N2/8/4n3/8/2K5 w - - 0 1,11,156,1636,20534,223507,2594412
39 8/8/4k3/3Nn3/3nN3/4K3/8/8 w - - 0 1,19,289,4442,73584,1198299,19870403
40 K7/8/2n5/1n6/8/8/8/k6N w - - 0 1,3,51,345,5301,38348,588695
41 k7/8/2N5/1N6/8/8/8/K6n w - - 0 1,17,54,835,5910,92250,688780
42 8/1n4N1/2k5/8/8/5K2/1N4n1/8 b - - 0 1,15,193,2816,40039,582642,8503277
43 8/1k6/8/5N2/8/4n3/8/2K5 b - - 0 1,16,180,2290,24640,288141,3147566
44 8/8/3K4/3Nn3/3nN3/4k3/8/8 b - - 0 1,4,68,1118,16199,281190,4405103
45 K7/8/2n5/1n6/8/8/8/k6N b - - 0 1,17,54,835,5910,92250,688780
46 k7/8/2N5/1N6/8/8/8/K6n b - - 0 1,3,51,345,5301,38348,588695
47 B6b/8/8/8/2K5/4k3/8/b6B w - - 0 1,17,278,4607,76778,1320507,22823890
48 8/8/1B6/7b/7k/8/2B1b3/7K w - - 0 1,21,316,5744,93338,1713368,28861171
49 k7/B7/1B6/1B6/8/8/8/K6b w - - 0 1,21,144,3242,32955,787524,7881673
50 K7/b7/1b6/1b6/8/8/8/k6B w - - 0 1,7,143,1416,31787,310862,7382896
51 B6b/8/8/8/2K5/5k2/8/b6B b - - 0 1,6,106,1829,31151,530585,9250746
52 8/8/1B6/7b/7k/8/2B1b3/7K b - - 0 1,17,309,5133,93603,1591064,29027891
53 k7/B7/1B6/1B6/8/8/8/K6b b - - 0 1,7,143,1416,31787,310862,7382896
54 K7/b7/1b6/1b6/8/8/8/k6B b - - 0 1,21,144,3242,32955,787524,7881673
55 7k/RR6/8/8/8/8/rr6/7K w - - 0 1,19,275,5300,104342,2161211,44956585
56 R6r/8/8/2K5/5k2/8/8/r6R w - - 0 1,36,1027,29215,771461,20506480,525169084
57 7k/RR6/8/8/8/8/rr6/7K b - - 0 1,19,275,5300,104342,2161211,44956585
58 R6r/8/8/2K5/5k2/8/8/r6R b - - 0 1,36,1027,29227,771368,20521342,524966748
59 6kq/8/8/8/8/8/8/7K w - - 0 1,2,36,143,3637,14893,391507
60 6KQ/8/8/8/8/8/8/7k b - - 0 1,2,36,143,3637,14893,391507
61 K7/8/8/3Q4/4q3/8/8/7k w - - 0 1,6,35,495,8349,166741,3370175
62 6qk/8/8/8/8/8/8/7K b - - 0 1,22,43,1015,4167,105749,419369
63 6KQ/8/8/8/8/8/8/7k b - - 0 1,2,36,143,3637,14893,391507
64 K7/8/8/3Q4/4q3/8/8/7k b - - 0 1,6,35,495,8349,166741,3370175
65 8/8/8/8/8/K7/P7/k7 w - - 0 1,3,7,43,199,1347,6249
66 8/8/8/8/8/7K/7P/7k w - - 0 1,3,7,43,199,1347,6249
67 K7/p7/k7/8/8/8/8/8 w - - 0 1,1,3,12,80,342,2343
68 7K/7p/7k/8/8/8/8/8 w - - 0 1,1,3,12,80,342,2343
69 8/2k1p3/3pP3/3P2K1/8/8/8/8 w - - 0 1,7,35,210,1091,7028,34834
70 8/8/8/8/8/K7/P7/k7 b - - 0 1,1,3,12,80,342,2343
71 8/8/8/8/8/7K/7P/7k b - - 0 1,1,3,12,80,342,2343
72 K7/p7/k7/8/8/8/8/8 b - - 0 1,3,7,43,199,1347,6249
73 7K/7p/7k/8/8/8/8/8 b - - 0 1,3,7,43,199,1347,6249
74 8/2k1p3/3pP3/3P2K1/8/8/8/8 b - - 0 1,5,35,182,1091,5408,34822
75 8/8/8/8/8/4k3/4P3/4K3 w - - 0 1,2,8,44,282,1814,11848
76 4k3/4p3/4K3/8/8/8/8/8 b - - 0 1,2,8,44,282,1814,11848
77 8/8/7k/7p/7P/7K/8/8 w - - 0 1,3,9,57,360,1969,10724
78 8/8/k7/p7/P7/K7/8/8 w - - 0 1,3,9,57,360,1969,10724
79 8/8/3k4/3p4/3P4/3K4/8/8 w - - 0 1,5,25,180,1294,8296,53138
80 8/3k4/3p4/8/3P4/3K4/8/8 w - - 0 1,8,61,483,3213,23599,157093
81 8/8/3k4/3p4/8/3P4/3K4/8 w - - 0 1,8,61,411,3213,21637,158065
82 k7/8/3p4/8/3P4/8/8/7K w - - 0 1,4,15,90,534,3450,20960
83 8/8/7k/7p/7P/7K/8/8 b - - 0 1,3,9,57,360,1969,10724
84 8/8/k7/p7/P7/K7/8/8 b - - 0 1,3,9,57,360,1969,10724
85 8/8/3k4/3p4/3P4/3K4/8/8 b - - 0 1,5,25,180,1294,8296,53138
86 8/3k4/3p4/8/3P4/3K4/8/8 b - - 0 1,8,61,411,3213,21637,158065
87 8/8/3k4/3p4/8/3P4/3K4/8 b - - 0 1,8,61,483,3213,23599,157093
88 k7/8/3p4/8/3P4/8/8/7K b - - 0 1,4,15,89,537,3309,21104
89 7k/3p4/8/8/3P4/8/8/K7 w - - 0 1,4,19,117,720,4661,32191
90 7k/8/8/3p4/8/8/3P4/K7 w - - 0 1,5,19,116,716,4786,30980
91 k7/8/8/7p/6P1/8/8/K7 w - - 0 1,5,22,139,877,6112,41874
92 k7/8/7p/8/8/6P1/8/K7 w - - 0 1,4,16,101,637,4354,29679
93 k7/8/8/6p1/7P/8/8/K7 w - - 0 1,5,22,139,877,6112,41874
94 k7/8/6p1/8/8/7P/8/K7 w - - 0 1,4,16,101,637,4354,29679
95 k7/8/8/3p4/4p3/8/8/7K w - - 0 1,3,15,84,573,3013,22886
96 k7/8/3p4/8/8/4P3/8/7K w - - 0 1,4,16,101,637,4271,28662
97 7k/3p4/8/8/3P4/8/8/K7 b - - 0 1,5,19,117,720,5014,32167
98 7k/8/8/3p4/8/8/3P4/K7 b - - 0 1,4,19,117,712,4658,30749
99 k7/8/8/7p/6P1/8/8/K7 b - - 0 1,5,22,139,877,6112,41874
100 k7/8/7p/8/8/6P1/8/K7 b - - 0 1,4,16,101,637,4354,29679
101 k7/8/8/6p1/7P/8/8/K7 b - - 0 1,5,22,139,877,6112,41874
102 k7/8/6p1/8/8/7P/8/K7 b - - 0 1,4,16,101,637,4354,29679
103 k7/8/8/3p4/4p3/8/8/7K b - - 0 1,5,15,102,569,4337,22579
104 k7/8/3p4/8/8/4P3/8/7K b - - 0 1,4,16,101,637,4271,28662
105 7k/8/8/p7/1P6/8/8/7K w - - 0 1,5,22,139,877,6112,41874
106 7k/8/p7/8/8/1P6/8/7K w - - 0 1,4,16,101,637,4354,29679
107 7k/8/8/1p6/P7/8/8/7K w - - 0 1,5,22,139,877,6112,41874
108 7k/8/1p6/8/8/P7/8/7K w - - 0 1,4,16,101,637,4354,29679
109 k7/7p/8/8/8/8/6P1/K7 w - - 0 1,5,25,161,1035,7574,55338
110 k7/6p1/8/8/8/8/7P/K7 w - - 0 1,5,25,161,1035,7574,55338
111 3k4/3pp3/8/8/8/8/3PP3/3K4 w - - 0 1,7,49,378,2902,24122,199002
112 7k/8/8/p7/1P6/8/8/7K b - - 0 1,5,22,139,877,6112,41874
113 7k/8/p7/8/8/1P6/8/7K b - - 0 1,4,16,101,637,4354,29679
114 7k/8/8/1p6/P7/8/8/7K b - - 0 1,5,22,139,877,6112,41874
115 7k/8/1p6/8/8/P7/8/7K b - - 0 1,4,16,101,637,4354,29679
116 k7/7p/8/8/8/8/6P1/K7 b - - 0 1,5,25,161,1035,7574,55338
117 k7/6p1/8/8/8/8/7P/K7 b - - 0 1,5,25,161,1035,7574,55338
118 3k4/3pp3/8/8/8/8/3PP3/3K4 b - - 0 1,7,49,378,2902,24122,199002
119 8/Pk6/8/8/8/8/6Kp/8 w - - 0 1,11,97,887,8048,90606,1030499
120 n1n5/1Pk5/8/8/8/8/5Kp1/5N1N w - - 0 1,24,421,7421,124608,2193768,37665329
121 8/PPPk4/8/8/8/8/4Kppp/8 w - - 0 1,18,270,4699,79355,1533145,28859283
122 n1n5/PPPk4/8/8/8/8/4Kppp/5N1N w - - 0 1,24,496,9483,182838,3605103,71179139
123 8/Pk6/8/8/8/8/6Kp/8 b - - 0 1,11,97,887,8048,90606,1030499
124 n1n5/1Pk5/8/8/8/8/5Kp1/5N1N b - - 0 1,24,421,7421,124608,2193768,37665329
125 8/PPPk4/8/8/8/8/4Kppp/8 b - - 0 1,18,270,4699,79355,1533145,28859283
126 n1n5/PPPk4/8/8/8/8/4Kppp/5N1N b - - 0 1,24,496,9483,182838,3605103,71179139
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Loaded 100 of 1742 files, more files were not shown because too many files have changed in this diff. Show more