first commit

master
Joachim Stolberg 2017-09-07 22:27:30 +02:00
parent 3c73aa4f4d
commit b4f004a6c8
19 changed files with 764 additions and 0 deletions

13
LICENSE.txt Normal file
View File

@ -0,0 +1,13 @@
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

109
button.lua Normal file
View File

@ -0,0 +1,109 @@
--[[
Tube Library
============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
CYCLE_TIME = 4
local function switch_on(pos, node, player_name)
if node.name ~= "tubelib:button_active" then
node.name = "tubelib:button_active"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
minetest.sound_play("button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
tubelib.send_cmnd(number, player_name, "start", nil)
end
end
local function switch_off(pos)
local node = minetest.get_node(pos)
if node.name ~= "tubelib:button" then
node.name = "tubelib:button"
minetest.swap_node(pos, node)
minetest.get_node_timer(pos):stop()
minetest.sound_play("button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
local meta = minetest.get_meta(pos)
local number = meta:get_string("number")
tubelib.send_cmnd(number, nil, "stop", nil)
end
end
minetest.register_node("tubelib:button", {
description = "Tubelib Button",
tiles = {
-- up, down, right, left, back, front
'tubelib_front.png',
'tubelib_front.png',
'tubelib_front.png',
'tubelib_front.png',
'tubelib_front.png',
"tubelib_button_off.png",
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", "size[4,3]"..
"field[0.5,0.5;3,1;number;Insert destination block number;]" ..
"button_exit[1,2;2,1;exit;Save]")
end,
on_receive_fields = function(pos, formname, fields, player)
if tubelib.check_number(fields.number) then
print(fields.number)
local meta = minetest.get_meta(pos)
meta:set_string("number", fields.number)
meta:set_string("formspec", nil)
end
end,
on_rightclick = function(pos, node, clicker)
switch_on(pos, node, clicker:get_player_name())
end,
paramtype2 = "facedir",
groups = {cracky=1},
is_ground_content = false,
})
minetest.register_node("tubelib:button_active", {
description = "Tubelib Button",
tiles = {
-- up, down, right, left, back, front
'tubelib_front.png',
'tubelib_front.png',
'tubelib_front.png',
'tubelib_front.png',
'tubelib_front.png',
"tubelib_button_on.png",
},
on_timer = switch_off,
paramtype2 = "facedir",
groups = {crumbly=0, not_in_creative_inventory=1},
is_ground_content = false,
drop = "tubelib:button",
})

94
command.lua Normal file
View File

@ -0,0 +1,94 @@
-------------------------------------------------------------------
-- Data base storage
-------------------------------------------------------------------
local storage = minetest.get_mod_storage()
local Key2Number = minetest.deserialize(storage:get_string("Key2Number")) or {}
local NextNumber = minetest.deserialize(storage:get_string("NextNumber")) or 1
local Number2Pos = minetest.deserialize(storage:get_string("Number2Pos")) or {}
function tubelib.update_mod_storage()
storage:set_string("Key2Number", minetest.serialize(Key2Number))
storage:set_string("NextNumber", minetest.serialize(NextNumber))
storage:set_string("Number2Pos", minetest.serialize(Number2Pos))
end
minetest.register_on_shutdown(function()
tubelib.update_mod_storage()
end)
-------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------
-- Store receive function for each type of block
tubelib.ReceiveFunction = {}
-- Determine position related node number for addressing purposes
local function get_number(pos)
local key = tubelib.get_key_str(pos)
if not Key2Number[key] then
Key2Number[key] = NextNumber
NextNumber = NextNumber + 1
end
return string.format("%.04u", Key2Number[key])
end
-- Get server block data { pos, name, owner }
function tubelib.get_server(dest_num)
if Number2Pos[dest_num] then
return Number2Pos[dest_num]
end
return nil
end
-- Return true if number is known
function tubelib.check_number(number)
return Number2Pos[number] ~= nil
end
-------------------------------------------------------------------
-- Registration functions
-------------------------------------------------------------------
-- Add server node position to the tubelib data base
function tubelib.add_server_node(pos, name, placer)
local number = get_number(pos)
Number2Pos[number] = {
pos = pos,
name = name,
owner = placer:get_player_name()
}
tubelib.update_mod_storage()
return number
end
-- Register server receive function for tubelib commmunication
-- Call this function only at load time!
function tubelib.register_receive_function(name, recv_clbk)
tubelib.ReceiveFunction[name] = recv_clbk
end
-------------------------------------------------------------------
-- Send function
-------------------------------------------------------------------
-- Send a command to the 'dest_num' with topic string (e.g. "start") and
-- topic related payload.
-- The player_name is needed to check the protection rights. If player is unknown
-- use nil instead.
function tubelib.send_cmnd(dest_num, player_name, topic, payload)
if Number2Pos[dest_num] then
local data = Number2Pos[dest_num]
if player_name == nil or not minetest.is_protected(data.pos, player_name) then
if tubelib.ReceiveFunction[data.name] then
return tubelib.ReceiveFunction[data.name](data.pos, topic, payload)
end
end
end
return false
end

1
depends.txt Normal file
View File

@ -0,0 +1 @@

1
description.txt Normal file
View File

@ -0,0 +1 @@
This lib includes tubes with a simple API for automation mods.

177
init.lua Normal file
View File

@ -0,0 +1,177 @@
--[[
Tube Library
============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
tubelib = {
NodeTypes = {},
}
tubelib.debug = true
tubelib.knownNodes = {
["tubelib:tube1"] = true,
["tubelib:tube2"] = true,
["tubelib:tube3"] = true,
["tubelib:tube4"] = true,
["tubelib:tube5"] = true,
["tubelib:tube6"] = true,
["default:chest_locked"] = true,
["default:chest"] = true,
}
tubelib.legacyNodes = {
["default:chest_locked"] = true,
["default:chest"] = true,
}
dofile(minetest.get_modpath("tubelib") .. "/tubes.lua")
dofile(minetest.get_modpath("tubelib") .. "/command.lua")
dofile(minetest.get_modpath("tubelib") .. "/button.lua")
dofile(minetest.get_modpath("tubelib") .. "/lamp.lua")
function tubelib.get_key_str(pos)
pos = minetest.pos_to_string(pos)
return '"'..string.sub(pos, 2, -2)..'"'
end
-- determine neighbor position based on current pos, node facedir
-- and the side F(orward), R(ight), B(ackward), L(eft), D(own), U(p)
function tubelib.get_pos(pos, facedir, side)
local offs = {F=0, R=1, B=2, L=3, D=4, U=5}
local _pos = table.copy(pos)
facedir = (facedir + offs[side]) % 4
local dir = core.facedir_to_dir(facedir)
return vector.add(_pos, dir)
end
-- determine position like "tubelib.get_pos", but
-- consider tubes in addition
function tubelib.get_pos_ext(pos, facedir, side)
local dst_pos = tubelib.get_pos(pos, facedir, side)
local node = minetest.get_node(dst_pos)
if node and string.find(node.name, "tubelib:tube") then
dst_pos = minetest.string_to_pos(minetest.get_meta(dst_pos):get_string("dest_pos"))
node = minetest.get_node(dst_pos)
end
return node, dst_pos
end
-- 6D variant of the facedir to dir conversion
function tubelib.facedir_to_dir(facedir)
local table = {[0] =
{x=0, y=0, z=1},
{x=1, y=0, z=0},
{x=0, y=0, z=-1},
{x=-1, y=0, z=0},
{x=0, y=-1, z=0},
{x=0, y=1, z=0},
}
return table[facedir]
end
local function get_facedir(placer)
if placer then
return minetest.dir_to_facedir(placer:get_look_dir(), false)
end
return 0
end
local function legacy_node(node)
if node and tubelib.legacyNodes[node.name] then
return true
end
return false
end
-------------------------------------------------------------------
-- Registration functions
-------------------------------------------------------------------
-- Register node name for tube push/pull calls
-- Call this function only at load time!
function tubelib.register_node_name(name)
tubelib.knownNodes[name] = true
end
-------------------------------------------------------------------
-- Client side API functions
-------------------------------------------------------------------
function tubelib.pull_items(pos, facedir, sides)
for _,side in ipairs(sides) do
local node, src_pos = tubelib.get_pos_ext(pos, facedir, side)
local key = tubelib.get_key_str(src_pos)
if tubelib.NodeTypes[node.name] and tubelib.NodeTypes[node.name].start_clbk then
return tubelib.NodeTypes[node.name].pull_clbk(src_pos)
elseif legacy_node(node) then
local meta = minetest.get_meta(src_pos)
local inv = meta:get_inventory()
return tubelib.get_item(inv, "main")
end
end
return nil
end
function tubelib.push_items(pos, facedir, sides, items)
for _,side in ipairs(sides) do
local node, dst_pos = tubelib.get_pos_ext(pos, facedir, side)
local key = tubelib.get_key_str(dst_pos)
if tubelib.NodeTypes[node.name] and tubelib.NodeTypes[node.name].start_clbk then
return tubelib.NodeTypes[node.name].push_clbk(dst_pos, items)
elseif legacy_node(node) then
local meta = minetest.get_meta(dst_pos)
local inv = meta:get_inventory()
return tubelib.put_item(inv, "main", items)
elseif node and node.name == "air" then
minetest.add_item(dst_pos, items)
return true
end
end
return false
end
-------------------------------------------------------------------
-- Server side helper functions
-------------------------------------------------------------------
function tubelib.get_item(inv, listname)
if inv:is_empty(listname) then
--print("nil")
return nil
end
local stack, slot
local size = inv:get_size(listname)
local offs = math.random(size)
--print("tubelib.get_item", offs)
for idx = 1, size do
local slot = ((idx + offs) % size) + 1
local items = inv:get_stack(listname, slot)
if items:get_count() > 0 then
local taken = items:take_item(1)
inv:set_stack(listname, slot, items)
return taken
end
end
return nil
end
function tubelib.put_item(inv, listname, items)
if inv:room_for_item(listname, items) then
inv:add_item(listname, items)
return true
end
return false
end

83
lamp.lua Normal file
View File

@ -0,0 +1,83 @@
--[[
Tube Library
============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
local function switch_on(pos, node)
node.name = "tubelib:lamp_on"
minetest.swap_node(pos, node)
return true
end
local function switch_off(pos, node)
node.name = "tubelib:lamp"
minetest.swap_node(pos, node)
return true
end
local function command_reception(pos, topic, payload)
local node = minetest.get_node(pos)
if string.match(topic, "start") then
return switch_on(pos, node)
elseif string.match(topic, "stop") then
return switch_off(pos, node)
else
return false
end
end
minetest.register_node("tubelib:lamp", {
description = "Tubelib Lamp",
tiles = {
'tubelib_lamp.png',
},
after_place_node = function(pos, placer)
local number = tubelib.add_server_node(pos, "tubelib:lamp", placer)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", number)
end,
on_rightclick = function(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
switch_on(pos, node)
end
end,
paramtype = 'light',
light_source = 0,
groups = {cracky=1},
is_ground_content = false,
})
minetest.register_node("tubelib:lamp_on", {
description = "Tubelib Lamp",
tiles = {
'tubelib_lamp.png',
},
on_rightclick = function(pos, node, clicker)
if not minetest.is_protected(pos, clicker:get_player_name()) then
switch_off(pos, node)
end
end,
paramtype = 'light',
light_source = 8,
groups = {crumbly=0, not_in_creative_inventory=1},
is_ground_content = false,
drop = "tubelib:lamp",
})
tubelib.register_receive_function("tubelib:lamp", command_reception)
tubelib.register_receive_function("tubelib:lamp_on", command_reception)

1
mod.conf Normal file
View File

@ -0,0 +1 @@
name=tubelib

BIN
sounds/button.ogg Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
textures/tubelib_front.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
textures/tubelib_hole.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

BIN
textures/tubelib_hole2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

BIN
textures/tubelib_knee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
textures/tubelib_knee2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

BIN
textures/tubelib_lamp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
textures/tubelib_tube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

285
tubes.lua Normal file
View File

@ -0,0 +1,285 @@
--[[
Tube Library
============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
local MAX_TUBE_LENGTH = 100
local TubeTypes = {
0,0,0,0,0,0,1,3,1,3, -- 01-10
4,5,3,1,3,1,4,5,1,3, -- 11-20
1,3,4,5,3,1,3,1,4,5, -- 21-30
2,2,2,2,0,2,2,2,5,2, -- 31-40
5,0, -- 40-41
}
local TubeFacedir = {
0,0,0,0,0,0,0,2,0,1, -- 01-10
2,2,2,1,3,1,3,3,0,3, -- 11-20
0,0,0,0,1,1,0,1,1,1, -- 21-30
0,0,0,0,0,0,0,0,0,0, -- 31-40
0,0, -- 40-41
}
-- Return neighbor tubes orientation relative to the given pos.
local function get_neighbor_tubes_orientation(pos)
local orientation = 0
local Nodes = {
minetest.get_node({x=pos.x , y=pos.y , z=pos.z+1}),
minetest.get_node({x=pos.x+1, y=pos.y , z=pos.z }),
minetest.get_node({x=pos.x , y=pos.y , z=pos.z-1}),
minetest.get_node({x=pos.x-1, y=pos.y , z=pos.z }),
minetest.get_node({x=pos.x , y=pos.y-1, z=pos.z }),
minetest.get_node({x=pos.x , y=pos.y+1, z=pos.z }),
}
for side,node in ipairs(Nodes) do
if tubelib.knownNodes[node.name] then
orientation = orientation * 6 + side
if orientation > 6 then
break
end
end
end
return orientation
end
local function determine_tube_node(pos)
local node = minetest.get_node(pos)
if not string.find(node.name, "tubelib:tube") then
return nil
end
local orientation = get_neighbor_tubes_orientation(pos)
if orientation > 6 then
node.name = "tubelib:tube"..TubeTypes[orientation]
node.param2 = TubeFacedir[orientation]
return node
elseif orientation > 0 then
orientation = orientation * 6 + (((node.param2 + 2) % 4) + 1)
node.name = "tubelib:tube"..TubeTypes[orientation]
node.param2 = TubeFacedir[orientation]
return node
end
return nil
end
local function update_tube(pos)
local node = determine_tube_node(pos)
if node then
minetest.swap_node(pos, node)
end
end
local OffsTable = {
{2,0}, -- tube1
{4,5}, -- tube2
{2,3}, -- tube3
{2,4}, -- tube4
{2,5}, -- tube5
}
local function nodetype_to_pos(pos, pos1, node)
local idx = string.byte(node.name, -1) - 48
local facedir1 = OffsTable[idx][1]
local facedir2 = OffsTable[idx][2]
if facedir1 < 4 then
facedir1 = (facedir1 + node.param2) % 4
end
if facedir2 < 4 then
facedir2 = (facedir2 + node.param2) % 4
end
local dir1 = tubelib.facedir_to_dir(facedir1)
local dir2 = tubelib.facedir_to_dir(facedir2)
local p1 = vector.add(pos1, dir1)
local p2 = vector.add(pos1, dir2)
if pos == nil then
return p1, p2
elseif vector.equals(p1, pos) then
return p2
else
return p1
end
end
local function walk_to_peer(pos, pos1)
local node = minetest.get_node(pos1)
local pos2
local cnt = 0
while string.find(node.name, "tubelib:tube") and cnt < MAX_TUBE_LENGTH do
pos2 = nodetype_to_pos(pos, pos1, node)
pos, pos1 = pos1, pos2
cnt = cnt + 1
node = minetest.get_node(pos1)
end
return pos, pos1
end
local function update_head_tubes(pos)
local node = minetest.get_node(pos)
if string.find(node.name, "tubelib:tube") then
local pos1, pos2 = nodetype_to_pos(nil, pos, node)
local peer1, dest1 = walk_to_peer(pos, pos1)
local peer2, dest2 = walk_to_peer(pos, pos2)
minetest.get_meta(peer1):set_string("dest_pos", minetest.pos_to_string(dest2))
minetest.get_meta(peer2):set_string("dest_pos", minetest.pos_to_string(dest1))
minetest.get_meta(pos1):set_string("dest_pos", nil)
minetest.get_meta(pos2):set_string("dest_pos", nil)
if tubelib.debug then
minetest.get_meta(peer1):set_string("infotext", minetest.pos_to_string(dest2))
minetest.get_meta(peer2):set_string("infotext", minetest.pos_to_string(dest1))
minetest.get_meta(pos1):set_string("infotext", nil)
minetest.get_meta(pos2):set_string("infotext", nil)
end
end
end
local function update_surrounding_tubes(pos)
update_tube({x=pos.x , y=pos.y , z=pos.z+1})
update_tube({x=pos.x+1, y=pos.y , z=pos.z })
update_tube({x=pos.x , y=pos.y , z=pos.z-1})
update_tube({x=pos.x-1, y=pos.y , z=pos.z })
update_tube({x=pos.x , y=pos.y-1, z=pos.z })
update_tube({x=pos.x , y=pos.y+1, z=pos.z })
update_tube(pos)
update_head_tubes(pos)
end
local function after_tube_removed(pos, node)
local pos1, pos2 = nodetype_to_pos(nil, pos, node)
local peer1, dest1 = walk_to_peer(pos, pos1)
local peer2, dest2 = walk_to_peer(pos, pos2)
minetest.get_meta(peer1):set_string("dest_pos", minetest.pos_to_string(pos))
minetest.get_meta(peer2):set_string("dest_pos", minetest.pos_to_string(pos))
minetest.get_meta(pos1):set_string("dest_pos", minetest.pos_to_string(dest1))
minetest.get_meta(pos2):set_string("dest_pos", minetest.pos_to_string(dest2))
if tubelib.debug then
minetest.get_meta(peer1):set_string("infotext", minetest.pos_to_string(pos))
minetest.get_meta(peer2):set_string("infotext", minetest.pos_to_string(pos))
minetest.get_meta(pos1):set_string("infotext", minetest.pos_to_string(dest1))
minetest.get_meta(pos2):set_string("infotext", minetest.pos_to_string(dest2))
end
end
local DefNodeboxes = {
-- x1 y1 z1 x2 y2 z2
{ -1/4, -1/4, -1/4, 1/4, 1/4, 1/4 },
{ -1/4, -1/4, -1/4, 1/4, 1/4, 1/4 },
}
local DirCorrections = {
{3, 6}, {2, 5}, -- standard tubes
{3, 1}, {3, 2}, {3, 5}, -- knees from front to..
}
local SelectBoxes = {
{ -1/4, -1/4, -1/2, 1/4, 1/4, 1/2 },
{ -1/4, -1/2, -1/4, 1/4, 1/2, 1/4 },
{ -1/2, -1/4, -1/2, 1/4, 1/4, 1/4 },
{ -1/4, -1/2, -1/2, 1/4, 1/4, 1/4 },
{ -1/4, -1/4, -1/2, 1/4, 1/2, 1/4 },
}
local TilesData = {
-- up, down, right, left, back, front
{
"tubelib_tube.png^[transformR90",
"tubelib_tube.png^[transformR90",
"tubelib_tube.png",
"tubelib_tube.png",
"tubelib_hole.png",
"tubelib_hole.png",
},
{
"tubelib_hole.png",
"tubelib_hole.png",
"tubelib_tube.png^[transformR90",
"tubelib_tube.png^[transformR90",
"tubelib_tube.png^[transformR90",
"tubelib_tube.png^[transformR90",
},
{
"tubelib_knee.png^[transformR270",
"tubelib_knee.png^[transformR180",
"tubelib_knee2.png^[transformR270",
"tubelib_hole2.png^[transformR90",
"tubelib_knee2.png^[transformR90",
"tubelib_hole2.png^[transformR270",
},
{
"tubelib_knee2.png",
"tubelib_hole2.png^[transformR180",
"tubelib_knee.png^[transformR270",
"tubelib_knee.png",
"tubelib_knee2.png",
"tubelib_hole2.png",
},
{
"tubelib_hole2.png",
"tubelib_knee2.png^[transformR180",
"tubelib_knee.png^[transformR180",
"tubelib_knee.png^[transformR90",
"tubelib_knee2.png^[transformR180",
"tubelib_hole2.png^[transformR180",
},
}
for idx,pos in ipairs(DirCorrections) do
node_box_data = table.copy(DefNodeboxes)
node_box_data[1][pos[1]] = node_box_data[1][pos[1]] * 2
node_box_data[2][pos[2]] = node_box_data[2][pos[2]] * 2
tiles_data = TilesData[idx]
if idx == 1 then
hidden = 0
else
hidden = 1
end
minetest.register_node("tubelib:tube"..idx, {
description = "Tubelib Tube",
tiles = tiles_data,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = node_box_data,
},
selection_box = {
type = "fixed",
fixed = SelectBoxes[idx],
},
collision_box = {
type = "fixed",
fixed = SelectBoxes[idx],
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
update_surrounding_tubes(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
after_tube_removed(pos, oldnode)
end,
paramtype2 = "facedir",
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky=3, stone=1, not_in_creative_inventory=hidden},
drop = "tubelib:tube1",
})
end