Compare commits

..

71 Commits
lsr ... master

Author SHA1 Message Date
Wuzzy
1923b6acde Fix bad loading of enchanting 2024-12-02 18:34:23 +01:00
Wuzzy
d6942aa43b Fix typos in Chess readme 2024-12-02 17:27:32 +01:00
Wuzzy
31f37b0665 Add translator comments (form: "--~") 2024-12-02 05:30:07 +01:00
Wuzzy
80c611d640 Update German translation 2024-12-02 05:10:36 +01:00
Wuzzy
720dda3178 Update locale files 2024-12-02 05:10:07 +01:00
Wuzzy
fc81d35ccd Fix missing bracket 2024-12-02 05:09:18 +01:00
Wuzzy
ae27cb8f4f Make enchanted tools inherit groups form original 2024-12-02 05:03:47 +01:00
Wuzzy
78c81f5da6 Soup: Add ingredient blacklist 2024-12-02 03:52:09 +01:00
Wuzzy
e66f056590 Cauldron: Print soup ingredient items to console 2024-12-02 03:44:26 +01:00
Wuzzy
26a7dbb78f Fix broken hammer register 2024-12-02 03:12:33 +01:00
Wuzzy
34d2c56f6d Add initial mod API documentation 2024-12-02 03:06:21 +01:00
Wuzzy
7ac1756f09 Reduce ench. bug net uses 2024-12-02 02:28:52 +01:00
Wuzzy
2c426f53f6 Merge branch 'enchant_bugnet' into enchant_api 2024-12-02 02:28:11 +01:00
Wuzzy
5b1335dbea Reduce ench. steel hoe uses 2024-12-02 02:27:03 +01:00
Wuzzy
5e479b9ac5 Add enchanted bugnet 2024-12-02 02:18:20 +01:00
Wuzzy
4832eb0470 Add enchanted hammers 2024-12-02 01:46:03 +01:00
Wuzzy
2a2cfa4113 Move enchant registrations to separate file 2024-12-02 00:46:38 +01:00
Wuzzy
c81ed3725d Move tool enchants lower in file 2024-12-02 00:39:04 +01:00
Wuzzy
7a746e5660 Add enchanted steel hoe 2024-12-01 23:43:25 +01:00
Wuzzy
f1cf8c3fb0 Add API to register custom tool enchantments 2024-12-01 23:41:26 +01:00
Wuzzy
32188e3eba Enchanting: Set enchantment strength in API 2024-12-01 22:08:11 +01:00
Wuzzy
e513314ae8 Refactor enchanting.lua for readability 2024-12-01 20:43:54 +01:00
Wuzzy
92f31e08cc Add public enchanting function 2024-12-01 20:37:41 +01:00
Wuzzy
8dfd5aade1 Remove dummy fields in enchanting 2024-12-01 20:09:43 +01:00
Wuzzy
a62ef6c183 Redo translation of enchanted tools 2024-12-01 20:08:56 +01:00
Wuzzy
2b91f0d013 Fix typo: "more damages" 2024-12-01 20:00:28 +01:00
Wuzzy
254ae5a4d5 Make enchant % translatable 2024-12-01 19:59:53 +01:00
Wuzzy
ed5b525dd8 Refactor enchanting tool registration 2024-12-01 19:54:45 +01:00
Wuzzy
06c2c9881a Raise min_minetest_version to 5.9 2024-12-01 17:56:06 +01:00
Wuzzy
617ab9b30e Merge branch 'somedumbguy-toolranks-support' 2024-12-01 17:53:34 +01:00
Wuzzy
4ce65f20e2 Update minetest.net to luanti.org link 2024-11-28 13:08:58 +01:00
Wuzzy
20e6a3b379 Rename Minetest to Luanti 2024-10-27 21:37:48 +01:00
Wuzzy
5502e5f052 Fix typo: "Your tool last longer" 2024-10-27 21:11:24 +01:00
Wuzzy
1f33988ac5 Fix sit_destruct calling funcs wrongly 2024-09-19 14:38:11 +02:00
Wuzzy
06e42034f3 Chessbot: Print out more errors in bad state 2024-09-19 14:17:26 +02:00
Wuzzy
2d5e149ba9 Increase bot move delay again 2024-09-19 14:07:31 +02:00
Wuzzy
0e3d242ec6 Merge branch 'chessbot_async' 2024-09-19 14:07:10 +02:00
Wuzzy
0ef5c3eee7 Chess: Fix crash if call get_figurine_id("") 2024-09-19 14:03:07 +02:00
Wuzzy
08991e877c Refactor chess code to access node meta less often 2024-09-19 13:28:58 +02:00
Wuzzy
1156940f99 Fix wrong naming convention for stairs 2024-09-19 10:58:53 +02:00
Wuzzy
ec7410b4e7 Register cut nodes instantly 2024-09-19 10:54:05 +02:00
Wuzzy
6ba25ff14c Simplify xdecor.register_cut documentation 2024-09-19 10:38:29 +02:00
Wuzzy
98cd448992 Doc xdecor.register_cut and check name collision 2024-09-19 10:36:15 +02:00
Wuzzy
2248c5aba6 Mark xdecor.register_cut as experimental 2024-09-19 10:23:28 +02:00
Wuzzy
46ccd93fc9 Add xdecor.register_cut 2024-09-19 10:08:32 +02:00
Wuzzy
454aa6ccc9 Refactor cuttable node registration method 2024-09-19 09:54:26 +02:00
Wuzzy
89c0c1f5a1 Clean up seat state after node destruct 2024-09-19 09:13:19 +02:00
Wuzzy
1ad835bd53 Can now sit down if clicking on side 2024-09-19 08:59:58 +02:00
Wuzzy
2e745748eb Use default animation speeds for sit/stand 2024-09-19 08:57:57 +02:00
Wuzzy
dde5e29a45 Add 'sittable' group to cushion and chair 2024-09-19 08:55:49 +02:00
Wuzzy
7a297813ca Rework sitting; no longer changes player physics 2024-09-19 08:55:21 +02:00
Wuzzy
06e4527ad9 Paintings at floor or ceiling can be rotated 90°
New MT 5.9.0 feature
2024-08-29 20:47:50 +02:00
Wuzzy
0ecd7a357d Fix flipped backside of painting 2024-08-29 20:36:36 +02:00
Wuzzy
2b41a41052 New bottom texture of cauldron+enchant/craftbench 2024-08-29 20:35:54 +02:00
Wuzzy
e39cfeae84 Mention decoration blocks in README 2024-07-25 11:17:10 +02:00
Wuzzy
ba85e514bf No longer set eye offset when sitting on chair
MTG is now doing the eye offset itself
2024-07-23 10:32:11 +02:00
Wuzzy
a84365d3e9 Add translatable mod description 2024-07-23 10:31:52 +02:00
Somedumbguy
b8222cdebe add toolranks to enchanted tools if the mod is enabled 2024-05-29 12:09:46 -04:00
Wuzzy
b981444b7c Fix crash when placing rope in non-creative 2024-03-08 14:55:43 +01:00
Wuzzy
569e4bfa78 Reduce max. rope extension length to 30 2024-03-08 14:49:24 +01:00
Wuzzy
3a896b4a90 Can't extend rope into protected area 2024-03-08 14:05:02 +01:00
Wuzzy
992616a3f0 Improve performance of placing very long ropes 2024-03-08 13:49:23 +01:00
Wuzzy
d88cfbf9d8 Improve curtain a bit 2024-03-08 13:06:30 +01:00
Wuzzy
e4d2154bac Turn curtain into a nodebox 2024-03-08 12:45:37 +01:00
Wuzzy
8610d1e88f Make red curtain darker 2024-03-08 12:37:05 +01:00
Wuzzy
4a10d2f68c Make trampoline a nodebox, add bottom texture 2024-03-08 12:27:24 +01:00
Wuzzy
bac5e5ebf1 Fix some minor mistakes in the Chess readme 2024-03-07 22:40:09 +01:00
Wuzzy
7b391dd8f3 Chess: Fix game not end if checkmate by promotion 2024-03-07 22:40:09 +01:00
Wuzzy
d7fab3bd96 Merge pull request 'Add Italian translation for chess' (#40) from Zughy/xdecor-libre:master into master
Reviewed-on: https://codeberg.org/Wuzzy/xdecor-libre/pulls/40
2024-03-07 20:20:04 +00:00
marco_a
b05c312968 Add Italian translation for chess 2024-03-07 00:11:56 +01:00
Wuzzy
0d8afaff76 Fix 'initial_properties' warning 2023-12-27 21:57:46 +01:00
31 changed files with 1385 additions and 651 deletions

32
API.md Normal file
View 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.

View File

@ -29,9 +29,9 @@ You need a chessboard to play. Craft yourself a chessboard like this:
BWB BWB
sss sss
B = Black Dye * B = Black Dye
W = White Dye * W = White Dye
s = Wooden Slab (from apple tree) * s = Wooden Slab (from apple tree)
Place the chessboard and examine it. You will see a close-up of the chessboard. 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 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 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 aid to see how badly hurt the player's “armies” are.
may change
The top right corner is used for starting a new game. Press The top right corner is used for starting a new game. Press
“New Game” to start a new game. This ends the current game. “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 If the square of the king is attacked, he and the player playing him
is considered to be in “check”. 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. king under attack is not allowed.
#### How to actually move #### 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. in a straight horizontal or vertical line from it.
It cannot move beyond pieces that are in the way. 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. The rook may be involved in Castling, see “King” below.
#### Bishop #### Bishop
@ -218,7 +214,7 @@ If all the conditions are met, heres how you castle:
Place the king two squares towards the rook you want to castle with. 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 This square is where the king will end up. The rook will then
automatically move towards the king and “jump” to the square automatically move towards the king and “jump” to the square
behind the king, from the rooks viewpoint. behind the king, from the rooks viewpoint.
**Remember**: You *must* move the king (not the rook) if you want **Remember**: You *must* move the king (not the rook) if you want
to castle. If you move the rook instead, this is considered to castle. If you move the rook instead, this is considered
@ -234,10 +230,12 @@ pieces have started).
The pawns basic moves are: The pawns basic moves are:
1. Single step: The pawn moves one step vertically towards the 1. Single step: The pawn moves one step vertically towards the
opponents side. It is not possible to walk backwards. opponents side.
2. Double step: Like a single step, but it moves two squares instead. 2. Double step: Like a single step, but it moves two squares instead.
This is only possible from the pawns start position. This is only possible from the pawns start position.
A pawn can never move backwards.
In both cases, the destination square must be empty as well as any crossed square. In both cases, the destination square must be empty as well as any crossed square.
The pawn cannot capture by a single or double step, however. The 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 ##### 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. 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 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 current player. Promotion is mandatory and no other moves are possible
until it is completed. 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. immediately activates its powers. This ends the move.
### The end of the game ### The end of the game
@ -438,8 +436,10 @@ dig the chessboard.
### The Chess Notation ### The Chess Notation
The list of moves is in a special notation called “algebraic notation”. There are many The chessboard interface shows a list of all moves on the right side.
variants of it, so this section explains what it means in X-Decor-libre.
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 This mod uses a longform figurine algebraic notation. “figurine” means that
icons are used for the chess pieces. “longform” means the start 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 #### 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:
* “10” if White won * “10” if White won
* “01” if Black won * “01” 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 #### Example
1. d2—d4 e7—e6 1. d2d4 e7e6
2. ♔e1d2 ♛d8h4 2. ♔e1d2 ♛d8h4
3. d4d5 e6×d5 3. d4d5 e6×d5
... ...
8. d8×d8♖ ♞b8-c6 8. d8×d8♖ ♞b8c6
9. e2e4 d4×e3 e.p. 9. e2e4 d4×e3 e.p.
Explanation of the moves: 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 * 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 * 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 * 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 * 9.: Ninth fullmove: White moves pawn from e2 to e4, black moves pawn from d4 to e3 and captures en passant
#### Other symbols #### Other symbols

View File

@ -1,14 +1,23 @@
## X-Decor-libre [`xdecor`] ## ## 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. 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 It is the same as X-Decor, except with all the non-free files replaced and with
bugfixes. There are no new features. 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 ## Special nodes
Most blocks in this mod are purely decorative, but there are also many special 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 * Pressure Plate: Step on it to activate doors next to it
* Chessboard: Play Chess against a player or the computer (see `CHESS_README.md`) * 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-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. 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 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 * Storage blocks now drop their inventory when exploded
* Made several strings translatable * Made several strings translatable
* Translation updates * Translation updates
* Add support for playerphysics mod
* Add description to every setting * Add description to every setting
* Add tooltip extensions for some interactive items (uses `tt` mod) * Add tooltip extensions for some interactive items (uses `tt` mod)
* Add crafting guide support for `unified_inventory` mod (honey) * Add crafting guide support for `unified_inventory` mod (honey)

View File

