Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1923b6acde | ||
|
d6942aa43b | ||
|
31f37b0665 | ||
|
80c611d640 | ||
|
720dda3178 | ||
|
fc81d35ccd | ||
|
ae27cb8f4f | ||
|
78c81f5da6 | ||
|
e66f056590 | ||
|
26a7dbb78f | ||
|
34d2c56f6d | ||
|
7ac1756f09 | ||
|
2c426f53f6 | ||
|
5b1335dbea | ||
|
5e479b9ac5 | ||
|
4832eb0470 | ||
|
2a2cfa4113 | ||
|
c81ed3725d | ||
|
7a746e5660 | ||
|
f1cf8c3fb0 | ||
|
32188e3eba | ||
|
e513314ae8 | ||
|
92f31e08cc | ||
|
8dfd5aade1 | ||
|
a62ef6c183 | ||
|
2b91f0d013 | ||
|
254ae5a4d5 | ||
|
ed5b525dd8 | ||
|
06c2c9881a | ||
|
617ab9b30e | ||
|
4ce65f20e2 | ||
|
20e6a3b379 | ||
|
5502e5f052 | ||
|
1f33988ac5 | ||
|
06e42034f3 | ||
|
2d5e149ba9 | ||
|
0e3d242ec6 | ||
|
0ef5c3eee7 | ||
|
08991e877c | ||
|
1156940f99 | ||
|
ec7410b4e7 | ||
|
6ba25ff14c | ||
|
98cd448992 | ||
|
2248c5aba6 | ||
|
46ccd93fc9 | ||
|
454aa6ccc9 | ||
|
89c0c1f5a1 | ||
|
1ad835bd53 | ||
|
2e745748eb | ||
|
dde5e29a45 | ||
|
7a297813ca | ||
|
06e4527ad9 | ||
|
0ecd7a357d | ||
|
2b41a41052 | ||
|
e39cfeae84 | ||
|
ba85e514bf | ||
|
a84365d3e9 | ||
|
b8222cdebe | ||
|
b981444b7c | ||
|
569e4bfa78 | ||
|
3a896b4a90 | ||
|
992616a3f0 | ||
|
d88cfbf9d8 | ||
|
e4d2154bac | ||
|
8610d1e88f | ||
|
4a10d2f68c | ||
|
bac5e5ebf1 | ||
|
7b391dd8f3 | ||
|
d7fab3bd96 | ||
|
b05c312968 | ||
|
0d8afaff76 |
32
API.md
Normal file
@ -0,0 +1,32 @@
|
||||
# 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.
|
||||
|
||||
## EXPERIMENTAL: 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.
|
||||
|
||||
THIS FEATURE IS EXPERIMENTAL!
|
||||
|
||||
See `src/workbench.lua` for details.
|
@ -29,9 +29,9 @@ 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)
|
||||
* 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.
|
||||
|
||||
@ -64,8 +64,7 @@ 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. This section
|
||||
may change
|
||||
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.
|
||||
@ -121,7 +120,7 @@ a capturing move are identical. Only for the pawn it is different
|
||||
|
||||
If the square of the king is attacked, he and the player playing him
|
||||
is considered to be in “check”.
|
||||
If a player is in check, any move which would put or leave the own
|
||||
While a player is in check, any move which would their own
|
||||
king under attack is not allowed.
|
||||
|
||||
#### How to actually move
|
||||
@ -149,9 +148,6 @@ 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 can move on a square occupied by an opponent, which
|
||||
w
|
||||
|
||||
The rook may be involved in Castling, see “King” below.
|
||||
|
||||
#### Bishop
|
||||
@ -218,7 +214,7 @@ If all the conditions are met, here’s how you castle:
|
||||
Place the king two squares towards the rook you want to castle with.
|
||||
This square is where the king will end up. The rook will then
|
||||
automatically move towards the king and “jump” to the square
|
||||
behind the king, from the rooks viewpoint.
|
||||
behind the king, from the rook’s viewpoint.
|
||||
|
||||
**Remember**: You *must* move the king (not the rook) if you want
|
||||
to castle. If you move the rook instead, this is considered
|
||||
@ -234,10 +230,12 @@ pieces have started).
|
||||
The pawn’s basic moves are:
|
||||
|
||||
1. Single step: The pawn moves one step vertically towards the
|
||||
opponent’s side. It is not possible to walk backwards.
|
||||
opponent’s side.
|
||||
2. Double step: Like a single step, but it moves two squares instead.
|
||||
This is only possible from the pawn’s start position.
|
||||
|
||||
A pawn can never move backwards.
|
||||
|
||||
In both cases, the destination square must be empty as well as any crossed square.
|
||||
The pawn cannot capture by a single or double step, however.
|
||||
|
||||
@ -289,7 +287,7 @@ 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)
|
||||
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
|
||||
@ -300,7 +298,7 @@ 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 replaced, which
|
||||
Once a piece was selected, the pawn will be replaced, which
|
||||
immediately activates its powers. This ends the move.
|
||||
|
||||
### The end of the game
|
||||
@ -438,8 +436,10 @@ dig the chessboard.
|
||||
|
||||
### The Chess Notation
|
||||
|
||||
The list of moves is in a special notation called “algebraic notation”. There are many
|
||||
variants of it, so this section explains what it means in X-Decor-libre.
|
||||
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
|
||||
@ -493,7 +493,7 @@ When a player castles, it is notated the following way:
|
||||
|
||||
#### Game completion
|
||||
|
||||
If the game completed, the end of the game showing the result is listed in a final separate line as:
|
||||
If the game came to an end, the game result is written in a final separate line as:
|
||||
|
||||
* “1–0” if White won
|
||||
* “0–1” if Black won
|
||||
@ -501,11 +501,11 @@ If the game completed, the end of the game showing the result is listed in a fin
|
||||
|
||||
#### Example
|
||||
|
||||
1. d2—d4 e7—e6
|
||||
1. d2–d4 e7–e6
|
||||
2. ♔e1–d2 ♛d8–h4
|
||||
3. d4–d5 e6×d5
|
||||
...
|
||||
8. d8×d8♖ ♞b8-c6
|
||||
8. d8×d8♖ ♞b8–c6
|
||||
9. e2–e4 d4×e3 e.p.
|
||||
|
||||
Explanation of the moves:
|
||||
@ -513,7 +513,7 @@ 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.: Eight fullmove: White moves pawn from d7 to d8, captures a piece and promotes it to rook, Black moves knight from b8 to c6
|
||||
* 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
|
||||
|
22
README.md
@ -1,14 +1,23 @@
|
||||
## X-Decor-libre [`xdecor`] ##
|
||||
|
||||
[![ContentDB](https://content.minetest.net/packages/Wuzzy/xdecor/shields/downloads/)](https://content.minetest.net/packages/Wuzzy/xdecor/)
|
||||
[![ContentDB](https://content.luanti.org/packages/Wuzzy/xdecor/shields/downloads/)](https://content.luanti.org/packages/Wuzzy/xdecor/)
|
||||
|
||||
X-Decor-libre is a libre Minetest mod which adds various decorative blocks
|
||||
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 Minetest.
|
||||
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
|
||||
@ -34,11 +43,13 @@ blocks with special features:
|
||||
* Pressure Plate: Step on it to activate doors next to it
|
||||
* Chessboard: Play Chess against a player or the computer (see `CHESS_README.md`)
|
||||
|
||||
The radio and speaker are purely decorative and have no special functionality.
|
||||
## 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 Minetest but it is (as the time of writing this text)
|
||||
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
|
||||
@ -100,7 +111,6 @@ Maintenance updates:
|
||||
* Storage blocks now drop their inventory when exploded
|
||||
* Made several strings translatable
|
||||
* Translation updates
|
||||
* Add support for playerphysics mod
|
||||
* Add description to every setting
|
||||
* Add tooltip extensions for some interactive items (uses `tt` mod)
|
||||
* Add crafting guide support for `unified_inventory` mod (honey)
|
||||
|
@ -1,50 +1,76 @@
|
||||
local mod_playerphysics = minetest.get_modpath("playerphysics") ~= nil
|
||||
local mod_player_api = minetest.get_modpath("player_api") ~= nil
|
||||
|
||||
local function top_face(pointed_thing)
|
||||
if not pointed_thing then return end
|
||||
return pointed_thing.above.y > pointed_thing.under.y
|
||||
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
|
||||
if not top_face(pointed_thing) then return end
|
||||
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()
|
||||
|
||||
for _, obj in pairs(objs) do
|
||||
if obj:is_player() and obj:get_player_name() ~= player_name then
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Stand up if sitting
|
||||
if sitting[player_name] then
|
||||
stand_up(player_name)
|
||||
|
||||
if player_api.player_attached[player_name] then
|
||||
clicker:set_pos(pos)
|
||||
clicker:set_eye_offset(vector.new(), vector.new())
|
||||
if mod_playerphysics then
|
||||
playerphysics.remove_physics_factor(clicker, "speed", "xdecor:sit_speed")
|
||||
playerphysics.remove_physics_factor(clicker, "jump", "xdecor:sit_jump")
|
||||
else
|
||||
clicker:set_physics_override({speed = 1, jump = 1})
|
||||
end
|
||||
player_api.player_attached[player_name] = false
|
||||
player_api.set_animation(clicker, "stand", 30)
|
||||
|
||||
elseif not player_api.player_attached[player_name] and node.param2 <= 3 and
|
||||
-- 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
|
||||
|
||||
clicker:set_eye_offset({x = 0, y = -7, z = 2}, vector.new())
|
||||
if mod_playerphysics then
|
||||
playerphysics.add_physics_factor(clicker, "speed", "xdecor:sit_speed", 0)
|
||||
playerphysics.add_physics_factor(clicker, "jump", "xdecor:sit_jump", 0)
|
||||
else
|
||||
clicker:set_physics_override({speed = 0, jump = 0})
|
||||
-- Can't sit down on note already occupied by player
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if seats_occupied[hash] then
|
||||
return
|
||||
end
|
||||
clicker:set_pos(pos)
|
||||
|
||||
player_api.player_attached[player_name] = true
|
||||
player_api.set_animation(clicker, "sit", 30)
|
||||
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)
|
||||
@ -55,19 +81,74 @@ function xdecor.sit(pos, node, clicker, pointed_thing)
|
||||
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
|
||||
for _, player in pairs(minetest.get_objects_inside_radius(pos, 0.1)) do
|
||||
if player:is_player() and
|
||||
player_api.player_attached[player:get_player_name()] then
|
||||
return false
|
||||
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)
|
||||
|
16
init.lua
@ -12,6 +12,7 @@ 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",
|
||||
@ -21,14 +22,23 @@ local subpart = {
|
||||
"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)
|
||||
if enable or enable == nil then
|
||||
local enable = minetest.settings:get_bool("enable_xdecor_" .. name, true)
|
||||
if enable then
|
||||
dofile(modpath .. "/src/" .. name .. ".lua")
|
||||
end
|
||||
end
|
||||
|
||||
--print(string.format("[xdecor] loaded in %.2f ms", (os.clock()-t)*1000))
|
||||
-- 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
|
||||
|
@ -1,15 +1,41 @@
|
||||
# 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!=
|
||||
@ -54,27 +80,6 @@ White Queen=
|
||||
Black Queen=
|
||||
White King=
|
||||
Black King=
|
||||
Select a game mode=
|
||||
Select a mode:=
|
||||
Singleplayer=
|
||||
Multiplayer=
|
||||
Bot vs Bot=
|
||||
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=
|
||||
Light a fire below to heat it up=
|
||||
Use a bowl to eat the soup=
|
||||
Drop foods inside to make a soup=
|
||||
@ -100,40 +105,33 @@ 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 @2@n@3=
|
||||
Enchanted @1 @2=
|
||||
Steel=
|
||||
Bronze=
|
||||
Mese=
|
||||
Diamond=
|
||||
Axe=
|
||||
Pickaxe=
|
||||
Shovel=
|
||||
Sword=
|
||||
Your weapon inflicts more damages=
|
||||
Your tool last longer=
|
||||
Your tool digs faster=
|
||||
Artificial Hive=
|
||||
Bees live here and produce honey=
|
||||
Honey=
|
||||
Made by bees=
|
||||
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's Mailbox=
|
||||
The mailbox is full.=
|
||||
Mailbox=
|
||||
Lets other players give you things=
|
||||
× @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=
|
||||
@ -196,10 +194,6 @@ Television=
|
||||
Wood Framed Glass=
|
||||
Radio=
|
||||
Speaker=
|
||||
@1 Stair=
|
||||
Inner @1 Stair=
|
||||
Outer @1 Stair=
|
||||
@1 Slab=
|
||||
Rope=
|
||||
Nanoslab=
|
||||
Micropanel=
|
||||
@ -211,13 +205,13 @@ Slab=
|
||||
Double Panel=
|
||||
Half-Stair=
|
||||
Stair=
|
||||
Work Bench=
|
||||
For cutting blocks, repairing tools with a hammer, crafting and storing items=
|
||||
@1 @2=
|
||||
Hammer=
|
||||
Repairs tools at the work bench=
|
||||
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=
|
||||
|
@ -1,15 +1,41 @@
|
||||
# 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!
|
||||
@ -54,27 +80,6 @@ White Queen=Weiße Dame
|
||||
Black Queen=Schwarze Dame
|
||||
White King=Weißer König
|
||||
Black King=Schwarzer König
|
||||
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
|
||||
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
|
||||
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
|
||||
@ -100,40 +105,33 @@ 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 @2@n@3=@1@2 (verzaubert)@n@3
|
||||
Enchanted @1 @2=@1@2 (verzaubert)
|
||||
Steel=Eisen
|
||||
Bronze=Bronze
|
||||
Mese=Mese
|
||||
Diamond=Diamant
|
||||
Axe=axt
|
||||
Pickaxe=spitzhacke
|
||||
Shovel=schaufel
|
||||
Sword=schwert
|
||||
Your weapon inflicts more damages=Ihre Waffe richtet mehr Schaden an
|
||||
Your tool last longer=Ihr Werkzeug hält länger
|
||||
Your tool digs faster=Ihr Werkzeug arbeitet schneller
|
||||
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
|
||||
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's Mailbox=Briefkasten von @1
|
||||
The mailbox is full.=Der Briefkasten ist voll.
|
||||
Mailbox=Briefkasten
|
||||
Lets other players give you things=Hiermit kann man von anderen Spielern Dinge erhalten
|
||||
× @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
|
||||
@ -196,10 +194,6 @@ Television=Fernseher
|
||||
Wood Framed Glass=Holzrahmenglas
|
||||
Radio=Radio
|
||||
Speaker=Lautsprecher
|
||||
@1 Stair=@1treppe
|
||||
Inner @1 Stair=Innere @1treppe
|
||||
Outer @1 Stair=Äußere @1treppe
|
||||
@1 Slab=@1platte
|
||||
Rope=Seil
|
||||
Nanoslab=nanoplatte
|
||||
Micropanel=mikropaneel
|
||||
@ -211,13 +205,13 @@ Slab=platte
|
||||
Double Panel=doppelpaneel
|
||||
Half-Stair=halbtreppe
|
||||
Stair=Treppe
|
||||
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
|
||||
Hammer=Hammer
|
||||
Repairs tools at the work bench=Repariert Werkzeuge an der Werkbank
|
||||
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
|
||||
|
@ -1,15 +1,41 @@
|
||||
# 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!=
|
||||
@ -54,27 +80,6 @@ White Queen=Reine blanche
|
||||
Black Queen=Reine noire
|
||||
White King=Roi blanc
|
||||
Black King=Roi noir
|
||||
Select a game mode=
|
||||
Select a mode:=Sélectionnez un mode de jeu:
|
||||
Singleplayer=Solo
|
||||
Multiplayer=Multijoueur
|
||||
Bot vs Bot=
|
||||
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=
|
||||
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 à l’intérieur pour faire une soupe
|
||||
@ -100,40 +105,33 @@ 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 d’enchantements
|
||||
Enchant your tools with mese crystals=
|
||||
Enchanted @1 @2@n@3=@2 en @1 enchantée@n@3
|
||||
Enchanted @1 @2=@2 en @1 enchantée
|
||||
Steel=Fer
|
||||
Bronze=Bronze
|
||||
Mese=Mese
|
||||
Diamond=Diamant
|
||||
Axe=Hache
|
||||
Pickaxe=Pioche
|
||||
Shovel=Pelle
|
||||
Sword=Épée
|
||||
Your weapon inflicts more damages=Votre arme inflige plus de dégâts
|
||||
Your tool last longer=Votre outil dure plus longtemps
|
||||
Your tool digs faster=Votre outil creuse plus vite
|
||||
Artificial Hive=Ruche artificielle
|
||||
Bees live here and produce honey=
|
||||
Honey=Miel
|
||||
Made by bees=
|
||||
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's Mailbox=Boite aux lettres de @1
|
||||
The mailbox is full.=La boite aux lettres est pleine.
|
||||
Mailbox=Boite aux lettres
|
||||
Lets other players give you things=
|
||||
× @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
|
||||
@ -196,10 +194,6 @@ Television=Télévision
|
||||
Wood Framed Glass=Verre encadré par du bois
|
||||
Radio=
|
||||
Speaker=
|
||||
@1 Stair=
|
||||
Inner @1 Stair=
|
||||
Outer @1 Stair=
|
||||
@1 Slab=
|
||||
Rope=Corde
|
||||
Nanoslab=
|
||||
Micropanel=
|
||||
@ -211,21 +205,22 @@ Slab=
|
||||
Double Panel=
|
||||
Half-Stair=
|
||||
Stair=
|
||||
Work Bench=Atelier
|
||||
For cutting blocks, repairing tools with a hammer, crafting and storing items=
|
||||
@1 @2=
|
||||
Hammer=Marteau
|
||||
Repairs tools at the work bench=
|
||||
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 #####
|
||||
|
||||
Dumb AI=IA stupide
|
||||
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 n’est 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 c’est votre tour de jouer, ou réessayez dans @1
|
||||
Cauldron (idle)=Chaudron (inactif)
|
||||
|
@ -1,47 +1,75 @@
|
||||
# textdomain: xdecor
|
||||
Weak Computer=
|
||||
Weak Computer 1=
|
||||
Weak Computer 2=
|
||||
# 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=
|
||||
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=
|
||||
resigned=
|
||||
winner=
|
||||
loser=
|
||||
draw=
|
||||
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.=
|
||||
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 altro gioca con il bianco!
|
||||
It's not your turn!=
|
||||
Someone else plays black pieces!=Qualcun altro gioca con il nero!
|
||||
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=
|
||||
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
|
||||
@ -54,27 +82,6 @@ White Queen=Regina bianca
|
||||
Black Queen=Regina nera
|
||||
White King=Re bianco
|
||||
Black King=Re nero
|
||||
Select a game mode=
|
||||
Select a mode:=Selezionare una modalità
|
||||
Singleplayer=Singolo giocatore
|
||||
Multiplayer=Multigiocatore
|
||||
Bot vs Bot=
|
||||
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=Nuova partita
|
||||
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=
|
||||
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
|
||||
@ -100,40 +107,33 @@ 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 @2@n@3=@2 su @1 incantesimo@n@3
|
||||
Enchanted @1 @2=@2 su @1 incantesimo
|
||||
Steel=Acciaio
|
||||
Bronze=Bronzo
|
||||
Mese=Mese
|
||||
Diamond=Diamante
|
||||
Axe=Ascia
|
||||
Pickaxe=Piccone
|
||||
Shovel=Pala
|
||||
Sword=Spada
|
||||
Your weapon inflicts more damages=La tua arma infligge più danno
|
||||
Your tool last longer=Il tuo utensile dura di più
|
||||
Your tool digs faster=Il tuo utensile scava più rapidamente
|
||||
Artificial Hive=Favo artificiale
|
||||
Bees live here and produce honey=
|
||||
Honey=Miele
|
||||
Made by bees=
|
||||
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's Mailbox=Cassetta delle lettere di @1
|
||||
The mailbox is full.=La cassetta delle lettere è piena
|
||||
Mailbox=Cassetta delle lettere
|
||||
Lets other players give you things=
|
||||
× @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
|
||||
@ -196,10 +196,6 @@ Television=Televisione
|
||||
Wood Framed Glass=Cornice in legno con vetro
|
||||
Radio=
|
||||
Speaker=
|
||||
@1 Stair=
|
||||
Inner @1 Stair=
|
||||
Outer @1 Stair=
|
||||
@1 Slab=
|
||||
Rope=Corda
|
||||
Nanoslab=
|
||||
Micropanel=
|
||||
@ -211,23 +207,22 @@ Slab=
|
||||
Double Panel=
|
||||
Half-Stair=
|
||||
Stair=
|
||||
Work Bench=Banco da lavoro
|
||||
For cutting blocks, repairing tools with a hammer, crafting and storing items=
|
||||
@1 @2=
|
||||
Hammer=Martello
|
||||
Repairs tools at the work bench=
|
||||
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 #####
|
||||
|
||||
Dumb AI=AI stupida
|
||||
You can't reset the chessboard, a game has been started. If you aren't a current player, try again in @1=Non si può resettare la partita, un gioco è in corso. Se non si è uno dei giocatori, riprovare in @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=Non si può scavare la scacchiera, una partita è in corso. Resettarla se si è uno dei giocatori, o riprovare in @1
|
||||
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
|
||||
|
6
mod.conf
@ -1,6 +1,6 @@
|
||||
name = xdecor
|
||||
title = X-Decor-libre
|
||||
description = A decoration mod meant to be simple and well-featured (libre version).
|
||||
description = A libre decoration mod meant to be simple and well-featured.
|
||||
depends = default, bucket, doors, farming, stairs, xpanes
|
||||
optional_depends = playerphysics, player_api, fire, moreblocks, mesecons, unified_inventory, tt
|
||||
min_minetest_version = 5.7.0
|
||||
optional_depends = player_api, fire, moreblocks, mesecons, unified_inventory, tt, toolranks
|
||||
min_minetest_version = 5.9
|
||||
|
134
src/chess.lua
@ -327,11 +327,10 @@ local function en_passant_to_string(double_step)
|
||||
return s_en_passant
|
||||
end
|
||||
|
||||
local function can_castle(meta, board, from_list, from_idx, to_idx)
|
||||
local function can_castle(board, from_idx, to_idx, castlingRights)
|
||||
local from_x, from_y = index_to_xy(from_idx)
|
||||
local to_x, to_y = index_to_xy(to_idx)
|
||||
local inv = meta:get_inventory()
|
||||
local kingPiece = inv:get_stack(from_list, from_idx):get_name()
|
||||
local kingPiece = board[from_idx]
|
||||
local kingColor
|
||||
if kingPiece:find("black") then
|
||||
kingColor = "black"
|
||||
@ -340,21 +339,21 @@ local function can_castle(meta, board, from_list, from_idx, to_idx)
|
||||
end
|
||||
local possible_castles = {
|
||||
-- white queenside
|
||||
{ y = 7, to_x = 2, rook_idx = 57, rook_goal = 60, acheck_dir = -1, color = "white", meta = "castlingWhiteL", rook_id = 1 },
|
||||
{ y = 7, to_x = 2, rook_idx = 57, rook_goal = 60, acheck_dir = -1, color = "white", rightName = "castlingWhiteL", rook_id = 1 },
|
||||
-- white kingside
|
||||
{ y = 7, to_x = 6, rook_idx = 64, rook_goal = 62, acheck_dir = 1, color = "white", meta = "castlingWhiteR", rook_id = 2 },
|
||||
{ y = 7, to_x = 6, rook_idx = 64, rook_goal = 62, acheck_dir = 1, color = "white", rightName = "castlingWhiteR", rook_id = 2 },
|
||||
-- black queenside
|
||||
{ y = 0, to_x = 2, rook_idx = 1, rook_goal = 4, acheck_dir = -1, color = "black", meta = "castlingBlackL", rook_id = 1 },
|
||||
{ y = 0, to_x = 2, rook_idx = 1, rook_goal = 4, acheck_dir = -1, color = "black", rightName = "castlingBlackL", rook_id = 1 },
|
||||
-- black kingside
|
||||
{ y = 0, to_x = 6, rook_idx = 8, rook_goal = 6, acheck_dir = 1, color = "black", meta = "castlingBlackR", rook_id = 2 },
|
||||
{ y = 0, to_x = 6, rook_idx = 8, rook_goal = 6, acheck_dir = 1, color = "black", rightName = "castlingBlackR", rook_id = 2 },
|
||||
}
|
||||
|
||||
for p=1, #possible_castles do
|
||||
local pc = possible_castles[p]
|
||||
if pc.color == kingColor and pc.to_x == to_x and to_y == pc.y and from_y == pc.y then
|
||||
local castlingMeta = meta:get_int(pc.meta)
|
||||
local rookPiece = inv:get_stack(from_list, pc.rook_idx):get_name()
|
||||
if castlingMeta == 1 and rookPiece == "realchess:rook_"..kingColor.."_"..pc.rook_id then
|
||||
local castlingRightVal = castlingRights[pc.rightName]
|
||||
local rookPiece = board[pc.rook_idx]
|
||||
if castlingRightVal == 1 and rookPiece == "realchess:rook_"..kingColor.."_"..pc.rook_id then
|
||||
-- Check if all squares between king and rook are empty
|
||||
local empty_start, empty_end
|
||||
if pc.acheck_dir == -1 then
|
||||
@ -367,7 +366,7 @@ local function can_castle(meta, board, from_list, from_idx, to_idx)
|
||||
empty_end = pc.rook_idx - 1
|
||||
end
|
||||
for i = empty_start, empty_end do
|
||||
if inv:get_stack(from_list, i):get_name() ~= "" then
|
||||
if board[i] ~= "" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -389,15 +388,15 @@ end
|
||||
-- Checks if a square to check if there is a piece that can be captured en passant. Returns true if this
|
||||
-- is the case, false otherwise.
|
||||
-- Parameters:
|
||||
-- * meta: chessboard node metadata
|
||||
-- * board: chessboard table
|
||||
-- * victim_color: color of the opponent to capture a piece from. "white" or "black". (so in White's turn, pass "black" here)
|
||||
-- * victim_index: board index of the square where you expect the victim to be
|
||||
local function can_capture_en_passant(meta, victim_color, victim_index)
|
||||
local inv = meta:get_inventory()
|
||||
local victimPiece = inv:get_stack("board", victim_index)
|
||||
local double_step_index = meta:get_int("prevDoublePawnStepTo")
|
||||
local victim_name = victimPiece:get_name()
|
||||
if double_step_index ~= 0 and double_step_index == victim_index and victim_name:find(victim_color) and victim_name:sub(11,14) == "pawn" then
|
||||
-- * prevDoublePawnStepTo: if a pawn did a double-step in the previous halfmove, this is the board index of the destination.
|
||||
-- if no pawn made a double-step in the previous halfmove, this is nil or 0.
|
||||
local function can_capture_en_passant(board, victim_color, victim_index, prevDoublePawnStepTo)
|
||||
local victimPiece = board[victim_index]
|
||||
local double_step_index = prevDoublePawnStepTo or 0
|
||||
if double_step_index ~= 0 and double_step_index == victim_index and victimPiece:find(victim_color) and victimPiece:sub(11,14) == "pawn" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@ -413,7 +412,7 @@ end
|
||||
-- Any key with a numeric value is a possible destination.
|
||||
-- The numeric value is a move rating for the bot and is 0 by default.
|
||||
-- Example: { [4] = 0, [9] = 0 } -- can move to squares 4 and 9
|
||||
local function get_theoretical_moves_from(meta, board, from_idx)
|
||||
local function get_theoretical_moves_from(board, from_idx, prevDoublePawnStepTo, castlingRights)
|
||||
local piece, color = board[from_idx]:match(":(%w+)_(%w+)")
|
||||
if not piece then
|
||||
return {}
|
||||
@ -450,7 +449,7 @@ local function get_theoretical_moves_from(meta, board, from_idx)
|
||||
can_capture = true
|
||||
else
|
||||
-- en passant
|
||||
if can_capture_en_passant(meta, "black", xy_to_index(to_x, from_y)) then
|
||||
if can_capture_en_passant(board, "black", xy_to_index(to_x, from_y), prevDoublePawnStepTo) then
|
||||
can_capture = true
|
||||
en_passant = true
|
||||
end
|
||||
@ -505,7 +504,7 @@ local function get_theoretical_moves_from(meta, board, from_idx)
|
||||
can_capture = true
|
||||
else
|
||||
-- en passant
|
||||
if can_capture_en_passant(meta, "white", xy_to_index(to_x, from_y)) then
|
||||
if can_capture_en_passant(board, "white", xy_to_index(to_x, from_y), prevDoublePawnStepTo) then
|
||||
can_capture = true
|
||||
en_passant = true
|
||||
end
|
||||
@ -783,11 +782,10 @@ local function get_theoretical_moves_from(meta, board, from_idx)
|
||||
|
||||
-- KING
|
||||
elseif piece == "king" then
|
||||
local inv = meta:get_inventory()
|
||||
-- King can't move to any attacked square
|
||||
-- king_board simulates the board with the king moved already.
|
||||
-- Required for the attacked() check to work
|
||||
local king_board = realchess.board_to_table(inv)
|
||||
local king_board = table.copy(board)
|
||||
king_board[to_idx] = king_board[from_idx]
|
||||
king_board[from_idx] = ""
|
||||
if realchess.attacked(color, to_idx, king_board) then
|
||||
@ -805,7 +803,7 @@ local function get_theoretical_moves_from(meta, board, from_idx)
|
||||
end
|
||||
|
||||
if dx > 1 or dy > 1 then
|
||||
local cc = can_castle(meta, board, "board", from_idx, to_idx)
|
||||
local cc = can_castle(board, from_idx, to_idx, castlingRights)
|
||||
if not cc then
|
||||
moves[to_idx] = nil
|
||||
end
|
||||
@ -847,10 +845,10 @@ end
|
||||
-- origin_index is the board index for the square to start the piece from (as string)
|
||||
-- and this is the key for a list of destination indixes.
|
||||
-- r1, r2, r3 ... are numeric values (normally 0) to "rate" this square for the bot.
|
||||
function realchess.get_theoretical_moves_for(meta, board, player)
|
||||
function realchess.get_theoretical_moves_for(board, player, prevDoublePawnStepTo, castlingRights)
|
||||
local moves = {}
|
||||
for i = 1, 64 do
|
||||
local possibleMoves = get_theoretical_moves_from(meta, board, i)
|
||||
local possibleMoves = get_theoretical_moves_from(board, i, prevDoublePawnStepTo, castlingRights)
|
||||
if next(possibleMoves) then
|
||||
local stack_name = board[i]
|
||||
if stack_name:find(player) then
|
||||
@ -1089,7 +1087,11 @@ end
|
||||
|
||||
local function get_figurine_id(piece_itemname)
|
||||
local piece_s = piece_itemname:match(":(%w+_%w+)")
|
||||
return figurines_str:match("(%d+)=chess_figurine_" .. piece_s)
|
||||
if not piece_s then
|
||||
return MOVES_LIST_SYMBOL_EMPTY
|
||||
else
|
||||
return figurines_str:match("(%d+)=chess_figurine_" .. piece_s)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1678,17 +1680,17 @@ local function update_formspec(meta)
|
||||
local turnWhite = minetest.colorize("#000001", playerWhiteDisplay)
|
||||
|
||||
-- several status words for the player
|
||||
-- player is in check
|
||||
--~ Chess: player is in check
|
||||
local check_s = minetest.colorize("#FF8000", "["..S("check").."]")
|
||||
-- player has been checkmated
|
||||
--~ Chess: player has been checkmated
|
||||
local mate_s = minetest.colorize("#FF0000", "["..S("checkmate").."]")
|
||||
-- player has resigned
|
||||
--~ Chess: player has resigned
|
||||
local resign_s = minetest.colorize("#FF0000", "["..S("resigned").."]")
|
||||
-- player has won
|
||||
--~ Chess: player has won
|
||||
local win_s = minetest.colorize("#26AB2B", "["..S("winner").."]")
|
||||
-- player has lost
|
||||
--~ Chess: player has lost
|
||||
local lose_s = minetest.colorize("#FF0000", "["..S("loser").."]")
|
||||
-- player has a draw
|
||||
--~ Chess: player has a draw
|
||||
local draw_s = minetest.colorize("#FF00FF", "["..S("draw").."]")
|
||||
|
||||
local status_black = ""
|
||||
@ -1733,6 +1735,7 @@ local function update_formspec(meta)
|
||||
if promotion == "black" then
|
||||
eaten_img = ""
|
||||
promotion_formstring =
|
||||
--~ Chess: Shown when black player can promote a pawn. Space for text is limited.
|
||||
"label[10.1,6.35;"..FS("PROMOTION\nFOR BLACK!").."]" ..
|
||||
"animated_image[10.05,7.2;2,2;p_img_white;pawn_black_promo_anim.png;5;100]"
|
||||
if botColor ~= "black" and botColor ~= "both" then
|
||||
@ -1748,6 +1751,7 @@ local function update_formspec(meta)
|
||||
elseif promotion == "white" then
|
||||
eaten_img = ""
|
||||
promotion_formstring =
|
||||
--~ Chess: Shown when white player can promote a pawn. Space for text is limited.
|
||||
"label[10.1,6.35;"..FS("PROMOTION\nFOR WHITE!").."]" ..
|
||||
"animated_image[10.05,7.2;2,2;p_img_white;pawn_white_promo_anim.png;5;100]"
|
||||
if botColor ~= "white" and botColor ~= "both" then
|
||||
@ -1765,8 +1769,10 @@ local function update_formspec(meta)
|
||||
local draw_claim_formstring = ""
|
||||
if drawClaim ~= "" and gameResult == "" then
|
||||
if lastMove == "black" or lastMove == "" then
|
||||
--~ Chess: Shown when white player wants to claim a draw. Space for text is limited.
|
||||
draw_claim_formstring = "label[10.1,6.35;"..FS("DRAW CLAIM\nBY WHITE!").."]"
|
||||
else
|
||||
--~ Chess: Shown when black player wants to claim a draw. Space for text is limited.
|
||||
draw_claim_formstring = "label[10.1,6.35;"..FS("DRAW CLAIM\nBY BLACK!").."]"
|
||||
end
|
||||
if drawClaim == "50_move_rule" then
|
||||
@ -1790,6 +1796,7 @@ local function update_formspec(meta)
|
||||
|
||||
if playerActionsAvailable and (playerWhite ~= "" and playerBlack ~= "") then
|
||||
game_buttons = game_buttons .. "image_button[14.56,9.7;0.8,0.8;chess_resign.png;resign;]" ..
|
||||
--~ Resign in Chess
|
||||
"tooltip[resign;"..FS("Resign").."]"
|
||||
end
|
||||
|
||||
@ -1810,12 +1817,14 @@ local function update_formspec(meta)
|
||||
-- Will trigger "draw claim" mode in which player must do the final move that triggers the draw
|
||||
game_buttons = game_buttons .. "image_button[13.36,9.7;0.8,0.8;chess_draw_50move_next.png;draw_50_moves;]"..
|
||||
"tooltip[draw_50_moves;"..
|
||||
--~ Chess
|
||||
FS("Invoke the 50-move rule for your next move").."]"
|
||||
elseif halfmoveClock >= DRAWCLAIM_LONGGAME_PLAYER then
|
||||
-- When the 50 moves without capture / pawn move have occured occur.
|
||||
-- Will insta-draw.
|
||||
game_buttons = game_buttons .. "image_button[13.36,9.7;0.8,0.8;chess_draw_50move.png;draw_50_moves;]"..
|
||||
"tooltip[draw_50_moves;"..
|
||||
--~ Chess
|
||||
FS("Invoke the 50-move rule and draw the game").."]"
|
||||
end
|
||||
|
||||
@ -1828,12 +1837,14 @@ local function update_formspec(meta)
|
||||
-- Will insta-draw.
|
||||
game_buttons = game_buttons .. "image_button[12.36,9.7;0.8,0.8;chess_draw_repeat3.png;draw_repeat_3;]"..
|
||||
"tooltip[draw_repeat_3;"..
|
||||
--~ Chess
|
||||
FS("Invoke the threefold repetition rule and draw the game").."]"
|
||||
elseif maxRepeatedPositions >= 2 then
|
||||
-- If the same position may be about to occur 3 times.
|
||||
-- Will trigger "draw claim" mode in which player must do the final move that triggers the draw.
|
||||
game_buttons = game_buttons .. "image_button[12.36,9.7;0.8,0.8;chess_draw_repeat3_next.png;draw_repeat_3;]"..
|
||||
"tooltip[draw_repeat_3;"..
|
||||
--~ Chess
|
||||
FS("Invoke the threefold repetition rule for your next move").."]"
|
||||
end
|
||||
end
|
||||
@ -1893,19 +1904,26 @@ local function update_formspec(meta)
|
||||
meta:set_string("formspec", formspec)
|
||||
end
|
||||
|
||||
local function update_game_result(meta)
|
||||
local function update_game_result(meta, lastMove)
|
||||
local inv = meta:get_inventory()
|
||||
local board_t = realchess.board_to_table(inv)
|
||||
|
||||
local playerWhite = meta:get_string("playerWhite")
|
||||
local playerBlack = meta:get_string("playerBlack")
|
||||
local prevDoublePawnStepTo = meta:get_int("prevDoublePawnStepTo")
|
||||
local castlingRights = {
|
||||
castlingWhiteR = meta:get_int("castlingWhiteR"),
|
||||
castlingWhiteL = meta:get_int("castlingWhiteL"),
|
||||
castlingBlackR = meta:get_int("castlingBlackR"),
|
||||
castlingBlackL = meta:get_int("castlingBlackL"),
|
||||
}
|
||||
|
||||
update_formspec(meta)
|
||||
local blackCanMove = false
|
||||
local whiteCanMove = false
|
||||
|
||||
local blackMoves = realchess.get_theoretical_moves_for(meta, board_t, "black")
|
||||
local whiteMoves = realchess.get_theoretical_moves_for(meta, board_t, "white")
|
||||
local blackMoves = realchess.get_theoretical_moves_for(board_t, "black", prevDoublePawnStepTo, castlingRights)
|
||||
local whiteMoves = realchess.get_theoretical_moves_for(board_t, "white", prevDoublePawnStepTo, castlingRights)
|
||||
if next(blackMoves) then
|
||||
blackCanMove = true
|
||||
end
|
||||
@ -1913,9 +1931,6 @@ local function update_game_result(meta)
|
||||
whiteCanMove = true
|
||||
end
|
||||
|
||||
-- assume lastMove was updated *after* the player moved
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
|
||||
local black_king_idx, white_king_idx = realchess.locate_kings(board_t)
|
||||
if not black_king_idx or not white_king_idx then
|
||||
minetest.log("error", "[xdecor] Chess: Insufficient kings on chessboard!")
|
||||
@ -1968,6 +1983,7 @@ local function update_game_result(meta)
|
||||
meta:set_string("gameResult", "draw")
|
||||
meta:set_string("gameResultReason", "stalemate")
|
||||
add_special_to_moves_list(meta, "draw")
|
||||
--~ Chess message
|
||||
send_message_2(playerWhite, playerBlack, S("The game ended up in a stalemate! It's a draw!"), botColor)
|
||||
minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw by stalemate")
|
||||
return
|
||||
@ -1988,6 +2004,7 @@ local function update_game_result(meta)
|
||||
meta:set_string("gameResult", "draw")
|
||||
meta:set_string("gameResultReason", "stalemate")
|
||||
add_special_to_moves_list(meta, "draw")
|
||||
--~ Chess message
|
||||
send_message_2(playerWhite, playerBlack, S("The game ended up in a stalemate! It's a draw!"), botColor)
|
||||
minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw by stalemate")
|
||||
return
|
||||
@ -1999,6 +2016,7 @@ local function update_game_result(meta)
|
||||
meta:set_string("gameResult", "draw")
|
||||
meta:set_string("gameResultReason", "dead_position")
|
||||
add_special_to_moves_list(meta, "draw")
|
||||
--~ Chess message
|
||||
send_message_2(playerWhite, playerBlack, S("The game ended up in a dead position! It's a draw!"), botColor)
|
||||
minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw by dead position")
|
||||
end
|
||||
@ -2114,6 +2132,7 @@ local function update_game_result(meta)
|
||||
meta:set_string("gameResult", "draw")
|
||||
meta:set_string("gameResultReason", "same_position_5")
|
||||
add_special_to_moves_list(meta, "draw")
|
||||
--~ Chess message when the fivefold repetition has happened
|
||||
local msg = S("The exact same position has occured 5 times. It's a draw!")
|
||||
send_message_2(playerWhite, playerBlack, msg, botColor)
|
||||
minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw because the same position has appeared 5 times")
|
||||
@ -2233,6 +2252,7 @@ function realchess.move(meta, from_list, from_index, to_list, to_index, playerNa
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
local playerWhite = meta:get_string("playerWhite")
|
||||
local playerBlack = meta:get_string("playerBlack")
|
||||
local prevDoublePawnStepTo = meta:get_int("prevDoublePawnStepTo")
|
||||
local kingMoved = false
|
||||
local thisMove -- Will replace lastMove when move is legal
|
||||
|
||||
@ -2349,7 +2369,8 @@ function realchess.move(meta, from_list, from_index, to_list, to_index, playerNa
|
||||
can_capture = true
|
||||
else
|
||||
-- en passant
|
||||
if can_capture_en_passant(meta, "black", xy_to_index(to_x, from_y)) then
|
||||
local board = realchess.board_to_table(inv)
|
||||
if can_capture_en_passant(board, "black", xy_to_index(to_x, from_y), prevDoublePawnStepTo) then
|
||||
can_capture = true
|
||||
en_passant_target = xy_to_index(to_x, from_y)
|
||||
end
|
||||
@ -2417,7 +2438,8 @@ function realchess.move(meta, from_list, from_index, to_list, to_index, playerNa
|
||||
can_capture = true
|
||||
else
|
||||
-- en passant
|
||||
if can_capture_en_passant(meta, "white", xy_to_index(to_x, from_y)) then
|
||||
local board = realchess.board_to_table(inv)
|
||||
if can_capture_en_passant(board, "white", xy_to_index(to_x, from_y), prevDoublePawnStepTo) then
|
||||
can_capture = true
|
||||
en_passant_target = xy_to_index(to_x, from_y)
|
||||
end
|
||||
@ -2669,9 +2691,15 @@ function realchess.move(meta, from_list, from_index, to_list, to_index, playerNa
|
||||
local check = true
|
||||
local inv = meta:get_inventory()
|
||||
local board = realchess.board_to_table(inv)
|
||||
local castlingRights = {
|
||||
castlingWhiteR = meta:get_int("castlingWhiteR"),
|
||||
castlingWhiteL = meta:get_int("castlingWhiteL"),
|
||||
castlingBlackR = meta:get_int("castlingBlackR"),
|
||||
castlingBlackL = meta:get_int("castlingBlackL"),
|
||||
}
|
||||
|
||||
-- Castling
|
||||
local cc, rook_start, rook_goal, rook_name = can_castle(meta, board, from_list, from_index, to_index)
|
||||
local cc, rook_start, rook_goal, rook_name = can_castle(board, from_index, to_index, castlingRights)
|
||||
if cc then
|
||||
inv:set_stack(from_list, rook_goal, rook_name)
|
||||
inv:set_stack(from_list, rook_start, "")
|
||||
@ -2799,11 +2827,11 @@ local function timeout_format(timeout_limit)
|
||||
local seconds = time_remaining % 60
|
||||
|
||||
if minutes == 0 then
|
||||
-- number of seconds
|
||||
--~ number of seconds
|
||||
return S("@1 s", seconds)
|
||||
end
|
||||
|
||||
-- number of minutes and seconds
|
||||
--~ number of minutes and seconds
|
||||
return S("@1 min @2 s", minutes, seconds)
|
||||
end
|
||||
|
||||
@ -2885,6 +2913,7 @@ function realchess.fields(pos, _, fields, sender)
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
if (playerName == playerWhite and playerWhite == "") or (playerName == playerBlack and playerBlack == "") then
|
||||
-- Can't resign before the player name has been recorded
|
||||
--~ Chess message when player tried to resign too early
|
||||
send_message(playerName, S("Resigning is not possible yet."))
|
||||
return
|
||||
end
|
||||
@ -3035,9 +3064,11 @@ function realchess.fields(pos, _, fields, sender)
|
||||
local pcolor = promo:sub(-5)
|
||||
local activePromo = meta:get_string("promotionActive")
|
||||
if activePromo == "" then
|
||||
--~ Chess message
|
||||
send_message(playerName, S("This isn't the time for promotion."))
|
||||
return
|
||||
elseif activePromo ~= pcolor then
|
||||
--~ Chess message
|
||||
send_message(playerName, S("It's not your turn! This promotion is meant for the other player."))
|
||||
return
|
||||
end
|
||||
@ -3045,6 +3076,7 @@ function realchess.fields(pos, _, fields, sender)
|
||||
realchess.promote_pawn(meta, pcolor, promo:sub(1, -7))
|
||||
return
|
||||
else
|
||||
--~ Chess message
|
||||
send_message(playerName, S("It's not your turn! This promotion is meant for the other player."))
|
||||
return
|
||||
end
|
||||
@ -3111,16 +3143,19 @@ function realchess.move_piece(meta, pieceFrom, from_list, from_index, to_list, t
|
||||
add_to_eaten_list(meta, pieceTo)
|
||||
end
|
||||
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
if lastMove == "" then lastMove = "black" end
|
||||
|
||||
local promo = meta:get_string("promotionActive") ~= ""
|
||||
if not promo then
|
||||
update_game_result(meta)
|
||||
update_game_result(meta, lastMove)
|
||||
lastMove = meta:get_string("lastMove")
|
||||
if lastMove == "" then lastMove = "black" end
|
||||
end
|
||||
update_formspec(meta)
|
||||
|
||||
local botColor = meta:get_string("botColor")
|
||||
if botColor == "" then botColor = "black" end
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
if lastMove == "" then lastMove = "black" end
|
||||
local mode = meta:get_string("mode")
|
||||
local gameResult = meta:get_string("gameResult")
|
||||
-- Let the bot play when it its turn
|
||||
@ -3208,6 +3243,7 @@ function realchess.promote_pawn(meta, color, promoteTo)
|
||||
meta:set_int("promotionPawnFromIdx", 0)
|
||||
meta:set_int("promotionPawnToIdx", 0)
|
||||
realchess.update_state(meta, from_idx, to_idx, color, promoteFrom:get_name(), pstr)
|
||||
update_game_result(meta, color)
|
||||
update_formspec(meta)
|
||||
|
||||
local botColor = meta:get_string("botColor")
|
||||
@ -3270,13 +3306,13 @@ if ENABLE_CHESS_GAMES then
|
||||
realchess.move(meta, from_list, from_index, to_list, to_index, playerName)
|
||||
-- We always return 0 to disable all *builtin* inventory moves, since
|
||||
-- we do it ourselves. This should be fine because there shouldn't be a
|
||||
-- conflict between this mod and Minetest then.
|
||||
-- conflict between this mod and Luanti then.
|
||||
return 0
|
||||
end
|
||||
chessboarddef.allow_metadata_inventory_take = function() return 0 end
|
||||
chessboarddef.allow_metadata_inventory_put = function() return 0 end
|
||||
-- Note: There is no on_move function because we put the entire move handling
|
||||
-- into the allow function above. The reason for this is of Minetest's
|
||||
-- into the allow function above. The reason for this is of Luanti's
|
||||
-- awkward behavior when swapping items.
|
||||
|
||||
minetest.register_lbm({
|
||||
|
172
src/chessbot.lua
@ -36,15 +36,60 @@ local function best_move(moves)
|
||||
return tonumber(choice_from), choice_to
|
||||
end
|
||||
|
||||
function chessbot.move(inv, meta)
|
||||
local board_t = realchess.board_to_table(inv)
|
||||
local lastMove = meta:get_string("lastMove")
|
||||
local gameResult = meta:get_string("gameResult")
|
||||
local botColor = meta:get_string("botColor")
|
||||
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"
|
||||
@ -60,62 +105,59 @@ function chessbot.move(inv, meta)
|
||||
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
|
||||
if (lastMove == opponentColor or ((botColor == "white" or botColor == "both") and lastMove == "")) and gameResult == "" then
|
||||
|
||||
local moves = realchess.get_theoretical_moves_for(meta, board_t, currentBotColor)
|
||||
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
|
||||
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
|
||||
|
||||
local pieceFrom = inv:get_stack("board", choice_from):get_name()
|
||||
local pieceTo = inv:get_stack("board", choice_to):get_name()
|
||||
|
||||
minetest.after(BOT_DELAY_MOVE, function()
|
||||
local gameResult = meta:get_string("gameResult")
|
||||
if gameResult ~= "" then
|
||||
return
|
||||
end
|
||||
local botColor = meta:get_string("botColor")
|
||||
if botColor == "" then
|
||||
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
|
||||
end
|
||||
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.promote(inv, meta, pawnIndex)
|
||||
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
|
||||
@ -124,9 +166,35 @@ function chessbot.promote(inv, meta, pawnIndex)
|
||||
else
|
||||
color = "black"
|
||||
end
|
||||
-- Always promote to queen
|
||||
realchess.promote_pawn(meta, color, "queen")
|
||||
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
|
||||
|
@ -1,8 +1,14 @@
|
||||
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 = {
|
||||
@ -21,13 +27,28 @@ local function set_infotext(meta, node)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add more ingredients here that make a soup.
|
||||
-- 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},
|
||||
@ -36,12 +57,17 @@ cauldron.cbox = {
|
||||
{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 minetest.get_item_group(nn, "fire") ~= 0 then
|
||||
if is_fire(nn) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
@ -159,6 +185,23 @@ local function eatable(itemstring)
|
||||
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)
|
||||
@ -192,13 +235,12 @@ function cauldron.boiling_timer(pos)
|
||||
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 food = itemstring:match(":([%w_]+)")
|
||||
local item = ItemStack(itemstring)
|
||||
local itemname = item:get_name()
|
||||
|
||||
for _, ingredient in ipairs(ingredients_list) do
|
||||
if food and (eatable(itemstring) or food:find(ingredient)) then
|
||||
ingredients[#ingredients + 1] = food
|
||||
break
|
||||
end
|
||||
if is_ingredient(itemname) then
|
||||
local basename = itemstring:match(":([%w_]+)")
|
||||
table.insert(ingredients, basename)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -247,7 +289,7 @@ xdecor.register("cauldron_empty", {
|
||||
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_sides.png"},
|
||||
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,
|
||||
@ -264,7 +306,7 @@ xdecor.register("cauldron_idle", {
|
||||
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_sides.png"},
|
||||
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),
|
||||
@ -278,7 +320,7 @@ xdecor.register("cauldron_idle_river_water", {
|
||||
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_sides.png"},
|
||||
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),
|
||||
@ -293,7 +335,7 @@ xdecor.register("cauldron_idle_soup", {
|
||||
is_ground_content = false,
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
drop = "xdecor:cauldron_empty",
|
||||
tiles = {"xdecor_cauldron_top_idle_soup.png", "xdecor_cauldron_sides.png"},
|
||||
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)
|
||||
@ -320,6 +362,7 @@ xdecor.register("cauldron_boiling", {
|
||||
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(),
|
||||
@ -344,6 +387,7 @@ xdecor.register("cauldron_boiling_river_water", {
|
||||
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(),
|
||||
@ -370,6 +414,7 @@ xdecor.register("cauldron_soup", {
|
||||
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(),
|
||||
@ -447,3 +492,26 @@ minetest.register_lbm({
|
||||
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
|
||||
|
115
src/enchanted_tools.lua
Normal 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
|
@ -1,69 +1,84 @@
|
||||
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
|
||||
local MESE_COST = 1
|
||||
|
||||
-- Force of the enchantments.
|
||||
local enchanting = {
|
||||
uses = 1.2, -- Durability
|
||||
times = 0.1, -- Efficiency
|
||||
damages = 1, -- Sharpness
|
||||
}
|
||||
|
||||
local function cap(str) return
|
||||
str:gsub("^%l", string.upper)
|
||||
end
|
||||
-- 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(enchant, orig_caps, fleshy)
|
||||
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 * enchanting.uses)
|
||||
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 -
|
||||
enchanting.times)
|
||||
bonus_defs.times)
|
||||
end
|
||||
|
||||
if fleshy then
|
||||
bonus.damages = to_percent(fleshy, fleshy + enchanting.damages)
|
||||
bonus.damages = to_percent(fleshy, fleshy + bonus_defs.damages)
|
||||
end
|
||||
|
||||
local specs = { -- not finished, to complete
|
||||
durable = {"#00baff", " (+" .. bonus.durable .. "%)"},
|
||||
fast = {"#74ff49", " (+" .. bonus.efficiency .. "%)"},
|
||||
sharp = {"#ffff00", " (+" .. bonus.damages .. "%)"},
|
||||
local specs = {
|
||||
durable = bonus.durable,
|
||||
fast = bonus.efficiency,
|
||||
sharp = bonus.damages,
|
||||
}
|
||||
|
||||
local enchant_loc = {
|
||||
fast = S("Efficiency"),
|
||||
durable = S("Durability"),
|
||||
sharp = S("Sharpness"),
|
||||
}
|
||||
|
||||
return minetest.colorize and minetest.colorize(specs[enchant][1],
|
||||
enchant_loc[enchant] .. specs[enchant][2]) or
|
||||
enchant_loc[enchant] .. specs[enchant][2]
|
||||
local percent = specs[enchant]
|
||||
return enchanting:get_tooltip_raw(enchant, percent)
|
||||
end
|
||||
|
||||
local enchant_buttons = {
|
||||
"image_button[3.6,0.67;4.75,0.85;bg_btn.png;fast;"..FS("Efficiency").."]" ..
|
||||
"image_button[3.6,1.65;4.75,1.05;bg_btn.png;durable;"..FS("Durability").."]",
|
||||
"image_button[3.6,2.8;4.75,0.85;bg_btn.png;sharp;"..FS("Sharpness").."]",
|
||||
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, num)
|
||||
function enchanting.formspec(pos, enchants)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local formspec = [[
|
||||
size[9,8.6;]
|
||||
@ -80,27 +95,28 @@ function enchanting.formspec(pos, num)
|
||||
listring[context;mese]
|
||||
image[2,2.9;1,1;mese_layout.png]
|
||||
]]
|
||||
.."tooltip[sharp;"..FS("Your weapon inflicts more damages").."]"
|
||||
.."tooltip[durable;"..FS("Your tool last longer").."]"
|
||||
--~ 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)
|
||||
|
||||
formspec = formspec .. (enchant_buttons[num] or "")
|
||||
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 tool_groups = {
|
||||
"axe, pick, shovel",
|
||||
"sword",
|
||||
}
|
||||
|
||||
for idx, tools in ipairs(tool_groups) do
|
||||
if tools:find(stackname:match(":(%w+)")) then
|
||||
enchanting.formspec(pos, idx)
|
||||
end
|
||||
local enchants = available_tool_enchants[stackname]
|
||||
if enchants then
|
||||
enchanting.formspec(pos, enchants)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -114,7 +130,7 @@ function enchanting.fields(pos, _, fields, sender)
|
||||
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
|
||||
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
|
||||
@ -122,7 +138,7 @@ function enchanting.fields(pos, _, fields, sender)
|
||||
|
||||
tool:replace(enchanted_tool)
|
||||
tool:add_wear(orig_wear)
|
||||
mese:take_item(mese_cost)
|
||||
mese:take_item(MESE_COST)
|
||||
inv:set_stack("mese", 1, mese)
|
||||
inv:set_stack("tool", 1, tool)
|
||||
end
|
||||
@ -140,12 +156,13 @@ function enchanting.blast(pos)
|
||||
end
|
||||
|
||||
local function allowed(tool)
|
||||
if not tool then return end
|
||||
|
||||
for item in pairs(reg_tools) do
|
||||
if item:find("enchanted_" .. tool) then
|
||||
return true
|
||||
end
|
||||
if not tool then
|
||||
return false
|
||||
end
|
||||
if reg_enchantable_tools[tool] then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
@ -154,7 +171,7 @@ function enchanting.put(_, listname, _, stack)
|
||||
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:match("[^:]+$")) then
|
||||
elseif listname == "tool" and allowed(stackname) then
|
||||
return 1
|
||||
end
|
||||
|
||||
@ -248,13 +265,15 @@ xdecor.register("enchantment_table", {
|
||||
})
|
||||
|
||||
minetest.register_entity("xdecor:book_open", {
|
||||
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,
|
||||
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({
|
||||
@ -276,65 +295,112 @@ minetest.register_lbm({
|
||||
end,
|
||||
})
|
||||
|
||||
function enchanting:register_tools(mod, def)
|
||||
for tool in pairs(def.tools) do
|
||||
for material in def.materials:gmatch("[%w_]+") do
|
||||
for enchant in def.tools[tool].enchants:gmatch("[%w_]+") do
|
||||
local original_tool = reg_tools[mod .. ":" .. tool .. "_" .. material]
|
||||
if not original_tool then break end
|
||||
local original_toolcaps = original_tool.tool_capabilities
|
||||
|
||||
if original_toolcaps then
|
||||
local original_damage_groups = original_toolcaps.damage_groups
|
||||
local original_groupcaps = original_toolcaps.groupcaps
|
||||
local groupcaps = table.copy(original_groupcaps)
|
||||
local fleshy = original_damage_groups.fleshy
|
||||
local full_punch_interval = original_toolcaps.full_punch_interval
|
||||
local max_drop_level = original_toolcaps.max_drop_level
|
||||
local group = next(original_groupcaps)
|
||||
|
||||
if enchant == "durable" then
|
||||
groupcaps[group].uses = ceil(original_groupcaps[group].uses *
|
||||
enchanting.uses)
|
||||
elseif enchant == "fast" then
|
||||
for i, time in pairs(original_groupcaps[group].times) do
|
||||
groupcaps[group].times[i] = time - enchanting.times
|
||||
end
|
||||
elseif enchant == "sharp" then
|
||||
fleshy = fleshy + enchanting.damages
|
||||
end
|
||||
|
||||
local arg1 = def.material_desc[material] or cap(material)
|
||||
local arg2 = def.tools[tool].desc or cap(tool)
|
||||
local arg3 = self:get_tooltip(enchant, original_groupcaps[group], fleshy)
|
||||
minetest.register_tool(":" .. mod .. ":enchanted_" .. tool .. "_" .. material .. "_" .. enchant, {
|
||||
description = S("Enchanted @1 @2\n@3", arg1, arg2, arg3),
|
||||
short_description = S("Enchanted @1 @2", arg1, arg2),
|
||||
inventory_image = original_tool.inventory_image .. "^[colorize:violet:50",
|
||||
wield_image = original_tool.wield_image,
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
tool_capabilities = {
|
||||
groupcaps = groupcaps, damage_groups = {fleshy = fleshy},
|
||||
full_punch_interval = full_punch_interval,
|
||||
max_drop_level = max_drop_level
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
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
|
||||
|
||||
enchanting:register_tools("default", {
|
||||
materials = "steel, bronze, mese, diamond",
|
||||
material_desc = {steel = S("Steel"), bronze = S("Bronze"), mese = S("Mese"), diamond = S("Diamond")},
|
||||
tools = {
|
||||
axe = {enchants = "durable, fast", desc = S("Axe")},
|
||||
pick = {enchants = "durable, fast", desc = S("Pickaxe")},
|
||||
shovel = {enchants = "durable, fast", desc = S("Shovel")},
|
||||
sword = {enchants = "sharp", desc = S("Sword")}
|
||||
},
|
||||
})
|
||||
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_too_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_too_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
|
||||
|
||||
@ -346,3 +412,95 @@ minetest.register_craft({
|
||||
{"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
|
||||
|
||||
|
@ -121,6 +121,7 @@ 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",
|
||||
|
@ -73,7 +73,9 @@ function itemframe.set_infotext(meta)
|
||||
local owner = meta:get_string("owner")
|
||||
if itemstring == "" then
|
||||
if owner ~= "" then
|
||||
meta:set_string("infotext", S("@1 (owned by @2)", S("Item Frame"), owner))
|
||||
--~ 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
|
||||
@ -158,6 +160,7 @@ 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,
|
||||
@ -181,12 +184,14 @@ xdecor.register("itemframe", {
|
||||
})
|
||||
|
||||
minetest.register_entity("xdecor:f_item", {
|
||||
visual = "wielditem",
|
||||
visual_size = {x = 0.33, y = 0.33},
|
||||
collisionbox = {0,0,0,0,0,0},
|
||||
pointable = false,
|
||||
physical = false,
|
||||
textures = {"air"},
|
||||
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
|
||||
|
@ -61,7 +61,7 @@ function mailbox:formspec(pos, owner, is_owner)
|
||||
-- List of donors. A line looks like this:
|
||||
-- <donor name> <item icon> × <item count>
|
||||
giver = giver .. "#FFFF00," .. giver_name .. "," .. i ..
|
||||
-- Times a certain item count; used for the mailbox donor list
|
||||
--~ 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 .. "=" ..
|
||||
@ -180,6 +180,7 @@ 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",
|
||||
|
@ -57,6 +57,7 @@ end
|
||||
function plate.register(material, desc, def)
|
||||
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,
|
||||
@ -98,6 +99,7 @@ plate.register("stone", "Stone", {
|
||||
|
||||
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,
|
||||
|
@ -200,7 +200,7 @@ 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},
|
||||
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, {
|
||||
@ -212,8 +212,8 @@ xdecor.register("chair", {
|
||||
{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)
|
||||
pos.y = pos.y + 0 -- Sitting position
|
||||
xdecor.sit(pos, node, clicker, pointed_thing)
|
||||
return itemstack
|
||||
end,
|
||||
@ -234,9 +234,11 @@ xdecor.register("cobweb", {
|
||||
})
|
||||
|
||||
local curtain_colors = {
|
||||
red = S("Red Curtain"),
|
||||
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
|
||||
@ -248,20 +250,27 @@ local cleanup_curtain_meta = function(_,_,_,drops)
|
||||
end
|
||||
end
|
||||
|
||||
for c, desc in pairs(curtain_colors) do
|
||||
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 = {"wool_white.png"},
|
||||
color = c,
|
||||
inventory_image = "wool_white.png^[colorize:" .. c ..
|
||||
":170^xdecor_curtain_open_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "wool_white.png^[colorize:" .. c .. ":170",
|
||||
drawtype = "signlike",
|
||||
paramtype2 = "colorwallmounted",
|
||||
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,
|
||||
selection_box = {type = "wallmounted"},
|
||||
on_rightclick = function(pos, node, _, itemstack)
|
||||
minetest.set_node(pos, {name = "xdecor:curtain_open_" .. c, param2 = node.param2})
|
||||
return itemstack
|
||||
@ -269,15 +278,28 @@ for c, desc in pairs(curtain_colors) do
|
||||
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 = {"wool_white.png^xdecor_curtain_open_overlay.png^[makealpha:255,126,126"},
|
||||
color = c,
|
||||
drawtype = "signlike",
|
||||
paramtype2 = "colorwallmounted",
|
||||
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,
|
||||
selection_box = {type="wallmounted"},
|
||||
drop = "xdecor:curtain_" .. c,
|
||||
on_rightclick = function(pos, node, _, itemstack)
|
||||
minetest.set_node(pos, {name="xdecor:curtain_" .. c, param2 = node.param2})
|
||||
@ -289,8 +311,8 @@ for c, desc in pairs(curtain_colors) do
|
||||
minetest.register_craft({
|
||||
output = "xdecor:curtain_" .. c .. " 4",
|
||||
recipe = {
|
||||
{"", "wool:" .. c, ""},
|
||||
{"", "wool:" .. c, ""}
|
||||
{"", craft_item, ""},
|
||||
{"", craft_item, ""}
|
||||
}
|
||||
})
|
||||
end
|
||||
@ -298,13 +320,13 @@ end
|
||||
xdecor.register("cushion", {
|
||||
description = S("Cushion"),
|
||||
tiles = {"xdecor_cushion.png"},
|
||||
groups = {snappy = 3, flammable = 3, fall_damage_add_percent = -50},
|
||||
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)
|
||||
pos.y = pos.y + 0 -- Sitting position
|
||||
xdecor.sit(pos, node, clicker, pointed_thing)
|
||||
return itemstack
|
||||
end
|
||||
@ -658,11 +680,12 @@ local painting_box = {
|
||||
|
||||
xdecor.register("painting_1", {
|
||||
description = S("Painting"),
|
||||
tiles = {"xdecor_painting_1.png"},
|
||||
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,
|
||||
@ -711,9 +734,10 @@ xdecor.register("painting_1", {
|
||||
|
||||
for i = 2, 4 do
|
||||
xdecor.register("painting_" .. i, {
|
||||
tiles = {"xdecor_painting_" .. i .. ".png"},
|
||||
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 = {
|
||||
@ -806,11 +830,26 @@ xdecor.register("tatami", {
|
||||
|
||||
xdecor.register("trampoline", {
|
||||
description = S("Trampoline"),
|
||||
tiles = {"xdecor_trampoline.png", "mailbox_blank16.png", "xdecor_trampoline_sides.png"},
|
||||
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 = xdecor.nodebox.slab_y(0.5),
|
||||
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",
|
||||
@ -901,6 +940,7 @@ xdecor.register("woodframed_glass", {
|
||||
|
||||
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
|
||||
|
47
src/rope.lua
@ -1,10 +1,16 @@
|
||||
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
|
||||
@ -15,32 +21,45 @@ function rope.place(itemstack, placer, pointed_thing)
|
||||
end
|
||||
local pos = pointed_thing.above
|
||||
-- Check protection
|
||||
if minetest.is_protected(pos, placer:get_player_name()) and
|
||||
not minetest.check_player_privs(placer, "protection_bypass") then
|
||||
minetest.record_protection_violation(pos, placer:get_player_name())
|
||||
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 max. rope length to max. stack size
|
||||
-- Prevents the rope to extend infinitely in Creative Mode
|
||||
local max_ropes = itemstack:get_stack_max()
|
||||
-- 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_placed = 0
|
||||
while oldnode.name == "air" and not itemstack:is_empty() and ropes_placed < max_ropes do
|
||||
local newnode = {name = stackname, param1 = 0}
|
||||
minetest.set_node(pos, newnode)
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
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_placed = ropes_placed + 1
|
||||
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_placed > 0 then
|
||||
if ropes_to_place > 0 then
|
||||
minetest.sound_play(ropesounds.place, {pos=start_pos}, true)
|
||||
end
|
||||
end
|
||||
|
@ -7,6 +7,9 @@ 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 = {
|
||||
@ -35,7 +38,7 @@ end
|
||||
|
||||
-- Tools allowed to be repaired
|
||||
function workbench:repairable(stack)
|
||||
-- Explicitly registeded as repairable: Overrides everything else
|
||||
-- Explicitly registered as repairable: Overrides everything else
|
||||
if custom_repairable[stack] then
|
||||
return true
|
||||
end
|
||||
@ -109,16 +112,16 @@ function workbench:get_output(inv, input, name)
|
||||
inv:set_list("forms", output)
|
||||
end
|
||||
|
||||
function workbench:register_special_cut(nodename, cutlist)
|
||||
registered_cuttable_nodes[nodename] = true
|
||||
special_cuts[nodename] = cutlist
|
||||
end
|
||||
|
||||
local main_fs = "label[0.9,1.23;"..FS("Cut").."]"
|
||||
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]
|
||||
@ -216,9 +219,11 @@ function workbench.timer(pos)
|
||||
return
|
||||
end
|
||||
|
||||
local hammerdef = hammer:get_definition()
|
||||
|
||||
-- Tool's wearing range: 0-65535; 0 = new condition
|
||||
tool:add_wear(-500)
|
||||
hammer:add_wear(700)
|
||||
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)
|
||||
@ -230,7 +235,7 @@ 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 stackname == "xdecor:hammer") or
|
||||
(listname == "hammer" and minetest.get_item_group(stackname, "repair_hammer") == 1) or
|
||||
listname == "storage" then
|
||||
return stack:get_count()
|
||||
end
|
||||
@ -255,7 +260,7 @@ function workbench.allow_move(pos, from_list, from_index, to_list, to_index, cou
|
||||
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 stack:get_name() == "xdecor:hammer" then
|
||||
if minetest.get_item_group(stack:get_name(), "repair_hammer") == 1 then
|
||||
return count
|
||||
end
|
||||
end
|
||||
@ -310,7 +315,7 @@ xdecor.register("workbench", {
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
tiles = {
|
||||
"xdecor_workbench_top.png","xdecor_workbench_top.png",
|
||||
"xdecor_workbench_top.png","xdecor_workbench_bottom.png",
|
||||
"xdecor_workbench_sides.png", "xdecor_workbench_sides.png",
|
||||
"xdecor_workbench_front.png", "xdecor_workbench_front.png"
|
||||
},
|
||||
@ -328,29 +333,11 @@ xdecor.register("workbench", {
|
||||
allow_metadata_inventory_move = workbench.allow_move
|
||||
})
|
||||
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
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
|
||||
registered_cuttable_nodes[nodename] = true
|
||||
end
|
||||
end
|
||||
|
||||
for _, d in ipairs(workbench.defs) do
|
||||
for i = 1, #cuttable_nodes do
|
||||
local node = cuttable_nodes[i]
|
||||
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 d[3] then
|
||||
if item_name and workbench_def[3] then
|
||||
local groups = {}
|
||||
local tiles
|
||||
groups.not_in_creative_inventory = 1
|
||||
@ -419,7 +406,7 @@ for i = 1, #cuttable_nodes do
|
||||
end
|
||||
end
|
||||
|
||||
local cutname = d[1]
|
||||
local cutname = workbench_def[1]
|
||||
local tiles_special_cut
|
||||
if custom_tiles and custom_tiles[cutname] then
|
||||
tiles_special_cut = custom_tiles[cutname]
|
||||
@ -427,9 +414,14 @@ for i = 1, #cuttable_nodes do
|
||||
tiles_special_cut = tiles
|
||||
end
|
||||
|
||||
minetest.register_node(":" .. node .. "_" .. cutname, {
|
||||
-- @1: Base node description (e.g. "Stone"); @2: modifier (e.g. "Nanoslab")
|
||||
description = S("@1 @2", def.description, d[4]),
|
||||
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",
|
||||
@ -438,7 +430,7 @@ for i = 1, #cuttable_nodes do
|
||||
use_texture_alpha = def.use_texture_alpha,
|
||||
groups = groups,
|
||||
is_ground_content = def.is_ground_content,
|
||||
node_box = xdecor.pixelbox(16, d[3]),
|
||||
node_box = xdecor.pixelbox(16, workbench_def[3]),
|
||||
sunlight_propagates = true,
|
||||
on_place = minetest.rotate_node
|
||||
})
|
||||
@ -453,32 +445,35 @@ for i = 1, #cuttable_nodes do
|
||||
("stairs:stair_outer_%s"):format(item_name)
|
||||
)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Craft items
|
||||
|
||||
minetest.register_tool("xdecor:hammer", {
|
||||
description = S("Hammer"),
|
||||
_tt_help = S("Repairs tools at the work bench"),
|
||||
inventory_image = "xdecor_hammer.png",
|
||||
wield_image = "xdecor_hammer.png",
|
||||
on_use = function() do
|
||||
return end
|
||||
function workbench:register_cut(nodename, cutlist)
|
||||
if registered_cuttable_nodes[nodename] then
|
||||
minetest.log("error", "[xdecor] Workbench: Tried to register cut for node "..node..", 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
|
||||
|
||||
-- Recipes
|
||||
|
||||
minetest.register_craft({
|
||||
output = "xdecor:hammer",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
|
||||
{"", "group:stick", ""}
|
||||
}
|
||||
})
|
||||
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 = {
|
||||
@ -487,6 +482,121 @@ minetest.register_craft({
|
||||
}
|
||||
})
|
||||
|
||||
-- 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", ""}
|
||||
}
|
||||
})
|
||||
|
BIN
textures/xdecor_cauldron_bottom.png
Normal file
After Width: | Height: | Size: 153 B |
BIN
textures/xdecor_curtain_open_overlay_bottom.png
Normal file
After Width: | Height: | Size: 97 B |
BIN
textures/xdecor_curtain_open_overlay_side.png
Normal file
After Width: | Height: | Size: 107 B |
BIN
textures/xdecor_curtain_open_overlay_top.png
Normal file
After Width: | Height: | Size: 93 B |
Before Width: | Height: | Size: 582 B After Width: | Height: | Size: 785 B |
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 273 B |
BIN
textures/xdecor_trampoline_bottom.png
Normal file
After Width: | Height: | Size: 237 B |
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 165 B |
BIN
textures/xdecor_workbench_bottom.png
Normal file
After Width: | Height: | Size: 648 B |