Charakterbewegungen hinzugefügt, Deko hinzugefügt, Kochrezepte angepasst

This commit is contained in:
N-Nachtigal 2025-05-14 16:36:42 +02:00
parent 95945c0306
commit a0c893ca0b
1124 changed files with 64294 additions and 763 deletions

22
mods/xdecor/.gitignore vendored Normal file
View file

@ -0,0 +1,22 @@
## Files related to minetest development cycle
/*.patch
# GNU Patch reject file
*.rej
## Editors and Development environments
*~
*.swp
*.bak*
*.orig
# Vim
*.vim
# Kate
.*.kate-swp
.swp.*
# Eclipse (LDT)
.project
.settings/
.buildpath
.metadata
# Idea IDE
.idea/*

14
mods/xdecor/.luacheckrc Normal file
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"
}

30
mods/xdecor/API.md Normal file
View file

@ -0,0 +1,30 @@
# 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.

521
mods/xdecor/CHESS_README.md Normal file
View file

@ -0,0 +1,521 @@
# 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 ones king in danger, to leave him in danger
or to capture the opponents 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 dont 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 opponents 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 their own
king under attack is not allowed.
#### How to actually move
Each move can be made by either clicking on the piece and then clicking again
on the destination. The destination is either an empty square or a square
occupied by an opponents 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 its your
opponents 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” them, if you will.
#### King
The king can move exactly one square in any direction: horizontally,
vertically or diagonally. Also, the king can never move to any square that
is attacked by an opponents 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 kings destination as well the square it crosses must not be under attack
- You can castle only horizontally
If all the conditions are met, heres 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 rooks 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 opponents side (i.e. the side on which the opponents
pieces have started).
The pawns basic moves are:
1. Single step: The pawn moves one step vertically towards the
opponents side.
2. Double step: Like a single step, but it moves two squares instead.
This is only possible from the pawns 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 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 opponents side, either left or right.
To illustrate, in the following diagram, the Xes 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, “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 pawns 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 its 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 its a players 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 ones name has been recorded on
the name plaque. Resigning is possible even when its 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.
NOTE: In general, a dead position is any position from which neither player can
give checkmate, no matter how they move, but only those 4 cases above
lead to an instant draw in X-Decor-libre because it is tricky to
determine whether any position is “dead”.
However, dead positions are still guaranteed to end the game eventually
due to the 75-move rule.
#### 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 its 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.
#### 75-move rule
If in the last 75 consecutive moves of each player, no piece was captured
and no pawn was moved, the game automatically ends in a draw.
Exception: If the last move has lead to a checkmate. In this case, checkmate
takes precedence.
#### 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.
Two positions are considered to be the same “same” if a position in which
the chessboard has the same pieces of the same color on the same squares,
it is the same player's turn, the castling rights are the same
and the vulnerability of pawns to en passant captures (if any) is the same.
Pawns are considered “vulnerable” to an en passant capture immediately
after a double step turn, no matter if is actually in danger of
being captured that way.
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 position has 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”.
#### Fivefold repetition rule
If the same position (as defined above) has appeared at for
least 5 times, the game is drawn.
#### 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 cant be dug and the game
cant 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 coordinated 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 Blacks 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, whites move, blacks 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: “a1a2”. 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:
* “00” for castling with the rook on file h (“kingside castling”)
* “000” 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:
* “10” if White won
* “01” if Black won
* “½–½” in case of a draw
#### Example
1. d2d4 e7e6
2. ♔e1d2 ♛d8h4
3. d4d5 e6×d5
...
8. d8×d8♖ ♞b8c6
9. e2e4 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.

63
mods/xdecor/LICENSE Normal file
View file

@ -0,0 +1,63 @@
┌──────────────────────────────────────────────────────────────────────┐
│ Copyright (c) 2015-2021 kilbith <jeanpatrick.guerrero@gmail.com> │
│ │
│ Code: Modified BSD License │
│ Textures: CC0 (credits: Gambit, kilbith, Cisoun) │
│ │
│ 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/ │
└──────────────────────────────────────────────────────────────────────┘
-- 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/

19
mods/xdecor/OLD_README.md Normal file
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)

147
mods/xdecor/README.md Normal file
View file

@ -0,0 +1,147 @@
## 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 new features.
## 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 nodes
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`)
## For developers
X-Decor-libre can be extended in a limited fashion. See `API.md` for details.
### X-Decor-libre vs X-Decor
X-Decor 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. No new features are
planned.
#### List of changes
The following bugs of X-Decor (as of 01/07/2023) are fixed:
* 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"
* Added fuel recipes for wooden-based things
* Changed a few confusing recipes to make more sense
* Improved textures for cut glass, obsidian glass, woodframed 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`
#### 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` file for licensing).
## 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 `OLD_README.md`.

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)

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"),
}

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

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)

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

44
mods/xdecor/init.lua Normal file
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

View file

@ -0,0 +1,217 @@
# textdomain: xdecor
A libre decoration mod meant to be simple and well-featured.=
@1 Stair=
Inner @1 Stair=
Outer @1 Stair=
@1 Slab=
Weak Computer=
Weak Computer 1=
Weak Computer 2=
Chess=
Chess Debug=
Select a game mode=
Select a mode:=
Singleplayer=
Multiplayer=
Bot vs Bot=
check=
checkmate=
resigned=
winner=
loser=
draw=
PROMOTION@nFOR BLACK!=
Promote pawn to:=
PROMOTION@nFOR WHITE!=
DRAW CLAIM@nBY WHITE!=
DRAW CLAIM@nBY BLACK!=
The player has invoked the 50-move rule for the next move. The next move might draw the game.=
The player has invoked the threefold-repetition rule for the next move. The next move might draw the game.=
New game=
Resign=
Select a color:=
White=
Black=
Invoke the 50-move rule for your next move=
Invoke the 50-move rule and draw the game=
Invoke the threefold repetition rule and draw the game=
Invoke the threefold repetition rule for your next move=
You have checkmated @1. You win!=
You were checkmated by @1. You lose!=
The game ended up in a stalemate! It's a draw!=
The game ended up in a dead position! It's a draw!=
No piece was captured and no pawn was moved for 75 consecutive moves of each player. It's a draw!=
You have drawn the game by invoking the 50-move rule.=
@1 has drawn the game by invoking the 50-move rule.=
You have failed to make a game-drawing move. The game continues.=
@1 made a draw claim using the 50-move rule but it was false. The game continues.=
The exact same position has occured 5 times. It's a draw!=
You have drawn the game by invoking the threefold repetition rule.=
@1 has drawn the game by invoking the threefold repetition rule.=
@1 made a draw claim using the threefold repetition rule but it was false. The game continues.=
Chess Board=
Someone else plays white pieces!=
It's not your turn!=
Someone else plays black pieces!=
Black cannot move first!=
@1 s=
@1 min @2 s=
You can't reset the chessboard, a game has been started. Try again in @1.=
Resigning is not possible yet.=
You have resigned.=
@1 has resigned. You win!=
You can't resign, you're not playing in this game.=
You can't claim a draw, it's not your turn!=
You're only a spectator in this game of Chess.=
This isn't the time for promotion.=
It's not your turn! This promotion is meant for the other player.=
You can't dig the chessboard, a game has been started. Reset it first or dig it again in @1.=
You can't dig the chessboard, a game has been started. Try it again in @1.=
Play a game of Chess against another player or the computer=
White Pawn=
Black Pawn=
White Rook=
Black Rook=
White Knight=
Black Knight=
White Bishop=
Black Bishop=
White Queen=
Black Queen=
White King=
Black King=
Light a fire below to heat it up=
Use a bowl to eat the soup=
Drop foods inside to make a soup=
Cauldron (empty)=
Cauldron (cold water)=
Cauldron (cold river water)=
Cauldron (cold soup)=
Cauldron (boiling water)=
Cauldron (boiling river water)=
Cauldron (boiling soup)=
No room in your inventory to add a bucket of water.=
No room in your inventory to add a bowl of soup.=
Cauldron=
For storing water and cooking soup=
Cauldron with Water (cold)=
Cauldron with River Water (cold)=
Cauldron with Soup (cold)=
Cauldron with Water (boiling)=
Cauldron with River Water (boiling)=
Cauldron with Soup (boiling)=
Bowl=
Bowl of soup=
Efficiency=
Durability=
Sharpness=
@1 (+@2%)=
Your weapon inflicts more damage=
Your tool lasts longer=
Your tool digs faster=
Enchantment Table=
Enchant your tools with mese crystals=
Enchanted @1@n@2=
Enchanted @1=
The bees are busy making honey.=
The bees are looking for flowers.=
The bees want to pollinate more flowers.=
The bees are idle.=
The bees are resting.=
Artificial Hive=
Bees live here and produce honey=
Honey=
Made by bees=
@1 (owned by @2)=
Item Frame=
For presenting a single item=
× @1=
Mailbox=
Last donators=
Send your goods to@n@1=
@1's Mailbox=
The mailbox is full.=
Lets other players give you things=
Opens doors when stepped on=
Wooden Pressure Plate=
Stone Pressure Plate=
Lever=
Opens doors when pulled=
Bamboo Frame=
Chainlink=
Rusty Iron Bars=
Wood Frame=
Barricade=
Barrel=
Wooden Cabinet=
24 inventory slots=
Half Wooden Cabinet=
8 inventory slots=
Plain Shelf=
Multi Shelf=
Candle=
Chair=
Cobweb=
Red Curtain=
Cushion=
Cushion Block=
Japanese Door=
Prison Door=
Rusty Prison Door=
Screen Door=
Paper Door=
Woodglass Door=
Ender Chest=
Interdimensional inventory=
Ivy=
Weathercock=
Lantern=
Hanging Lantern=
Steel Lattice Light Box=
Wooden Cross Light Box=
Wooden Rhombus Light Box=
Potted White Dandelion=
Potted Yellow Dandelion=
Potted Geranium=
Potted Rose=
Potted Tulip=
Potted Viola=
Painting=
Garden Stone Path=
Cactus Brick=
Coal Stone Tile=
Polished Desert Stone Block=
Hardened Clay=
Moon Brick=
Runestone=
Polished Stone Block=
Packed Ice=
Wooden Tile=
Table=
Tatami=
Trampoline=
Television=
Wood Framed Glass=
Radio=
Speaker=
Rope=
Nanoslab=
Micropanel=
Microslab=
Thin Stair=
Cube=
Panel=
Slab=
Double Panel=
Half-Stair=
Stair=
Cut=
Repair=
Crafting=
Storage=
Back=
Work Bench=
For cutting blocks, repairing tools with a hammer, crafting and storing items=
@1 @2=
Repairs tools at the work bench=
Hammer=

View file

@ -0,0 +1,217 @@
# textdomain: xdecor
A libre decoration mod meant to be simple and well-featured.=Eine freie simple Dekorationsmod mit netten Features.
@1 Stair=@1treppe
Inner @1 Stair=Innere @1treppe
Outer @1 Stair=Äußere @1treppe
@1 Slab=@1platte
Weak Computer=Schwacher Computer
Weak Computer 1=Schwacher Computer 1
Weak Computer 2=Schwacher Computer 2
Chess=Schach
Chess Debug=Schachdebug
Select a game mode=Wählen Sie einen Spielmodus
Select a mode:=Modus wählen:
Singleplayer=Einzelspieler
Multiplayer=Mehrspieler
Bot vs Bot=Bot vs. Bot
check=Schach
checkmate=Schachmatt
resigned=aufgegeben
winner=Sieger
loser=Verlierer
draw=Remis
PROMOTION@nFOR BLACK!=UMWANDLUNG@nFÜR SCHWARZ!
Promote pawn to:=Bauer umwandeln zu:
PROMOTION@nFOR WHITE!=UMWANDLUNG@nFÜR WEISS!
DRAW CLAIM@nBY WHITE!=REMISANSPRUCH@nVON WEISS!
DRAW CLAIM@nBY BLACK!=REMISANSPRUCH@nVON SCHWARZ!
The player has invoked the 50-move rule for the next move. The next move might draw the game.=Der Spieler will für den nächsten Zug die 50-Züge-Regel anwenden. Der nächste Zug könnte die Partie remis enden lassen.
The player has invoked the threefold-repetition rule for the next move. The next move might draw the game.=Der Spieler will für den nächsten Zug die Regel für wiederholte Stellungen anwenden. Der nächste Zug könnte die Partie remis enden lassen.
New game=Neues Spiel
Resign=Aufgeben
Select a color:=Farbe wählen:
White=Weiß
Black=Schwarz
Invoke the 50-move rule for your next move=50-Züge-Regel für den nächsten Zug anwenden
Invoke the 50-move rule and draw the game=50-Züge-Regel anwenden und die Partie remis enden lassen
Invoke the threefold repetition rule and draw the game=Stellungswiederholungsregel anwenden und die Partie remis enden lassen
Invoke the threefold repetition rule for your next move=Stellungswiederholungsregel für den nächsten Zug anwenden
You have checkmated @1. You win!=Sie haben @1 schachmatt gesetzt. Sieg!
You were checkmated by @1. You lose!=Sie wurden von @1 schachmatt gesetzt. Niederlage!
The game ended up in a stalemate! It's a draw!=Das Partie endete in einem Patt. Das ist ein Remis!
The game ended up in a dead position! It's a draw!=Das Partie endete in einer toten Stellung. Das ist ein Remis!
No piece was captured and no pawn was moved for 75 consecutive moves of each player. It's a draw!=Für 75 Züge in Folge von je beiden Spielern wurde keine Figur geschlagen und kein Bauer gezogen. Es ist ein Remis!
You have drawn the game by invoking the 50-move rule.=Sie haben die Partie remis enden lassen, indem Sie die 50-Züge-Regel angewandt haben.
@1 has drawn the game by invoking the 50-move rule.=@1 hat die Partie mittels der 50-Züge Regel remis enden lassen.
You have failed to make a game-drawing move. The game continues.=Sie haben keinen Zug gemacht, der die Partie remis enden lässt. Die Partie geht weiter.
@1 made a draw claim using the 50-move rule but it was false. The game continues.=@1 hat ein Remis mittels der 50-Züge-Regel beansprucht aber lag falsch. Die Partie geht weiter.
The exact same position has occured 5 times. It's a draw!=Die exakt gleiche Stellung ist 5 mal aufgetreten. Das ist ein Remis!
You have drawn the game by invoking the threefold repetition rule.=Sie haben die Partie remis enden lassen, indem Sie die Stellungswiederholungsregel angewandt haben.
@1 has drawn the game by invoking the threefold repetition rule.=@1 hat die Partie remis mittels der Stellungswiederholungsregel enden lassen.
@1 made a draw claim using the threefold repetition rule but it was false. The game continues.=@1 hat ein Remis mittels der Stellungswiederholungsregel beansprucht aber lag falsch. Die Partie geht weiter.
Chess Board=Schachbrett
Someone else plays white pieces!=Jemand anderes spielt Weiß!
It's not your turn!=Sie sind nicht am Zug!
Someone else plays black pieces!=Jemand anderes spielt Schwarz!
Black cannot move first!=Schwarz darf nicht zuerst ziehen!
@1 s=@1 s
@1 min @2 s=@1 min @2 s
You can't reset the chessboard, a game has been started. Try again in @1.=Sie können das Schachbrett nicht zurücksetzen, es wurde eine Partie gestartet. Versuchen Sie es erneut in @1.
Resigning is not possible yet.=Aufgeben ist noch nicht möglich.
You have resigned.=Sie haben aufgegeben.
@1 has resigned. You win!=@1 hat aufgegeben. Sie haben gewonnen!
You can't resign, you're not playing in this game.=Sie können nicht aufgeben, Sie spielen nicht in dieser Partie.
You can't claim a draw, it's not your turn!=Sie können kein Remis beanspruchen, Sie sind nicht am Zug!
You're only a spectator in this game of Chess.=Sie sind nur Zuschauer bei dieser Schachpartie.
This isn't the time for promotion.=Jetzt ist nicht die Zeit für eine Umwandlung.
It's not your turn! This promotion is meant for the other player.=Sie sind nicht am Zug! Diese Umwandlung ist für den anderen Spieler.
You can't dig the chessboard, a game has been started. Reset it first or dig it again in @1.=Sie können das Schachbrett nicht abbauen, es wurde eine Partie gestartet. Setzen Sie es zuerst zurück oder bauen Sie es in @1 erneut ab.
You can't dig the chessboard, a game has been started. Try it again in @1.=Sie können das Schachbrett nicht abbauen, es wurde eine Partie gestartet. Versuchen Sie es erneut in @1.
Play a game of Chess against another player or the computer=Für Schachspiele gegen einen anderen Spieler oder den Computer
White Pawn=Weißer Bauer
Black Pawn=Schwarzer Bauer
White Rook=Weißer Turm
Black Rook=Schwarzer Turm
White Knight=Weißer Springer
Black Knight=Schwarzer Springer
White Bishop=Weißer Läufer
Black Bishop=Schwarzer Läufer
White Queen=Weiße Dame
Black Queen=Schwarze Dame
White King=Weißer König
Black King=Schwarzer König
Light a fire below to heat it up=Entfachen Sie unten ein Feuer, um ihn zu erhitzen
Use a bowl to eat the soup=Schüssel benutzen, um die Suppe zu essen
Drop foods inside to make a soup=Nahrungsmittel einwerfen, um Suppe zu machen
Cauldron (empty)=Kessel (leer)
Cauldron (cold water)=Kessel (kaltes Wasser)
Cauldron (cold river water)=Kessel (kaltes Flusswasser)
Cauldron (cold soup)=Kessel (kalte Suppe)
Cauldron (boiling water)=Kessel (kochendes Wasser)
Cauldron (boiling river water)=Kessel (kochendes Flusswasser)
Cauldron (boiling soup)=Kessel (kochende Supppe)
No room in your inventory to add a bucket of water.=Zu wenig Platz im Inventar für einen Eimer Wasser.
No room in your inventory to add a bowl of soup.=Zu wenig Platz im Inventar für eine Schüssel voll Suppe.
Cauldron=Kessel
For storing water and cooking soup=Zur Lagerung von Wasser und zum Suppe kochen
Cauldron with Water (cold)=Kessel mit Wasser (kalt)
Cauldron with River Water (cold)=Kessel mit Flusssasser (kalt)
Cauldron with Soup (cold)=Kessel mit Suppe (kalt)
Cauldron with Water (boiling)=Kessel mit Wasser (kochend)
Cauldron with River Water (boiling)=Kessel mit Flusswasser (kochend)
Cauldron with Soup (boiling)=Kessel mit Suppe (kochend)
Bowl=Schüssel
Bowl of soup=Schüssel mit Suppe
Efficiency=Effizienz
Durability=Haltbarkeit
Sharpness=Schärfe
@1 (+@2%)=@1 (+@2%)
Your weapon inflicts more damage=Ihre Waffe richtet mehr Schaden an
Your tool lasts longer=Ihr Werkzeug hält länger
Your tool digs faster=Ihr Werkzeug arbeitet schneller
Enchantment Table=Zaubertisch
Enchant your tools with mese crystals=Werkzeuge mit Mesekristallen verzaubern
Enchanted @1@n@2=@1 (verzaubert)@n@2
Enchanted @1=@1 (verzaubert)
The bees are busy making honey.=Die Bienen sind beschäftigt, Honig herzustellen.
The bees are looking for flowers.=Die Bienen halten nach Blumen Ausschau.
The bees want to pollinate more flowers.=Die Bienen wollen mehr Blumen bestäuben.
The bees are idle.=Die Bienen sind unbeschäftigt.
The bees are resting.=Die Bienen ruhen sich aus.
Artificial Hive=Künstlicher Bienenstock
Bees live here and produce honey=Hier leben Bienen, die Honig produzieren
Honey=Honig
Made by bees=Hergestellt von Bienen
@1 (owned by @2)=@1 (Eigentum von @2)
Item Frame=Gegenstandsrahmen
For presenting a single item=Präsentiert einen einzelnen Gegenstand
× @1=× @1
Mailbox=Briefkasten
Last donators=Letzte Spender
Send your goods to@n@1=Senden Sie Ihre Waren an@n@1
@1's Mailbox=Briefkasten von @1
The mailbox is full.=Der Briefkasten ist voll.
Lets other players give you things=Hiermit kann man von anderen Spielern Dinge erhalten
Opens doors when stepped on=Öffnet Türen beim Betreten
Wooden Pressure Plate=Holzdruckplatte
Stone Pressure Plate=Steindruckplatte
Lever=Schalthebel
Opens doors when pulled=Öffnet Türen beim Betätigen
Bamboo Frame=Bambusgerüst
Chainlink=Maschendraht
Rusty Iron Bars=Rostige Eisenstäbe
Wood Frame=Holzrahmen
Barricade=Barrikade
Barrel=Fass
Wooden Cabinet=Holzschrank
24 inventory slots=24 Inventarplätze
Half Wooden Cabinet=Halber Holzschrank
8 inventory slots=8 Inventarplätze
Plain Shelf=Schlichtes Regal
Multi Shelf=Mehrzweckregal
Candle=Kerze
Chair=Stuhl
Cobweb=Spinnenwebe
Red Curtain=Roter Vorhang
Cushion=Sitzkissen
Cushion Block=Sitzkissenblock
Japanese Door=Japanische Tür
Prison Door=Kerkertür
Rusty Prison Door=Rostige Kerkertür
Screen Door=Fliegengittertür
Paper Door=Papiertür
Woodglass Door=Tür mit Lichtausschnitt
Ender Chest=Endertruhe
Interdimensional inventory=Interdimensionales Inventar
Ivy=Efeu
Weathercock=Wetterhahn
Lantern=Laterne
Hanging Lantern=Hängende Laterne
Steel Lattice Light Box=Stahlgitterlichtbox
Wooden Cross Light Box=Holzkreuzlichtbox
Wooden Rhombus Light Box=Holzrautenlichtbox
Potted White Dandelion=Weißer Löwenzahn im Topf
Potted Yellow Dandelion=Gelber Löwenzahn im Topf
Potted Geranium=Geranie im Topf
Potted Rose=Rose im Topf
Potted Tulip=Tulpe im Topf
Potted Viola=Veilchen im Topf
Painting=Gemälde
Garden Stone Path=Steingartenweg
Cactus Brick=Kaktusziegel
Coal Stone Tile=Kohlesteinkachel
Polished Desert Stone Block=Hochglanzwüstensteinblock
Hardened Clay=Gehärteter Ton
Moon Brick=Mondziegel
Runestone=Runenstein
Polished Stone Block=Hochglanzsteinblock
Packed Ice=Packeis
Wooden Tile=Holzkachel
Table=Tisch
Tatami=Tatamimatte
Trampoline=Trampolin
Television=Fernseher
Wood Framed Glass=Holzrahmenglas
Radio=Radio
Speaker=Lautsprecher
Rope=Seil
Nanoslab=nanoplatte
Micropanel=mikropaneel
Microslab=mikroplatte
Thin Stair=flachtreppe
Cube=würfel
Panel=paneel
Slab=platte
Double Panel=doppelpaneel
Half-Stair=halbtreppe
Stair=Treppe
Cut=Zuschnitt
Repair=Reparatur
Crafting=Fertigung
Storage=Lager
Back=Zurück
Work Bench=Werkbank
For cutting blocks, repairing tools with a hammer, crafting and storing items=Für Blockzuschnitt, Werkzeugreparatur mit Hammer, Fertigung und Lagerung
@1 @2=@1@2
Repairs tools at the work bench=Repariert Werkzeuge an der Werkbank
Hammer=Hammer

View file

@ -0,0 +1,236 @@
# textdomain: xdecor
A libre decoration mod meant to be simple and well-featured.=
@1 Stair=
Inner @1 Stair=
Outer @1 Stair=
@1 Slab=
Weak Computer=
Weak Computer 1=
Weak Computer 2=
Chess=Echecs
Chess Debug=
Select a game mode=
Select a mode:=Sélectionnez un mode de jeu:
Singleplayer=Solo
Multiplayer=Multijoueur
Bot vs Bot=
check=échec
checkmate=
resigned=
winner=
loser=
draw=
PROMOTION@nFOR BLACK!=
Promote pawn to:=
PROMOTION@nFOR WHITE!=
DRAW CLAIM@nBY WHITE!=
DRAW CLAIM@nBY BLACK!=
The player has invoked the 50-move rule for the next move. The next move might draw the game.=
The player has invoked the threefold-repetition rule for the next move. The next move might draw the game.=
New game=Nouvelle partie
Resign=
Select a color:=
White=
Black=
Invoke the 50-move rule for your next move=
Invoke the 50-move rule and draw the game=
Invoke the threefold repetition rule and draw the game=
Invoke the threefold repetition rule for your next move=
You have checkmated @1. You win!=
You were checkmated by @1. You lose!=
The game ended up in a stalemate! It's a draw!=
The game ended up in a dead position! It's a draw!=
No piece was captured and no pawn was moved for 75 consecutive moves of each player. It's a draw!=
You have drawn the game by invoking the 50-move rule.=
@1 has drawn the game by invoking the 50-move rule.=
You have failed to make a game-drawing move. The game continues.=
@1 made a draw claim using the 50-move rule but it was false. The game continues.=
The exact same position has occured 5 times. It's a draw!=
You have drawn the game by invoking the threefold repetition rule.=
@1 has drawn the game by invoking the threefold repetition rule.=
@1 made a draw claim using the threefold repetition rule but it was false. The game continues.=
Chess Board=Echiquier
Someone else plays white pieces!=Quelquun dautre joue les pièces blanches !
It's not your turn!=
Someone else plays black pieces!=Quelquun dautre joue les pièces noires !
Black cannot move first!=
@1 s=
@1 min @2 s=
You can't reset the chessboard, a game has been started. Try again in @1.=
Resigning is not possible yet.=
You have resigned.=
@1 has resigned. You win!=
You can't resign, you're not playing in this game.=
You can't claim a draw, it's not your turn!=
You're only a spectator in this game of Chess.=
This isn't the time for promotion.=
It's not your turn! This promotion is meant for the other player.=
You can't dig the chessboard, a game has been started. Reset it first or dig it again in @1.=
You can't dig the chessboard, a game has been started. Try it again in @1.=
Play a game of Chess against another player or the computer=
White Pawn=Pion blanc
Black Pawn=Pion noir
White Rook=Tour blanche
Black Rook=Tour noire
White Knight=Cavalier blanc
Black Knight=Cavalier noir
White Bishop=Fou blanc
Black Bishop=Fou noir
White Queen=Reine blanche
Black Queen=Reine noire
White King=Roi blanc
Black King=Roi noir
Light a fire below to heat it up=
Use a bowl to eat the soup=Utilisez un bol pour boire la soupe
Drop foods inside to make a soup=Placez des ingrédients à lintérieur pour faire une soupe
Cauldron (empty)=Chaudron (vide)
Cauldron (cold water)=
Cauldron (cold river water)=
Cauldron (cold soup)=
Cauldron (boiling water)=
Cauldron (boiling river water)=
Cauldron (boiling soup)=
No room in your inventory to add a bucket of water.=Pas de place dans votre inventaire pour ajouter un seau deau.
No room in your inventory to add a bowl of soup.=Pas de place dans votre inventaire pour ajouter un bol de soupe.
Cauldron=Chaudron
For storing water and cooking soup=
Cauldron with Water (cold)=
Cauldron with River Water (cold)=
Cauldron with Soup (cold)=
Cauldron with Water (boiling)=
Cauldron with River Water (boiling)=
Cauldron with Soup (boiling)=
Bowl=Bol
Bowl of soup=Bol de soupe
Efficiency=Efficacité
Durability=Durabilité
Sharpness=Tranchant
@1 (+@2%)=
Your weapon inflicts more damage=Votre arme inflige plus de dégâts
Your tool lasts longer=Votre outil dure plus longtemps
Your tool digs faster=Votre outil creuse plus vite
Enchantment Table=Table denchantements
Enchant your tools with mese crystals=
Enchanted @1@n@2=
Enchanted @1=
The bees are busy making honey.=
The bees are looking for flowers.=
The bees want to pollinate more flowers.=
The bees are idle.=
The bees are resting.=
Artificial Hive=Ruche artificielle
Bees live here and produce honey=
Honey=Miel
Made by bees=
@1 (owned by @2)=@1 (propriété de @2)
Item Frame=Cadre
For presenting a single item=
× @1=
Mailbox=Boite aux lettres
Last donators=Derniers donateurs
Send your goods to@n@1=Envoyer vos biens à@n@1
@1's Mailbox=Boite aux lettres de @1
The mailbox is full.=La boite aux lettres est pleine.
Lets other players give you things=
Opens doors when stepped on=
Wooden Pressure Plate=Plaque de pression en bois
Stone Pressure Plate=Plaque de pression en pierre
Lever=Levier
Opens doors when pulled=
Bamboo Frame=Cadre en bambou
Chainlink=Maillon de chaîne
Rusty Iron Bars=Barreaux en fer rouillé
Wood Frame=Cadre en bois
Barricade=Barricade
Barrel=Tonneau
Wooden Cabinet=Meuble en bois
24 inventory slots=
Half Wooden Cabinet=Demi meuble en bois
8 inventory slots=
Plain Shelf=
Multi Shelf=Étagères multiple
Candle=Bougie
Chair=Chaise
Cobweb=Toile daraignée
Red Curtain=Rideaux rouge
Cushion=Coussin
Cushion Block=Bloc de coussin
Japanese Door=Porte japonaise
Prison Door=Porte de prison
Rusty Prison Door=Barreaux de prison rouillés
Screen Door=Porte avec moustiquaire
Paper Door=
Woodglass Door=Porte vitrée
Ender Chest=Coffre de lEnd
Interdimensional inventory=
Ivy=Lierre
Weathercock=
Lantern=Lanterne
Hanging Lantern=
Steel Lattice Light Box=
Wooden Cross Light Box=
Wooden Rhombus Light Box=
Potted White Dandelion=Pissenlit blanc en pot
Potted Yellow Dandelion=Pissenlit jaune en pot
Potted Geranium=Géranium en pot
Potted Rose=Rose en pot
Potted Tulip=Tulipe en pot
Potted Viola=Violette en pot
Painting=Tableau
Garden Stone Path=Chemin de pierres de jardin
Cactus Brick=Brique en cactus
Coal Stone Tile=Carreau en charbon et pierre
Polished Desert Stone Block=
Hardened Clay=Argile durcie
Moon Brick=Brique lunaire
Runestone=Pierre runique
Polished Stone Block=
Packed Ice=Glace compactée
Wooden Tile=Carreau en bois
Table=Table
Tatami=Tatami
Trampoline=Trampoline
Television=Télévision
Wood Framed Glass=Verre encadré par du bois
Radio=
Speaker=
Rope=Corde
Nanoslab=
Micropanel=
Microslab=
Thin Stair=
Cube=
Panel=
Slab=
Double Panel=
Half-Stair=
Stair=
Cut=Couper
Repair=Réparer
Crafting=Fabrication
Storage=Stockage
Back=Retour
Work Bench=Atelier
For cutting blocks, repairing tools with a hammer, crafting and storing items=
@1 @2=
Repairs tools at the work bench=
Hammer=Marteau
##### not used anymore #####
Enchanted @1 @2@n@3=@2 en @1 enchantée@n@3
Enchanted @1 @2=@2 en @1 enchantée
You can't reset the chessboard, a game has been started. If you aren't a current player, try again in @1=Vous ne pouvez pas mettre à zéro léchiquier, une partie a été commencée. Si ce nest pas votre tour de jouer, réessayez dans @1
You can't dig the chessboard, a game has been started. Reset it first if you're a current player, or dig it again in @1=Vous ne pouvez pas récupérer léchiquier, une partie à été commencée. Remettez le à zéro si vous cest votre tour de jouer, ou réessayez dans @1
Cauldron (idle)=Chaudron (inactif)
Cauldron (active) - Drop foods inside to make a soup=Chaudron (actif) - Placez des ingrédients à lintérieur pour faire une soupe
Cauldron (active) - Use a bowl to eat the soup=Chaudron (actif) - Utilisez un bol pour boire la soupe
Cauldron (active)=Chaudron (actif)
Bees are busy making honey…=Les abeilles sont occupées à fabriquer du miel…
Desert Stone Tile=Carreau en pierre du désert
Empty Shelf=Étagère vide
Iron Light Box=Boite lumineuse en fer
Slide Door=Porte coulissante
Stone Tile=Carreau en pierre
Wooden Light Box=Boite lumineuse en bois

View file

@ -0,0 +1,236 @@
# textdomain: xdecor
# author: Zughy (chess)
# Nota per chi traduce: ho usato italiano inclusivo (ə per il singolare) e usato il participio presente per giocatore e spettatore (=giocante e osservante) come esperimento di inclusività. Per gli apostrofi ho usato ` (un - un' -> un`)
A libre decoration mod meant to be simple and well-featured.=
@1 Stair=
Inner @1 Stair=
Outer @1 Stair=
@1 Slab=
Weak Computer=Computer facile
Weak Computer 1=Computer facile 1
Weak Computer 2=Computer facile 2
Chess=Scacchi
Chess Debug=Debug scacchi
Select a game mode=Seleziona modalità di gioco
Select a mode:=Seleziona una modalità:
Singleplayer=Giocante singolə
Multiplayer=Multigiocante
Bot vs Bot=Bot contro Bot
check=scacco
checkmate=scacco matto
resigned=arresə
winner=vince
loser=perde
draw=patta
PROMOTION@nFOR BLACK!=PROMOZIONE@nPER IL NERO!
Promote pawn to:=Promuovi pedone a:
PROMOTION@nFOR WHITE!=PROMOZIONE@nPER IL BIANCO!
DRAW CLAIM@nBY WHITE!=PATTA DICHIARATA@nDAL BIANCO!
DRAW CLAIM@nBY BLACK!=PATTA DICHIARATA@nDAL NERO!
The player has invoked the 50-move rule for the next move. The next move might draw the game.=Lə giocante ha invocato la regola delle 50 mosse per la prossima mossa. Quest'ultima potrebbe portare a una patta.
The player has invoked the threefold-repetition rule for the next move. The next move might draw the game.=Lə giocante ha invocato la regola della tripla ripetizione per la prossima mossa. Quest'ultima potrebbe portare a una patta
New game=Nuova partita
Resign=Arrenditi
Select a color:=Seleziona un colore:
White=Bianco
Black=Nero
Invoke the 50-move rule for your next move=Invoca la regola delle 50 mosse per la tua prossima mossa
Invoke the 50-move rule and draw the game=Invoca la regola delle 50 mosse e chiudi in patta
Invoke the threefold repetition rule and draw the game=Invoca la regola della tripla ripetizione e chiudi in patta
Invoke the threefold repetition rule for your next move=Invoca la regola della tripla ripetizione per la tua prossima mossa
You have checkmated @1. You win!=Scacco matto a @1. Hai vinto!
You were checkmated by @1. You lose!=Scacco matto da @1. Hai perso!
The game ended up in a stalemate! It's a draw!=È uno stallo! Patta!
The game ended up in a dead position! It's a draw!=È una posizione morta! Patta!
No piece was captured and no pawn was moved for 75 consecutive moves of each player. It's a draw!=Nessun giocante ha catturato pezzi né mosso pedoni per 75 mosse consecutive. Patta!
You have drawn the game by invoking the 50-move rule.=Hai chiuso in patta con l'invocazione della regola delle 50 mosse.
@1 has drawn the game by invoking the 50-move rule.=@1 ha chiuso in patta con l'invocazione della regola delle 50 mosse.
You have failed to make a game-drawing move. The game continues.=Non sei riuscitə a fare una mossa che portasse a una patta. Il gioco continua.
@1 made a draw claim using the 50-move rule but it was false. The game continues.=@1 ha tentato la patta con la regola delle 50 mosse ma ha fallito. La partita continua.
The exact same position has occured 5 times. It's a draw!=La stessa identica mossa si è ripetuta per 5 volte. Patta!
You have drawn the game by invoking the threefold repetition rule.=Hai chiuso in patta con l'invocazione della regola della tripla ripetizione.
@1 has drawn the game by invoking the threefold repetition rule.=@1 ha chiuso in patta con l'invocazione della regola della tripla ripetizione.
@1 made a draw claim using the threefold repetition rule but it was false. The game continues.=@1 ha tentato la patta con la regola della tripla ripetizione ma ha fallito. La partita continua.
Chess Board=Scacchiera
Someone else plays white pieces!=Qualcun`altrə sta già giocando con il bianco!
It's not your turn!=Non è il tuo turno!
Someone else plays black pieces!=Qualcun`altrə sta già giocando con il nero!
Black cannot move first!=Il nero non può muovere per primo!
@1 s=@1 s
@1 min @2 s=@1 min @2 s
You can't reset the chessboard, a game has been started. Try again in @1.=Non puoi ripristinare la scacchiera, c'è una partita in corso. Riprova tra @1.
Resigning is not possible yet.=Non ci si può ancora arrendere.
You have resigned.=Ti sei arresə.
@1 has resigned. You win!=@1 si è arresə. Hai vinto!
You can't resign, you're not playing in this game.=Non ti puoi arrendere, non stai giocando in questa partita.
You can't claim a draw, it's not your turn!=Non puoi chiamare patta, non è il tuo turno!
You're only a spectator in this game of Chess.=Sei solo un`osservante in questa partita di scacchi.
This isn't the time for promotion.=Non è il momento per una promozione.
It's not your turn! This promotion is meant for the other player.=Non è il tuo turno! Questa promozione spetta all'altrə giocante.
You can't dig the chessboard, a game has been started. Reset it first or dig it again in @1.=Non puoi rimuovere la scacchiera, c'è una partita in corso. Ripristinala prima, o riprova a rimuoverla in @1.
You can't dig the chessboard, a game has been started. Try it again in @1.=Non puoi rimuovere la scacchiera, c'è una partita in corso. Riprova tra @1.
Play a game of Chess against another player or the computer=Gioca una partita a scacchi contro un`altrə giocante o contro il computer
White Pawn=Pedone bianco
Black Pawn=Pedone nero
White Rook=Torre bianca
Black Rook=Torre nera
White Knight=Cavallo bianco
Black Knight=Cavallo nero
White Bishop=Alfiere bianco
Black Bishop=Alfiere nero
White Queen=Regina bianca
Black Queen=Regina nera
White King=Re bianco
Black King=Re nero
Light a fire below to heat it up=
Use a bowl to eat the soup=Utilizzare una ciotola per mangiare la zuppa
Drop foods inside to make a soup=Mettere gli ingredienti all'interno per fare una zuppa
Cauldron (empty)=Calderone (vuoto)
Cauldron (cold water)=
Cauldron (cold river water)=
Cauldron (cold soup)=
Cauldron (boiling water)=
Cauldron (boiling river water)=
Cauldron (boiling soup)=
No room in your inventory to add a bucket of water.=Non c'è spazio nell'inventario per aggiungere un secchio di acqua.
No room in your inventory to add a bowl of soup.=Non c'è spazio nell'inventario per aggiungere una ciotola di zuppa.
Cauldron=Calderone
For storing water and cooking soup=
Cauldron with Water (cold)=
Cauldron with River Water (cold)=
Cauldron with Soup (cold)=
Cauldron with Water (boiling)=
Cauldron with River Water (boiling)=
Cauldron with Soup (boiling)=
Bowl=Ciotola
Bowl of soup=Ciotola di zuppa
Efficiency=Efficacia
Durability=Durabilità
Sharpness=Affilatezza
@1 (+@2%)=
Your weapon inflicts more damage=La tua arma infligge più danno
Your tool lasts longer=Il tuo utensile dura di più
Your tool digs faster=Il tuo utensile scava più rapidamente
Enchantment Table=Tavolo per migliorie
Enchant your tools with mese crystals=
Enchanted @1@n@2=
Enchanted @1=
The bees are busy making honey.=
The bees are looking for flowers.=
The bees want to pollinate more flowers.=
The bees are idle.=
The bees are resting.=
Artificial Hive=Favo artificiale
Bees live here and produce honey=
Honey=Miele
Made by bees=
@1 (owned by @2)=@1 (proprietà di @2)
Item Frame=Teca
For presenting a single item=
× @1=
Mailbox=Cassetta delle lettere
Last donators=Ultimi donatori
Send your goods to@n@1=Invia i tuoi item a@n@1
@1's Mailbox=Cassetta delle lettere di @1
The mailbox is full.=La cassetta delle lettere è piena
Lets other players give you things=
Opens doors when stepped on=
Wooden Pressure Plate=Placca di pressione di legno
Stone Pressure Plate=Placca di pressione di pietra
Lever=Leva
Opens doors when pulled=
Bamboo Frame=Cornice di bambù
Chainlink=Cotta di maglia
Rusty Iron Bars=Sbarre di prigione arrugginite
Wood Frame=Cornice in legno
Barricade=Barricata
Barrel=Barile
Wooden Cabinet=Stipo di legno
24 inventory slots=
Half Wooden Cabinet=Stipo di legno a metà
8 inventory slots=
Plain Shelf=
Multi Shelf=Mensole
Candle=Candela
Chair=Sedia
Cobweb=Ragnatela
Red Curtain=Tenda rossa
Cushion=Cuscino
Cushion Block=Blocco di cuscini
Japanese Door=Porta giapponese
Prison Door=Porta di prigione
Rusty Prison Door=Porta di prigione arrugginita
Screen Door=Porta a schermo
Paper Door=
Woodglass Door=Porta di vetro
Ender Chest=Baule ender
Interdimensional inventory=
Ivy=Edera
Weathercock=
Lantern=Lanterna
Hanging Lantern=
Steel Lattice Light Box=
Wooden Cross Light Box=
Wooden Rhombus Light Box=
Potted White Dandelion=Soffione bianco in vaso
Potted Yellow Dandelion=Soffione giallo in vaso
Potted Geranium=Geranio in vaso
Potted Rose=Rosa in vaso
Potted Tulip=Tulipano in vaso
Potted Viola=Violetta in vaso
Painting=Dipinto
Garden Stone Path=Sentiero da giardino in pietra
Cactus Brick=Mattone di cactus
Coal Stone Tile=Mattonella di pietra di carbone
Polished Desert Stone Block=
Hardened Clay=Argilla indurita
Moon Brick=Mattone lunare
Runestone=Pietra runica
Polished Stone Block=
Packed Ice=Ghiaccio compatto
Wooden Tile=Mattonella di legno
Table=Tavolo
Tatami=Tatami
Trampoline=Trampolino
Television=Televisione
Wood Framed Glass=Cornice in legno con vetro
Radio=
Speaker=
Rope=Corda
Nanoslab=
Micropanel=
Microslab=
Thin Stair=
Cube=
Panel=
Slab=
Double Panel=
Half-Stair=
Stair=
Cut=Tagliare
Repair=Riparare
Crafting=Fabbricare
Storage=Conservare
Back=Indietro
Work Bench=Banco da lavoro
For cutting blocks, repairing tools with a hammer, crafting and storing items=
@1 @2=
Repairs tools at the work bench=
Hammer=Martello
##### not used anymore #####
Enchanted @1 @2@n@3=@2 su @1 incantesimo@n@3
Enchanted @1 @2=@2 su @1 incantesimo
Cauldron (idle)=Calderone (inattivo)
Cauldron (active) - Drop foods inside to make a soup=Calderone (attivo) - Mettere gli ingredienti all'interno per fare una zuppa.
Cauldron (active) - Use a bowl to eat the soup=Calderone (actif) - Utilizzare una ciotola per mangiare la zuppa
Cauldron (active)=Calderone (attivo)
Bees are busy making honey…=Le api sono occupate a fare il miele…
Desert Stone Tile=Mattonella di pietra del deserto
Empty Shelf=Mensola vuota
Iron Light Box=Scatola luminosa di ferro
Slide Door=Porta scorrevole
Stone Tile=Mattonella di pietra
Wooden Light Box=Mattonella luminosa di legno

8
mods/xdecor/mod.conf Normal file
View file

@ -0,0 +1,8 @@
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 = player_api, fire, moreblocks, mesecons, unified_inventory, tt, toolranks
min_minetest_version = 5.9
release = 29354
author = Wuzzy

BIN
mods/xdecor/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

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.

3385
mods/xdecor/src/chess.lua Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,200 @@
local chessbot = {}
local realchess = xdecor.chess
-- 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
local function best_move(moves)
local value, choices = 0, {}
for from, _ in pairs(moves) do
for to, val in pairs(_) do
if val > value then
value = val
choices = {{
from = from,
to = to
}}
elseif val == value then
choices[#choices + 1] = {
from = from,
to = to
}
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
function chessbot.choose_move(board_t, meta_t)
local lastMove = meta_t["lastMove"]
local gameResult = meta_t["gameResult"]
local botColor = meta_t["botColor"]
local prevDoublePawnStepTo = meta_t["prevDoublePawnStepTo"]
local castlingRights = {
castlingWhiteR = meta_t["castlingWhiteR"],
castlingWhiteL = meta_t["castlingWhiteL"],
castlingBlackR = meta_t["castlingBlackR"],
castlingBlackL = meta_t["castlingBlackL"],
}
if botColor == "" then
return
end
local currentBotColor, opponentColor
if botColor == "black" then
currentBotColor = "black"
opponentColor = "white"
elseif botColor == "white" then
currentBotColor = "white"
opponentColor = "black"
elseif botColor == "both" then
opponentColor = lastMove
if lastMove == "black" or lastMove == "" then
currentBotColor = "white"
else
currentBotColor = "black"
end
end
if (lastMove == opponentColor or ((botColor == "white" or botColor == "both") and lastMove == "")) and gameResult == "" then
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
end
local choice_from, choice_to = best_move(safe_moves)
if choice_from == nil then
-- No best move: stalemate or checkmate
return
end
return choice_from, choice_to
else
minetest.log("error", "[xdecor] Chess: chessbot.choose_move was apparently called in an invalid game state!")
return
end
end
chessbot.perform_move = function(choice_from, choice_to, meta)
local lastMove = meta:get_string("lastMove")
local botColor = meta:get_string("botColor")
local currentBotColor, opponentColor
local botName
if botColor == "black" then
currentBotColor = "black"
opponentColor = "white"
elseif botColor == "white" then
currentBotColor = "white"
opponentColor = "black"
elseif botColor == "both" then
opponentColor = lastMove
if lastMove == "black" or lastMove == "" then
currentBotColor = "white"
else
currentBotColor = "black"
end
end
-- Bot resigns if no move chosen
if not choice_from or not choice_to then
realchess.resign(meta, currentBotColor)
return
end
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(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(meta, currentBotColor)
end
else
minetest.log("error", "[xdecor] Chess: chessbot.perform_move: No last move!")
end
end
function chessbot.choose_promote(board_t, pawnIndex)
-- Bot always promotes to queen
return "queen"
end
function chessbot.perform_promote(meta, promoteTo)
minetest.after(BOT_DELAY_PROMOTE, function()
local lastMove = meta:get_string("lastMove")
local color
if lastMove == "black" or lastMove == "" then
color = "white"
else
color = "black"
end
realchess.promote_pawn(meta, color, promoteTo)
end)
end
function chessbot.move(inv, meta)
local board_t = realchess.board_to_table(inv)
local meta_t = {
lastMove = meta:get_string("lastMove"),
gameResult = meta:get_string("gameResult"),
botColor = meta:get_string("botColor"),
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"),
}
local choice_from, choice_to = chessbot.choose_move(board_t, meta_t)
minetest.after(BOT_DELAY_MOVE, function()
chessbot.perform_move(choice_from, choice_to, meta)
end)
end
function chessbot.promote(inv, meta, pawnIndex)
local board_t = realchess.board_to_table(inv)
local promoteTo = chessbot.choose_promote(board_t, pawnIndex)
if not promoteTo then
promoteTo = "queen"
end
chessbot.perform_promote(meta, promoteTo)
end
return chessbot

517
mods/xdecor/src/cooking.lua Normal file
View file

@ -0,0 +1,517 @@
local cauldron, sounds = {}, {}
local S = minetest.get_translator("xdecor")
-- Set to true to print soup ingredients and fire nodes 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.
-- The cauldron will check if any of these strings are contained in the itemname
-- after the ":".
local ingredients_list = {
"apple", "mushroom", "honey", "pumpkin", "egg", "bread", "meat",
"chicken", "carrot", "potato", "melon", "rhubarb", "cucumber",
"corn", "beans", "berries", "grapes", "tomato", "wheat"
}
-- List of items that can never be soup ingredients. Overwrites anything else.
local non_ingredients = {
-- xdecor
"xdecor:bowl_soup",
-- 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 fire
local function is_fire(itemstring)
return minetest.get_item_group(itemstring, "fire") ~= 0
end
-- Returns true if the node at pos is above fire
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 fire group
if is_fire(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
minetest.chat_send_player(clicker:get_player_name(),
S("No room in your inventory to add a bucket of water."))
return itemstack
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)
if non_ingredients_keyed[itemstring] then
return false
end
local basename = itemstring:match(":([%w_]+)")
if not basename then
return false
end
for _, ingredient in ipairs(ingredients_list) do
if eatable(itemstring) or 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 item_name == "xdecor:bowl" or item_name == "farming:bowl" then
if wield_item:get_count() > 1 then
if inv:room_for_item("main", "xdecor:bowl_soup 1") then
itemstack:take_item()
inv:add_item("main", "xdecor:bowl_soup 1")
else
minetest.chat_send_player(clicker:get_player_name(),
S("No room in your inventory to add a bowl of soup."))
return itemstack
end
else
itemstack:replace("xdecor:bowl_soup 1")
end
minetest.set_node(pos, {name = "xdecor:cauldron_empty", param2 = node.param2})
end
return itemstack
end
xdecor.register("cauldron_empty", {
description = S("Cauldron"),
_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", {
description = S("Bowl"),
inventory_image = "xdecor_bowl.png",
wield_image = "xdecor_bowl.png",
groups = {food_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 = {},
stack_max = 1,
on_use = minetest.item_eat(30, "xdecor:bowl")
})
-- 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 and fire nodes
-- in console
minetest.register_on_mods_loaded(function()
local ingredients = {}
local fires = {}
for k,v in pairs(minetest.registered_items) do
if is_ingredient(k) then
table.insert(ingredients, k)
end
if is_fire(k) then
table.insert(fires, k)
end
end
table.sort(ingredients)
table.sort(fires)
local str_i = table.concat(ingredients, ", ")
local str_f = table.concat(fires, ", ")
print("[xdecor] List of ingredients for soup: "..str_i)
print("[xdecor] List of nodes that can heat cauldron: "..str_f)
end)
end

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

View file

@ -0,0 +1,506 @@
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 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"),
_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,
},
})
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
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)
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
elseif enchant == "sharp" then
fleshy = original_damage_groups.fleshy
fleshy = fleshy + bonus_defs.damages
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

204
mods/xdecor/src/hive.lua Normal file
View file

@ -0,0 +1,204 @@
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", {
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,
},
})
-- 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", {
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

View file

@ -0,0 +1,249 @@
-- 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 {}
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
--~ Item frame infotext. @1 = item frame name, @2 = owner name (player)
meta:set_string("infotext", S("@1 (owned by @2)",
S("Item Frame"), owner))
else
meta:set_string("infotext", S("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 = S("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 and 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 = S("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,
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()
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
end,
get_staticdata = function(self)
if self.nodename and self.texture then
return self.nodename .. ";" .. self.texture
end
return ""
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,
})

211
mods/xdecor/src/mailbox.lua Normal file
View file

@ -0,0 +1,211 @@
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").."]"
.."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;]" ..
"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)
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"}
}
})

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"}
}
})

962
mods/xdecor/src/nodes.lua Normal file
View file

@ -0,0 +1,962 @@
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"), {
_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,
_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 function door_access(name)
return name:find("prison")
end
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,
},
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,
},
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 = door_access(name),
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"),
_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
local xdecor_potted = {
dandelion_white = S("Potted White Dandelion"),
dandelion_yellow = S("Potted Yellow Dandelion"),
geranium = S("Potted Geranium"),
rose = S("Potted Rose"),
tulip = S("Potted Tulip"),
viola = S("Potted Viola"),
}
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},
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)
})
minetest.register_craft({
output = "xdecor:potted_" .. f,
recipe = {
{"default:clay_brick", "flowers:" .. f, "default:clay_brick"},
{"", "default:clay_brick", ""}
}
})
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()
})
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", {
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
mods/xdecor/src/recipes.lua Normal file
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,
})

132
mods/xdecor/src/rope.lua Normal file
View file

@ -0,0 +1,132 @@
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}
local digger_inv = digger:get_inventory()
while minetest.get_node(below).name == rope_name do
minetest.remove_node(below)
below.y = below.y - 1
num = num + 1
end
if num == 0 then return end
-- Play dig sound manually
minetest.sound_play(ropesounds.dug, {pos=pos}, true)
-- 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
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"}
}
})

View file

@ -0,0 +1,602 @@
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 DEFAULT_HAMMER_REPAIR = 500
local DEFAULT_HAMMER_REPAIR_COST = 700
-- Nodeboxes definitions
workbench.defs = {
-- Name Yield Nodeboxes (X Y Z W H L) Description
{"nanoslab", 16, {{ 0, 0, 0, 8, 1, 8 }}, S("Nanoslab")},
{"micropanel", 16, {{ 0, 0, 0, 16, 1, 8 }}, S("Micropanel")},
{"microslab", 8, {{ 0, 0, 0, 16, 1, 16 }}, S("Microslab")},
{"thinstair", 8, {{ 0, 7, 0, 16, 1, 8 },
{ 0, 15, 8, 16, 1, 8 }}, S("Thin Stair")},
{"cube", 4, {{ 0, 0, 0, 8, 8, 8 }}, S("Cube")},
{"panel", 4, {{ 0, 0, 0, 16, 8, 8 }}, S("Panel")},
{"slab", 2, nil, S("Slab") },
{"doublepanel", 2, {{ 0, 0, 0, 16, 8, 8 },
{ 0, 8, 8, 16, 8, 8 }}, S("Double Panel")},
{"halfstair", 2, {{ 0, 0, 0, 8, 8, 16 },
{ 0, 8, 8, 8, 8, 8 }}, S("Half-Stair")},
{"stair_outer", 1, nil, nil},
{"stair", 1, nil, S("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
item = nbox[3] and item or "stairs:" .. cuttype .. "_" .. name:match(":(.*)")
if 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 = ""..
--~ Verb shown in workbench form where you can cut a node
"label[0.9,1.23;"..FS("Cut").."]"
--~ Verb shown in workbench form 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 workbench form
.."button[0,0;2,1;craft;"..FS("Crafting").."]"
--~ Button in workbench form
.."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[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 (to_list == "tool" and from_list == "hammer") 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
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"),
_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)
local mod_name, item_name = node:match("^(.-):(.*)")
local def = minetest.registered_nodes[node]
if item_name and workbench_def[3] then
local groups = {}
local tiles
groups.not_in_creative_inventory = 1
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
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, S("@1 Stair", def.description),
def.sounds)
stairs.register_stair_inner(item_name, node,
groups, custom_tiles.stair_inner, "", def.sounds, nil, S("Inner @1 Stair", def.description))
stairs.register_stair_outer(item_name, node,
groups, custom_tiles.stair_outer, "", def.sounds, nil, S("Outer @1 Stair", def.description))
end
if custom_tiles.slab then
stairs.register_slab(item_name, node,
groups, custom_tiles.slab, S("@1 Slab", def.description),
def.sounds)
end
else
stairs.register_stair_and_slab(item_name, node,
groups, 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
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
minetest.register_node(":" .. cutnodename, {
--~ Format of the description of a cut node. @1: Base node description (e.g. "Stone"); @2: modifier (e.g. "Nanoslab")
description = S("@1 @2", def.description, workbench_def[4]),
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
sounds = def.sounds,
tiles = tiles_special_cut,
use_texture_alpha = def.use_texture_alpha,
groups = groups,
is_ground_content = def.is_ground_content,
node_box = xdecor.pixelbox(16, workbench_def[3]),
sunlight_propagates = true,
on_place = minetest.rotate_node
})
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, cutlist)
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)
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"}
}
})
-- Register default cuttable blocks
do
local cuttable_nodes = {}
-- 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
for i = 1, #cuttable_nodes do
local node = cuttable_nodes[i]
workbench:register_cut(node)
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" })
--[[ 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,
_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
--[[ EXPERIMENTAL FUNCTION:
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. 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.
Returns true if all nodes were registered successfully,
returns false (and writes to error log) if any error occurred.
]]
xdecor.register_cut = function(nodename)
return workbench:register_cut(nodename)
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", ""}
}
})

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 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Some files were not shown because too many files have changed in this diff Show more