@ -1,50 +1,76 @@
local mod_playerphysics = minetest.get_modpath("playerphysics") ~= nil
local mod_player_api = minetest.get_modpath("player_api") ~= nil local mod_player_api = minetest.get_modpath("player_api") ~= nil
local function top_face(pointed_thing) local sitting = {}
if not pointed_thing then return end local seats_occupied = {}
return pointed_thing.above.y > pointed_thing.under.y
local function bottom_face(pointed_thing)
if not pointed_thing then
return
end
return pointed_thing.above.y < pointed_thing.under.y
end 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) function xdecor.sit(pos, node, clicker, pointed_thing)
if not mod_player_api then return end if not mod_player_api then
if not top_face(pointed_thing) then return end 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 player_name = clicker:get_player_name()
local objs = minetest.get_objects_inside_radius(pos, 0.1) local objs = minetest.get_objects_inside_radius(pos, 0.1)
local vel = clicker:get_velocity() local vel = clicker:get_velocity()
local ctrl = clicker:get_player_control() local ctrl = clicker:get_player_control()
for _, obj in pairs(objs) do -- Stand up if sitting
if obj:is_player() and obj:get_player_name() ~= player_name then if sitting[player_name] then
return stand_up(player_name)
end
end
if player_api.player_attached[player_name] then -- Sit down if not sitting and not attached
clicker:set_pos(pos) elseif not sitting[player_name] and not player_api.player_attached[player_name] and node.param2 <= 3 and
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
not ctrl.sneak and vector.equals(vel, vector.new()) then not ctrl.sneak and vector.equals(vel, vector.new()) then
clicker:set_eye_offset({x = 0, y = -7, z = 2}, vector.new()) -- Can't sit down on note already occupied by player
if mod_playerphysics then local hash = minetest.hash_node_position(pos)
playerphysics.add_physics_factor(clicker, "speed", "xdecor:sit_speed", 0) if seats_occupied[hash] then
playerphysics.add_physics_factor(clicker, "jump", "xdecor:sit_jump", 0) return
else
clicker:set_physics_override({speed = 0, jump = 0})
end end
clicker:set_pos(pos)
player_api.player_attached[player_name] = true 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 if node.param2 == 0 then
clicker:set_look_horizontal(0) clicker:set_look_horizontal(0)
@ -55,19 +81,74 @@ function xdecor.sit(pos, node, clicker, pointed_thing)
elseif node.param2 == 3 then elseif node.param2 == 3 then
clicker:set_look_horizontal(math.pi/2) clicker:set_look_horizontal(math.pi/2)
end end
minetest.log("action", "[xdecor] "..player_name.." sits down at "..minetest.pos_to_string(pos, 0))
end end
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) function xdecor.sit_dig(pos, digger)
if not mod_player_api then if not mod_player_api then
return true return true
end end
for _, player in pairs(minetest.get_objects_inside_radius(pos, 0.1)) do local hash = minetest.hash_node_position(pos)
if player:is_player() and if seats_occupied[hash] then
player_api.player_attached[player:get_player_name()] then
return false return false
end end
end
return true return true
end end
-- To be called when a seat (sittable node) got destroyed
-- to clean up state. Precisely, this should be used
-- as the `after_destruct` handler.
function xdecor.sit_destruct(pos)
local hash = minetest.hash_node_position(pos)
local occupier = seats_occupied[hash]
if occupier then
stand_up(occupier)
seats_occupied[hash] = nil
sitting[occupier] = nil
end
end
-- Automatically cause players to stand up if they pressed a control
-- or moved away from the seat
minetest.register_globalstep(function(dtime)
local to_stand_up = {}
for player_name, sitting_pos in pairs(sitting) do
local player = minetest.get_player_by_name(player_name)
if player then
local ctrl = player:get_player_control()
if ctrl.up or ctrl.down or ctrl.left or ctrl.right or ctrl.sneak or ctrl.jump then
table.insert(to_stand_up, player_name)
elseif vector.distance(player:get_pos(), sitting_pos) > 0.55 then
table.insert(to_stand_up, player_name)
end
end
end
for s=1, #to_stand_up do
stand_up(to_stand_up[s])
end
end)
-- Force player to stand on death (to the seat is released)
minetest.register_on_dieplayer(function(player)
local player_name = player:get_player_name()
if sitting[player_name] then
stand_up(player_name)
end
end)
minetest.register_on_leaveplayer(function(player)
local player_name = player:get_player_name()
if sitting[player_name] then
local hash = minetest.hash_node_position(sitting[player_name])
seats_occupied[hash] = nil
sitting[player_name] = nil
end
end)

View File

@ -12,6 +12,7 @@ dofile(modpath .. "/handlers/registration.lua")
dofile(modpath .. "/src/nodes.lua") dofile(modpath .. "/src/nodes.lua")
dofile(modpath .. "/src/recipes.lua") dofile(modpath .. "/src/recipes.lua")
-- Load modules that can be enabled and disabled by settings
local subpart = { local subpart = {
"chess", "chess",
"cooking", "cooking",
@ -21,14 +22,23 @@ local subpart = {
"mailbox", "mailbox",
"mechanisms", "mechanisms",
"rope", "rope",
-- Workbench MUST be loaded after all other subparts that register nodes
-- last for the default 'cut node' registrations to work
"workbench", "workbench",
} }
for _, name in ipairs(subpart) do for _, name in ipairs(subpart) do
local enable = minetest.settings:get_bool("enable_xdecor_" .. name) local enable = minetest.settings:get_bool("enable_xdecor_" .. name, true)
if enable or enable == nil then if enable then
dofile(modpath .. "/src/" .. name .. ".lua") dofile(modpath .. "/src/" .. name .. ".lua")
end end
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

View File

@ -1,15 +1,41 @@
# textdomain: xdecor # 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=
Weak Computer 1= Weak Computer 1=
Weak Computer 2= Weak Computer 2=
Chess= Chess=
Chess Debug= Chess Debug=
Select a game mode=
Select a mode:=
Singleplayer=
Multiplayer=
Bot vs Bot=
check= check=
checkmate= checkmate=
resigned= resigned=
winner= winner=
loser= loser=
draw= 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 have checkmated @1. You win!=
You were checkmated by @1. You lose!= You were checkmated by @1. You lose!=
The game ended up in a stalemate! It's a draw!= The game ended up in a stalemate! It's a draw!=
@ -54,27 +80,6 @@ White Queen=
Black Queen= Black Queen=
White King= White King=
Black 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= Light a fire below to heat it up=
Use a bowl to eat the soup= Use a bowl to eat the soup=
Drop foods inside to make a soup= Drop foods inside to make a soup=
@ -100,40 +105,33 @@ Bowl of soup=
Efficiency= Efficiency=
Durability= Durability=
Sharpness= Sharpness=
@1 (+@2%)=
Your weapon inflicts more damage=
Your tool lasts longer=
Your tool digs faster=
Enchantment Table= Enchantment Table=
Enchant your tools with mese crystals= Enchant your tools with mese crystals=
Enchanted @1 @2@n@3= Enchanted @1@n@2=
Enchanted @1 @2= Enchanted @1=
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=
The bees are busy making honey.= The bees are busy making honey.=
The bees are looking for flowers.= The bees are looking for flowers.=
The bees want to pollinate more flowers.= The bees want to pollinate more flowers.=
The bees are idle.= The bees are idle.=
The bees are resting.= The bees are resting.=
Artificial Hive=
Bees live here and produce honey=
Honey=
Made by bees=
@1 (owned by @2)= @1 (owned by @2)=
Item Frame= Item Frame=
For presenting a single item= For presenting a single item=
@1's Mailbox=
The mailbox is full.=
Mailbox=
Lets other players give you things=
× @1= × @1=
Mailbox=
Last donators= Last donators=
Send your goods to@n@1= Send your goods to@n@1=
@1's Mailbox=
The mailbox is full.=
Lets other players give you things=
Opens doors when stepped on= Opens doors when stepped on=
Wooden Pressure Plate= Wooden Pressure Plate=
Stone Pressure Plate= Stone Pressure Plate=
@ -196,10 +194,6 @@ Television=
Wood Framed Glass= Wood Framed Glass=
Radio= Radio=
Speaker= Speaker=
@1 Stair=
Inner @1 Stair=
Outer @1 Stair=
@1 Slab=
Rope= Rope=
Nanoslab= Nanoslab=
Micropanel= Micropanel=
@ -211,13 +205,13 @@ Slab=
Double Panel= Double Panel=
Half-Stair= Half-Stair=
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= Cut=
Repair= Repair=
Crafting= Crafting=
Storage= Storage=
Back= Back=
Work Bench=
For cutting blocks, repairing tools with a hammer, crafting and storing items=
@1 @2=
Repairs tools at the work bench=
Hammer=

View File

@ -1,15 +1,41 @@
# textdomain: xdecor # 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=Schwacher Computer
Weak Computer 1=Schwacher Computer 1 Weak Computer 1=Schwacher Computer 1
Weak Computer 2=Schwacher Computer 2 Weak Computer 2=Schwacher Computer 2
Chess=Schach Chess=Schach
Chess Debug=Schachdebug 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 check=Schach
checkmate=Schachmatt checkmate=Schachmatt
resigned=aufgegeben resigned=aufgegeben
winner=Sieger winner=Sieger
loser=Verlierer loser=Verlierer
draw=Remis 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 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! You were checkmated by @1. You lose!=Sie wurden von @1 schachmatt gesetzt. Niederlage!
The game ended up in a stalemate! It's a draw!=Das Partie endete in einem Patt. Das ist ein Remis! The game ended up in a 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 Black Queen=Schwarze Dame
White King=Weißer König White King=Weißer König
Black King=Schwarzer 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 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 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 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 Efficiency=Effizienz
Durability=Haltbarkeit Durability=Haltbarkeit
Sharpness=Schärfe 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 Enchantment Table=Zaubertisch
Enchant your tools with mese crystals=Werkzeuge mit Mesekristallen verzaubern Enchant your tools with mese crystals=Werkzeuge mit Mesekristallen verzaubern
Enchanted @1 @2@n@3=@1@2 (verzaubert)@n@3 Enchanted @1@n@2=@1 (verzaubert)@n@2
Enchanted @1 @2=@1@2 (verzaubert) Enchanted @1=@1 (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
The bees are busy making honey.=Die Bienen sind beschäftigt, Honig herzustellen. 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 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 want to pollinate more flowers.=Die Bienen wollen mehr Blumen bestäuben.
The bees are idle.=Die Bienen sind unbeschäftigt. The bees are idle.=Die Bienen sind unbeschäftigt.
The bees are resting.=Die Bienen ruhen sich aus. 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) @1 (owned by @2)=@1 (Eigentum von @2)
Item Frame=Gegenstandsrahmen Item Frame=Gegenstandsrahmen
For presenting a single item=Präsentiert einen einzelnen Gegenstand 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 × @1=× @1
Mailbox=Briefkasten
Last donators=Letzte Spender Last donators=Letzte Spender
Send your goods to@n@1=Senden Sie Ihre Waren an@n@1 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 Opens doors when stepped on=Öffnet Türen beim Betreten
Wooden Pressure Plate=Holzdruckplatte Wooden Pressure Plate=Holzdruckplatte
Stone Pressure Plate=Steindruckplatte Stone Pressure Plate=Steindruckplatte
@ -196,10 +194,6 @@ Television=Fernseher
Wood Framed Glass=Holzrahmenglas Wood Framed Glass=Holzrahmenglas
Radio=Radio Radio=Radio
Speaker=Lautsprecher Speaker=Lautsprecher
@1 Stair=@1treppe
Inner @1 Stair=Innere @1treppe
Outer @1 Stair=Äußere @1treppe
@1 Slab=@1platte
Rope=Seil Rope=Seil
Nanoslab=nanoplatte Nanoslab=nanoplatte
Micropanel=mikropaneel Micropanel=mikropaneel
@ -211,13 +205,13 @@ Slab=platte
Double Panel=doppelpaneel Double Panel=doppelpaneel
Half-Stair=halbtreppe Half-Stair=halbtreppe
Stair=Treppe 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 Cut=Zuschnitt
Repair=Reparatur Repair=Reparatur
Crafting=Fertigung Crafting=Fertigung
Storage=Lager Storage=Lager
Back=Zurück Back=Zurück
Work Bench=Werkbank
For cutting blocks, repairing tools with a hammer, crafting and storing items=Für Blockzuschnitt, Werkzeugreparatur mit Hammer, Fertigung und Lagerung
@1 @2=@1@2
Repairs tools at the work bench=Repariert Werkzeuge an der Werkbank
Hammer=Hammer

View File

@ -1,15 +1,41 @@
# textdomain: xdecor # 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=
Weak Computer 1= Weak Computer 1=
Weak Computer 2= Weak Computer 2=
Chess=Echecs Chess=Echecs
Chess Debug= Chess Debug=
Select a game mode=
Select a mode:=Sélectionnez un mode de jeu:
Singleplayer=Solo
Multiplayer=Multijoueur
Bot vs Bot=
check=échec check=échec
checkmate= checkmate=
resigned= resigned=
winner= winner=
loser= loser=
draw= 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 have checkmated @1. You win!=
You were checkmated by @1. You lose!= You were checkmated by @1. You lose!=
The game ended up in a stalemate! It's a draw!= The game ended up in a stalemate! It's a draw!=
@ -54,27 +80,6 @@ White Queen=Reine blanche
Black Queen=Reine noire Black Queen=Reine noire
White King=Roi blanc White King=Roi blanc
Black King=Roi noir 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= Light a fire below to heat it up=
Use a bowl to eat the soup=Utilisez un bol pour boire la soupe Use a bowl to eat the soup=Utilisez un bol pour boire la soupe
Drop foods inside to make a soup=Placez des ingrédients à lintérieur pour faire une soupe Drop foods inside to make a soup=Placez des ingrédients à lintérieur pour faire une soupe
@ -100,40 +105,33 @@ Bowl of soup=Bol de soupe
Efficiency=Efficacité Efficiency=Efficacité
Durability=Durabilité Durability=Durabilité
Sharpness=Tranchant Sharpness=Tranchant
@1 (+@2%)=
Your weapon inflicts more damage=Votre arme inflige plus de dégâts
Your tool lasts longer=Votre outil dure plus longtemps
Your tool digs faster=Votre outil creuse plus vite
Enchantment Table=Table denchantements Enchantment Table=Table denchantements
Enchant your tools with mese crystals= Enchant your tools with mese crystals=
Enchanted @1 @2@n@3=@2 en @1 enchantée@n@3 Enchanted @1@n@2=
Enchanted @1 @2=@2 en @1 enchantée Enchanted @1=
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=
The bees are busy making honey.= The bees are busy making honey.=
The bees are looking for flowers.= The bees are looking for flowers.=
The bees want to pollinate more flowers.= The bees want to pollinate more flowers.=
The bees are idle.= The bees are idle.=
The bees are resting.= 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) @1 (owned by @2)=@1 (propriété de @2)
Item Frame=Cadre Item Frame=Cadre
For presenting a single item= 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= × @1=
Mailbox=Boite aux lettres
Last donators=Derniers donateurs Last donators=Derniers donateurs
Send your goods to@n@1=Envoyer vos biens à@n@1 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= Opens doors when stepped on=
Wooden Pressure Plate=Plaque de pression en bois Wooden Pressure Plate=Plaque de pression en bois
Stone Pressure Plate=Plaque de pression en pierre Stone Pressure Plate=Plaque de pression en pierre
@ -196,10 +194,6 @@ Television=Télévision
Wood Framed Glass=Verre encadré par du bois Wood Framed Glass=Verre encadré par du bois
Radio= Radio=
Speaker= Speaker=
@1 Stair=
Inner @1 Stair=
Outer @1 Stair=
@1 Slab=
Rope=Corde Rope=Corde
Nanoslab= Nanoslab=
Micropanel= Micropanel=
@ -211,21 +205,22 @@ Slab=
Double Panel= Double Panel=
Half-Stair= Half-Stair=
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 Cut=Couper
Repair=Réparer Repair=Réparer
Crafting=Fabrication Crafting=Fabrication
Storage=Stockage Storage=Stockage
Back=Retour 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 ##### ##### 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 nest pas votre tour de jouer, réessayez dans @1 You can't reset the chessboard, a game has been started. If you aren't a current player, try again in @1=Vous ne pouvez pas mettre à zéro léchiquier, une partie a été commencée. Si ce nest pas votre tour de jouer, réessayez dans @1
You can't dig the chessboard, a game has been started. Reset it first if you're a current player, or dig it again in @1=Vous ne pouvez pas récupérer léchiquier, une partie à été commencée. Remettez le à zéro si vous cest votre tour de jouer, ou réessayez dans @1 You can't dig the chessboard, a game has been started. Reset it first if you're a current player, or dig it again in @1=Vous ne pouvez pas récupérer léchiquier, une partie à été commencée. Remettez le à zéro si vous cest votre tour de jouer, ou réessayez dans @1
Cauldron (idle)=Chaudron (inactif) Cauldron (idle)=Chaudron (inactif)

