Compare commits

...

10 Commits

Author SHA1 Message Date
Wuzzy
71b1b193f6 Mechanisms can't affect doors you don't own 2024-12-26 16:56:09 +01:00
Wuzzy
fc6b2c8165 Fix pressure plate disabling too fast 2024-12-26 16:39:42 +01:00
Wuzzy
2038dd181c Add LBM to restart node timer of active actuators 2024-12-26 16:13:53 +01:00
Wuzzy
62ef5b8c83 Switch to using node timers for mechanisms 2024-12-26 16:08:52 +01:00
Wuzzy
5c2194763c Doors check active actuators before closing again 2024-12-26 15:21:19 +01:00
Wuzzy
204d289992 Refactor mechanisms code 2024-12-26 15:04:47 +01:00
Wuzzy
318ed48456 Disallow placing nodes on "on" lever by default 2024-12-26 14:46:53 +01:00
Wuzzy
51c1d9b239 Fix wrong string construction in cut node error 2024-12-02 19:54:32 +01:00
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
4 changed files with 228 additions and 60 deletions

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.
@ -298,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
@ -501,11 +501,11 @@ If the game came to an end, the game result is written in a final separate line
#### 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

@ -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",
@ -24,16 +25,20 @@ local subpart = {
-- Workbench MUST be loaded after all other subparts that register nodes -- Workbench MUST be loaded after all other subparts that register nodes
-- last for the default 'cut node' registrations to work -- last for the default 'cut node' registrations to work
"workbench", "workbench",
-- Enchanted tools registered last because they depend on previous
-- subparts
"enchanted_tools",
} }
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

