v1.00 Extracted from TechPack to be used as standalone mod
parent
f3cd8fc992
commit
ff39a821d2
14
README.md
14
README.md
|
@ -1,7 +1,3 @@
|
|||
:boom: This Mod is now part of the Modpack ![TechPack](https://github.com/joe7575/techpack).
|
||||
This repository here is no longer maintained. :boom:
|
||||
|
||||
```
|
||||
# Tube Library
|
||||
|
||||
Minetest Tube Mod for item exchange via lumber tubes and wireless message communication between nodes.
|
||||
|
@ -23,6 +19,9 @@ The mod provides:
|
|||
Hints for Admins: ![manual.md](https://github.com/joe7575/Minetest-Tubelib/blob/master/manual.md)
|
||||
Programmers Manual: ![api.md](https://github.com/joe7575/Minetest-Tubelib/blob/master/api.md)
|
||||
|
||||
This mod is part of the ModPack ![TechPack](https://github.com/joe7575/techpack)
|
||||
A Tutorial to this Mod is available as ![TechPack Wiki](https://github.com/joe7575/techpack/wiki)
|
||||
|
||||
Browse on: ![GitHub](https://github.com/joe7575/Minetest-Tubelib)
|
||||
|
||||
Download: ![GitHub](https://github.com/joe7575/Minetest-Tubelib/archive/master.zip)
|
||||
|
@ -32,7 +31,10 @@ Download: ![GitHub](https://github.com/joe7575/Minetest-Tubelib/archive/master.z
|
|||
default
|
||||
|
||||
# License
|
||||
Copyright (C) 2017 Joachim Stolberg
|
||||
Copyright (C) 2017-2018 Joachim Stolberg
|
||||
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt
|
||||
Textures: CC0
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
default
|
||||
|
||||
|
|
13
api.md
13
api.md
|
@ -197,18 +197,12 @@ Because several nodes could be addressed, the function don't return any response
|
|||
|
||||
|
||||
```LUA
|
||||
tubelib.send_request(number, placer_name, clicker_name, topic, payload)
|
||||
tubelib.send_request(number, topic, payload)
|
||||
```
|
||||
In contrast to `send_message` this functions send a message to exactly one node
|
||||
referenced by `number` and returns the node response.
|
||||
The message is based on the topic string (e.g. "state") and
|
||||
topic related payload.
|
||||
The placer and clicker names are needed to check the protection rights.
|
||||
`placer_name` is the name of the player, who places the node.
|
||||
`clicker_name` is the name of the player, who uses the node.
|
||||
`placer_name` of sending and receiving nodes have to be the same.
|
||||
If every player should be able to send a message, use nil for clicker_name.
|
||||
|
||||
|
||||
|
||||
## 4. Code Snippets
|
||||
|
@ -279,10 +273,11 @@ and as templates for own projects:
|
|||
## 6. Further information
|
||||
|
||||
The complete functionality is implemented in the file
|
||||
![command.lua](https://github.com/joe7575/Minetest-Tubelib/blob/master/command.lua).
|
||||
![command.lua](https://github.com/joe7575/techpack/blob/master/tubelib/command.lua).
|
||||
This file has further helper functions and is recommended for deeper study.
|
||||
|
||||
## 7. History
|
||||
|
||||
2017-10-02 First draft
|
||||
2017-10-29 Commands start/stop replaced by on/off
|
||||
2017-10-29 Commands start/stop replaced by on/off
|
||||
2018-03-31 Corrections for 'send_request' and 'add_node'
|
||||
|
|
|
@ -49,6 +49,7 @@ minetest.register_node("tubelib:blackhole", {
|
|||
tubelib.remove_node(pos) -- <<=== tubelib
|
||||
end,
|
||||
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
|
|
11
button.lua
11
button.lua
|
@ -52,7 +52,11 @@ local function switch_off(pos)
|
|||
local own_num = meta:get_string("own_num")
|
||||
local numbers = meta:get_string("numbers")
|
||||
local placer_name = meta:get_string("placer_name")
|
||||
tubelib.send_message(numbers, placer_name, nil, "off", own_num) -- <<=== tubelib
|
||||
local clicker_name = nil
|
||||
if meta:get_string("public") == "false" then
|
||||
clicker_name = meta:get_string("clicker_name")
|
||||
end
|
||||
tubelib.send_message(numbers, placer_name, clicker_name, "off", own_num) -- <<=== tubelib
|
||||
end
|
||||
|
||||
|
||||
|
@ -123,6 +127,7 @@ minetest.register_node("tubelib:button", {
|
|||
end
|
||||
end,
|
||||
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
|
@ -153,13 +158,15 @@ minetest.register_node("tubelib:button_active", {
|
|||
end,
|
||||
|
||||
on_timer = switch_off,
|
||||
on_rotate = screwdriver.disallow,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
groups = {crumbly=0, not_in_creative_inventory=1},
|
||||
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
drop = "tubelib:button",
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
|
|
77
command.lua
77
command.lua
|
@ -23,13 +23,14 @@ local Version = minetest.deserialize(storage:get_string("Version")) or 1
|
|||
local Number2Pos = minetest.deserialize(storage:get_string("Number2Pos")) or {}
|
||||
|
||||
local function update_mod_storage()
|
||||
minetest.log("action", "[Tubelib] Store data...")
|
||||
storage:set_string("NextNumber", minetest.serialize(NextNumber))
|
||||
storage:set_string("Version", minetest.serialize(Version))
|
||||
storage:set_string("Number2Pos", minetest.serialize(Number2Pos))
|
||||
storage:set_string("Key2Number", nil) -- not used any more
|
||||
-- store data each hour
|
||||
minetest.after(60*60, update_mod_storage)
|
||||
print("[Tubelib] Data stored")
|
||||
minetest.log("action", "[Tubelib] Data stored")
|
||||
end
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
|
@ -94,6 +95,20 @@ local function generate_Key2Number()
|
|||
end
|
||||
end
|
||||
|
||||
local function not_protected(pos, placer_name, clicker_name)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local cached_name = meta:get_string("tubelib_cached_name")
|
||||
if placer_name and (placer_name == cached_name or not minetest.is_protected(pos, placer_name)) then
|
||||
meta:set_string("tubelib_cached_name", placer_name)
|
||||
if clicker_name == nil or not minetest.is_protected(pos, clicker_name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- API helper functions
|
||||
-------------------------------------------------------------------
|
||||
|
@ -158,7 +173,7 @@ end
|
|||
-- Node construction/destruction functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
-- Add node to the tubelib lists and update the tube surrounding.
|
||||
-- Add node to the tubelib lists.
|
||||
-- Function determines and returns the node position number,
|
||||
-- needed for message communication.
|
||||
function tubelib.add_node(pos, name)
|
||||
|
@ -168,8 +183,6 @@ function tubelib.add_node(pos, name)
|
|||
pos = pos,
|
||||
name = name,
|
||||
}
|
||||
-- update surrounding tubes
|
||||
tubelib.update_tubes(pos)
|
||||
return number
|
||||
end
|
||||
|
||||
|
@ -204,12 +217,17 @@ end
|
|||
function tubelib.register_node(name, add_names, node_definition)
|
||||
tubelib_NodeDef[name] = node_definition
|
||||
-- store facedir table for all known node names
|
||||
tubelib.knownNodes[name] = true
|
||||
Name2Name[name] = name
|
||||
for _,n in ipairs(add_names) do
|
||||
tubelib.knownNodes[n] = true
|
||||
Name2Name[n] = name
|
||||
end
|
||||
if node_definition.on_pull_item or node_definition.on_push_item or
|
||||
node_definition.is_pusher then
|
||||
tubelib.KnownNodes[name] = true
|
||||
for _,n in ipairs(add_names) do
|
||||
tubelib.KnownNodes[n] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
@ -220,13 +238,9 @@ function tubelib.send_message(numbers, placer_name, clicker_name, topic, payload
|
|||
for _,num in ipairs(string_split(numbers, " ")) do
|
||||
if Number2Pos[num] and Number2Pos[num].name then
|
||||
local data = Number2Pos[num]
|
||||
if placer_name and not minetest.is_protected(data.pos, placer_name) then
|
||||
if clicker_name == nil or not minetest.is_protected(data.pos, clicker_name) then
|
||||
if data and data.name then
|
||||
if tubelib_NodeDef[data.name] and tubelib_NodeDef[data.name].on_recv_message then
|
||||
tubelib_NodeDef[data.name].on_recv_message(data.pos, topic, payload)
|
||||
end
|
||||
end
|
||||
if not_protected(data.pos, placer_name, clicker_name) then
|
||||
if tubelib_NodeDef[data.name] and tubelib_NodeDef[data.name].on_recv_message then
|
||||
tubelib_NodeDef[data.name].on_recv_message(data.pos, topic, payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -236,10 +250,8 @@ end
|
|||
function tubelib.send_request(number, topic, payload)
|
||||
if Number2Pos[number] and Number2Pos[number].name then
|
||||
local data = Number2Pos[number]
|
||||
if data and data.name then
|
||||
if tubelib_NodeDef[data.name] and tubelib_NodeDef[data.name].on_recv_message then
|
||||
return tubelib_NodeDef[data.name].on_recv_message(data.pos, topic, payload)
|
||||
end
|
||||
if tubelib_NodeDef[data.name] and tubelib_NodeDef[data.name].on_recv_message then
|
||||
return tubelib_NodeDef[data.name].on_recv_message(data.pos, topic, payload)
|
||||
end
|
||||
end
|
||||
return false
|
||||
|
@ -251,6 +263,7 @@ end
|
|||
|
||||
function tubelib.pull_items(pos, side, player_name)
|
||||
local npos, facedir = get_neighbor_pos(pos, side)
|
||||
if npos == nil then return end
|
||||
local nside, node = get_node_side(npos, facedir)
|
||||
local name = Name2Name[node.name]
|
||||
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_pull_item then
|
||||
|
@ -261,6 +274,7 @@ end
|
|||
|
||||
function tubelib.push_items(pos, side, items, player_name)
|
||||
local npos, facedir = get_neighbor_pos(pos, side)
|
||||
if npos == nil then return end
|
||||
local nside, node = get_node_side(npos, facedir)
|
||||
local name = Name2Name[node.name]
|
||||
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_push_item then
|
||||
|
@ -274,6 +288,7 @@ end
|
|||
|
||||
function tubelib.unpull_items(pos, side, items, player_name)
|
||||
local npos, facedir = get_neighbor_pos(pos, side)
|
||||
if npos == nil then return end
|
||||
local nside, node = get_node_side(npos, facedir)
|
||||
local name = Name2Name[node.name]
|
||||
if tubelib_NodeDef[name] and tubelib_NodeDef[name].on_unpull_item then
|
||||
|
@ -282,6 +297,20 @@ function tubelib.unpull_items(pos, side, items, player_name)
|
|||
return false
|
||||
end
|
||||
|
||||
function tubelib.pull_stack(pos, side, player_name)
|
||||
local npos, facedir = get_neighbor_pos(pos, side)
|
||||
if npos == nil then return end
|
||||
local nside, node = get_node_side(npos, facedir)
|
||||
local name = Name2Name[node.name]
|
||||
if tubelib_NodeDef[name] then
|
||||
if tubelib_NodeDef[name].on_pull_stack then
|
||||
return tubelib_NodeDef[name].on_pull_stack(npos, nside, player_name)
|
||||
elseif tubelib_NodeDef[name].on_pull_item then
|
||||
return tubelib_NodeDef[name].on_pull_item(npos, nside, player_name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Server side helper functions
|
||||
|
@ -363,6 +392,20 @@ function tubelib.get_num_items(meta, listname, num)
|
|||
return nil
|
||||
end
|
||||
|
||||
function tubelib.get_stack(meta, listname)
|
||||
local inv = meta:get_inventory()
|
||||
local item = tubelib.get_item(meta, listname)
|
||||
if item and inv:contains_item(listname, item) then
|
||||
-- try to remove a complete stack
|
||||
item:set_count(98)
|
||||
local taken = inv:remove_item(listname, item)
|
||||
-- add the already removed
|
||||
taken:set_count(taken:get_count() + 1)
|
||||
return taken
|
||||
end
|
||||
return item
|
||||
end
|
||||
|
||||
-- Return "full", "loaded", or "empty" depending
|
||||
-- on the number of fuel stack items.
|
||||
-- Function only works on fuel inventories with one stacks/99 items
|
||||
|
|
|
@ -25,7 +25,6 @@ local TICKS_TO_SLEEP = 5
|
|||
local CYCLE_TIME = 2
|
||||
local STOP_STATE = 0
|
||||
local STANDBY_STATE = -1
|
||||
local FAULT_STATE = -2
|
||||
|
||||
-- Return a key/value table with all items and the corresponding stack numbers
|
||||
local function invlist_content_as_kvlist(list)
|
||||
|
@ -214,6 +213,8 @@ local function keep_running(pos, elapsed)
|
|||
local inv = meta:get_inventory()
|
||||
local list = inv:get_list("src")
|
||||
local kvSrc = invlist_content_as_kvlist(list)
|
||||
local counter = minetest.deserialize(meta:get_string("item_counter")) or
|
||||
{red=0, green=0, blue=0, yellow=0}
|
||||
|
||||
-- calculate the filter settings only once
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
|
@ -242,6 +243,7 @@ local function keep_running(pos, elapsed)
|
|||
if not tubelib.push_items(pos, side, item, player_name) then -- <<=== tubelib
|
||||
tubelib.put_item(meta, "src", item)
|
||||
else
|
||||
counter[listname] = counter[listname] + 1
|
||||
busy = true
|
||||
end
|
||||
end
|
||||
|
@ -258,6 +260,7 @@ local function keep_running(pos, elapsed)
|
|||
if not tubelib.push_items(pos, side, item, player_name) then -- <<=== tubelib
|
||||
tubelib.put_item(meta, "src", item)
|
||||
else
|
||||
counter[listname] = counter[listname] + 1
|
||||
busy = true
|
||||
end
|
||||
end
|
||||
|
@ -277,6 +280,7 @@ local function keep_running(pos, elapsed)
|
|||
end
|
||||
end
|
||||
|
||||
meta:set_string("item_counter", minetest.serialize(counter))
|
||||
meta:set_int("running", running)
|
||||
return true
|
||||
end
|
||||
|
@ -302,7 +306,7 @@ local function on_receive_fields(pos, formname, fields, player)
|
|||
filter_settings(pos)
|
||||
|
||||
if fields.button ~= nil then
|
||||
if running > STOP_STATE or running == FAULT_STATE then
|
||||
if running > STOP_STATE then
|
||||
stop_the_machine(pos)
|
||||
else
|
||||
start_the_machine(pos)
|
||||
|
@ -312,6 +316,24 @@ local function on_receive_fields(pos, formname, fields, player)
|
|||
end
|
||||
end
|
||||
|
||||
-- tubelib command to turn on/off filter channels
|
||||
local function change_filter_settings(pos, slot, val)
|
||||
local slots = {["red"] = 1, ["green"] = 2, ["blue"] = 3, ["yellow"] = 4}
|
||||
local meta = minetest.get_meta(pos)
|
||||
local filter = minetest.deserialize(meta:get_string("filter"))
|
||||
local num = slots[slot] or 1
|
||||
if num >= 1 and num <= 4 then
|
||||
filter[num] = val == "on"
|
||||
end
|
||||
meta:set_string("filter", minetest.serialize(filter))
|
||||
|
||||
filter_settings(pos)
|
||||
|
||||
local running = meta:get_int("running")
|
||||
meta:set_string("formspec", distributor_formspec(tubelib.state(running), filter))
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_node("tubelib:distributor", {
|
||||
description = "Tubelib Distributor",
|
||||
tiles = {
|
||||
|
@ -340,6 +362,7 @@ minetest.register_node("tubelib:distributor", {
|
|||
inv:set_size('green', 6)
|
||||
inv:set_size('red', 6)
|
||||
inv:set_size('blue', 6)
|
||||
meta:set_string("item_counter", minetest.serialize({red=0, green=0, blue=0, yellow=0}))
|
||||
end,
|
||||
|
||||
on_receive_fields = on_receive_fields,
|
||||
|
@ -361,6 +384,7 @@ minetest.register_node("tubelib:distributor", {
|
|||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
|
||||
on_timer = keep_running,
|
||||
on_rotate = screwdriver.disallow,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
|
@ -399,6 +423,7 @@ minetest.register_node("tubelib:distributor_active", {
|
|||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
|
||||
on_timer = keep_running,
|
||||
on_rotate = screwdriver.disallow,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
|
@ -441,6 +466,15 @@ tubelib.register_node("tubelib:distributor", {"tubelib:distributor_active"}, {
|
|||
local meta = minetest.get_meta(pos)
|
||||
local running = meta:get_int("running")
|
||||
return tubelib.statestring(running)
|
||||
elseif topic == "filter" then
|
||||
return change_filter_settings(pos, payload.slot, payload.val)
|
||||
elseif topic == "counter" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return minetest.deserialize(meta:get_string("item_counter")) or
|
||||
{red=0, green=0, blue=0, yellow=0}
|
||||
elseif topic == "clear_counter" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("item_counter", minetest.serialize({red=0, green=0, blue=0, yellow=0}))
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
|
|
13
init.lua
13
init.lua
|
@ -3,7 +3,7 @@
|
|||
Tube Library
|
||||
============
|
||||
|
||||
Copyright (C) 2017 Joachim Stolberg
|
||||
Copyright (C) 2017,2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
@ -19,7 +19,11 @@
|
|||
2017-11-02 v0.08 Data base changed, aging of node positions added
|
||||
2017-11-04 v0.09 functions set_data/get_data added
|
||||
2018-01-27 v0.10 WLAN Chip added, recipes reviced, Pusher state 'blocked' added,
|
||||
function send_request changed
|
||||
function send_request changed
|
||||
2018-08-09 v1.00 Extracted from TechPack to be used as standalone mod
|
||||
- new tubing algorithm
|
||||
- tubelib.pull_stack()/tubelib.get_stack() added
|
||||
- item counter for pusher/distributor added
|
||||
|
||||
]]--
|
||||
|
||||
|
@ -28,7 +32,7 @@ tubelib = {
|
|||
NodeDef = {}, -- node registration info
|
||||
}
|
||||
|
||||
tubelib.version = 0.10
|
||||
tubelib.version = 1.00
|
||||
|
||||
|
||||
--------------------------- conversion to v0.04
|
||||
|
@ -83,7 +87,8 @@ minetest.register_craft({
|
|||
})
|
||||
|
||||
|
||||
dofile(minetest.get_modpath("tubelib") .. "/tubes.lua")
|
||||
dofile(minetest.get_modpath("tubelib") .. "/tubes1.lua")
|
||||
dofile(minetest.get_modpath("tubelib") .. "/tubes2.lua")
|
||||
dofile(minetest.get_modpath("tubelib") .. "/command.lua")
|
||||
dofile(minetest.get_modpath("tubelib") .. "/states.lua")
|
||||
dofile(minetest.get_modpath("tubelib") .. "/pusher.lua")
|
||||
|
|
39
manual.md
39
manual.md
|
@ -1,39 +0,0 @@
|
|||
# Tubelib Library
|
||||
|
||||
## Hints for Admins and Players
|
||||
|
||||
Tubelib is little useful for itself, it makes only sense with extensions such as:
|
||||
- ![tubelib_addons1](https://github.com/joe7575/tubelib_addons1) with farming nodes like Harvester, Quarry, Grinder, Autocrafter, Fermenter and Reformer.
|
||||
- ![tubelib_addons2](https://github.com/joe7575/tubelib_addons2) with control task nodes like Timer, Sequencer, Repeater, Gate, Door, Access Lock and Color Lamp.
|
||||
|
||||
However Tubelib provides the following basic nodes:
|
||||
|
||||
### Tubes
|
||||
Tubes allow the item exchange between two nodes. Tube forks are not possible. You have to use chests
|
||||
or other inventory nodes as hubs to build more complex structures.
|
||||
Tubes for itself are passive. For item exchange you have to use pulling/pushing nodes in addition.
|
||||
The maximum tube length is limited to 100 nodes.
|
||||
|
||||
### Pusher
|
||||
The Pusher is able to pull one item out of one inventory node and pushing it into another inventory node directly or by means of tubes.
|
||||
It the source node is empty or the destination node full, the Pusher goes into STANDBY state for some seconds.
|
||||
|
||||
### Distributor
|
||||
The Distributor works as filter and pusher. It allows to divide and distribute incoming items into 4 tube channels.
|
||||
The channels can be switched on/off and individually configured with up to 6 items. The filter passes the configured
|
||||
items and restrains all others. To increase the throughput, one item can be added several times to a filter.
|
||||
An unconfigured but activated filter allows to pass up to 6 remaining items.
|
||||
|
||||
### Button/Switch
|
||||
The Button/Switch is a simple communication node for the Tubelib wireless communication.
|
||||
This node can be configured as button and switch. For the button configuration different switching
|
||||
times from 2 to 16 seconds are possible. The Button/Switch node has to be configured with the destination
|
||||
number of the receiving node (e.g. Lamp). This node allows to address several receivers by means or their numbers.
|
||||
All numbers of the receiving nodes have to be added a configuration time.
|
||||
|
||||
### Lamp
|
||||
The Lamp is a receiving node, showing its destination/communication number via "infotext".
|
||||
The Lamp can be switched on/off by means of the right mouse button (use function) or by means of messages commands
|
||||
from a Button/Switch or any other command sending node.
|
||||
|
||||
|
11
pusher.lua
11
pusher.lua
|
@ -88,6 +88,7 @@ local function keep_running(pos, elapsed)
|
|||
local node = minetest.get_node(pos)
|
||||
return goto_blocked(pos, node)
|
||||
end
|
||||
meta:set_int("item_counter", meta:get_int("item_counter") + 1)
|
||||
if running <= 0 then
|
||||
local node = minetest.get_node(pos)
|
||||
return switch_on(pos, node)
|
||||
|
@ -123,6 +124,7 @@ minetest.register_node("tubelib:pusher", {
|
|||
local number = tubelib.add_node(pos, "tubelib:pusher") -- <<=== tubelib
|
||||
meta:set_string("number", number)
|
||||
meta:set_string("infotext", "Pusher "..number..": stopped")
|
||||
meta:set_int("item_counter", 0)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
|
@ -136,6 +138,7 @@ minetest.register_node("tubelib:pusher", {
|
|||
end,
|
||||
|
||||
on_timer = keep_running,
|
||||
on_rotate = screwdriver.disallow,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
|
@ -201,6 +204,7 @@ minetest.register_node("tubelib:pusher_active", {
|
|||
end,
|
||||
|
||||
on_timer = keep_running,
|
||||
on_rotate = screwdriver.disallow,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
|
@ -224,6 +228,7 @@ tubelib.register_node("tubelib:pusher", {"tubelib:pusher_active"}, {
|
|||
on_pull_item = nil, -- pusher has no inventory
|
||||
on_push_item = nil, -- pusher has no inventory
|
||||
on_unpull_item = nil, -- pusher has no inventory
|
||||
is_pusher = true, -- is a pulling/pushing node
|
||||
|
||||
on_recv_message = function(pos, topic, payload)
|
||||
local node = minetest.get_node(pos)
|
||||
|
@ -235,6 +240,12 @@ tubelib.register_node("tubelib:pusher", {"tubelib:pusher_active"}, {
|
|||
local meta = minetest.get_meta(pos)
|
||||
local running = meta:get_int("running") or tubelib.STATE_STOPPED
|
||||
return tubelib.statestring(running)
|
||||
elseif topic == "counter" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_int("item_counter")
|
||||
elseif topic == "clear_counter" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:set_int("item_counter", 0)
|
||||
else
|
||||
return "not supported"
|
||||
end
|
||||
|
|
|
@ -29,7 +29,7 @@ tubelib.StatesImg = {
|
|||
"tubelib_inv_button_standby.png",
|
||||
"tubelib_inv_button_error.png"
|
||||
}
|
||||
|
||||
|
||||
-- Return state button image for the node inventory
|
||||
function tubelib.state_button(state)
|
||||
if state and state < 5 and state > 0 then
|
||||
|
@ -37,7 +37,10 @@ function tubelib.state_button(state)
|
|||
end
|
||||
return tubelib.StatesImg[tubelib.FAULT]
|
||||
end
|
||||
|
||||
|
||||
-- State string based on button states
|
||||
tubelib.states = {"stopped", "running", "standby", "fault"}
|
||||
|
||||
|
||||
--
|
||||
-- 'running' variable States
|
||||
|
|
438
tubes.lua
438
tubes.lua
|
@ -1,438 +0,0 @@
|
|||
--[[
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
["default:furnace"] = true,
|
||||
["default:furnace_active"] = true,
|
||||
}
|
||||
|
||||
-- Localize functions to avoid table lookups (better performance).
|
||||
local string_find = string.find
|
||||
local minetest_get_meta = minetest.get_meta
|
||||
local minetest_get_node = minetest.get_node
|
||||
local KnownNodes = tubelib.knownNodes
|
||||
local vector_add = vector.add
|
||||
local vector_equals = vector.equals
|
||||
|
||||
-- Translate from contact side to facedir
|
||||
local SideToFacedir = {B=0, R=1, F=2, L=3, D=4, U=5}
|
||||
|
||||
-- 6D variant of the facedir to dir conversion
|
||||
local Facedir2Dir = {[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},
|
||||
}
|
||||
|
||||
-- Determine position from and facedir to the node on the other side or the tubes.
|
||||
-- Param pos: my pos
|
||||
-- Param npos: neighbor tube pos
|
||||
local function remote_node(pos, npos)
|
||||
local dest_pos = minetest.string_to_pos(minetest.get_meta(npos):get_string("dest_pos"))
|
||||
-- two possible reasons, why dest_pos == pos:
|
||||
-- 1) wrong side of a single tube node
|
||||
-- 2) node connected with itself. In this case "dest_pos2" is not available
|
||||
if dest_pos and vector_equals(dest_pos, pos) then
|
||||
local dest_pos2 = minetest.string_to_pos(minetest.get_meta(npos):get_string("dest_pos2"))
|
||||
if dest_pos2 == nil then
|
||||
local facedir = minetest.get_meta(npos):get_int("facedir")
|
||||
return pos, facedir -- node connected with itself
|
||||
else
|
||||
local facedir2 = minetest.get_meta(npos):get_int("facedir2")
|
||||
return dest_pos2, facedir2 -- one tube connection
|
||||
end
|
||||
else
|
||||
local facedir = minetest.get_meta(npos):get_int("facedir")
|
||||
return dest_pos, facedir -- multi tube connection
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Calculate the facedir to the other node, based on both node positions
|
||||
local function dir_to_facedir(my_pos, other_pos)
|
||||
if my_pos.z ~= other_pos.z then return my_pos.z - other_pos.z + 1 end
|
||||
if my_pos.x ~= other_pos.x then return my_pos.x - other_pos.x + 2 end
|
||||
if my_pos.y > other_pos.y then return 5 else return 4 end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- API functions
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- Determine neighbor position and own facedir to the node.
|
||||
-- based on own pos and contact side 'B' - 'U'.
|
||||
-- Function considers also tube connections.
|
||||
function tubelib.get_neighbor_pos(pos, side)
|
||||
local facedir = SideToFacedir[side]
|
||||
if facedir < 4 then
|
||||
local node = minetest_get_node(pos)
|
||||
facedir = (facedir + node.param2) % 4
|
||||
end
|
||||
local npos = vector_add(pos, Facedir2Dir[facedir])
|
||||
local node = minetest_get_node(npos)
|
||||
if node and string_find(node.name, "tubelib:tube") then
|
||||
npos, facedir = remote_node(pos, npos)
|
||||
end
|
||||
return npos, facedir
|
||||
end
|
||||
|
||||
-- use Voxel Manipulator to read the node
|
||||
function tubelib.read_node_with_vm(pos)
|
||||
local vm = VoxelManip()
|
||||
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
||||
local data = vm:get_data()
|
||||
local param2_data = vm:get_param2_data()
|
||||
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||||
return {
|
||||
name=minetest.get_name_from_content_id(data[area:index(pos.x, pos.y, pos.z)]),
|
||||
param2 = param2_data[area:index(pos.x, pos.y, pos.z)]
|
||||
}
|
||||
end
|
||||
|
||||
local read_node_with_vm = tubelib.read_node_with_vm
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Tube placement
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
-- 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 KnownNodes[node.name] then -- registered node?
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
-- The tube on 'pos1' has two ends and thus two neighbor position.
|
||||
-- One is the given 'pos', the other position is returned.
|
||||
-- Param mpos: my node position
|
||||
-- Param opos: the other tube position
|
||||
-- Param node: the tube node
|
||||
local function nodetype_to_pos(mpos, opos, 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 = Facedir2Dir[facedir1]
|
||||
local dir2 = Facedir2Dir[facedir2]
|
||||
local p1 = vector.add(opos, dir1)
|
||||
local p2 = vector.add(opos, dir2)
|
||||
|
||||
if mpos == nil then
|
||||
return p1, p2
|
||||
elseif vector.equals(p1, mpos) then
|
||||
return p2
|
||||
else
|
||||
return p1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Walk to the other end of the tube line, starting at 'pos1'.
|
||||
-- Returns: cnt - number of tube nodes
|
||||
-- pos - the peer tube node
|
||||
-- pos1 - the destination position, connected with 'pos'
|
||||
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_or_nil(pos1) or read_node_with_vm(pos1)
|
||||
end
|
||||
return cnt, pos, pos1
|
||||
end
|
||||
|
||||
-- Update head tubes with peer pos and facedir of the other end
|
||||
-- Needed for StackItem exchange.
|
||||
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 cnt1, peer1, dest1 = walk_to_peer(pos, pos1)
|
||||
local cnt2, peer2, dest2 = walk_to_peer(pos, pos2)
|
||||
|
||||
if cnt1 == 0 and cnt2 == 0 then -- first tube node placed?
|
||||
-- we have to store both dest positions
|
||||
minetest.get_meta(peer1):set_string("dest_pos", minetest.pos_to_string(dest1))
|
||||
minetest.get_meta(peer1):set_int("facedir", dir_to_facedir(peer1, dest1))
|
||||
minetest.get_meta(peer1):set_string("dest_pos2", minetest.pos_to_string(dest2))
|
||||
minetest.get_meta(peer1):set_int("facedir2", dir_to_facedir(peer2, dest2))
|
||||
else
|
||||
minetest.get_meta(peer1):set_string("dest_pos", minetest.pos_to_string(dest2))
|
||||
minetest.get_meta(peer1):set_int("facedir", dir_to_facedir(peer2, dest2))
|
||||
minetest.get_meta(peer2):set_string("dest_pos", minetest.pos_to_string(dest1))
|
||||
minetest.get_meta(peer2):set_int("facedir", dir_to_facedir(peer1, dest1))
|
||||
end
|
||||
|
||||
-- delete meta data from old head nodes
|
||||
if cnt1 > 1 then
|
||||
minetest.get_meta(pos1):from_table(nil)
|
||||
end
|
||||
if cnt2 > 1 then
|
||||
minetest.get_meta(pos2):from_table(nil)
|
||||
end
|
||||
return cnt1 + cnt2
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Update all tubes arround the currently placed tube
|
||||
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)
|
||||
return update_head_tubes(pos) < MAX_TUBE_LENGTH
|
||||
end
|
||||
|
||||
|
||||
-- Update tubes after a tube node is removed
|
||||
local function after_tube_removed(pos, node)
|
||||
local pos1, pos2 = nodetype_to_pos(nil, pos, node)
|
||||
update_head_tubes(pos1)
|
||||
update_head_tubes(pos2)
|
||||
end
|
||||
|
||||
-- API function, called from all nodes, which shall be connected to tubes
|
||||
function tubelib.update_tubes(pos)
|
||||
update_tube( {x=pos.x , y=pos.y , z=pos.z+1})
|
||||
update_head_tubes({x=pos.x , y=pos.y , z=pos.z+1})
|
||||
update_tube( {x=pos.x+1, y=pos.y , z=pos.z })
|
||||
update_head_tubes({x=pos.x+1, y=pos.y , z=pos.z })
|
||||
update_tube( {x=pos.x , y=pos.y , z=pos.z-1})
|
||||
update_head_tubes({x=pos.x , y=pos.y , z=pos.z-1})
|
||||
update_tube( {x=pos.x-1, y=pos.y , z=pos.z })
|
||||
update_head_tubes({x=pos.x-1, y=pos.y , z=pos.z })
|
||||
update_tube( {x=pos.x , y=pos.y-1, z=pos.z })
|
||||
update_head_tubes({x=pos.x , y=pos.y-1, z=pos.z })
|
||||
update_tube( {x=pos.x , y=pos.y+1, z=pos.z })
|
||||
update_head_tubes({x=pos.x , y=pos.y+1, z=pos.z })
|
||||
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)
|
||||
if update_surrounding_tubes(pos) == false then
|
||||
after_tube_removed(pos, minetest.get_node(pos))
|
||||
minetest.remove_node(pos)
|
||||
return itemstack
|
||||
end
|
||||
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 = {choppy=2, cracky=3, stone=1, not_in_creative_inventory=hidden},
|
||||
drop = "tubelib:tube1",
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "tubelib:tube1 4",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", "group:wood"},
|
||||
{"", "group:wood", ""},
|
||||
{"group:wood", "", "default:tin_ingot"},
|
||||
},
|
||||
})
|
|
@ -0,0 +1,283 @@
|
|||
--[[
|
||||
|
||||
Tube Library
|
||||
============
|
||||
|
||||
Copyright (C) 2017-2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
||||
History:
|
||||
see init.lua
|
||||
|
||||
tubes1.lua: Functions to place and remove tubes
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
-- debugging
|
||||
local P = minetest.pos_to_string
|
||||
|
||||
local MAX_TUBE_LENGTH = 100
|
||||
|
||||
-- Conversion from tube number/param2 (number*10 + param2) to tube hole dirs (view from the inside)
|
||||
local TubeDirs = {
|
||||
[10] = {1,3},
|
||||
[11] = {2,4},
|
||||
[12] = {1,3},
|
||||
[13] = {2,4},
|
||||
[20] = {5,6},
|
||||
[21] = {5,6},
|
||||
[22] = {5,6},
|
||||
[23] = {5,6},
|
||||
[30] = {3,4},
|
||||
[31] = {1,4},
|
||||
[32] = {1,2},
|
||||
[33] = {2,3},
|
||||
[40] = {3,5},
|
||||
[41] = {4,5},
|
||||
[42] = {1,5},
|
||||
[43] = {2,5},
|
||||
[50] = {3,6},
|
||||
[51] = {4,6},
|
||||
[52] = {1,6},
|
||||
[53] = {2,6},
|
||||
}
|
||||
|
||||
-- Conversion from tube dirs (dir1 * 10 + dir2) to tube number/param2
|
||||
local TubeNodeAttr = {}
|
||||
|
||||
for k,v in pairs(TubeDirs) do
|
||||
local key = v[1] * 10 + v[2]
|
||||
local number = math.floor(k / 10)
|
||||
local param2 = k % 10
|
||||
TubeNodeAttr[key] = {number = number, param2 = param2}
|
||||
end
|
||||
|
||||
|
||||
-- Convertion of 'dir' (view from the outside to inside and vs)
|
||||
local Turn180Deg = {3,4,1,2,6,5}
|
||||
|
||||
local Dir2Offset = {
|
||||
{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}
|
||||
}
|
||||
|
||||
tubelib.TubeNames = {
|
||||
["tubelib:tube1"] = true,
|
||||
["tubelib:tube2"] = true,
|
||||
["tubelib:tube3"] = true,
|
||||
["tubelib:tube4"] = true,
|
||||
["tubelib:tube5"] = true,
|
||||
}
|
||||
|
||||
-- used for registered nodes
|
||||
tubelib.KnownNodes = {
|
||||
["tubelib:tube1"] = true,
|
||||
["tubelib:tube2"] = true,
|
||||
["tubelib:tube3"] = true,
|
||||
["tubelib:tube4"] = true,
|
||||
["tubelib:tube5"] = true,
|
||||
}
|
||||
|
||||
-- use Voxel Manipulator to read the node
|
||||
function tubelib.read_node_with_vm(pos)
|
||||
local vm = VoxelManip()
|
||||
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
||||
local data = vm:get_data()
|
||||
local param2_data = vm:get_param2_data()
|
||||
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||||
return {
|
||||
name = minetest.get_name_from_content_id(data[area:index(pos.x, pos.y, pos.z)]),
|
||||
param2 = param2_data[area:index(pos.x, pos.y, pos.z)]
|
||||
}
|
||||
end
|
||||
|
||||
local function get_tube_number_and_param2(dir1, dir2)
|
||||
if dir1 == dir2 then
|
||||
dir2 = Turn180Deg[dir1]
|
||||
end
|
||||
if dir1 > dir2 then
|
||||
dir1, dir2 = dir2, dir1
|
||||
end
|
||||
local item = TubeNodeAttr[dir1*10 + dir2]
|
||||
return item.number, item.param2
|
||||
end
|
||||
|
||||
-- convert 6D-dir to position
|
||||
local function get_tube_pos(pos, dir)
|
||||
return vector.add(pos, Dir2Offset[dir])
|
||||
end
|
||||
|
||||
-- Tube open sides
|
||||
local function get_tube_dirs(pos, node)
|
||||
if node == nil then
|
||||
node = minetest.get_node_or_nil(pos) or tubelib.read_node_with_vm(pos)
|
||||
end
|
||||
if tubelib.TubeNames[node.name] then
|
||||
local ttype = (string.byte(node.name, -1) - 48) * 10 + node.param2
|
||||
return TubeDirs[ttype][1], TubeDirs[ttype][2]
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
function tubelib.get_next_tube(pos, dir)
|
||||
pos = get_tube_pos(pos, dir)
|
||||
local dir1, dir2 = get_tube_dirs(pos)
|
||||
|
||||
if dir1 then
|
||||
dir = Turn180Deg[dir]
|
||||
if dir == dir1 then
|
||||
return pos, dir2
|
||||
elseif dir == dir2 then
|
||||
return pos, dir1
|
||||
end
|
||||
end
|
||||
return pos, nil
|
||||
end
|
||||
|
||||
local function is_known_node(pos, dir)
|
||||
if dir then
|
||||
pos = get_tube_pos(pos, dir)
|
||||
local node = minetest.get_node_or_nil(pos) or tubelib.read_node_with_vm(pos)
|
||||
if tubelib.KnownNodes[node.name] and not tubelib.TubeNames[node.name] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- Walk to the other end of the tube line, starting at 'pos/dir'.
|
||||
-- Returns: cnt - number of tube nodes
|
||||
-- pos - the peer tube node
|
||||
-- dir - dir to the drop position, next after 'pos'
|
||||
function tubelib.walk_to_peer(pos, dir)
|
||||
local cnt = 0
|
||||
while cnt < MAX_TUBE_LENGTH do
|
||||
local new_pos, new_dir = tubelib.get_next_tube(pos, dir)
|
||||
if not new_dir then
|
||||
break
|
||||
end
|
||||
cnt = cnt + 1
|
||||
pos, dir = new_pos, new_dir
|
||||
end
|
||||
return cnt, pos, dir
|
||||
end
|
||||
|
||||
-- Delete meta data of the peer node
|
||||
function tubelib.delete_meta_data(pos, node)
|
||||
local dir1, dir2 = get_tube_dirs(pos, node)
|
||||
local cnt1 = 0
|
||||
local dir
|
||||
if dir1 then
|
||||
cnt1, pos, dir = tubelib.walk_to_peer(pos, dir1)
|
||||
-- delete meta on peer tube
|
||||
if cnt1 > 0 then
|
||||
minetest.get_meta(pos):from_table(nil)
|
||||
end
|
||||
end
|
||||
local cnt2 = 0
|
||||
if dir2 then
|
||||
cnt2, pos, dir = tubelib.walk_to_peer(pos, dir2)
|
||||
-- delete meta on peer tube
|
||||
if cnt2 > 0 then
|
||||
minetest.get_meta(pos):from_table(nil)
|
||||
end
|
||||
end
|
||||
return cnt1 + cnt2
|
||||
end
|
||||
|
||||
local function swap_node(pos, node_num, param2)
|
||||
local node = minetest.get_node(pos)
|
||||
node.name = "tubelib:tube"..node_num
|
||||
node.param2 = param2
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
local function is_connected(pos, dir)
|
||||
if dir then
|
||||
pos = get_tube_pos(pos, dir)
|
||||
local dir1,dir2 = get_tube_dirs(pos)
|
||||
-- return true if connected
|
||||
dir = Turn180Deg[dir]
|
||||
return dir == dir1 or dir == dir2
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function is_tubelib_block(pos, dir)
|
||||
if dir then
|
||||
pos = get_tube_pos(pos, dir)
|
||||
local dir1,dir2 = get_tube_dirs(pos)
|
||||
-- return true if connected
|
||||
dir = Turn180Deg[dir]
|
||||
return dir == dir1 or dir == dir2
|
||||
end
|
||||
return false
|
||||
end
|
||||
local function update_next_tube(dir, pos)
|
||||
-- test if tube is connected with neighbor tubes
|
||||
local dir1, dir2 = get_tube_dirs(pos)
|
||||
local conn1 = is_connected(pos, dir1) or is_known_node(pos, dir1)
|
||||
local conn2 = is_connected(pos, dir2) or is_known_node(pos, dir2)
|
||||
-- already connected or no tube arround?
|
||||
if conn1 == conn2 then
|
||||
return
|
||||
end
|
||||
if conn1 then
|
||||
dir2 = Turn180Deg[dir]
|
||||
else
|
||||
dir1 = Turn180Deg[dir]
|
||||
end
|
||||
local node_num, param2 = get_tube_number_and_param2(dir1, dir2)
|
||||
swap_node(pos, node_num, param2)
|
||||
end
|
||||
|
||||
-- update new placed tube
|
||||
local function update_tube(pos, dir)
|
||||
local dir1 = nil
|
||||
local dir2 = nil
|
||||
-- search on all 6 pos for up to 2 tubes with open holes or
|
||||
-- other tubelib compatible nodes
|
||||
for dir = 1,6 do
|
||||
if not dir1 and is_connected(pos, dir) then
|
||||
dir1 = dir
|
||||
elseif not dir2 and is_connected(pos, dir) then
|
||||
dir2 = dir
|
||||
end
|
||||
end
|
||||
if not dir1 or not dir2 then
|
||||
for dir = 1,6 do
|
||||
if not dir1 and is_known_node(pos, dir) then
|
||||
dir1 = dir
|
||||
elseif not dir2 and is_known_node(pos, dir) then
|
||||
dir2 = dir
|
||||
end
|
||||
end
|
||||
end
|
||||
dir1 = dir1 or dir
|
||||
dir2 = dir2 or Turn180Deg[dir]
|
||||
local node_num, param2 = get_tube_number_and_param2(dir1, dir2)
|
||||
swap_node(pos, node_num, param2)
|
||||
end
|
||||
|
||||
function tubelib.update_tubes(pos, dir)
|
||||
-- Update all tubes arround the currently placed tube
|
||||
update_next_tube(1, {x=pos.x , y=pos.y , z=pos.z+1})
|
||||
update_next_tube(2, {x=pos.x+1, y=pos.y , z=pos.z })
|
||||
update_next_tube(3, {x=pos.x , y=pos.y , z=pos.z-1})
|
||||
update_next_tube(4, {x=pos.x-1, y=pos.y , z=pos.z })
|
||||
update_next_tube(5, {x=pos.x , y=pos.y-1, z=pos.z })
|
||||
update_next_tube(6, {x=pos.x , y=pos.y+1, z=pos.z })
|
||||
-- Update the placed tube
|
||||
update_tube(pos, dir)
|
||||
return tubelib.delete_meta_data(pos, minetest.get_node(pos)) < MAX_TUBE_LENGTH
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
--[[
|
||||
|
||||
Tube Library
|
||||
============
|
||||
|
||||
Copyright (C) 2017-2018 Joachim Stolberg
|
||||
|
||||
LGPLv2.1+
|
||||
See LICENSE.txt for more information
|
||||
|
||||
History:
|
||||
see init.lua
|
||||
|
||||
tubes2.lua: Node registration and API function to move items via tubes
|
||||
|
||||
]]--
|
||||
|
||||
-- Convertion of contact side to facedir
|
||||
local SideToFacedir = {B=0, R=1, F=2, L=3, D=4, U=5}
|
||||
|
||||
-- Calculate the facedir to the other node, based on both node positions
|
||||
local function dir_to_facedir(my_pos, other_pos)
|
||||
if my_pos.z ~= other_pos.z then return my_pos.z - other_pos.z + 1 end
|
||||
if my_pos.x ~= other_pos.x then return my_pos.x - other_pos.x + 2 end
|
||||
if my_pos.y > other_pos.y then return 5 else return 4 end
|
||||
end
|
||||
|
||||
local function remote_node(pos, dir)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local cnt
|
||||
|
||||
-- legacy tube?
|
||||
if meta:get_string("dest_pos2") ~= "" then
|
||||
meta:from_table(nil)
|
||||
end
|
||||
|
||||
-- data available
|
||||
local dest_pos = meta:get_string("dest_pos")
|
||||
if dest_pos ~= "" then
|
||||
local npos = minetest.string_to_pos(dest_pos)
|
||||
local facedir = meta:get_int("facedir")
|
||||
return npos, facedir+1
|
||||
end
|
||||
|
||||
-- determine data and store as meta
|
||||
cnt, pos, dir = tubelib.walk_to_peer(pos, dir)
|
||||
local pos1,_ = tubelib.get_next_tube(pos, dir)
|
||||
meta:set_string("dest_pos", minetest.pos_to_string(pos1))
|
||||
meta:set_int("facedir", dir - 1)
|
||||
|
||||
return pos1, dir
|
||||
end
|
||||
|
||||
-- Determine neighbor position and own facedir to the node.
|
||||
-- based on own pos and contact side 'B' - 'U'.
|
||||
-- Function considers also tube connections.
|
||||
function tubelib.get_neighbor_pos(pos, side)
|
||||
local facedir = SideToFacedir[side]
|
||||
local dir
|
||||
if facedir < 4 then
|
||||
local node = minetest.get_node(pos)
|
||||
dir = ((facedir + node.param2) % 4) + 1
|
||||
end
|
||||
local npos, ndir = tubelib.get_next_tube(pos, dir)
|
||||
local node = minetest.get_node(npos)
|
||||
if tubelib.TubeNames[node.name] then
|
||||
if ndir then
|
||||
npos, ndir = remote_node(npos, ndir)
|
||||
end
|
||||
return npos, dir-1
|
||||
end
|
||||
return npos, facedir
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Node registration
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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
|
||||
local 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
|
||||
|
||||
local tiles_data = TilesData[idx]
|
||||
local hidden
|
||||
|
||||
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)
|
||||
local res
|
||||
local pitch = placer:get_look_pitch()
|
||||
if pitch > 1 then
|
||||
res = tubelib.update_tubes(pos, 6)
|
||||
elseif pitch < -1 then
|
||||
res = tubelib.update_tubes(pos, 5)
|
||||
else
|
||||
local dir = placer:get_look_dir()
|
||||
local facedir = minetest.dir_to_facedir(dir)
|
||||
res = tubelib.update_tubes(pos, facedir + 1)
|
||||
end
|
||||
if res == false then
|
||||
tubelib.delete_meta_data(pos, minetest.get_node(pos))
|
||||
minetest.remove_node(pos)
|
||||
return itemstack
|
||||
end
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
tubelib.delete_meta_data(pos, oldnode)
|
||||
end,
|
||||
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype2 = "facedir",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {choppy=2, cracky=3, stone=1, not_in_creative_inventory=hidden},
|
||||
drop = "tubelib:tube1",
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "tubelib:tube1 4",
|
||||
recipe = {
|
||||
{"default:steel_ingot", "", "group:wood"},
|
||||
{"", "group:wood", ""},
|
||||
{"group:wood", "", "default:tin_ingot"},
|
||||
},
|
||||
})
|
Loading…
Reference in New Issue