View File

@ -1,47 +1,75 @@
# textdomain: xdecor # textdomain: xdecor
Weak Computer= # author: Zughy (chess)
Weak Computer 1= # 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`)
Weak Computer 2= 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=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 check=scacco
checkmate= checkmate=scacco matto
resigned= resigned=arresə
winner= winner=vince
loser= loser=perde
draw= draw=patta
You have checkmated @1. You win!= PROMOTION@nFOR BLACK!=PROMOZIONE@nPER IL NERO!
You were checkmated by @1. You lose!= Promote pawn to:=Promuovi pedone a:
The game ended up in a stalemate! It's a draw!= PROMOTION@nFOR WHITE!=PROMOZIONE@nPER IL BIANCO!
The game ended up in a dead position! It's a draw!= DRAW CLAIM@nBY WHITE!=PATTA DICHIARATA@nDAL BIANCO!
No piece was captured and no pawn was moved for 75 consecutive moves of each player. It's a draw!= DRAW CLAIM@nBY BLACK!=PATTA DICHIARATA@nDAL NERO!
You have drawn the game by invoking the 50-move rule.= 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.
@1 has drawn the game by invoking the 50-move rule.= 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
You have failed to make a game-drawing move. The game continues.= New game=Nuova partita
@1 made a draw claim using the 50-move rule but it was false. The game continues.= Resign=Arrenditi
The exact same position has occured 5 times. It's a draw!= Select a color:=Seleziona un colore:
You have drawn the game by invoking the threefold repetition rule.= White=Bianco
@1 has drawn the game by invoking the threefold repetition rule.= Black=Nero
@1 made a draw claim using the threefold repetition rule but it was false. The game continues.= 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 Chess Board=Scacchiera
Someone else plays white pieces!=Qualcun altro gioca con il bianco! Someone else plays white pieces!=Qualcun`altrə sta già giocando con il bianco!
It's not your turn!= It's not your turn!=Non è il tuo turno!
Someone else plays black pieces!=Qualcun altro gioca con il nero! Someone else plays black pieces!=Qualcun`altrə sta già giocando con il nero!
Black cannot move first!= Black cannot move first!=Il nero non può muovere per primo!
@1 s= @1 s=@1 s
@1 min @2 s= @1 min @2 s=@1 min @2 s
You can't reset the chessboard, a game has been started. Try again in @1.= 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.= Resigning is not possible yet.=Non ci si può ancora arrendere.
You have resigned.= You have resigned.=Ti sei arresə.
@1 has resigned. You win!= @1 has resigned. You win!=@1 si è arresə. Hai vinto!
You can't resign, you're not playing in this game.= 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!= 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.= 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.= 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.= 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.= 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.= 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= 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 White Pawn=Pedone bianco
Black Pawn=Pedone nero Black Pawn=Pedone nero
White Rook=Torre bianca White Rook=Torre bianca
@ -54,27 +82,6 @@ White Queen=Regina bianca
Black Queen=Regina nera Black Queen=Regina nera
White King=Re bianco White King=Re bianco
Black King=Re nero 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= Light a fire below to heat it up=
Use a bowl to eat the soup=Utilizzare una ciotola per mangiare la zuppa 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 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 Efficiency=Efficacia
Durability=Durabilità Durability=Durabilità
Sharpness=Affilatezza 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 Enchantment Table=Tavolo per migliorie
Enchant your tools with mese crystals= Enchant your tools with mese crystals=
Enchanted @1 @2@n@3=@2 su @1 incantesimo@n@3 Enchanted @1@n@2=
Enchanted @1 @2=@2 su @1 incantesimo Enchanted @1=
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=
The bees are busy making honey.= The bees are busy making honey.=
The bees are looking for flowers.= The bees are looking for flowers.=
The bees want to pollinate more flowers.= The bees want to pollinate more flowers.=
The bees are idle.= The bees are idle.=
The bees are resting.= 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) @1 (owned by @2)=@1 (proprietà di @2)
Item Frame=Teca Item Frame=Teca
For presenting a single item= 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= × @1=
Mailbox=Cassetta delle lettere
Last donators=Ultimi donatori Last donators=Ultimi donatori
Send your goods to@n@1=Invia i tuoi item a@n@1 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= Opens doors when stepped on=
Wooden Pressure Plate=Placca di pressione di legno Wooden Pressure Plate=Placca di pressione di legno
Stone Pressure Plate=Placca di pressione di pietra Stone Pressure Plate=Placca di pressione di pietra
@ -196,10 +196,6 @@ Television=Televisione
Wood Framed Glass=Cornice in legno con vetro Wood Framed Glass=Cornice in legno con vetro
Radio= Radio=
Speaker= Speaker=
@1 Stair=
Inner @1 Stair=
Outer @1 Stair=
@1 Slab=
Rope=Corda Rope=Corda
Nanoslab= Nanoslab=
Micropanel= Micropanel=
@ -211,23 +207,22 @@ Slab=
Double Panel= Double Panel=
Half-Stair= Half-Stair=
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 Cut=Tagliare
Repair=Riparare Repair=Riparare
Crafting=Fabbricare Crafting=Fabbricare
Storage=Conservare Storage=Conservare
Back=Indietro 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 ##### ##### not used anymore #####
Dumb AI=AI stupida Enchanted @1 @2@n@3=@2 su @1 incantesimo@n@3
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 Enchanted @1 @2=@2 su @1 incantesimo
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
Cauldron (idle)=Calderone (inattivo) 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) - Drop foods inside to make a soup=Calderone (attivo) - Mettere gli ingredienti all'interno per fare una zuppa.
Cauldron (active) - Use a bowl to eat the soup=Calderone (actif) - Utilizzare una ciotola per mangiare la zuppa Cauldron (active) - Use a bowl to eat the soup=Calderone (actif) - Utilizzare una ciotola per mangiare la zuppa

View File

@ -1,6 +1,6 @@
name = xdecor name = xdecor
title = X-Decor-libre 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 depends = default, bucket, doors, farming, stairs, xpanes
optional_depends = playerphysics, player_api, fire, moreblocks, mesecons, unified_inventory, tt optional_depends = player_api, fire, moreblocks, mesecons, unified_inventory, tt, toolranks
min_minetest_version = 5.7.0 min_minetest_version = 5.9

View File

