EinsDreiDreiSieben/mods/xdecor/src/mechanisms.lua

325 lines
10 KiB
Lua

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