@ -6,55 +6,174 @@ screwdriver = screwdriver or {}
local S = minetest.get_translator("xdecor") local S = minetest.get_translator("xdecor")
local ALPHA_OPAQUE = minetest.features.use_texture_alpha_string_modes and "opaque" or false local ALPHA_OPAQUE = minetest.features.use_texture_alpha_string_modes and "opaque" or false
local function door_toggle(pos_actuator, pos_door, player) -- Number of seconds an actuator (pressure plate, lever) stays active.
local player_name = player:get_player_name() -- After this time, it will return to the disabled state again.
local actuator = minetest.get_node(pos_actuator) local DISABLE_ACTUATOR_AFTER = 2.0
local door = doors.get(pos_door)
if not door then return end
if actuator.name:sub(-4) == "_off" then -- Effect area of pressure plates and levers. Doors within this area
minetest.set_node(pos_actuator, -- can be affected.
{name = actuator.name:gsub("_off", "_on"), param2 = actuator.param2}) local PRESSURE_PLATE_AREA_MIN = {x = -2, y = 0, z = -2}
local PRESSURE_PLATE_AREA_MAX = {x = 2, y = 0, z = 2}
local LEVER_AREA_MIN = {x = -2, y = -1, z = -2}
local LEVER_AREA_MAX = {x = 2, y = 1, z = 2}
-- Pressure plates check for players within this radius
local PRESSURE_PLATE_PLAYER_RADIUS = 0.8
-- Interval in seconds that pressure plates check for players
local PRESSURE_PLATE_CHECK_TIMER = 0.1
local function door_open(pos_door, player)
local door = doors.get(pos_door)
if not door then
return
end end
door:open(player) door:open(player)
end
minetest.after(2, function() local function door_close(pos_door, player)
if minetest.get_node(pos_actuator).name:sub(-3) == "_on" then local door = doors.get(pos_door)
minetest.set_node(pos_actuator, if not door then
{name = actuator.name, param2 = actuator.param2}) return
end
door:close(player)
end
-- Returns true if the door node at pos is currently next to any
-- active actuator node (lever, pressure plate)
local function door_is_actuatored(pos_door)
local minp = vector.add(LEVER_AREA_MIN, pos_door)
local maxp = vector.add(LEVER_AREA_MAX, pos_door)
local levers = minetest.find_nodes_in_area(minp, maxp, "group:lever")
for l=1, #levers do
local lnode = minetest.get_node(levers[l])
if minetest.get_item_group(lnode.name, "xdecor_actuator") == 2 then
return true
end end
-- Re-get player object (or nil) because 'player' could end
-- be an invalid object at this time (player left) minp = vector.add(PRESSURE_PLATE_AREA_MIN, pos_door)
door:close(minetest.get_player_by_name(player_name)) maxp = vector.add(PRESSURE_PLATE_AREA_MAX, pos_door)
end) local pressure_plates = minetest.find_nodes_in_area(minp, maxp, "group:pressure_plate")
for p=1, #pressure_plates do
local pnode = minetest.get_node(pressure_plates[p])
if minetest.get_item_group(pnode.name, "xdecor_actuator") == 2 then
return true
end
end
return false
end
local function actuator_timeout(pos_actuator, actuator_area_min, actuator_area_max)
local actuator = minetest.get_node(pos_actuator)
-- Get name of last player that triggered the actuator
local meta = minetest.get_meta(pos_actuator)
local last_triggerer_str = meta:get_string("last_triggerer")
local last_triggerer_obj = minetest.get_player_by_name(last_triggerer_str)
-- Turn off actuator
if minetest.get_item_group(actuator.name, "xdecor_actuator") == 2 then
local def = minetest.registered_nodes[actuator.name]
if def._xdecor_actuator_off then
minetest.set_node(pos_actuator, { name = def._xdecor_actuator_off, param2 = actuator.param2 })
end
end
-- Close neighboring doors that are no longer next to any active actuator
local minp = vector.add(actuator_area_min, pos_actuator)
local maxp = vector.add(actuator_area_max, pos_actuator)
local doors = minetest.find_nodes_in_area(minp, maxp, "group:door")
for d=1, #doors do
if not door_is_actuatored(doors[d]) then
local dnode = minetest.get_node(doors[d])
local ddef = minetest.registered_nodes[dnode.name]
if (ddef.protected and last_triggerer_obj) or (not ddef.protected) then
door_close(doors[d], last_triggerer_obj)
end
end
end
end
local function actuator_activate(pos_actuator, actuator_area_min, actuator_area_max, player)
local player_name = player:get_player_name()
local actuator = minetest.get_node(pos_actuator)
local ga = minetest.get_item_group(actuator.name, "xdecor_actuator")
if ga == 2 then
-- No-op if actuator is already active
return
elseif ga == 1 then
local def = minetest.registered_nodes[actuator.name]
-- Turn actuator on
if def._xdecor_actuator_on then
minetest.set_node(pos_actuator, { name = def._xdecor_actuator_on, param2 = actuator.param2 })
-- Store name of last player that triggered the actuator
local meta = minetest.get_meta(pos_actuator)
meta:set_string("last_triggerer", player_name)
end
end
-- Turn on neighboring doors
local minp = vector.add(actuator_area_min, pos_actuator)
local maxp = vector.add(actuator_area_max, pos_actuator)
local doors = minetest.find_nodes_in_area(minp, maxp, "group:door")
for i = 1, #doors do
door_open(doors[i], player)
end
end end
function plate.construct(pos) function plate.construct(pos)
local timer = minetest.get_node_timer(pos) local timer = minetest.get_node_timer(pos)
timer:start(0.1) timer:start(PRESSURE_PLATE_CHECK_TIMER)
end
function plate.has_player_standing_on(pos)
local objs = minetest.get_objects_inside_radius(pos, PRESSURE_PLATE_PLAYER_RADIUS)
for _, player in pairs(objs) do
if player:is_player() then
return true, player
end
end
return false
end end
function plate.timer(pos) function plate.timer(pos)
local objs = minetest.get_objects_inside_radius(pos, 0.8) if not doors.get then
if not next(objs) or not doors.get then return true end return true
end
local minp = {x = pos.x - 2, y = pos.y, z = pos.z - 2} local ok, player = plate.has_player_standing_on(pos)
local maxp = {x = pos.x + 2, y = pos.y, z = pos.z + 2} if ok then
local doors = minetest.find_nodes_in_area(minp, maxp, "group:door") actuator_activate(pos, PRESSURE_PLATE_AREA_MIN, PRESSURE_PLATE_AREA_MAX, player)
return false
for _, player in pairs(objs) do
if player:is_player() then
for i = 1, #doors do
door_toggle(pos, doors[i], player)
end
break
end
end end
return true return true
end end
function plate.construct_on(pos)
local timer = minetest.get_node_timer(pos)
timer:start(DISABLE_ACTUATOR_AFTER)
end
function plate.timer_on(pos)
if plate.has_player_standing_on(pos) then
-- If player is still standing on active pressure plate, restart timer
local timer = minetest.get_node_timer(pos)
timer:start(DISABLE_ACTUATOR_AFTER)
return
end
actuator_timeout(pos, PRESSURE_PLATE_AREA_MIN, PRESSURE_PLATE_AREA_MAX)
end
function plate.register(material, desc, def) function plate.register(material, desc, def)
local groups
if def.groups then
groups = table.copy(def.groups)
else
groups = {}
end
groups.pressure_plate = 1
groups.xdecor_actuator = 1
xdecor.register("pressure_" .. material .. "_off", { xdecor.register("pressure_" .. material .. "_off", {
description = def.description or (desc .. " Pressure Plate"), description = def.description or (desc .. " Pressure Plate"),
--~ Pressure plate tooltip --~ Pressure plate tooltip
@ -63,25 +182,34 @@ function plate.register(material, desc, def)
use_texture_alpha = ALPHA_OPAQUE, use_texture_alpha = ALPHA_OPAQUE,
drawtype = "nodebox", drawtype = "nodebox",
node_box = xdecor.pixelbox(16, {{1, 0, 1, 14, 1, 14}}), node_box = xdecor.pixelbox(16, {{1, 0, 1, 14, 1, 14}}),
groups = def.groups, groups = groups,
is_ground_content = false, is_ground_content = false,
sounds = def.sounds, sounds = def.sounds,
sunlight_propagates = true, sunlight_propagates = true,
on_rotate = screwdriver.rotate_simple, on_rotate = screwdriver.rotate_simple,
on_construct = plate.construct, on_construct = plate.construct,
on_timer = plate.timer on_timer = plate.timer,
_xdecor_actuator_off = "xdecor:pressure_"..material.."_off",
_xdecor_actuator_on = "xdecor:pressure_"..material.."_on",
}) })
local groups_on = table.copy(groups)
groups_on.xdecor_actuator = 2
groups_on.pressure_plate = 2
xdecor.register("pressure_" .. material .. "_on", { xdecor.register("pressure_" .. material .. "_on", {
tiles = {"xdecor_pressure_" .. material .. ".png"}, tiles = {"xdecor_pressure_" .. material .. ".png"},
use_texture_alpha = ALPHA_OPAQUE, use_texture_alpha = ALPHA_OPAQUE,
drawtype = "nodebox", drawtype = "nodebox",
node_box = xdecor.pixelbox(16, {{1, 0, 1, 14, 0.4, 14}}), node_box = xdecor.pixelbox(16, {{1, 0, 1, 14, 0.4, 14}}),
groups = def.groups, groups = groups_on,
is_ground_content = false, is_ground_content = false,
sounds = def.sounds, sounds = def.sounds,
drop = "xdecor:pressure_" .. material .. "_off", drop = "xdecor:pressure_" .. material .. "_off",
sunlight_propagates = true, sunlight_propagates = true,
on_rotate = screwdriver.rotate_simple on_rotate = screwdriver.rotate_simple,
on_construct = plate.construct_on,
on_timer = plate.timer_on,
_xdecor_actuator_off = "xdecor:pressure_"..material.."_off",
_xdecor_actuator_on = "xdecor:pressure_"..material.."_on",
}) })
end end
@ -105,25 +233,22 @@ xdecor.register("lever_off", {
use_texture_alpha = ALPHA_OPAQUE, use_texture_alpha = ALPHA_OPAQUE,
drawtype = "nodebox", drawtype = "nodebox",
node_box = xdecor.pixelbox(16, {{2, 1, 15, 12, 14, 1}}), node_box = xdecor.pixelbox(16, {{2, 1, 15, 12, 14, 1}}),
groups = {cracky = 3, oddly_breakable_by_hand = 2}, groups = {cracky = 3, oddly_breakable_by_hand = 2, lever = 1, xdecor_actuator = 1},
is_ground_content = false, is_ground_content = false,
sounds = default.node_sound_stone_defaults(), sounds = default.node_sound_stone_defaults(),
sunlight_propagates = true, sunlight_propagates = true,
on_rotate = screwdriver.rotate_simple, on_rotate = screwdriver.rotate_simple,
on_rightclick = function(pos, node, clicker, itemstack) on_rightclick = function(pos, node, clicker, itemstack)
if not doors.get then return itemstack end if not doors.get then
local minp = {x = pos.x - 2, y = pos.y - 1, z = pos.z - 2} return itemstack
local maxp = {x = pos.x + 2, y = pos.y + 1, z = pos.z + 2}
local doors = minetest.find_nodes_in_area(minp, maxp, "group:door")
for i = 1, #doors do
door_toggle(pos, doors[i], clicker)
end end
actuator_activate(pos, LEVER_AREA_MIN, LEVER_AREA_MAX, clicker)
return itemstack return itemstack
end, end,
_xdecor_itemframe_offset = -3.5, _xdecor_itemframe_offset = -3.5,
_xdecor_actuator_off = "xdecor:lever_off",
_xdecor_actuator_on = "xdecor:lever_on",
}) })
xdecor.register("lever_on", { xdecor.register("lever_on", {
@ -131,12 +256,50 @@ xdecor.register("lever_on", {
use_texture_alpha = ALPHA_OPAQUE, use_texture_alpha = ALPHA_OPAQUE,
drawtype = "nodebox", drawtype = "nodebox",
node_box = xdecor.pixelbox(16, {{2, 1, 15, 12, 14, 1}}), node_box = xdecor.pixelbox(16, {{2, 1, 15, 12, 14, 1}}),
groups = {cracky = 3, oddly_breakable_by_hand = 2, not_in_creative_inventory = 1}, groups = {cracky = 3, oddly_breakable_by_hand = 2, lever = 2, xdecor_actuator = 2, not_in_creative_inventory = 1},
is_ground_content = false, is_ground_content = false,
sounds = default.node_sound_stone_defaults(), sounds = default.node_sound_stone_defaults(),
sunlight_propagates = true, sunlight_propagates = true,
on_rotate = screwdriver.rotate_simple, on_rotate = screwdriver.rotate_simple,
drop = "xdecor:lever_off" on_rightclick = function(pos, node, clicker, itemstack)
-- Prevent placing nodes on activated lever with the place key
-- for consistent behavior with the lever in "off" state.
-- The player may still place nodes using [Sneak].
return itemstack
end,
on_construct = function(pos)
local timer = minetest.get_node_timer(pos)
timer:start(DISABLE_ACTUATOR_AFTER)
end,
on_timer = function(pos)
local node = minetest.get_node(pos)
actuator_timeout(pos, LEVER_AREA_MIN, LEVER_AREA_MAX)
end,
drop = "xdecor:lever_off",
_xdecor_itemframe_offset = -3.5,
_xdecor_actuator_off = "xdecor:lever_off",
_xdecor_actuator_on = "xdecor:lever_on",
})
-- Make sure the node timers of active actuators are still
-- active when these nodes load again. If not, start them
-- again to trigger their timer action, which is expected
-- to turn off the actuator soon.
minetest.register_lbm({
label = "Restart actuator timers (X-Decor-libre)",
name = "xdecor:restart_actuator_timers",
nodenames = { "group:xdecor_actuator" },
run_at_every_load = true,
action = function(pos, node)
local g = minetest.get_item_group(node.name, "xdecor_actuator")
if g ~= 2 then
return
end
local timer = minetest.get_node_timer(pos)
if not timer:is_started() then
timer:start(DISABLE_ACTUATOR_AFTER)
end
end,
}) })
-- Recipes -- Recipes

View File

@ -450,7 +450,7 @@ end
function workbench:register_cut(nodename, cutlist) function workbench:register_cut(nodename, cutlist)
if registered_cuttable_nodes[nodename] then if registered_cuttable_nodes[nodename] then
minetest.log("error", "[xdecor] Workbench: Tried to register cut for node "..node..", but it was already registered!") minetest.log("error", "[xdecor] Workbench: Tried to register cut for node "..nodename..", but it was already registered!")
return false return false
end end
local ok = true local ok = true