@ -327,11 +327,10 @@ local function en_passant_to_string(double_step)
return s_en_passant return s_en_passant
end 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 from_x, from_y = index_to_xy(from_idx)
local to_x, to_y = index_to_xy(to_idx) local to_x, to_y = index_to_xy(to_idx)
local inv = meta:get_inventory() local kingPiece = board[from_idx]
local kingPiece = inv:get_stack(from_list, from_idx):get_name()
local kingColor local kingColor
if kingPiece:find("black") then if kingPiece:find("black") then
kingColor = "black" kingColor = "black"
@ -340,21 +339,21 @@ local function can_castle(meta, board, from_list, from_idx, to_idx)
end end
local possible_castles = { local possible_castles = {
-- white queenside -- 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 -- 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 -- 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 -- 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 for p=1, #possible_castles do
local pc = possible_castles[p] 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 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 castlingRightVal = castlingRights[pc.rightName]
local rookPiece = inv:get_stack(from_list, pc.rook_idx):get_name() local rookPiece = board[pc.rook_idx]
if castlingMeta == 1 and rookPiece == "realchess:rook_"..kingColor.."_"..pc.rook_id then if castlingRightVal == 1 and rookPiece == "realchess:rook_"..kingColor.."_"..pc.rook_id then
-- Check if all squares between king and rook are empty -- Check if all squares between king and rook are empty
local empty_start, empty_end local empty_start, empty_end
if pc.acheck_dir == -1 then 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 empty_end = pc.rook_idx - 1
end end
for i = empty_start, empty_end do for i = empty_start, empty_end do
if inv:get_stack(from_list, i):get_name() ~= "" then if board[i] ~= "" then
return false return false
end end
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 -- 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. -- is the case, false otherwise.
-- Parameters: -- 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_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 -- * 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) -- * prevDoublePawnStepTo: if a pawn did a double-step in the previous halfmove, this is the board index of the destination.
local inv = meta:get_inventory() -- if no pawn made a double-step in the previous halfmove, this is nil or 0.
local victimPiece = inv:get_stack("board", victim_index) local function can_capture_en_passant(board, victim_color, victim_index, prevDoublePawnStepTo)
local double_step_index = meta:get_int("prevDoublePawnStepTo") local victimPiece = board[victim_index]
local victim_name = victimPiece:get_name() local double_step_index = prevDoublePawnStepTo or 0
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 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 return true
end end
return false return false
@ -413,7 +412,7 @@ end
-- Any key with a numeric value is a possible destination. -- 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. -- 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 -- 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+)") local piece, color = board[from_idx]:match(":(%w+)_(%w+)")
if not piece then if not piece then
return {} return {}
@ -450,7 +449,7 @@ local function get_theoretical_moves_from(meta, board, from_idx)
can_capture = true can_capture = true
else else
-- en passant -- 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 can_capture = true
en_passant = true en_passant = true
end end
@ -505,7 +504,7 @@ local function get_theoretical_moves_from(meta, board, from_idx)
can_capture = true can_capture = true
else else
-- en passant -- 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 can_capture = true
en_passant = true en_passant = true
end end
@ -783,11 +782,10 @@ local function get_theoretical_moves_from(meta, board, from_idx)
-- KING -- KING
elseif piece == "king" then elseif piece == "king" then
local inv = meta:get_inventory()
-- King can't move to any attacked square -- King can't move to any attacked square
-- king_board simulates the board with the king moved already. -- king_board simulates the board with the king moved already.
-- Required for the attacked() check to work -- 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[to_idx] = king_board[from_idx]
king_board[from_idx] = "" king_board[from_idx] = ""
if realchess.attacked(color, to_idx, king_board) then if realchess.attacked(color, to_idx, king_board) then
@ -805,7 +803,7 @@ local function get_theoretical_moves_from(meta, board, from_idx)
end end
if dx > 1 or dy > 1 then 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 if not cc then
moves[to_idx] = nil moves[to_idx] = nil
end end
@ -847,10 +845,10 @@ end
-- origin_index is the board index for the square to start the piece from (as string) -- 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. -- 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. -- 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 = {} local moves = {}
for i = 1, 64 do 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 if next(possibleMoves) then
local stack_name = board[i] local stack_name = board[i]
if stack_name:find(player) then if stack_name:find(player) then
@ -1089,8 +1087,12 @@ end
local function get_figurine_id(piece_itemname) local function get_figurine_id(piece_itemname)
local piece_s = piece_itemname:match(":(%w+_%w+)") local piece_s = piece_itemname:match(":(%w+_%w+)")
if not piece_s then
return MOVES_LIST_SYMBOL_EMPTY
else
return figurines_str:match("(%d+)=chess_figurine_" .. piece_s) return figurines_str:match("(%d+)=chess_figurine_" .. piece_s)
end end
end
local fs_gamemode_x local fs_gamemode_x
@ -1678,17 +1680,17 @@ local function update_formspec(meta)
local turnWhite = minetest.colorize("#000001", playerWhiteDisplay) local turnWhite = minetest.colorize("#000001", playerWhiteDisplay)
-- several status words for the player -- several status words for the player
-- player is in check --~ Chess: player is in check
local check_s = minetest.colorize("#FF8000", "["..S("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").."]") local mate_s = minetest.colorize("#FF0000", "["..S("checkmate").."]")
-- player has resigned --~ Chess: player has resigned
local resign_s = minetest.colorize("#FF0000", "["..S("resigned").."]") local resign_s = minetest.colorize("#FF0000", "["..S("resigned").."]")
-- player has won --~ Chess: player has won
local win_s = minetest.colorize("#26AB2B", "["..S("winner").."]") local win_s = minetest.colorize("#26AB2B", "["..S("winner").."]")
-- player has lost --~ Chess: player has lost
local lose_s = minetest.colorize("#FF0000", "["..S("loser").."]") 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 draw_s = minetest.colorize("#FF00FF", "["..S("draw").."]")
local status_black = "" local status_black = ""
@ -1733,6 +1735,7 @@ local function update_formspec(meta)
if promotion == "black" then if promotion == "black" then
eaten_img = "" eaten_img = ""
promotion_formstring = 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!").."]" .. "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]" "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 if botColor ~= "black" and botColor ~= "both" then
@ -1748,6 +1751,7 @@ local function update_formspec(meta)
elseif promotion == "white" then elseif promotion == "white" then
eaten_img = "" eaten_img = ""
promotion_formstring = 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!").."]" .. "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]" "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 if botColor ~= "white" and botColor ~= "both" then
@ -1765,8 +1769,10 @@ local function update_formspec(meta)
local draw_claim_formstring = "" local draw_claim_formstring = ""
if drawClaim ~= "" and gameResult == "" then if drawClaim ~= "" and gameResult == "" then
if lastMove == "black" or lastMove == "" 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!").."]" draw_claim_formstring = "label[10.1,6.35;"..FS("DRAW CLAIM\nBY WHITE!").."]"
else 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!").."]" draw_claim_formstring = "label[10.1,6.35;"..FS("DRAW CLAIM\nBY BLACK!").."]"
end end
if drawClaim == "50_move_rule" then if drawClaim == "50_move_rule" then
@ -1790,6 +1796,7 @@ local function update_formspec(meta)
if playerActionsAvailable and (playerWhite ~= "" and playerBlack ~= "") then 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;]" .. 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").."]" "tooltip[resign;"..FS("Resign").."]"
end 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 -- 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;]".. 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;".. "tooltip[draw_50_moves;"..
--~ Chess
FS("Invoke the 50-move rule for your next move").."]" FS("Invoke the 50-move rule for your next move").."]"
elseif halfmoveClock >= DRAWCLAIM_LONGGAME_PLAYER then elseif halfmoveClock >= DRAWCLAIM_LONGGAME_PLAYER then
-- When the 50 moves without capture / pawn move have occured occur. -- When the 50 moves without capture / pawn move have occured occur.
-- Will insta-draw. -- Will insta-draw.
game_buttons = game_buttons .. "image_button[13.36,9.7;0.8,0.8;chess_draw_50move.png;draw_50_moves;]".. 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;".. "tooltip[draw_50_moves;"..
--~ Chess
FS("Invoke the 50-move rule and draw the game").."]" FS("Invoke the 50-move rule and draw the game").."]"
end end
@ -1828,12 +1837,14 @@ local function update_formspec(meta)
-- Will insta-draw. -- Will insta-draw.
game_buttons = game_buttons .. "image_button[12.36,9.7;0.8,0.8;chess_draw_repeat3.png;draw_repeat_3;]".. 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;".. "tooltip[draw_repeat_3;"..
--~ Chess
FS("Invoke the threefold repetition rule and draw the game").."]" FS("Invoke the threefold repetition rule and draw the game").."]"
elseif maxRepeatedPositions >= 2 then elseif maxRepeatedPositions >= 2 then
-- If the same position may be about to occur 3 times. -- 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. -- 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;]".. 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;".. "tooltip[draw_repeat_3;"..
--~ Chess
FS("Invoke the threefold repetition rule for your next move").."]" FS("Invoke the threefold repetition rule for your next move").."]"
end end
end end
@ -1893,19 +1904,26 @@ local function update_formspec(meta)
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
end end
local function update_game_result(meta) local function update_game_result(meta, lastMove)
local inv = meta:get_inventory() local inv = meta:get_inventory()
local board_t = realchess.board_to_table(inv) local board_t = realchess.board_to_table(inv)
local playerWhite = meta:get_string("playerWhite") local playerWhite = meta:get_string("playerWhite")
local playerBlack = meta:get_string("playerBlack") 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) update_formspec(meta)
local blackCanMove = false local blackCanMove = false
local whiteCanMove = false local whiteCanMove = false
local blackMoves = realchess.get_theoretical_moves_for(meta, board_t, "black") local blackMoves = realchess.get_theoretical_moves_for(board_t, "black", prevDoublePawnStepTo, castlingRights)
local whiteMoves = realchess.get_theoretical_moves_for(meta, board_t, "white") local whiteMoves = realchess.get_theoretical_moves_for(board_t, "white", prevDoublePawnStepTo, castlingRights)
if next(blackMoves) then if next(blackMoves) then
blackCanMove = true blackCanMove = true
end end
@ -1913,9 +1931,6 @@ local function update_game_result(meta)
whiteCanMove = true whiteCanMove = true
end 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) local black_king_idx, white_king_idx = realchess.locate_kings(board_t)
if not black_king_idx or not white_king_idx then if not black_king_idx or not white_king_idx then
minetest.log("error", "[xdecor] Chess: Insufficient kings on chessboard!") 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("gameResult", "draw")
meta:set_string("gameResultReason", "stalemate") meta:set_string("gameResultReason", "stalemate")
add_special_to_moves_list(meta, "draw") 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) 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") minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw by stalemate")
return return
@ -1988,6 +2004,7 @@ local function update_game_result(meta)
meta:set_string("gameResult", "draw") meta:set_string("gameResult", "draw")
meta:set_string("gameResultReason", "stalemate") meta:set_string("gameResultReason", "stalemate")
add_special_to_moves_list(meta, "draw") 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) 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") minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw by stalemate")
return return
@ -1999,6 +2016,7 @@ local function update_game_result(meta)
meta:set_string("gameResult", "draw") meta:set_string("gameResult", "draw")
meta:set_string("gameResultReason", "dead_position") meta:set_string("gameResultReason", "dead_position")
add_special_to_moves_list(meta, "draw") 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) 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") minetest.log("action", "[xdecor] Chess: A game between "..playerWhite.." and "..playerBlack.." ended in a draw by dead position")
end end
@ -2114,6 +2132,7 @@ local function update_game_result(meta)
meta:set_string("gameResult", "draw") meta:set_string("gameResult", "draw")
meta:set_string("gameResultReason", "same_position_5") meta:set_string("gameResultReason", "same_position_5")
add_special_to_moves_list(meta, "draw") 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!") local msg = S("The exact same position has occured 5 times. It's a draw!")
send_message_2(playerWhite, playerBlack, msg, botColor) 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") 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 lastMove = meta:get_string("lastMove")
local playerWhite = meta:get_string("playerWhite") local playerWhite = meta:get_string("playerWhite")
local playerBlack = meta:get_string("playerBlack") local playerBlack = meta:get_string("playerBlack")
local prevDoublePawnStepTo = meta:get_int("prevDoublePawnStepTo")
local kingMoved = false local kingMoved = false
local thisMove -- Will replace lastMove when move is legal 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 can_capture = true
else else
-- en passant -- 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 can_capture = true
en_passant_target = xy_to_index(to_x, from_y) en_passant_target = xy_to_index(to_x, from_y)
end end
@ -2417,7 +2438,8 @@ function realchess.move(meta, from_list, from_index, to_list, to_index, playerNa
can_capture = true can_capture = true
else else
-- en passant -- 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 can_capture = true
en_passant_target = xy_to_index(to_x, from_y) en_passant_target = xy_to_index(to_x, from_y)
end end
@ -2669,9 +2691,15 @@ function realchess.move(meta, from_list, from_index, to_list, to_index, playerNa
local check = true local check = true
local inv = meta:get_inventory() local inv = meta:get_inventory()
local board = realchess.board_to_table(inv) 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 -- 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 if cc then
inv:set_stack(from_list, rook_goal, rook_name) inv:set_stack(from_list, rook_goal, rook_name)
inv:set_stack(from_list, rook_start, "") inv:set_stack(from_list, rook_start, "")
@ -2799,11 +2827,11 @@ local function timeout_format(timeout_limit)
local seconds = time_remaining % 60 local seconds = time_remaining % 60
if minutes == 0 then if minutes == 0 then
-- number of seconds --~ number of seconds
return S("@1 s", seconds) return S("@1 s", seconds)
end end
-- number of minutes and seconds --~ number of minutes and seconds
return S("@1 min @2 s", minutes, seconds) return S("@1 min @2 s", minutes, seconds)
end end
@ -2885,6 +2913,7 @@ function realchess.fields(pos, _, fields, sender)
local lastMove = meta:get_string("lastMove") local lastMove = meta:get_string("lastMove")
if (playerName == playerWhite and playerWhite == "") or (playerName == playerBlack and playerBlack == "") then if (playerName == playerWhite and playerWhite == "") or (playerName == playerBlack and playerBlack == "") then
-- Can't resign before the player name has been recorded -- 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.")) send_message(playerName, S("Resigning is not possible yet."))
return return
end end
@ -3035,9 +3064,11 @@ function realchess.fields(pos, _, fields, sender)
local pcolor = promo:sub(-5) local pcolor = promo:sub(-5)
local activePromo = meta:get_string("promotionActive") local activePromo = meta:get_string("promotionActive")
if activePromo == "" then if activePromo == "" then
--~ Chess message
send_message(playerName, S("This isn't the time for promotion.")) send_message(playerName, S("This isn't the time for promotion."))
return return
elseif activePromo ~= pcolor then elseif activePromo ~= pcolor then
--~ Chess message
send_message(playerName, S("It's not your turn! This promotion is meant for the other player.")) send_message(playerName, S("It's not your turn! This promotion is meant for the other player."))
return return
end end
@ -3045,6 +3076,7 @@ function realchess.fields(pos, _, fields, sender)
realchess.promote_pawn(meta, pcolor, promo:sub(1, -7)) realchess.promote_pawn(meta, pcolor, promo:sub(1, -7))
return return
else else
--~ Chess message
send_message(playerName, S("It's not your turn! This promotion is meant for the other player.")) send_message(playerName, S("It's not your turn! This promotion is meant for the other player."))
return return
end end
@ -3111,16 +3143,19 @@ function realchess.move_piece(meta, pieceFrom, from_list, from_index, to_list, t
add_to_eaten_list(meta, pieceTo) add_to_eaten_list(meta, pieceTo)
end end
local lastMove = meta:get_string("lastMove")
if lastMove == "" then lastMove = "black" end
local promo = meta:get_string("promotionActive") ~= "" local promo = meta:get_string("promotionActive") ~= ""
if not promo then 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 end
update_formspec(meta) update_formspec(meta)
local botColor = meta:get_string("botColor") local botColor = meta:get_string("botColor")
if botColor == "" then botColor = "black" end 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 mode = meta:get_string("mode")
local gameResult = meta:get_string("gameResult") local gameResult = meta:get_string("gameResult")
-- Let the bot play when it its turn -- 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("promotionPawnFromIdx", 0)
meta:set_int("promotionPawnToIdx", 0) meta:set_int("promotionPawnToIdx", 0)
realchess.update_state(meta, from_idx, to_idx, color, promoteFrom:get_name(), pstr) realchess.update_state(meta, from_idx, to_idx, color, promoteFrom:get_name(), pstr)
update_game_result(meta, color)
update_formspec(meta) update_formspec(meta)
local botColor = meta:get_string("botColor") 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) realchess.move(meta, from_list, from_index, to_list, to_index, playerName)
-- We always return 0 to disable all *builtin* inventory moves, since -- 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 -- 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 return 0
end end
chessboarddef.allow_metadata_inventory_take = function() return 0 end chessboarddef.allow_metadata_inventory_take = function() return 0 end
chessboarddef.allow_metadata_inventory_put = 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 -- 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. -- awkward behavior when swapping items.
minetest.register_lbm({ minetest.register_lbm({

View File

@ -36,15 +36,60 @@ local function best_move(moves)
return tonumber(choice_from), choice_to return tonumber(choice_from), choice_to
end end
function chessbot.move(inv, meta) function chessbot.choose_move(board_t, meta_t)
local board_t = realchess.board_to_table(inv) local lastMove = meta_t["lastMove"]
local lastMove = meta:get_string("lastMove") local gameResult = meta_t["gameResult"]
local gameResult = meta:get_string("gameResult") local botColor = meta_t["botColor"]
local botColor = meta:get_string("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 if botColor == "" then
return return
end end
local currentBotColor, opponentColor 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 local botName
if botColor == "black" then if botColor == "black" then
currentBotColor = "black" currentBotColor = "black"
@ -60,34 +105,26 @@ function chessbot.move(inv, meta)
currentBotColor = "black" currentBotColor = "black"
end end
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 if currentBotColor == "white" then
botName = meta:get_string("playerWhite") botName = meta:get_string("playerWhite")
else else
botName = meta:get_string("playerBlack") botName = meta:get_string("playerBlack")
end 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
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") local gameResult = meta:get_string("gameResult")
if gameResult ~= "" then if gameResult ~= "" then
return return
end end
local botColor = meta:get_string("botColor") local botColor = meta:get_string("botColor")
if botColor == "" then if botColor == "" then
minetest.log("error", "[xdecor] Chess: chessbot.perform_move: botColor in meta string was empty!")
return return
end end
local lastMove = meta:get_string("lastMove") local lastMove = meta:get_string("lastMove")
@ -110,12 +147,17 @@ function chessbot.move(inv, meta)
if not moveOK then if not moveOK then
realchess.resign(meta, currentBotColor) realchess.resign(meta, currentBotColor)
end end
end else
end) minetest.log("error", "[xdecor] Chess: chessbot.perform_move: No last move!")
end end
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() minetest.after(BOT_DELAY_PROMOTE, function()
local lastMove = meta:get_string("lastMove") local lastMove = meta:get_string("lastMove")
local color local color
@ -124,9 +166,35 @@ function chessbot.promote(inv, meta, pawnIndex)
else else
color = "black" color = "black"
end end
-- Always promote to queen realchess.promote_pawn(meta, color, promoteTo)
realchess.promote_pawn(meta, color, "queen")
end) end)
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 return chessbot

View File

@ -1,8 +1,14 @@
local cauldron, sounds = {}, {} local cauldron, sounds = {}, {}
local S = minetest.get_translator("xdecor") 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") 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") 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 hint_recipe = S("Drop foods inside to make a soup")
local infotexts = { local infotexts = {
@ -21,13 +27,28 @@ local function set_infotext(meta, node)
end end
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 = { local ingredients_list = {
"apple", "mushroom", "honey", "pumpkin", "egg", "bread", "meat", "apple", "mushroom", "honey", "pumpkin", "egg", "bread", "meat",
"chicken", "carrot", "potato", "melon", "rhubarb", "cucumber", "chicken", "carrot", "potato", "melon", "rhubarb", "cucumber",
"corn", "beans", "berries", "grapes", "tomato", "wheat" "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 = { cauldron.cbox = {
{0, 0, 0, 16, 16, 0}, {0, 0, 0, 16, 16, 0},
{0, 0, 16, 16, 16, 0}, {0, 0, 16, 16, 16, 0},
@ -36,12 +57,17 @@ cauldron.cbox = {
{0, 0, 0, 16, 8, 16} {0, 0, 0, 16, 8, 16}
} }
-- Returns true is given item is a fire
local function is_fire(itemstring)
return minetest.get_item_group(itemstring, "fire") ~= 0
end
-- Returns true if the node at pos is above fire -- Returns true if the node at pos is above fire
local function is_heated(pos) local function is_heated(pos)
local below_node = {x = pos.x, y = pos.y - 1, z = pos.z} local below_node = {x = pos.x, y = pos.y - 1, z = pos.z}
local nn = minetest.get_node(below_node).name local nn = minetest.get_node(below_node).name
-- Check fire group -- Check fire group
if minetest.get_item_group(nn, "fire") ~= 0 then if is_fire(nn) then
return true return true
else else
return false return false
@ -159,6 +185,23 @@ local function eatable(itemstring)
return string.format("%q", string.dump(on_use_def)):find("item_eat") return string.format("%q", string.dump(on_use_def)):find("item_eat")
end 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) function cauldron.boiling_timer(pos)
-- Cool down cauldron if there is no fire -- Cool down cauldron if there is no fire
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
@ -192,13 +235,12 @@ function cauldron.boiling_timer(pos)
for _, obj in pairs(objs) do for _, obj in pairs(objs) do
if obj and not obj:is_player() and obj:get_luaentity().itemstring then if obj and not obj:is_player() and obj:get_luaentity().itemstring then
local itemstring = obj:get_luaentity().itemstring 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 is_ingredient(itemname) then
if food and (eatable(itemstring) or food:find(ingredient)) then local basename = itemstring:match(":([%w_]+)")
ingredients[#ingredients + 1] = food table.insert(ingredients, basename)
break
end
end end
end end
end end
@ -247,7 +289,7 @@ xdecor.register("cauldron_empty", {
groups = {cracky=2, oddly_breakable_by_hand=1,cauldron=1}, groups = {cracky=2, oddly_breakable_by_hand=1,cauldron=1},
is_ground_content = false, is_ground_content = false,
on_rotate = screwdriver.rotate_simple, 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(), sounds = default.node_sound_metal_defaults(),
collision_box = xdecor.pixelbox(16, cauldron.cbox), collision_box = xdecor.pixelbox(16, cauldron.cbox),
on_rightclick = cauldron.filling, 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}, groups = {cracky=2, oddly_breakable_by_hand=1, not_in_creative_inventory=1,cauldron=2},
is_ground_content = false, is_ground_content = false,
on_rotate = screwdriver.rotate_simple, 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(), sounds = default.node_sound_metal_defaults(),
drop = "xdecor:cauldron_empty", drop = "xdecor:cauldron_empty",
collision_box = xdecor.pixelbox(16, cauldron.cbox), 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}, groups = {cracky=2, oddly_breakable_by_hand=1, not_in_creative_inventory=1,cauldron=2},
is_ground_content = false, is_ground_content = false,
on_rotate = screwdriver.rotate_simple, 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(), sounds = default.node_sound_metal_defaults(),
drop = "xdecor:cauldron_empty", drop = "xdecor:cauldron_empty",
collision_box = xdecor.pixelbox(16, cauldron.cbox), collision_box = xdecor.pixelbox(16, cauldron.cbox),
@ -293,7 +335,7 @@ xdecor.register("cauldron_idle_soup", {
is_ground_content = false, is_ground_content = false,
on_rotate = screwdriver.rotate_simple, on_rotate = screwdriver.rotate_simple,
drop = "xdecor:cauldron_empty", 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(), sounds = default.node_sound_metal_defaults(),
collision_box = xdecor.pixelbox(16, cauldron.cbox), collision_box = xdecor.pixelbox(16, cauldron.cbox),
on_construct = function(pos) on_construct = function(pos)
@ -320,6 +362,7 @@ xdecor.register("cauldron_boiling", {
name = "xdecor_cauldron_top_anim_boiling_water.png", name = "xdecor_cauldron_top_anim_boiling_water.png",
animation = {type = "vertical_frames", length = 3.0} animation = {type = "vertical_frames", length = 3.0}
}, },
"xdecor_cauldron_bottom.png",
"xdecor_cauldron_sides.png" "xdecor_cauldron_sides.png"
}, },
sounds = default.node_sound_metal_defaults(), 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", name = "xdecor_cauldron_top_anim_boiling_river_water.png",
animation = {type = "vertical_frames", length = 3.0} animation = {type = "vertical_frames", length = 3.0}
}, },
"xdecor_cauldron_bottom.png",
"xdecor_cauldron_sides.png" "xdecor_cauldron_sides.png"
}, },
sounds = default.node_sound_metal_defaults(), sounds = default.node_sound_metal_defaults(),
@ -370,6 +414,7 @@ xdecor.register("cauldron_soup", {
name = "xdecor_cauldron_top_anim_soup.png", name = "xdecor_cauldron_top_anim_soup.png",
animation = {type = "vertical_frames", length = 3.0} animation = {type = "vertical_frames", length = 3.0}
}, },
"xdecor_cauldron_bottom.png",
"xdecor_cauldron_sides.png" "xdecor_cauldron_sides.png"
}, },
sounds = default.node_sound_metal_defaults(), sounds = default.node_sound_metal_defaults(),
@ -447,3 +492,26 @@ minetest.register_lbm({
set_infotext(meta, node) set_infotext(meta, node)
end, 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
View File

@ -0,0 +1,115 @@
-- Register enchanted tools.
local S = minetest.get_translator("xdecor")
-- Number of uses for the (normal) steel hoe from Minetest Game (as of 01/12/20224)
-- This is technically redundant because we cannot access that number
-- directly, but it's unlikely to change in future because Minetest Game is
-- unlikely to change.
local STEEL_HOE_USES = 500
-- Modifier of the steel hoe uses for the enchanted steel hoe
local STEEL_HOE_USES_MODIFIER = 2.2
-- Modifier of the bug net uses for the enchanted bug net
local BUG_NET_USES_MODIFIER = 4
-- Multiplies by much faster the fast hammer repairs
local HAMMER_FAST_MODIFIER = 1.3
-- Reduces the wear taken by the hammer for a single repair step
-- (absolute value)
local HAMMER_DURABLE_MODIFIER = 100
-- Register enchantments for default tools from Minetest Game
local materials = {"steel", "bronze", "mese", "diamond"}
local tooltypes = {
{ "axe", { "durable", "fast" }, "choppy" },
{ "pick", { "durable", "fast" }, "cracky" },
{ "shovel", { "durable", "fast" }, "crumbly" },
{ "sword", { "sharp" }, nil },
}
for t=1, #tooltypes do
for m=1, #materials do
local tooltype = tooltypes[t][1]
local enchants = tooltypes[t][2]
local dig_group = tooltypes[t][3]
local material = materials[m]
xdecor.register_enchantable_tool("default:"..tooltype.."_"..material, {
enchants = enchants,
dig_group = dig_group,
})
end
end
-- Register enchantment for bug net
xdecor.register_enchantable_tool("fireflies:bug_net", {
enchants = { "durable" },
dig_group = "catchable",
bonuses = {
uses = BUG_NET_USES_MODIFIER,
}
})
-- Register enchanted steel hoe (more durability)
if farming.register_hoe then
local percent = math.round((STEEL_HOE_USES_MODIFIER - 1) * 100)
local hitem = ItemStack("farming:hoe_steel")
local hdesc = hitem:get_short_description() or "farming:hoe_steel"
local ehdesc, ehsdesc = xdecor.enchant_description(hdesc, "durable", percent)
farming.register_hoe(":farming:enchanted_hoe_steel_durable", {
description = ehdesc,
short_description = ehsdesc,
inventory_image = xdecor.enchant_texture("farming_tool_steelhoe.png"),
max_uses = STEEL_HOE_USES * STEEL_HOE_USES_MODIFIER,
groups = {hoe = 1, not_in_creative_inventory = 1}
})
xdecor.register_custom_enchantable_tool("farming:hoe_steel", {
durable = "farming:enchanted_hoe_steel_durable",
})
end
-- Register enchanted hammer (more durbility and efficiency)
local hammerdef = minetest.registered_items["xdecor:hammer"]
if hammerdef then
local hitem = ItemStack("xdecor:hammer")
local hdesc = hitem:get_short_description() or "xdecor:hammer"
local repair = hammerdef._xdecor_hammer_repair
local repair_cost = hammerdef._xdecor_hammer_repair_cost
-- Durable hammer (reduces wear taken by each repair step)
local d_repair_cost_modified = repair_cost - HAMMER_DURABLE_MODIFIER
local d_percent = math.round(100 - d_repair_cost_modified/repair_cost * 100)
local d_ehdesc, d_ehsdesc = xdecor.enchant_description(hdesc, "durable", d_percent)
xdecor.register_hammer("xdecor:enchanted_hammer_durable", {
description = d_ehdesc,
short_description = d_ehsdesc,
image = xdecor.enchant_texture("xdecor_hammer.png"),
repair_cost = d_repair_cost_modified,
groups = {repair_hammer = 1, not_in_creative_inventory = 1}
})
-- Fast hammer (increases both repair amount and repair cost per
-- repair step by an equal amount)
local f_repair_modified = math.round(repair * HAMMER_FAST_MODIFIER)
local repair_diff = f_repair_modified - repair
local f_repair_cost_modified = repair_cost + repair_diff
local f_percent = math.round(HAMMER_FAST_MODIFIER * 100 - 100)
local f_ehdesc, f_ehsdesc = xdecor.enchant_description(hdesc, "fast", f_percent)
xdecor.register_hammer("xdecor:enchanted_hammer_fast", {
description = f_ehdesc,
short_description = f_ehsdesc,
image = xdecor.enchant_texture("xdecor_hammer.png"),
repair = f_repair_modified,
repair_cost = f_repair_cost_modified,
groups = {repair_hammer = 1, not_in_creative_inventory = 1}
})
xdecor.register_custom_enchantable_tool("xdecor:hammer", {
durable = "xdecor:enchanted_hammer_durable",
fast = "xdecor:enchanted_hammer_fast",
})
end

View File

@ -1,69 +1,84 @@
local enchanting = {}
screwdriver = screwdriver or {} screwdriver = screwdriver or {}
local S = minetest.get_translator("xdecor") local S = minetest.get_translator("xdecor")
local NS = function(s) return s end
local FS = function(...) return minetest.formspec_escape(S(...)) end local FS = function(...) return minetest.formspec_escape(S(...)) end
local ceil, abs, random = math.ceil, math.abs, math.random local ceil, abs, random = math.ceil, math.abs, math.random
local reg_tools = minetest.registered_tools local reg_tools = minetest.registered_tools
local reg_enchantable_tools = {}
local available_tool_enchants = {}
-- Cost in Mese crystal(s) for enchanting. -- Cost in Mese crystal(s) for enchanting.
local mese_cost = 1 local MESE_COST = 1
-- Force of the enchantments. -- Default strenth of the enchantments
local enchanting = { local DEFAULT_ENCHANTING_USES = 1.2 -- Durability
uses = 1.2, -- Durability local DEFAULT_ENCHANTING_TIMES = 0.1 -- Efficiency
times = 0.1, -- Efficiency local DEFAULT_ENCHANTING_DAMAGES = 1 -- Sharpness
damages = 1, -- Sharpness
}
local function cap(str) return
str:gsub("^%l", string.upper)
end
local function to_percent(orig_value, final_value) local function to_percent(orig_value, final_value)
return abs(ceil(((final_value - orig_value) / orig_value) * 100)) return abs(ceil(((final_value - orig_value) / orig_value) * 100))
end 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} local bonus = {durable = 0, efficiency = 0, damages = 0}
if orig_caps then 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 local sum_caps_times = 0
for i=1, #orig_caps.times do for i=1, #orig_caps.times do
sum_caps_times = sum_caps_times + orig_caps.times[i] sum_caps_times = sum_caps_times + orig_caps.times[i]
end end
local average_caps_time = sum_caps_times / #orig_caps.times local average_caps_time = sum_caps_times / #orig_caps.times
bonus.efficiency = to_percent(average_caps_time, average_caps_time - bonus.efficiency = to_percent(average_caps_time, average_caps_time -
enchanting.times) bonus_defs.times)
end end
if fleshy then if fleshy then
bonus.damages = to_percent(fleshy, fleshy + enchanting.damages) bonus.damages = to_percent(fleshy, fleshy + bonus_defs.damages)
end end
local specs = { -- not finished, to complete local specs = {
durable = {"#00baff", " (+" .. bonus.durable .. "%)"}, durable = bonus.durable,
fast = {"#74ff49", " (+" .. bonus.efficiency .. "%)"}, fast = bonus.efficiency,
sharp = {"#ffff00", " (+" .. bonus.damages .. "%)"}, sharp = bonus.damages,
} }
local percent = specs[enchant]
local enchant_loc = { return enchanting:get_tooltip_raw(enchant, percent)
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]
end end
local enchant_buttons = { local enchant_buttons = {
"image_button[3.6,0.67;4.75,0.85;bg_btn.png;fast;"..FS("Efficiency").."]" .. fast = "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").."]", durable = "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").."]", 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 meta = minetest.get_meta(pos)
local formspec = [[ local formspec = [[
size[9,8.6;] size[9,8.6;]
@ -80,27 +95,28 @@ function enchanting.formspec(pos, num)
listring[context;mese] listring[context;mese]
image[2,2.9;1,1;mese_layout.png] image[2,2.9;1,1;mese_layout.png]
]] ]]
.."tooltip[sharp;"..FS("Your weapon inflicts more damages").."]" --~ Sharpness enchantment
.."tooltip[durable;"..FS("Your tool last longer").."]" .."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").."]" .."tooltip[fast;"..FS("Your tool digs faster").."]"
..default.gui_slots .. default.get_hotbar_bg(0.55, 4.5) ..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) meta:set_string("formspec", formspec)
end end
function enchanting.on_put(pos, listname, _, stack) function enchanting.on_put(pos, listname, _, stack)
if listname == "tool" then if listname == "tool" then
local stackname = stack:get_name() local stackname = stack:get_name()
local tool_groups = { local enchants = available_tool_enchants[stackname]
"axe, pick, shovel", if enchants then
"sword", enchanting.formspec(pos, enchants)
}
for idx, tools in ipairs(tool_groups) do
if tools:find(stackname:match(":(%w+)")) then
enchanting.formspec(pos, idx)
end
end end
end end
end end
@ -114,7 +130,7 @@ function enchanting.fields(pos, _, fields, sender)
local mod, name = tool:get_name():match("(.*):(.*)") local mod, name = tool:get_name():match("(.*):(.*)")
local enchanted_tool = (mod or "") .. ":enchanted_" .. (name or "") .. "_" .. next(fields) 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", { minetest.sound_play("xdecor_enchanting", {
to_player = sender:get_player_name(), to_player = sender:get_player_name(),
gain = 0.8 gain = 0.8
@ -122,7 +138,7 @@ function enchanting.fields(pos, _, fields, sender)
tool:replace(enchanted_tool) tool:replace(enchanted_tool)
tool:add_wear(orig_wear) tool:add_wear(orig_wear)
mese:take_item(mese_cost) mese:take_item(MESE_COST)
inv:set_stack("mese", 1, mese) inv:set_stack("mese", 1, mese)
inv:set_stack("tool", 1, tool) inv:set_stack("tool", 1, tool)
end end
@ -140,12 +156,13 @@ function enchanting.blast(pos)
end end
local function allowed(tool) local function allowed(tool)
if not tool then return end if not tool then
return false
for item in pairs(reg_tools) do
if item:find("enchanted_" .. tool) then
return true
end end
if reg_enchantable_tools[tool] then
return true
else
return false
end end
end end
@ -154,7 +171,7 @@ function enchanting.put(_, listname, _, stack)
if listname == "mese" and (stackname == "default:mese_crystal" or if listname == "mese" and (stackname == "default:mese_crystal" or
stackname == "imese:industrial_mese_crystal") then stackname == "imese:industrial_mese_crystal") then
return stack:get_count() return stack:get_count()
elseif listname == "tool" and allowed(stackname:match("[^:]+$")) then elseif listname == "tool" and allowed(stackname) then
return 1 return 1
end end
@ -248,6 +265,7 @@ xdecor.register("enchantment_table", {
}) })
minetest.register_entity("xdecor:book_open", { minetest.register_entity("xdecor:book_open", {
initial_properties = {
visual = "sprite", visual = "sprite",
visual_size = {x=0.75, y=0.75}, visual_size = {x=0.75, y=0.75},
collisionbox = {0,0,0,0,0,0}, collisionbox = {0,0,0,0,0,0},
@ -255,6 +273,7 @@ minetest.register_entity("xdecor:book_open", {
physical = false, physical = false,
textures = {"xdecor_book_open.png"}, textures = {"xdecor_book_open.png"},
static_save = false, static_save = false,
},
}) })
minetest.register_lbm({ minetest.register_lbm({
@ -276,65 +295,112 @@ minetest.register_lbm({
end, end,
}) })
function enchanting:register_tools(mod, def) function enchanting:enchant_texture(img)
for tool in pairs(def.tools) do if img == nil or img == "" or type(img) ~= "string" then
for material in def.materials:gmatch("[%w_]+") do return "no_texture.png"
for enchant in def.tools[tool].enchants:gmatch("[%w_]+") do else
local original_tool = reg_tools[mod .. ":" .. tool .. "_" .. material] return "("..img.. ")^[colorize:violet:50"
if not original_tool then break end end
local original_toolcaps = original_tool.tool_capabilities end
if original_toolcaps then 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_damage_groups = original_toolcaps.damage_groups
local original_groupcaps = original_toolcaps.groupcaps 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 groupcaps = table.copy(original_groupcaps)
local fleshy = original_damage_groups.fleshy
local full_punch_interval = original_toolcaps.full_punch_interval local full_punch_interval = original_toolcaps.full_punch_interval
local max_drop_level = original_toolcaps.max_drop_level local max_drop_level = original_toolcaps.max_drop_level
local group = next(original_groupcaps) 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 if enchant == "durable" then
groupcaps[group].uses = ceil(original_groupcaps[group].uses * groupcaps[dig_group].uses = ceil(original_groupcaps[dig_group].uses *
enchanting.uses) bonus_defs.uses)
elseif enchant == "fast" then elseif enchant == "fast" then
for i, time in pairs(original_groupcaps[group].times) do for i, time in pairs(original_groupcaps[dig_group].times) do
groupcaps[group].times[i] = time - enchanting.times groupcaps[dig_group].times[i] = time - bonus_defs.times
end end
elseif enchant == "sharp" then elseif enchant == "sharp" then
fleshy = fleshy + enchanting.damages 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 end
local arg1 = def.material_desc[material] or cap(material) local arg1 = original_desc
local arg2 = def.tools[tool].desc or cap(tool) local arg2 = self:get_tooltip(enchant, original_groupcaps[dig_group], fleshy, bonus_defs)
local arg3 = self:get_tooltip(enchant, original_groupcaps[group], fleshy) local enchantedTool = original_tool.mod_origin .. ":enchanted_" .. original_basename .. "_" .. enchant
minetest.register_tool(":" .. mod .. ":enchanted_" .. tool .. "_" .. material .. "_" .. enchant, {
description = S("Enchanted @1 @2\n@3", arg1, arg2, arg3), local invimg = original_tool.inventory_image
short_description = S("Enchanted @1 @2", arg1, arg2), invimg = enchanting:enchant_texture(invimg)
inventory_image = original_tool.inventory_image .. "^[colorize:violet:50", local wieldimg = original_tool.wield_image
wield_image = original_tool.wield_image, if wieldimg == nil or wieldimg == "" then
groups = {not_in_creative_inventory = 1}, 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 = { tool_capabilities = {
groupcaps = groupcaps, damage_groups = {fleshy = fleshy}, groupcaps = groupcaps, damage_groups = {fleshy = fleshy},
full_punch_interval = full_punch_interval, full_punch_interval = full_punch_interval,
max_drop_level = max_drop_level max_drop_level = max_drop_level
} },
pointabilities = original_tool.pointabilities,
}) })
if minetest.get_modpath("toolranks") then
toolranks.add_tool(enchantedTool)
end end
end end
end available_tool_enchants[original_tool_name] = table.copy(def.enchants)
end reg_enchantable_tools[original_tool_name] = true
end end
enchanting:register_tools("default", { function enchanting:register_custom_tool(original_tool_name, enchanted_tools)
materials = "steel, bronze, mese, diamond", if not available_tool_enchants[original_tool_name] then
material_desc = {steel = S("Steel"), bronze = S("Bronze"), mese = S("Mese"), diamond = S("Diamond")}, available_tool_enchants[original_tool_name] = {}
tools = { end
axe = {enchants = "durable, fast", desc = S("Axe")}, for enchant, v in pairs(enchanted_tools) do
pick = {enchants = "durable, fast", desc = S("Pickaxe")}, table.insert(available_tool_enchants[original_tool_name], enchant)
shovel = {enchants = "durable, fast", desc = S("Shovel")}, end
sword = {enchants = "sharp", desc = S("Sword")} reg_enchantable_tools[original_tool_name] = true
}, end
})
-- Recipes -- Recipes
@ -346,3 +412,95 @@ minetest.register_craft({
{"default:obsidian", "default:obsidian", "default:obsidian"} {"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

View File

@ -121,6 +121,7 @@ end
xdecor.register("hive", { xdecor.register("hive", {
description = S("Artificial Hive"), description = S("Artificial Hive"),
--~ Tooltip of artificial hive
_tt_help = S("Bees live here and produce honey"), _tt_help = S("Bees live here and produce honey"),
tiles = {"xdecor_hive_top.png", "xdecor_hive_top.png", tiles = {"xdecor_hive_top.png", "xdecor_hive_top.png",
"xdecor_hive_side.png", "xdecor_hive_side.png", "xdecor_hive_side.png", "xdecor_hive_side.png",

View File

@ -73,7 +73,9 @@ function itemframe.set_infotext(meta)
local owner = meta:get_string("owner") local owner = meta:get_string("owner")
if itemstring == "" then if itemstring == "" then
if owner ~= "" 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 else
meta:set_string("infotext", S("Item Frame")) meta:set_string("infotext", S("Item Frame"))
end end
@ -158,6 +160,7 @@ end
xdecor.register("itemframe", { xdecor.register("itemframe", {
description = S("Item Frame"), description = S("Item Frame"),
--~ Item frame tooltip
_tt_help = S("For presenting a single item"), _tt_help = S("For presenting a single item"),
groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3}, groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 3},
is_ground_content = false, is_ground_content = false,
@ -181,12 +184,14 @@ xdecor.register("itemframe", {
}) })
minetest.register_entity("xdecor:f_item", { minetest.register_entity("xdecor:f_item", {
initial_properties = {
visual = "wielditem", visual = "wielditem",
visual_size = {x = 0.33, y = 0.33}, visual_size = {x = 0.33, y = 0.33},
collisionbox = {0,0,0,0,0,0}, collisionbox = {0,0,0,0,0,0},
pointable = false, pointable = false,
physical = false, physical = false,
textures = {"air"}, textures = {"air"},
},
on_activate = function(self, staticdata) on_activate = function(self, staticdata)
local pos = self.object:get_pos() local pos = self.object:get_pos()
if minetest.get_node(pos).name ~= "xdecor:itemframe" then if minetest.get_node(pos).name ~= "xdecor:itemframe" then

View File

@ -61,7 +61,7 @@ function mailbox:formspec(pos, owner, is_owner)
-- List of donors. A line looks like this: -- List of donors. A line looks like this:
-- <donor name> <item icon> × <item count> -- <donor name> <item icon> × <item count>
giver = giver .. "#FFFF00," .. giver_name .. "," .. i .. 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) .. "," ",#FFFFFF," .. FS("× @1", stack_count) .. ","
img = img .. i .. "=" .. img = img .. i .. "=" ..
@ -180,6 +180,7 @@ end
xdecor.register("mailbox", { xdecor.register("mailbox", {
description = S("Mailbox"), description = S("Mailbox"),
--~ Mailbox tooltip
_tt_help = S("Lets other players give you things"), _tt_help = S("Lets other players give you things"),
tiles = {"xdecor_mailbox_top.png", "xdecor_mailbox_bottom.png", tiles = {"xdecor_mailbox_top.png", "xdecor_mailbox_bottom.png",
"xdecor_mailbox_side.png", "xdecor_mailbox_side.png", "xdecor_mailbox_side.png", "xdecor_mailbox_side.png",

View File

@ -57,6 +57,7 @@ end
function plate.register(material, desc, def) function plate.register(material, desc, def)
xdecor.register("pressure_" .. material .. "_off", { xdecor.register("pressure_" .. material .. "_off", {
description = def.description or (desc .. " Pressure Plate"), description = def.description or (desc .. " Pressure Plate"),
--~ Pressure plate tooltip
_tt_help = S("Opens doors when stepped on"), _tt_help = S("Opens doors when stepped on"),
tiles = {"xdecor_pressure_" .. material .. ".png"}, tiles = {"xdecor_pressure_" .. material .. ".png"},
use_texture_alpha = ALPHA_OPAQUE, use_texture_alpha = ALPHA_OPAQUE,
@ -98,6 +99,7 @@ plate.register("stone", "Stone", {
xdecor.register("lever_off", { xdecor.register("lever_off", {
description = S("Lever"), description = S("Lever"),
--~ Lever tooltip
_tt_help = S("Opens doors when pulled"), _tt_help = S("Opens doors when pulled"),
tiles = {"xdecor_lever_off.png"}, tiles = {"xdecor_lever_off.png"},
use_texture_alpha = ALPHA_OPAQUE, use_texture_alpha = ALPHA_OPAQUE,

View File

@ -200,7 +200,7 @@ xdecor.register("chair", {
description = S("Chair"), description = S("Chair"),
tiles = {"xdecor_wood.png"}, tiles = {"xdecor_wood.png"},
sounds = default.node_sound_wood_defaults(), 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, is_ground_content = false,
on_rotate = screwdriver.rotate_simple, on_rotate = screwdriver.rotate_simple,
node_box = xdecor.pixelbox(16, { node_box = xdecor.pixelbox(16, {
@ -212,8 +212,8 @@ xdecor.register("chair", {
{3, 6, 3, 10, 2, 8} {3, 6, 3, 10, 2, 8}
}), }),
can_dig = xdecor.sit_dig, can_dig = xdecor.sit_dig,
after_destruct = xdecor.sit_destruct,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
pos.y = pos.y + 0 -- Sitting position
xdecor.sit(pos, node, clicker, pointed_thing) xdecor.sit(pos, node, clicker, pointed_thing)
return itemstack return itemstack
end, end,
@ -234,9 +234,11 @@ xdecor.register("cobweb", {
}) })
local curtain_colors = { 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. -- For preserve_metadata for curtains.
-- Erases metadata from the drops -- Erases metadata from the drops
-- because the item metadata should be empty -- because the item metadata should be empty
@ -248,20 +250,27 @@ local cleanup_curtain_meta = function(_,_,_,drops)
end end
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, { xdecor.register("curtain_" .. c, {
description = desc, description = desc,
walkable = false, walkable = false,
tiles = {"wool_white.png"}, tiles = {base_texture, "("..base_texture..")^[transformFY", base_texture},
color = c, use_texture_alpha = ALPHA_CLIP,
inventory_image = "wool_white.png^[colorize:" .. c .. inventory_image = base_texture.."^xdecor_curtain_open_overlay.png^[makealpha:255,126,126",
":170^xdecor_curtain_open_overlay.png^[makealpha:255,126,126", wield_image = base_texture.."^xdecor_curtain_open_overlay.png^[makealpha:255,126,126",
wield_image = "wool_white.png^[colorize:" .. c .. ":170", drawtype = "nodebox",
drawtype = "signlike", paramtype2 = "wallmounted",
paramtype2 = "colorwallmounted", 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}, groups = {dig_immediate = 3, flammable = 3},
is_ground_content = false, is_ground_content = false,
selection_box = {type = "wallmounted"},
on_rightclick = function(pos, node, _, itemstack) on_rightclick = function(pos, node, _, itemstack)
minetest.set_node(pos, {name = "xdecor:curtain_open_" .. c, param2 = node.param2}) minetest.set_node(pos, {name = "xdecor:curtain_open_" .. c, param2 = node.param2})
return itemstack return itemstack
@ -269,15 +278,28 @@ for c, desc in pairs(curtain_colors) do
preserve_metadata = cleanup_curtain_meta, preserve_metadata = cleanup_curtain_meta,
}) })
local open_tile = base_texture.."^xdecor_curtain_open_overlay.png^[makealpha:255,126,126"
xdecor.register("curtain_open_" .. c, { xdecor.register("curtain_open_" .. c, {
tiles = {"wool_white.png^xdecor_curtain_open_overlay.png^[makealpha:255,126,126"}, tiles = {
color = c, open_tile,
drawtype = "signlike", "("..open_tile..")^[transformFY",
paramtype2 = "colorwallmounted", 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, walkable = false,
groups = {dig_immediate = 3, flammable = 3, not_in_creative_inventory = 1}, groups = {dig_immediate = 3, flammable = 3, not_in_creative_inventory = 1},
is_ground_content = false, is_ground_content = false,
selection_box = {type="wallmounted"},
drop = "xdecor:curtain_" .. c, drop = "xdecor:curtain_" .. c,
on_rightclick = function(pos, node, _, itemstack) on_rightclick = function(pos, node, _, itemstack)
minetest.set_node(pos, {name="xdecor:curtain_" .. c, param2 = node.param2}) 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({ minetest.register_craft({
output = "xdecor:curtain_" .. c .. " 4", output = "xdecor:curtain_" .. c .. " 4",
recipe = { recipe = {
{"", "wool:" .. c, ""}, {"", craft_item, ""},
{"", "wool:" .. c, ""} {"", craft_item, ""}
} }
}) })
end end
@ -298,13 +320,13 @@ end
xdecor.register("cushion", { xdecor.register("cushion", {
description = S("Cushion"), description = S("Cushion"),
tiles = {"xdecor_cushion.png"}, 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, is_ground_content = false,
on_place = minetest.rotate_node, on_place = minetest.rotate_node,
node_box = xdecor.nodebox.slab_y(0.5), node_box = xdecor.nodebox.slab_y(0.5),
can_dig = xdecor.sit_dig, can_dig = xdecor.sit_dig,
after_destruct = xdecor.sit_destruct,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
pos.y = pos.y + 0 -- Sitting position
xdecor.sit(pos, node, clicker, pointed_thing) xdecor.sit(pos, node, clicker, pointed_thing)
return itemstack return itemstack
end end
@ -658,11 +680,12 @@ local painting_box = {
xdecor.register("painting_1", { xdecor.register("painting_1", {
description = S("Painting"), 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, use_texture_alpha = ALPHA_OPAQUE,
inventory_image = "xdecor_painting_empty.png", inventory_image = "xdecor_painting_empty.png",
wield_image = "xdecor_painting_empty.png", wield_image = "xdecor_painting_empty.png",
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
sunlight_propagates = true, sunlight_propagates = true,
groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 2, attached_node = 1}, groups = {choppy = 3, oddly_breakable_by_hand = 2, flammable = 2, attached_node = 1},
is_ground_content = false, is_ground_content = false,
@ -711,9 +734,10 @@ xdecor.register("painting_1", {
for i = 2, 4 do for i = 2, 4 do
xdecor.register("painting_" .. i, { 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, use_texture_alpha = ALPHA_OPAQUE,
paramtype2 = "wallmounted", paramtype2 = "wallmounted",
wallmounted_rotate_vertical = true,
drop = "xdecor:painting_1", drop = "xdecor:painting_1",
sunlight_propagates = true, sunlight_propagates = true,
groups = { groups = {
@ -806,11 +830,26 @@ xdecor.register("tatami", {
xdecor.register("trampoline", { xdecor.register("trampoline", {
description = S("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, use_texture_alpha = ALPHA_CLIP,
groups = {cracky = 3, oddly_breakable_by_hand = 1, fall_damage_add_percent = -80, bouncy = 90}, groups = {cracky = 3, oddly_breakable_by_hand = 1, fall_damage_add_percent = -80, bouncy = 90},
is_ground_content = false, 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({ sounds = default.node_sound_defaults({
footstep = { footstep = {
name = "xdecor_bouncy", name = "xdecor_bouncy",
@ -901,6 +940,7 @@ xdecor.register("woodframed_glass", {
local devices = { local devices = {
{ "radio", S("Radio"), default.node_sound_metal_defaults() }, { "radio", S("Radio"), default.node_sound_metal_defaults() },
--~ as in "loudspeaker"
{ "speaker", S("Speaker"), default.node_sound_metal_defaults() }, { "speaker", S("Speaker"), default.node_sound_metal_defaults() },
} }
for _, v in pairs(devices) do for _, v in pairs(devices) do

View File

@ -1,10 +1,16 @@
local rope = {} local rope = {}
local S = minetest.get_translator("xdecor") local S = minetest.get_translator("xdecor")
-- Maximum length a rope can extend to
local MAX_ROPES = 30
local ropesounds = default.node_sound_leaves_defaults() local ropesounds = default.node_sound_leaves_defaults()
-- Code by Mirko K. (modified by Temperest, Wulfsdad, kilbith and Wuzzy) (License: GPL). -- Code by Mirko K. (modified by Temperest, Wulfsdad, kilbith and Wuzzy) (License: GPL).
function rope.place(itemstack, placer, pointed_thing) 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 if pointed_thing.type == "node" then
-- Use pointed node's on_rightclick function first, if present -- Use pointed node's on_rightclick function first, if present
if placer and not placer:get_player_control().sneak then if placer and not placer:get_player_control().sneak then
@ -15,32 +21,45 @@ function rope.place(itemstack, placer, pointed_thing)
end end
local pos = pointed_thing.above local pos = pointed_thing.above
-- Check protection -- Check protection
if minetest.is_protected(pos, placer:get_player_name()) and if minetest.is_protected(pos, pname) and not protection_bypass then
not minetest.check_player_privs(placer, "protection_bypass") then minetest.record_protection_violation(pos, pname)
minetest.record_protection_violation(pos, placer:get_player_name())
return itemstack return itemstack
end end
local oldnode = minetest.get_node(pos) local oldnode = minetest.get_node(pos)
local stackname = itemstack:get_name() local stackname = itemstack:get_name()
-- Limit max. rope length to max. stack size -- Limit rope length to max. stack size or MAX_ROPES (whatever is smaller).
-- Prevents the rope to extend infinitely in Creative Mode -- Prevents the rope to extend infinitely in Creative Mode.
local max_ropes = itemstack:get_stack_max() 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 start_pos = table.copy(pos)
local ropes_placed = 0 local ropes_to_place = 0
while oldnode.name == "air" and not itemstack:is_empty() and ropes_placed < max_ropes do local new_rope_nodes = {}
local newnode = {name = stackname, param1 = 0} while oldnode.name == "air" and (creative or (ropes_to_place < itemstack:get_count())) and ropes_to_place < max_ropes do
minetest.set_node(pos, newnode) -- Stop extending rope into protected area
if not minetest.is_creative_enabled(placer:get_player_name()) then if minetest.is_protected(pos, pname) and not protection_bypass then
itemstack:take_item() break
end end
table.insert(new_rope_nodes, table.copy(pos))
pos.y = pos.y - 1 pos.y = pos.y - 1
oldnode = minetest.get_node(pos) oldnode = minetest.get_node(pos)
ropes_placed = ropes_placed + 1 ropes_to_place = ropes_to_place + 1
end 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 -- Play placement sound manually
if ropes_placed > 0 then if ropes_to_place > 0 then
minetest.sound_play(ropesounds.place, {pos=start_pos}, true) minetest.sound_play(ropesounds.place, {pos=start_pos}, true)
end end
end end

View File

@ -7,6 +7,9 @@ local min, ceil = math.min, math.ceil
local S = minetest.get_translator("xdecor") local S = minetest.get_translator("xdecor")
local FS = function(...) return minetest.formspec_escape(S(...)) end local FS = function(...) return minetest.formspec_escape(S(...)) end
local DEFAULT_HAMMER_REPAIR = 500
local DEFAULT_HAMMER_REPAIR_COST = 700
-- Nodeboxes definitions -- Nodeboxes definitions
workbench.defs = { workbench.defs = {
@ -35,7 +38,7 @@ end
-- Tools allowed to be repaired -- Tools allowed to be repaired
function workbench:repairable(stack) function workbench:repairable(stack)
-- Explicitly registeded as repairable: Overrides everything else -- Explicitly registered as repairable: Overrides everything else
if custom_repairable[stack] then if custom_repairable[stack] then
return true return true
end end
@ -109,16 +112,16 @@ function workbench:get_output(inv, input, name)
inv:set_list("forms", output) inv:set_list("forms", output)
end end
function workbench:register_special_cut(nodename, cutlist) local main_fs = ""..
registered_cuttable_nodes[nodename] = true --~ Verb shown in workbench form where you can cut a node
special_cuts[nodename] = cutlist "label[0.9,1.23;"..FS("Cut").."]"
end --~ Verb shown in workbench form where you can repair an item
local main_fs = "label[0.9,1.23;"..FS("Cut").."]"
.."label[0.9,2.23;"..FS("Repair").."]" .."label[0.9,2.23;"..FS("Repair").."]"
..[[ box[-0.05,1;2.05,0.9;#555555] ..[[ box[-0.05,1;2.05,0.9;#555555]
box[-0.05,2;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[0,0;2,1;craft;"..FS("Crafting").."]"
--~ Button in workbench form
.."button[2,0;2,1;storage;"..FS("Storage").."]" .."button[2,0;2,1;storage;"..FS("Storage").."]"
..[[ image[3,1;1,1;gui_arrow.png] ..[[ image[3,1;1,1;gui_arrow.png]
image[0,1;1,1;worktable_saw.png] image[0,1;1,1;worktable_saw.png]
@ -216,9 +219,11 @@ function workbench.timer(pos)
return return
end end
local hammerdef = hammer:get_definition()
-- Tool's wearing range: 0-65535; 0 = new condition -- Tool's wearing range: 0-65535; 0 = new condition
tool:add_wear(-500) tool:add_wear(-hammerdef._xdecor_hammer_repair or DEFAULT_HAMMER_REPAIR)
hammer:add_wear(700) hammer:add_wear(hammerdef._xdecor_hammer_repair_cost or DEFAULT_HAMMER_REPAIR_COST)
inv:set_stack("tool", 1, tool) inv:set_stack("tool", 1, tool)
inv:set_stack("hammer", 1, hammer) inv:set_stack("hammer", 1, hammer)
@ -230,7 +235,7 @@ function workbench.allow_put(pos, listname, index, stack, player)
local stackname = stack:get_name() local stackname = stack:get_name()
if (listname == "tool" and workbench:repairable(stackname)) or if (listname == "tool" and workbench:repairable(stackname)) or
(listname == "input" and workbench:cuttable(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 listname == "storage" then
return stack:get_count() return stack:get_count()
end 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 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 inv = minetest.get_inventory({type="node", pos=pos})
local stack = inv:get_stack(from_list, from_index) 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 return count
end end
end end
@ -310,7 +315,7 @@ xdecor.register("workbench", {
is_ground_content = false, is_ground_content = false,
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
tiles = { 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_sides.png", "xdecor_workbench_sides.png",
"xdecor_workbench_front.png", "xdecor_workbench_front.png" "xdecor_workbench_front.png", "xdecor_workbench_front.png"
}, },
@ -328,29 +333,11 @@ xdecor.register("workbench", {
allow_metadata_inventory_move = workbench.allow_move allow_metadata_inventory_move = workbench.allow_move
}) })
local function register_cut_raw(node, workbench_def)
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 mod_name, item_name = node:match("^(.-):(.*)") local mod_name, item_name = node:match("^(.-):(.*)")
local def = minetest.registered_nodes[node] local def = minetest.registered_nodes[node]
if item_name and d[3] then if item_name and workbench_def[3] then
local groups = {} local groups = {}
local tiles local tiles
groups.not_in_creative_inventory = 1 groups.not_in_creative_inventory = 1
@ -419,7 +406,7 @@ for i = 1, #cuttable_nodes do
end end
end end
local cutname = d[1] local cutname = workbench_def[1]
local tiles_special_cut local tiles_special_cut
if custom_tiles and custom_tiles[cutname] then if custom_tiles and custom_tiles[cutname] then
tiles_special_cut = custom_tiles[cutname] tiles_special_cut = custom_tiles[cutname]
@ -427,9 +414,14 @@ for i = 1, #cuttable_nodes do
tiles_special_cut = tiles tiles_special_cut = tiles
end end
minetest.register_node(":" .. node .. "_" .. cutname, { local cutnodename = node .. "_" .. cutname
-- @1: Base node description (e.g. "Stone"); @2: modifier (e.g. "Nanoslab") if minetest.registered_nodes[cutnodename] then
description = S("@1 @2", def.description, d[4]), 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", paramtype = "light",
paramtype2 = "facedir", paramtype2 = "facedir",
drawtype = "nodebox", drawtype = "nodebox",
@ -438,7 +430,7 @@ for i = 1, #cuttable_nodes do
use_texture_alpha = def.use_texture_alpha, use_texture_alpha = def.use_texture_alpha,
groups = groups, groups = groups,
is_ground_content = def.is_ground_content, 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, sunlight_propagates = true,
on_place = minetest.rotate_node on_place = minetest.rotate_node
}) })
@ -453,32 +445,35 @@ for i = 1, #cuttable_nodes do
("stairs:stair_outer_%s"):format(item_name) ("stairs:stair_outer_%s"):format(item_name)
) )
end end
return true
end
function workbench:register_cut(nodename, cutlist)
if registered_cuttable_nodes[nodename] then
minetest.log("error", "[xdecor] Workbench: Tried to register cut for node "..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
end end
end) registered_cuttable_nodes[nodename] = true
return ok
-- 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
end end
})
-- Recipes function workbench:register_special_cut(nodename, cutlist)
if registered_cuttable_nodes[nodename] or special_cuts[nodename] then
minetest.register_craft({ minetest.log("error", "[xdecor] Workbench: Tried to register special cut for node "..nodename..", but it was already registered!")
output = "xdecor:hammer", return false
recipe = { end
{"default:steel_ingot", "group:stick", "default:steel_ingot"}, registered_cuttable_nodes[nodename] = true
{"", "group:stick", ""} special_cuts[nodename] = cutlist
} end
})
-- Workbench craft
minetest.register_craft({ minetest.register_craft({
output = "xdecor:workbench", output = "xdecor:workbench",
recipe = { 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 -- Special cuts for cushion block and cabinet
workbench:register_special_cut("xdecor:cushion_block", { slab = "xdecor:cushion" }) workbench:register_special_cut("xdecor:cushion_block", { slab = "xdecor:cushion" })
workbench:register_special_cut("xdecor:cabinet", { slab = "xdecor:cabinet_half" }) workbench:register_special_cut("xdecor:cabinet", { slab = "xdecor:cabinet_half" })
--[[ API FUNCTIONS ]]
--[[ Register a custom hammer (for repairing).
A hammer repair items at the work bench. The workbench repeatedly
checks if a hammer and a repairable tool are in the slots. The hammer
will repair the tool in regular intervals. This is called a "step".
In each step, the hammer reduces the wear of the repairable
tool but increases its own wear, each by a fixed amount.
This function allows you to register a custom hammer with custom
name, item image and wear stats.
Arguments:
* name: Internal itemname
* def: Definition table:
* description: Item `description`
* image: Inventory image and wield image
* groups: Item groups (MUST contain at least `repair_hammer = 1`)
* repair: How much item wear the hammer repairs per step
* repair_cost: How much item wear the hammer takes itself per step
Note: Mind the implication of repair_cost! If repair_cost is lower than
repair, this means practically infinite durability if you have two
hammers that repair each other. If repair_cost is higher than repair,
then hammers will break eventually.
]]
function xdecor.register_hammer(name, def)
minetest.register_tool(name, {
description = def.description,
_tt_help = S("Repairs tools at the work bench"),
inventory_image = def.image,
wield_image = def.image,
on_use = function() do
return end
end,
groups = def.groups,
_xdecor_hammer_repair = def.repair or DEFAULT_HAMMER_REPAIR,
_xdecor_hammer_repair_cost = def.repair_cost or DEFAULT_HAMMER_REPAIR_COST,
})
end
--[[ EXPERIMENTAL FUNCTION:
Registers various 'cut' node variants for the node with the given nodename,
which will be available in the workbench.
This must only be called once per node. Calling it again is an error.
The following nodes will be registered:
* <nodename>_nanoslab
* <nodename>_micropanel
* <nodename>_microslab
* <nodename>_thinstair
* <nodename>_cube
* <nodename>_panel
* <nodename>_doublepanel
* <nodename>_halfstair
You MUST make sure these names are not already taken before
calling this function. Failing to do so is an error.
Additionally, a slab, stair, inner stair and outer stair
will be registered by using the `stairs` mod if the slab
node does not exist yet. Refer to the `stairs` mod documentation
for details.
Returns true if all nodes were registered successfully,
returns false (and writes to error log) if any error occurred.
]]
xdecor.register_cut = function(nodename)
return workbench:register_cut(nodename)
end
--[[ END OF API FUNCTIONS ]]
-- Register xdecor's built-in hammer
xdecor.register_hammer("xdecor:hammer", {
description = S("Hammer"),
image = "xdecor_hammer.png",
groups = { repair_hammer = 1 },
repair = DEFAULT_HAMMER_REPAIR,
repair_cost = DEFAULT_HAMMER_REPAIR_COST,
})
-- Hammer recipes
minetest.register_craft({
output = "xdecor:hammer",
recipe = {
{"default:steel_ingot", "group:stick", "default:steel_ingot"},
{"", "group:stick", ""}
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 B

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B