Upload files to "lwcomponents"

This commit is contained in:
Mavori 2025-06-27 17:52:27 +02:00
commit 12990637cc
5 changed files with 2230 additions and 0 deletions

View file

@ -0,0 +1,196 @@
local utils = ...
local RUN_INTERVAL = 0.1
local processes = { index = 0, current = 0 }
local after_job
local function run_processes ()
if #processes > 0 then
processes.index = (processes.index % #processes) + 1
local process = processes[processes.index]
processes.current = processes.index
process.timer = os.clock ()
process.resume ()
processes.current = 0
end
if #processes > 0 then
after_job = minetest.after (RUN_INTERVAL, run_processes)
else
processes.index = 0
processes.current = 0
after_job = nil
end
end
local long_process = { }
function long_process.remove (id)
if id then
for i = 1, #processes, 1 do
if processes[i].id == id then
local breaker = ((processes.current == id) and processes[i].breaker)
if processes.current == i then
processes.current = 0
elseif processes.current > i then
processes.current = processes.current - 1
end
if processes.index >= i then
processes.index = processes.index - 1
end
table.remove (processes, i)
if breaker then
breaker ()
end
return true
end
end
elseif processes[processes.current] then
local breaker = processes[processes.current].breaker
if processes.index >= processes.current then
processes.index = processes.index - 1
end
table.remove (processes, processes.current)
processes.current = 0
if breaker then
breaker ()
end
return true
end
return false
end
function long_process.breaker (id)
if id then
for i = 1, #processes, 1 do
if processes[i].id == id then
return processes[i].breaker
end
end
elseif processes[processes.current] then
return processes[processes.current].breaker
end
return nil
end
function long_process.timer (id)
if id then
for i = 1, #processes, 1 do
if processes[i].id == id then
return processes[i].timer
end
end
elseif processes[processes.current] then
return processes[processes.current].timer
end
return os.clock () + 3600
end
function long_process.expire (secs, id)
if id then
for i = 1, #processes, 1 do
if processes[i].id == id then
if (processes[i].timer + secs) <= os.clock () then
processes[i].breaker ()
return true
end
end
end
elseif processes[processes.current] then
if (processes[processes.current].timer + secs) <= os.clock () then
processes[processes.current].breaker ()
return true
end
end
return false
end
function long_process.add (func, callback, ...)
local args = { ... }
local id = math.random (1000000)
local thread
local function thread_breaker ()
coroutine.yield (thread)
end
local function thread_func ()
return func (unpack (args))
end
local function thread_resume ()
local results = { coroutine.resume (thread) }
if coroutine.status (thread) == "dead" then
if callback then
callback (id, unpack (results))
end
long_process.remove (id)
end
end
thread = coroutine.create (thread_func)
if thread and coroutine.status (thread) == "suspended" then
processes[#processes + 1] =
{
id = id,
resume = thread_resume,
breaker = thread_breaker,
callback = callback,
timer = os.clock ()
}
if not after_job then
after_job = minetest.after (RUN_INTERVAL, run_processes)
end
return id
end
return nil
end
utils.long_process = long_process

6
lwcomponents/mod.conf Normal file
View file

@ -0,0 +1,6 @@
author = loosewheel
description = Various components for mesecons and digilines.
title = LWComponents
name = lwcomponents
depends =
optional_depends = mesecons, digilines, unifieddyes, intllib, hopper, digistuff, pipeworks, lwwires

439
lwcomponents/movefloor.lua Normal file
View file

@ -0,0 +1,439 @@
local utils = ...
local S = utils.S
if utils.mesecon_supported then
local mesecon_rules =
{
{ x = 1, y = 1, z = 0 },
{ x = -1, y = 1, z = 0 },
{ x = 0, y = 1, z = 1 },
{ x = 0, y = 1, z = -1 },
{ x = 1, y = -1, z = 0 },
{ x = -1, y = -1, z = 0 },
{ x = 0, y = -1, z = 1 },
{ x = 0, y = -1, z = -1 },
}
local max_push = 3
local function get_movefloor_direction (rulename)
if rulename.y > 0 then
return { x = 0, y = 1, z = 0 }
elseif rulename.y < 0 then
return { x = 0, y = -1, z = 0 }
end
end
local function add_movefloor_list (pos, list)
for i = 1, #list do
if list[i].x == pos.x and
list[i].y == pos.y and
list[i].z == pos.z then
return false
end
end
list[#list + 1] = { x = pos.x, y = pos.y, z = pos.z }
return true
end
local function find_adjoining_movefloor (pos, list)
local tpos =
{
{ x = pos.x + 1, y = pos.y, z = pos.z },
{ x = pos.x - 1, y = pos.y, z = pos.z },
{ x = pos.x, y = pos.y, z = pos.z + 1 },
{ x = pos.x, y = pos.y, z = pos.z - 1 }
}
for i = 1, #tpos do
local node = minetest.get_node (tpos[i])
if node and node.name == "lwcomponents:movefloor" then
if add_movefloor_list (tpos[i], list) then
find_adjoining_movefloor (tpos[i], list)
end
end
end
end
local function get_node_height (node)
local height = 0
local def = minetest.registered_nodes[node.name]
if def and type (def.collision_box) == "table" then
if def.collision_box.type and def.collision_box.type == "regular" then
height = 1
else
for _, box in pairs (def.collision_box) do
if type (box) == "table" then
if type (box[5]) == "number" then
height = box[5]
else
for _, b in ipairs (box) do
if type (b[5]) == "number" and b[5] > height then
height = b[5]
end
end
end
end
end
end
end
return height
end
local function get_affected_nodes (floor_list)
local list = { }
local max_height = 0
local protected = false
for _, fpos in ipairs (floor_list) do
for y = 0, max_push, 1 do
local npos = vector.add (fpos, { x = 0, y = y, z = 0 })
local node = utils.get_far_node (npos)
if node and node.name ~= "air" then
local meta = minetest.get_meta (npos)
local timer = minetest.get_node_timer (npos)
local h = get_node_height (node) + npos.y - fpos.y - 0.5
list[#list + 1] =
{
pos = npos,
node = node,
meta = (meta and meta:to_table ()),
timeout = (timer and timer:get_timeout ()) or 0,
elapsed = (timer and timer:get_elapsed ()) or 0
}
if h > max_height then
max_height = h
end
if utils.is_protected (npos, nil) then
protected = true
end
end
end
end
return list, math.ceil (max_height), protected
end
local function get_entity_height (obj, base)
local height = 0
if obj.get_pos then
local pos = obj:get_pos ()
if obj.get_luaentity then
local entity = obj:get_luaentity ()
if entity and entity.name then
local def = minetest.registered_entities[entity.name]
if def and type (def.collisionbox) == "table" and
type (def.collisionbox[5]) == "number" then
height = def.collisionbox[5] + pos.y - base
end
end
end
local props = obj:get_properties ()
if props and props.collisionbox and type (props.collisionbox) == "table" and
type (props.collisionbox[5]) == "number" then
if props.collisionbox[5] > height then
height = props.collisionbox[5] + pos.y - base
end
end
end
return height
end
local function get_affected_entities (floor_list)
local list = { }
local max_height = 0
for _, fpos in pairs (floor_list) do
local min_pos = vector.subtract (fpos, { x = 0.4999, y = 0.4999, z = 0.4999 })
local max_pos = vector.add (fpos, { x = 0.4999, y = max_push + 0.4999, z = 0.4999 })
local objects = minetest.get_objects_in_area (min_pos, max_pos)
for _, obj in ipairs (objects) do
local h = get_entity_height (obj, fpos.y + 0.5)
list[#list + 1] =
{
pos = obj:get_pos (),
obj = obj
}
if h > max_height then
max_height = h
end
end
end
return list, math.ceil (max_height)
end
local function is_obstructed (floor_list, height)
for _, fpos in pairs (floor_list) do
local npos = vector.add (fpos, { x = 0, y = height, z = 0 })
if utils.is_protected (npos, nil) then
return true
end
local node = utils.get_far_node (npos)
if node and node.name ~= "air" then
local def = minetest.registered_nodes[node.name]
if not def or not def.buildable_to then
return true
end
end
end
return false
end
local function move_entities (list, move, players)
for _, entry in ipairs (list) do
if entry.obj then
if players or not entry.obj:is_player () then
local pos
if entry.obj:is_player () then
pos = vector.add (entry.pos, { x = move.x, y = move.y + 0.1, z = move.z })
else
pos = vector.add (entry.pos, move)
end
if entry.obj.move_to then
entry.obj:move_to (pos)
elseif entry.set_pos then
entry.obj:set_pos (pos)
end
end
end
end
end
local function update_player_position (list)
for _, entry in ipairs (list) do
local player = minetest.get_player_by_name (entry.name)
if player then
local pos = player:get_pos ()
if pos.y < entry.pos.y then
pos.y = entry.pos.y + 0.1
player:set_pos (pos)
end
end
end
end
local function queue_player_update (list, move)
local players = { }
for _, entry in ipairs (list) do
if entry.obj and entry.obj:is_player () then
players[#players + 1] =
{
pos = vector.add (entry.pos, move),
name = entry.obj:get_player_name ()
}
end
end
if #players > 0 then
minetest.after(0.1, update_player_position, players)
end
end
local function move_nodes (list, move)
if move.y > 0 then
for i = #list, 1, -1 do
local pos = vector.add (list[i].pos, move)
minetest.remove_node (list[i].pos)
minetest.set_node (pos, list[i].node)
if list[i].meta then
local meta = minetest.get_meta (pos)
if meta then
meta:from_table (list[i].meta)
end
end
if list[i].timeout > 0 then
local timer = minetest.get_node_timer (pos)
if timer then
timer:set (list[i].timeout, list[i].elapsed)
end
end
end
else
for i = 1, #list, 1 do
local pos = vector.add (list[i].pos, move)
minetest.remove_node (list[i].pos)
minetest.set_node (pos, list[i].node)
if list[i].meta then
local meta = minetest.get_meta (pos)
if meta then
meta:from_table (list[i].meta)
end
end
if list[i].timeout > 0 then
local timer = minetest.get_node_timer (pos)
if timer then
timer:set (list[i].timeout, list[i].elapsed)
end
end
end
end
end
local function check_for_falling (list)
for _, pos in ipairs (list) do
minetest.check_for_falling (vector.add (pos, { x = 0, y = max_push + 1, z = 0 }))
end
end
local function movefloor_move (pos, node, rulename)
local direction = get_movefloor_direction (rulename)
local list =
{
{ x = pos.x, y = pos.y, z = pos.z }
}
find_adjoining_movefloor (pos, list)
local nodes, height, protected = get_affected_nodes (list)
if protected then
return
end
local entities, h = get_affected_entities (list)
if h > height then
height = h
end
if is_obstructed (list, (direction.y > 0 and height + 1) or -1) then
return
end
if direction.y > 0 then
move_entities (entities, direction, true)
move_nodes (nodes, direction)
queue_player_update (entities, direction)
else
move_nodes (nodes, direction)
move_entities (entities, direction, false)
check_for_falling (list)
queue_player_update (entities, direction)
end
minetest.sound_play ("lwmovefloor", { pos = pos, max_hear_distance = 10, gain = 1.0 }, true)
end
local function mesecon_support ()
return
{
effector =
{
rules = table.copy (mesecon_rules),
action_on = function (pos, node, rulename)
-- do something to turn the effector on
if rulename then
movefloor_move (pos, node, rulename)
end
end
}
}
end
minetest.register_node("lwcomponents:movefloor", {
description = S("Moving Floor"),
tiles = { "lwmovefloortop.png", "lwmovefloortop.png",
"lwmovefloorside.png", "lwmovefloorside.png",
"lwmovefloorside.png", "lwmovefloorside.png" },
sunlight_propagates = false,
drawtype = "normal",
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
}
},
groups = { cracky = 2 },
--sounds = default.node_sound_wood_defaults (),
mesecons = mesecon_support (),
})
end

1353
lwcomponents/pistons.lua Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
local utils = ...
local S = utils.S
if utils.digilines_supported and utils.digistuff_supported then
local function on_contruct (pos)
local meta = minetest.get_meta(pos)
local spec =
"size[7.5,3]"..
"field[1,1;6,2;channel;Channel;${channel}]"..
"button_exit[2.5,2;3,1;submit;Set]"
meta:set_string("formspec", spec)
end
local function on_receive_fields (pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
if fields.submit then
if fields.channel ~= "" then
meta:set_string ("channel", fields.channel)
meta:set_string ("formspec", "")
minetest.swap_node (pos, { name = "lwcomponents:player_button_off",
param2 = minetest.get_node(pos).param2 })
else
minetest.chat_send_player (sender:get_player_name(), "Please set a channel!")
end
end
end
local function player_button_push (pos, node, player)
local meta = minetest.get_meta (pos)
if player and player:is_player () then
local channel = meta:get_string ("channel")
local formspec = meta:get_string ("formspec")
if channel:len () > 0 and formspec:len () == 0 then
utils.digilines_receptor_send (pos,
digistuff.button_get_rules (node),
channel,
{ action = "player",
name = player:get_player_name () })
end
end
if node.name == "lwcomponents:player_button_off" then
node.name = "lwcomponents:player_button_on"
minetest.swap_node(pos, node)
if digistuff.mesecons_installed then
minetest.sound_play ("mesecons_button_push", { pos = pos })
end
minetest.get_node_timer (pos):start (0.25)
end
end
local function player_button_turnoff (pos)
local node = minetest.get_node(pos)
if node.name == "lwcomponents:player_button_on" then
node.name = "lwcomponents:player_button_off"
minetest.swap_node (pos, node)
if digistuff.mesecons_installed then
minetest.sound_play ("mesecons_button_pop", { pos = pos })
end
end
end
minetest.register_node ("lwcomponents:player_button", {
description = S("Player Button"),
drawtype = "nodebox",
tiles = {
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button.png"
},
use_texture_alpha = "clip",
paramtype = "light",
paramtype2 = "facedir",
legacy_wallmounted = true,
walkable = false,
sunlight_propagates = true,
drop = "lwcomponents:player_button",
selection_box = {
type = "fixed",
fixed = { -6/16, -6/16, 5/16, 6/16, 6/16, 8/16 }
},
node_box = {
type = "fixed",
fixed = {
{ -6/16, -6/16, 6/16, 6/16, 6/16, 8/16 }, -- the thin plate behind the button
{ -4/16, -2/16, 4/16, 4/16, 2/16, 6/16 } -- the button itself
}
},
groups = { dig_immediate = 2, digiline_receiver = 1 },
_digistuff_channelcopier_fieldname = "channel",
sounds = default and default.node_sound_stone_defaults(),
digiline =
{
receptor = {},
wire = {
rules = digistuff.button_get_rules,
},
},
on_construct = on_contruct,
after_place_node = digistuff.place_receiver,
after_destruct = digistuff.remove_receiver,
on_receive_fields = on_receive_fields,
})
minetest.register_node ("lwcomponents:player_button_off", {
description = S("Player Button"),
drawtype = "nodebox",
tiles = {
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button.png"
},
use_texture_alpha = "clip",
paramtype = "light",
paramtype2 = "facedir",
legacy_wallmounted = true,
walkable = false,
sunlight_propagates = true,
drop = "lwcomponents:player_button",
selection_box = {
type = "fixed",
fixed = { -6/16, -6/16, 5/16, 6/16, 6/16, 8/16 }
},
node_box = {
type = "fixed",
fixed = {
{ -6/16, -6/16, 6/16, 6/16, 6/16, 8/16 }, -- the thin plate behind the button
{ -4/16, -2/16, 4/16, 4/16, 2/16, 6/16 } -- the button itself
}
},
groups = { dig_immediate = 2, digiline_receiver = 1, not_in_creative_inventory = 1 },
_digistuff_channelcopier_fieldname = "channel",
sounds = default and default.node_sound_stone_defaults(),
digiline =
{
receptor = {},
wire = {
rules = digistuff.button_get_rules,
},
effector = {
action = digistuff.button_handle_digilines,
},
},
after_destruct = digistuff.remove_receiver,
on_rightclick = player_button_push,
})
minetest.register_node ("lwcomponents:player_button_on", {
description = S("Player Button"),
drawtype = "nodebox",
tiles = {
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_side.png",
"lwplayer_button_on.png"
},
use_texture_alpha = "clip",
paramtype = "light",
paramtype2 = "facedir",
legacy_wallmounted = true,
walkable = false,
sunlight_propagates = true,
light_source = 7,
drop = "lwcomponents:player_button",
selection_box = {
type = "fixed",
fixed = { -6/16, -6/16, 5/16, 6/16, 6/16, 8/16 }
},
node_box = {
type = "fixed",
fixed = {
{ -6/16, -6/16, 6/16, 6/16, 6/16, 8/16 },
{ -4/16, -2/16, 11/32, 4/16, 2/16, 6/16 }
}
},
groups = { dig_immediate = 2, digiline_receiver = 1, not_in_creative_inventory = 1 },
_digistuff_channelcopier_fieldname = "channel",
sounds = default and default.node_sound_stone_defaults(),
digiline =
{
receptor = {},
wire = {
rules = digistuff.button_get_rules,
},
},
after_destruct = digistuff.remove_receiver,
on_rightclick = function (pos, node, clicker, itemstack, pointed_thing)
-- so clicking when depressed doesn't place node
end,
on_timer = player_button_turnoff,
})
end -- utils.digilines_supported and utils.digistuff_supported