Major rewrite of items in tubes

This commit is contained in:
Novatux 2014-08-14 16:22:03 +02:00
parent a9866aa5d5
commit d5480f5ff0
7 changed files with 796 additions and 471 deletions

110
autoplace_tubes.lua Normal file → Executable file
View File

@ -1,72 +1,38 @@
-- autorouting for pneumatic tubes
local function in_table(table,element)
for _,el in ipairs(table) do
if el==element then return true end
end
return false
end
local function is_tube(nodename)
return in_table(pipeworks.tubenodes,nodename)
end
if pipeworks == nil then
pipeworks = {}
return table.contains(pipeworks.tubenodes, nodename)
end
--a function for determining which side of the node we are on
local function nodeside(node, tubedir)
if node and (node.param2 < 0 or node.param2 > 23) then node.param2 = 0 end
--get a vector pointing back
local backdir = minetest.facedir_to_dir(node.param2)
--check whether the vector is equivalent to the tube direction; if it is, the tube's on the backside
if backdir.x == tubedir.x and backdir.y == tubedir.y and backdir.z == tubedir.z then
return "back"
if node.param2 < 0 or node.param2 > 23 then
node.param2 = 0
end
--check whether the vector is antiparallel with the tube direction; that indicates the front
if backdir.x == -tubedir.x and backdir.y == -tubedir.y and backdir.z == -tubedir.z then
local backdir = minetest.facedir_to_dir(node.param2)
local back = vector.dot(backdir, tubedir)
if back == 1 then
return "back"
elseif back == -1 then
return "front"
end
--facedir is defined in terms of the top-bottom axis of the node; we'll take advantage of that
local topdir = ({[0]={x=0, y=1, z=0},
{x=0, y=0, z=1},
{x=0, y=0, z=-1},
{x=1, y=0, z=0},
{x=-1, y=0, z=0},
{x=0, y=-1, z=0}})[math.floor(node.param2/4)]
--is this the top?
if topdir.x == tubedir.x and topdir.y == tubedir.y and topdir.z == tubedir.z then
local topdir = minetest.facedir_to_top_dir(node.param2)
local top = vector.dot(topdir, tubedir)
if top == 1 then
return "top"
end
--or the bottom?
if topdir.x == -tubedir.x and topdir.y == -tubedir.y and topdir.z == -tubedir.z then
elseif top == -1 then
return "bottom"
end
--we shall apply some maths to obtain the right-facing vector
local rightdir = {x=topdir.y*backdir.z - backdir.y*topdir.z,
y=topdir.z*backdir.x - backdir.z*topdir.x,
z=topdir.x*backdir.y - backdir.x*topdir.y}
--is this the right side?
if rightdir.x == tubedir.x and rightdir.y == tubedir.y and rightdir.z == tubedir.z then
local rightdir = minetest.facedir_to_right_dir(node.param2)
local right = vector.dot(rightdir, tubedir)
if right == 1 then
return "right"
end
--or the left?
if rightdir.x == -tubedir.x and rightdir.y == -tubedir.y and rightdir.z == -tubedir.z then
else
return "left"
end
--we should be done by now; initiate panic mode
minetest.log("error", "nodeside has been confused by its parameters; see pipeworks autoplace_tubes.lua, line 78")
end
local vts = {0, 3, 1, 4, 2, 5}
@ -78,23 +44,23 @@ local function tube_autoroute(pos)
if not is_tube(nctr.name) then return end
local adjustments = {
{ x=-1, y=0, z=0 },
{ x=1, y=0, z=0 },
{ x=0, y=-1, z=0 },
{ x=0, y=1, z=0 },
{ x=0, y=0, z=-1 },
{ x=0, y=0, z=1 }
{x = -1, y = 0, z = 0},
{x = 1, y = 0, z = 0},
{x = 0, y = -1, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y = 0, z = -1},
{x = 0, y = 0, z = 1}
}
-- xm = 1, xp = 2, ym = 3, yp = 4, zm = 5, zp = 6
local positions = {}
local nodes = {}
for i,adj in ipairs(adjustments) do
positions[i] = {x=pos.x+adj.x, y=pos.y+adj.y, z=pos.z+adj.z}
for i, adj in ipairs(adjustments) do
positions[i] = vector.add(pos, adj)
nodes[i] = minetest.get_node(positions[i])
end
for i,node in ipairs(nodes) do
for i, node in ipairs(nodes) do
local idef = minetest.registered_nodes[node.name]
-- handle the tubes themselves
if is_tube(node.name) then
@ -102,7 +68,9 @@ local function tube_autoroute(pos)
-- handle new style connectors
elseif idef and idef.tube and idef.tube.connect_sides then
local dir = adjustments[i]
if idef.tube.connect_sides[nodeside(node, {x=-dir.x, y=-dir.y, z=-dir.z})] then active[i] = 1 end
if idef.tube.connect_sides[nodeside(node, vector.multiply(dir, -1))] then
active[i] = 1
end
end
end
@ -110,18 +78,17 @@ local function tube_autoroute(pos)
local nodedef = minetest.registered_nodes[nctr.name]
local basename = nodedef.basename
local newname
if nodedef.style == "old" then
local nsurround = ""
for i,n in ipairs(active) do
nsurround = nsurround .. n
for i, n in ipairs(active) do
nsurround = nsurround..n
end
nctr.name = basename.."_"..nsurround
elseif nodedef.style == "6d" then
local s = 0
for i,n in ipairs(active) do
for i, n in ipairs(active) do
if n == 1 then
s = s+2^vts[i]
s = s + 2^vts[i]
end
end
nctr.name = basename.."_"..tube_table[s]
@ -131,14 +98,9 @@ local function tube_autoroute(pos)
end
function pipeworks.scan_for_tube_objects(pos)
if not pos or not pos.x or not pos.y or not pos.z then return end
tube_autoroute({ x=pos.x-1, y=pos.y , z=pos.z })
tube_autoroute({ x=pos.x+1, y=pos.y , z=pos.z })
tube_autoroute({ x=pos.x , y=pos.y-1, z=pos.z })
tube_autoroute({ x=pos.x , y=pos.y+1, z=pos.z })
tube_autoroute({ x=pos.x , y=pos.y , z=pos.z-1 })
tube_autoroute({ x=pos.x , y=pos.y , z=pos.z+1 })
tube_autoroute(pos)
for side = 0, 6 do
tube_autoroute(vector.add(pos, directions.side_to_dir(side)))
end
end
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack)
@ -157,7 +119,7 @@ minetest.register_on_dignode(function(pos, oldnode, digger)
end
end)
if minetest.get_modpath("mesecons_mvps") ~= nil then
if minetest.get_modpath("mesecons_mvps") then
mesecon:register_on_mvps_move(function(moved_nodes)
for _, n in ipairs(moved_nodes) do
pipeworks.scan_for_tube_objects(n.pos)

144
common.lua Executable file
View File

@ -0,0 +1,144 @@
----------------------
-- Vector functions --
----------------------
function vector.cross(a, b)
return {
x = a.y * b.z - a.z * b.y,
y = a.z * b.x - a.x * b.z,
z = a.x * b.y - a.y * b.x
}
end
function vector.dot(a, b)
return a.x * b.x + a.y * b.y + a.z * b.z
end
-----------------------
-- Facedir functions --
-----------------------
function minetest.facedir_to_top_dir(facedir)
return ({[0] = {x = 0, y = 1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z = -1},
{x = 1, y = 0, z = 0},
{x = -1, y = 0, z = 0},
{x = 0, y = -1, z = 0}})
[math.floor(facedir / 4)]
end
function minetest.facedir_to_right_dir(facedir)
return vector.cross(
minetest.facedir_to_top_dir(facedir),
minetest.facedir_to_dir(facedir)
)
end
directions = {}
function directions.side_to_dir(side)
return ({[0] = vector.new(),
vector.new( 0, 1, 0),
vector.new( 0, -1, 0),
vector.new( 1, 0, 0),
vector.new(-1, 0, 0),
vector.new( 0, 0, 1),
vector.new( 0, 0, -1)
})[side]
end
function directions.dir_to_side(dir)
local c = vector.dot(dir, vector.new(1, 2, 3)) + 4
return ({6, 2, 4, 0, 3, 1, 5})[c]
end
----------------------
-- String functions --
----------------------
--[[function string.split(str, sep)
local fields = {}
local index = 1
local expr = "([^"..sep.."])+"
string.gsub(str, expr, function(substring)
fields[index] = substring
index = index + 1
end)
return fields
end]]
function string.startswith(str, substr)
return str:sub(1, substr:len()) == substr
end
---------------------
-- Table functions --
---------------------
function table.contains(tbl, element)
for _, elt in pairs(tbl) do
if elt == element then
return true
end
end
return false
end
function table.extend(tbl, tbl2)
local index = #tbl + 1
for _, elt in ipairs(tbl2) do
tbl[index] = elt
index = index + 1
end
end
function table.recursive_replace(tbl, pattern, replace_with)
if type(tbl) == "table" then
local tbl2 = {}
for key, value in pairs(tbl) do
tbl2[key] = table.recursive_replace(value, pattern, replace_with)
end
return tbl2
elseif type(tbl) == "string" then
return tbl:gsub(pattern, replace_with)
else
return tbl
end
end
------------------------
-- Formspec functions --
------------------------
fs_helpers = {}
function fs_helpers.on_receive_fields(pos, fields)
local meta = minetest.get_meta(pos)
for field, value in pairs(fields) do
if field:startswith("fs_helpers_cycling:") then
local l = field:split(":")
local new_value = tonumber(l[2])
local meta_name = l[3]
meta:set_int(meta_name, new_value)
end
end
end
function fs_helpers.cycling_button(meta, base, meta_name, values)
local current_value = meta:get_int(meta_name)
local new_value = (current_value + 1) % (#values)
local text = values[current_value + 1]
local field = "fs_helpers_cycling:"..new_value..":"..meta_name
return base..";"..field..";"..text.."]"
end
---------
-- Env --
---------
function minetest.load_position(pos)
if minetest.get_node_or_nil(pos) then
return
end
local vm = minetest.get_voxel_manip()
vm:read_from_map(pos, pos)
end

2
init.lua Normal file → Executable file
View File

@ -106,9 +106,11 @@ end
-------------------------------------------
-- Load the various other parts of the mod
dofile(pipeworks.modpath.."/common.lua")
dofile(pipeworks.modpath.."/models.lua")
dofile(pipeworks.modpath.."/autoplace_pipes.lua")
dofile(pipeworks.modpath.."/autoplace_tubes.lua")
dofile(pipeworks.modpath.."/luaentity.lua")
dofile(pipeworks.modpath.."/item_transport.lua")
dofile(pipeworks.modpath.."/flowing_logic.lua")
dofile(pipeworks.modpath.."/crafts.lua")

395
item_transport.lua Normal file → Executable file
View File

@ -1,34 +1,17 @@
dofile(pipeworks.modpath.."/compat.lua")
--and an extra function for getting the right-facing vector
local function facedir_to_right_dir(facedir)
--find the other directions
local backdir = minetest.facedir_to_dir(facedir)
local topdir = ({[0]={x=0, y=1, z=0},
{x=0, y=0, z=1},
{x=0, y=0, z=-1},
{x=1, y=0, z=0},
{x=-1, y=0, z=0},
{x=0, y=-1, z=0}})[math.floor(facedir/4)]
--return a cross product
return {x=topdir.y*backdir.z - backdir.y*topdir.z,
y=topdir.z*backdir.x - backdir.z*topdir.x,
z=topdir.x*backdir.y - backdir.x*topdir.y}
end
local fakePlayer = {
get_player_name = function() return ":pipeworks" end,
-- any other player functions called by allow_metadata_inventory_take anywhere...
-- perhaps a custom metaclass that errors specially when fakePlayer.<property> is not found?
}
function pipeworks.tube_item(pos, item)
function pipeworks.tube_item(pos, start_pos, velocity, item)
-- Take item in any format
local stack = ItemStack(item)
local obj = minetest.add_entity(pos, "pipeworks:tubed_item")
obj:get_luaentity():set_item(stack:to_string())
local obj = luaentity.add_entity(pos, "pipeworks:tubed_item")
obj:set_item(stack:to_string())
obj.start_pos = vector.new(start_pos)
obj:setvelocity(velocity)
--obj:set_color("red") -- todo: this is test-only code
return obj
end
@ -52,19 +35,16 @@ local function set_filter_formspec(data, meta)
"item_image[0,0;1,1;pipeworks:"..data.name.."]"..
"label[1,0;"..minetest.formspec_escape(itemname).."]"..
"label[0,1;Prefer item types:]"..
"list[current_name;main;0,1.5;8,2;]"
local slotseq_mode = meta:get_int("slotseq_mode")
if slotseq_mode == 1 then
formspec = formspec .. "button[0,3.5;4,1;slotseq_mode2;Sequence slots Randomly]"
elseif slotseq_mode == 2 then
formspec = formspec .. "button[0,3.5;4,1;slotseq_mode0;Sequence slots by Rotation]"
else
formspec = formspec .. "button[0,3.5;4,1;slotseq_mode1;Sequence slots by Priority]"
end
formspec = formspec .. "list[current_player;main;0,4.5;8,4;]"
"list[current_name;main;0,1.5;8,2;]"..
fs_helpers.cycling_button(meta, "button[0,3.5;4,1", "slotseq_mode",
{"Sequence slots by Priority",
"Sequence slots Randomly",
"Sequence slots by Rotation"})..
"list[current_player;main;0,4.5;8,4;]"
meta:set_string("formspec", formspec)
end
-- todo SOON: this function has *way too many* parameters
local function grabAndFire(data,slotseq_mode,filtmeta,frominv,frominvname,frompos,fromnode,filtername,fromtube,fromdef,dir,all)
local sposes = {}
for spos,stack in ipairs(frominv:get_list(frominvname)) do
@ -130,10 +110,9 @@ local function grabAndFire(data,slotseq_mode,filtmeta,frominv,frominvname,frompo
fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer)
end
end
local item1 = pipeworks.tube_item(vector.add(frompos, vector.multiply(dir, 1.4)), item)
item1:get_luaentity().start_pos = vector.add(frompos, dir)
item1:setvelocity(dir)
item1:setacceleration({x=0, y=0, z=0})
local pos = vector.add(frompos, vector.multiply(dir, 1.4))
local start_pos = vector.add(frompos, dir)
local item1 = pipeworks.tube_item(pos, start_pos, dir, item)
return true-- only fire one item, please
end
end
@ -143,8 +122,8 @@ end
local function punch_filter(data, filtpos, filtnode)
local filtmeta = minetest.get_meta(filtpos)
local filtinv = filtmeta:get_inventory()
local dir = facedir_to_right_dir(filtnode.param2)
local frompos = {x=filtpos.x - dir.x, y=filtpos.y - dir.y, z=filtpos.z - dir.z}
local dir = minetest.facedir_to_right_dir(filtnode.param2)
local frompos = vector.subtract(filtpos, dir)
local fromnode = minetest.get_node(frompos)
if not fromnode then return end
local fromdef = minetest.registered_nodes[fromnode.name]
@ -197,7 +176,7 @@ for _, data in ipairs({
"pipeworks_"..data.name.."_top.png",
},
paramtype2 = "facedir",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, mesecon = 2},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
@ -208,28 +187,17 @@ for _, data in ipairs({
inv:set_size("main", 8*2)
end,
on_receive_fields = function(pos, formname, fields, sender)
fs_helpers.on_receive_fields(pos, fields)
local meta = minetest.get_meta(pos)
for k, _ in pairs(fields) do
if k:sub(1, 12) == "slotseq_mode" then
local mode = tonumber(k:sub(13, 13))
meta:set_int("slotseq_mode", mode)
meta:set_int("slotseq_index", mode == 2 and 1 or 0)
end
end
meta:set_int("slotseq_index", 1)
set_filter_formspec(data, meta)
set_filter_infotext(data, meta)
end,
can_dig = function(pos,player)
can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
after_place_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
end,
after_dig_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
end,
mesecons = {
effector = {
action_on = function(pos, node)
@ -237,21 +205,13 @@ for _, data in ipairs({
end,
},
},
tube={connect_sides={right=1}},
tube = {connect_sides = {right = 1}},
on_punch = function (pos, node, puncher)
punch_filter(data, pos, node)
end,
})
end
local function roundpos(pos)
return {x=math.floor(pos.x+0.5),y=math.floor(pos.y+0.5),z=math.floor(pos.z+0.5)}
end
local function addVect(pos,vect)
return {x=pos.x+vect.x,y=pos.y+vect.y,z=pos.z+vect.z}
end
local adjlist={{x=0,y=0,z=1},{x=0,y=0,z=-1},{x=0,y=1,z=0},{x=0,y=-1,z=0},{x=1,y=0,z=0},{x=-1,y=0,z=0}}
function pipeworks.notvel(tbl, vel)
@ -263,18 +223,20 @@ function pipeworks.notvel(tbl, vel)
end
local function go_next(pos, velocity, stack)
local chests = {}
local tubes = {}
local next_positions = {}
local max_priority = 0
local cnode = minetest.get_node(pos)
local cmeta = minetest.get_meta(pos)
local n
local can_go
local speed = math.abs(velocity.x + velocity.y + velocity.z)
if speed == 0 then
speed = 1
end
local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed,speed=speed}
if speed >= 4.1 then
speed = 4
elseif speed >= 1.1 then
speed = speed-0.1
speed = speed - 0.1
else
speed = 1
end
@ -284,58 +246,40 @@ local function go_next(pos, velocity, stack)
else
can_go = pipeworks.notvel(adjlist, vel)
end
local meta = nil
for _,vect in ipairs(can_go) do
local npos = addVect(pos,vect)
for _, vect in ipairs(can_go) do
local npos = vector.add(pos, vect)
local node = minetest.get_node(npos)
local tube_receiver = minetest.get_item_group(node.name,"tubedevice_receiver")
meta = minetest.get_meta(npos)
local tubelike = meta:get_int("tubelike")
if tube_receiver == 1 then
if minetest.registered_nodes[node.name].tube and
minetest.registered_nodes[node.name].tube.can_insert and
minetest.registered_nodes[node.name].tube.can_insert(npos, node, stack, vect) then
local i = #chests + 1
chests[i] = {}
chests[i].pos = npos
chests[i].vect = vect
local tubedevice = minetest.get_item_group(node.name, "tubedevice")
local tube_def = minetest.registered_nodes[node.name].tube
local tube_priority = (tube_def and tube_def.priority) or 100
if tubedevice > 0 and tube_priority >= max_priority then
if not tube_def or not tube_def.can_insert or
tubedef.can_insert(npos, node, stack, vect) then
if tube_priority > max_priority then
max_priority = tube_priority
next_positions = {}
end
next_positions[#next_positions + 1] = {pos = npos, vect = vect}
end
elseif tubelike == 1 then
local i = #tubes + 1
tubes[i] = {}
tubes[i].pos = npos
tubes[i].vect = vect
end
end
if chests[1] == nil then--no chests found
if tubes[1] == nil then
return 0
else
n = (cmeta:get_int("tubedir")%(#tubes)) + 1
if pipeworks.enable_cyclic_mode then
cmeta:set_int("tubedir",n)
end
velocity.x = tubes[n].vect.x*vel.speed
velocity.y = tubes[n].vect.y*vel.speed
velocity.z = tubes[n].vect.z*vel.speed
end
else
n = (cmeta:get_int("tubedir")%(#chests))+1
if pipeworks.enable_cyclic_mode then
cmeta:set_int("tubedir",n)
end
velocity.x = chests[n].vect.x*speed
velocity.y = chests[n].vect.y*speed
velocity.z = chests[n].vect.z*speed
if not next_positions[1] then
return false, nil
end
return 1
local n = (cmeta:get_int("tubedir") % (#next_positions)) + 1
if pipeworks.enable_cyclic_mode then
cmeta:set_int("tubedir", n)
end
local new_velocity = vector.multiply(next_positions[n].vect, vel.speed)
return true, new_velocity
end
minetest.register_entity("pipeworks:tubed_item", {
initial_properties = {
hp_max = 1,
physical = false,
-- collisionbox = {0,0,0,0,0,0},
collisionbox = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1},
visual = "wielditem",
visual_size = {x = 0.15, y = 0.15},
@ -343,113 +287,126 @@ minetest.register_entity("pipeworks:tubed_item", {
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = false,
start_pos = {},
route = {},
removed = false
},
itemstring = '',
physical_state = false,
set_item = function(self, itemstring)
self.itemstring = itemstring
from_data = function(self, itemstring)
local stack = ItemStack(itemstring)
self.object:set_properties({
is_visible = true,
textures = { stack:get_name() },
})
local def = stack:get_definition()
self.object:setyaw((def and def.type == "node") and 0 or math.pi * 0.25)
end,
get_staticdata = function(self)
if self.start_pos == nil or self.removed then
return
end
local velocity = self.object:getvelocity()
self.object:setpos(self.start_pos)
return minetest.serialize({
itemstring = self.itemstring,
velocity = velocity,
start_pos = self.start_pos
})
end,
on_activate = function(self, staticdata)
if staticdata=="" or staticdata==nil then return end
local item = minetest.deserialize(staticdata)
local stack = ItemStack(item.itemstring)
local itemtable = stack:to_table()
local itemname = nil
if itemtable then
itemname = stack:to_table().name
end
if itemname then
self.start_pos=item.start_pos
self.object:setvelocity(item.velocity)
self.object:setacceleration({x=0, y=0, z=0})
self.object:setpos(item.start_pos)
local item_texture = nil
local item_type = ""
if minetest.registered_items[itemname] then
item_texture = minetest.registered_items[itemname].inventory_image
item_type = minetest.registered_items[itemname].type
end
self:set_item(item.itemstring)
self.object:set_properties({
is_visible = true,
textures = {stack:get_name()}
})
local def = stack:get_definition()
self.object:setyaw((def and def.type == "node") and 0 or math.pi * 0.25)
end,
get_staticdata = luaentity.get_staticdata,
on_activate = luaentity.on_activate,
})
minetest.register_entity("pipeworks:color_entity", {
initial_properties = {
hp_max = 1,
physical = false,
collisionbox = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1},
visual = "cube",
visual_size = {x = 3.5, y = 3.5, z = 3.5}, -- todo: find correct size
textures = {""},
is_visible = false,
},
physical_state = false,
from_data = function(self, color)
local t = "pipeworks_color_"..color..".png"
local prop = {
is_visible = true,
visual = "cube",
textures = {t, t, t, t, t, t} -- todo: textures
}
self.object:set_properties(prop)
end,
get_staticdata = luaentity.get_staticdata,
on_activate = luaentity.on_activate,
})
luaentity.register_entity("pipeworks:tubed_item", {
itemstring = '',
item_entity = nil,
color_entity = nil,
color = nil,
start_pos = nil,
set_item = function(self, item)
local itemstring = ItemStack(item):to_string() -- Accept any input format
if self.itemstring == itemstring then
return
end
if self.item_entity then
self:remove_attached_entity(self.item_entity)
end
self.itemstring = itemstring
self.item_entity = self:add_attached_entity("pipeworks:tubed_item", itemstring)
end,
remove = function(self)
self.object:remove()
self.removed = true
self.itemstring = ''
set_color = function(self, color)
if self.color == color then
return
end
self.color = color
if self.color_entity then
self:remove_attached_entity(self.color_entity)
end
if color then
self.color_entity = self:add_attached_entity("pipeworks:color_entity", color)
else
self.color_entity = nil
end
end,
on_step = function(self, dtime)
if self.removed then
return
end
if self.start_pos == nil then
local pos = self.object:getpos()
self.start_pos = roundpos(pos)
local pos = self:getpos()
self.start_pos = vector.round(pos)
self:setpos(pos)
end
local pos = self.object:getpos()
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local tubelike = meta:get_int("tubelike")
local stack = ItemStack(self.itemstring)
local drop_pos = nil
local velocity = self.object:getvelocity()
if velocity == nil then return end
local velocitycopy = {x = velocity.x, y = velocity.y, z = velocity.z}
local pos = self:getpos()
local stack = ItemStack(self.itemstring)
local drop_pos
local velocity = self:getvelocity()
local moved = false
local speed = math.abs(velocity.x + velocity.y + velocity.z)
if speed == 0 then
speed = 1
moved = true
end
local vel = {x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed}
if math.abs(vel.x) == 1 then
local next_node = math.abs(pos.x - self.start_pos.x)
if next_node >= 1 then
self.start_pos.x = self.start_pos.x + vel.x
moved = true
end
elseif math.abs(vel.y) == 1 then
local next_node = math.abs(pos.y - self.start_pos.y)
if next_node >= 1 then
self.start_pos.y = self.start_pos.y + vel.y
moved = true
end
elseif math.abs(vel.z) == 1 then
local next_node = math.abs(pos.z - self.start_pos.z)
if next_node >= 1 then
self.start_pos.z = self.start_pos.z + vel.z
moved = true
end
if vector.distance(pos, self.start_pos) >= 1 then
self.start_pos = vector.add(self.start_pos, vel)
moved = true
end
local sposcopy = {x = self.start_pos.x, y = self.start_pos.y, z = self.start_pos.z}
node = minetest.get_node(self.start_pos)
minetest.load_position(self.start_pos)
local node = minetest.get_node(self.start_pos)
if moved and minetest.get_item_group(node.name, "tubedevice_receiver") == 1 then
local leftover = nil
local leftover
if minetest.registered_nodes[node.name].tube and minetest.registered_nodes[node.name].tube.insert_object then
leftover = minetest.registered_nodes[node.name].tube.insert_object(self.start_pos, node, stack, vel)
else
@ -459,62 +416,48 @@ minetest.register_entity("pipeworks:tubed_item", {
self:remove()
return
end
velocity.x = -velocity.x
velocity.y = -velocity.y
velocity.z = -velocity.z
self.object:setvelocity(velocity)
velocity = vector.multiply(velocity, -1)
self:setvelocity(velocity)
self:set_item(leftover:to_string())
return
end
if moved then
if go_next (self.start_pos, velocity, stack) == 0 then
local found_next, new_velocity = go_next(self.start_pos, velocity, stack) -- todo: color
if not found_next then
drop_pos = minetest.find_node_near(vector.add(self.start_pos, velocity), 1, "air")
if drop_pos then
minetest.item_drop(stack, "", drop_pos)
self:remove()
return
end
end
end
if velocity.x~=velocitycopy.x or velocity.y~=velocitycopy.y or velocity.z~=velocitycopy.z or
self.start_pos.x~=sposcopy.x or self.start_pos.y~=sposcopy.y or self.start_pos.z~=sposcopy.z then
self.object:setpos(self.start_pos)
self.object:setvelocity(velocity)
if new_velocity and not vector.equals(velocity, new_velocity) then
self:setpos(self.start_pos)
self:setvelocity(new_velocity)
end
end
end
})
if minetest.get_modpath("mesecons_mvps") ~= nil then
local function add_table(table,toadd)
local i = 1
while true do
o = table[i]
if o == toadd then return end
if o == nil then break end
i = i+1
end
table[i] = toadd
end
if minetest.get_modpath("mesecons_mvps") then
mesecon:register_mvps_unmov("pipeworks:tubed_item")
mesecon:register_mvps_unmov("pipeworks:color_entity")
mesecon:register_on_mvps_move(function(moved_nodes)
local objects_to_move = {}
local moved = {}
for _, n in ipairs(moved_nodes) do
local objects = minetest.get_objects_inside_radius(n.oldpos, 1)
for _, obj in ipairs(objects) do
local entity = obj:get_luaentity()
if entity and entity.name == "pipeworks:tubed_item" then
--objects_to_move[#objects_to_move+1] = obj
add_table(objects_to_move, obj)
end
end
moved[minetest.hash_node_position(n.oldpos)] = vector.subtract(n.pos, n.oldpos)
end
if #objects_to_move > 0 then
local dir = vector.subtract(moved_nodes[1].pos, moved_nodes[1].oldpos)
for _, obj in ipairs(objects_to_move) do
local entity = obj:get_luaentity()
obj:setpos(vector.add(obj:getpos(), dir))
entity.start_pos = vector.add(entity.start_pos, dir)
for id, entity in pairs(luaentity.entities) do
if entity.name == "pipeworks:tubed_item" then
local pos = entity:getpos()
local rpos = vector.round(pos)
local dir = moved[minetest.hash_node_position(rpos)]
if dir then
entity:setpos(vector.add(pos, dir))
entity.start_pos = vector.add(entity.start_pos, dir)
end
end
end
end)

335
luaentity.lua Executable file
View File

@ -0,0 +1,335 @@
local max_entity_id = 1000000000000 -- If you need more, there's a problem with your code
luaentity = {}
luaentity.registered_entities = {}
local filename = minetest.get_worldpath().."/luaentities"
local function read_file()
local f = io.open(filename, "r")
if f == nil then return {} end
local t = f:read("*all")
f:close()
if t == "" or t == nil then return {} end
return minetest.deserialize(t)
end
local function write_file(tbl)
local f = io.open(filename, "w")
f:write(minetest.serialize(tbl))
f:close()
end
local function read_entities()
local t = read_file()
for _, entity in pairs(t) do
setmetatable(entity, luaentity.registered_entities[entity.name])
end
return t
end
local function write_entities()
for _, entity in pairs(luaentity.entities) do
setmetatable(entity, nil)
for _, attached in pairs(entity._attached_entities) do
if attached.entity then
attached.entity:remove()
attached.entity = nil
end
end
entity._attached_entities_master = nil
end
write_file(luaentity.entities)
end
minetest.after(0, function()
luaentity.entities = read_entities()
end)
minetest.register_on_shutdown(write_entities)
-- todo: load that from file (datastorage?) -> don't forget about metatables (are those serialized?) / do not blindly save -> the attached_entities have to be removed
luaentity.entities_index = 0
local function get_blockpos(pos)
return {x = math.floor(pos.x / 16),
y = math.floor(pos.y / 16),
z = math.floor(pos.z / 16)}
end
local active_blocks = {} -- These only contain active blocks near players (i.e., not forceloaded ones)
local handle_active_blocks_step = 2
local handle_active_blocks_timer = 0
minetest.register_globalstep(function(dtime)
handle_active_blocks_timer = handle_active_blocks_timer + dtime
if handle_active_blocks_timer >= handle_active_blocks_step then
handle_active_blocks_timer = handle_active_blocks_timer - handle_active_blocks_step
local active_block_range = tonumber(minetest.setting_get("active_block_range"))
local new_active_blocks = {}
for _, player in ipairs(minetest.get_connected_players()) do
local blockpos = get_blockpos(player:getpos())
local minp = vector.subtract(blockpos, active_block_range)
local maxp = vector.add(blockpos, active_block_range)
for x = minp.x, maxp.x do
for y = minp.y, maxp.y do
for z = minp.z, maxp.z do
local pos = {x = x, y = y, z = z}
new_active_blocks[minetest.hash_node_position(pos)] = pos
end
end
end
end
active_blocks = new_active_blocks
-- todo: callbacks on block load/unload
end
end)
local function is_active(pos)
return active_blocks[minetest.hash_node_position(get_blockpos(pos))] ~= nil
end
local entitydef_default = {
_attach = function(self, attached, attach_to)
local attached_def = self._attached_entities[attached]
local attach_to_def = self._attached_entities[attach_to]
attached_def.entity:set_attach(
attach_to_def.entity, "",
vector.subtract(attached_def.offset, attach_to_def.offset), -- todo: Does not work because is object space
vector.new(0, 0, 0)
)
end,
_set_master = function(self, index)
self._attached_entities_master = index
if not index then
return
end
local def = self._attached_entities[index]
if not def.entity then
return
end
def.entity:setpos(vector.add(self._pos, def.offset))
def.entity:setvelocity(self._velocity)
def.entity:setacceleration(self._acceleration)
end,
_attach_all = function(self)
local master = self._attached_entities_master
if not master then
return
end
for id, entity in pairs(self._attached_entities) do
if id ~= master and entity.entity then
self:_attach(id, master)
end
end
end,
_detach_all = function(self)
local master = self._attached_entities_master
for id, entity in pairs(self._attached_entities) do
if id ~= master and entity.entity then
entity.entity:set_detach()
end
end
end,
_add_attached = function(self, index)
local entity = self._attached_entities[index]
if entity.entity then
return
end
local entity_pos = vector.add(self._pos, entity.offset)
if not is_active(entity_pos) then
return
end
local ent = minetest.add_entity(entity_pos, entity.name):get_luaentity()
ent:from_data(entity.data)
ent.parent_id = self._id
ent.attached_id = index
entity.entity = ent.object
local master = self._attached_entities_master
if master then
self:_attach(index, master)
else
self:_set_master(index)
end
end,
_remove_attached = function(self, index)
local master = self._attached_entities_master
local entity = self._attached_entities[index]
local ent = entity.entity
entity.entity = nil
if index == master then
self:_detach_all()
local newmaster
for id, attached in pairs(self._attached_entities) do
if id ~= master and attached.entity then
newmaster = id
break
end
end
self:_set_master(newmaster)
self:_attach_all()
elseif master and ent then
ent:set_detach()
end
if ent then
ent:remove()
end
end,
_add_loaded = function(self)
for id, _ in pairs(self._attached_entities) do
self:_add_attached(id)
end
end,
getid = function(self)
return self._id
end,
getpos = function(self)
return vector.new(self._pos)
end,
setpos = function(self, pos)
self._pos = vector.new(pos)
--for _, entity in pairs(self._attached_entities) do
-- if entity.entity then
-- entity.entity:setpos(vector.add(self._pos, entity.offset))
-- end
--end
local master = self._attached_entities_master
if master then
local master_def = self._attached_entities[master]
master_def.entity:setpos(vector.add(self._pos, master_def.offset))
end
end,
getvelocity = function(self)
return vector.new(self._velocity)
end,
setvelocity = function(self, velocity)
self._velocity = vector.new(velocity)
local master = self._attached_entities_master
if master then
self._attached_entities[master].entity:setvelocity(self._velocity)
end
end,
getacceleration = function(self)
return vector.new(self._acceleration)
end,
setacceleration = function(self, acceleration)
self._acceleration = vector.new(acceleration)
local master = self._attached_entities_master
if master then
self._attached_entities[master].entity:setacceleration(self._acceleration)
end
end,
remove = function(self)
self:_detach_all()
for _, entity in pairs(self._attached_entities) do
if entity.entity then
entity.entity:remove()
end
end
luaentity.entities[self._id] = nil
end,
add_attached_entity = function(self, name, data, offset)
local index = #self._attached_entities + 1
self._attached_entities[index] = {
name = name,
data = data,
offset = vector.new(offset),
}
self:_add_attached(index)
return index
end,
remove_attached_entity = function(self, index)
self:_remove_attached(index)
self._attached_entities[index] = nil
end,
}
function luaentity.register_entity(name, prototype)
-- name = check_modname_prefix(name)
prototype.name = name
setmetatable(prototype, {__index = entitydef_default})
prototype.__index = prototype -- Make it possible to use it as metatable
luaentity.registered_entities[name] = prototype
end
-- function luaentity.get_entity_definition(entity)
-- return luaentity.registered_entities[entity.name]
-- end
function luaentity.add_entity(pos, name)
local index = luaentity.entities_index
while luaentity.entities[index] do
index = index + 1
if index >= max_entity_id then
index = 0
end
end
luaentity.entities_index = index
local entity = {
name = name,
_id = index,
_pos = vector.new(pos),
_velocity = {x = 0, y = 0, z = 0},
_acceleration = {x = 0, y = 0, z = 0},
_attached_entities = {},
}
local prototype = luaentity.registered_entities[name]
setmetatable(entity, prototype) -- Default to prototype for other methods
luaentity.entities[index] = entity
if entity.on_activate then
entity:on_activate()
end
return entity
end
-- todo: check if remove in get_staticdata works
function luaentity.get_staticdata(self)
local parent = luaentity.entities[self.parent_id]
if parent and parent._remove_attached then
parent:_remove_attached(self.attached_id)
end
return "toremove"
end
function luaentity.on_activate(self, staticdata)
if staticdata == "toremove" then
self.object:remove()
end
end
function luaentity.get_objects_inside_radius(pos, radius)
local objects = {}
local index = 1
for id, entity in pairs(luaentity.entities) do
if vector.distance(pos, entity:getpos()) <= radius then
objects[index] = entity
index = index + 1
end
end
end
minetest.register_globalstep(function(dtime)
for id, entity in pairs(luaentity.entities) do
local master = entity._attached_entities_master
if master then
local master_def = entity._attached_entities[master]
local master_entity = master_def.entity
entity._pos = vector.subtract(master_entity:getpos(), master_def.offset)
entity._velocity = master_entity:getvelocity()
entity._acceleration = master_entity:getacceleration()
else
entity._pos = vector.add(vector.add(
entity._pos,
vector.multiply(entity._velocity, dtime)),
vector.multiply(entity._acceleration, 0.5 * dtime * dtime))
entity._velocity = vector.add(
entity._velocity,
vector.multiply(entity._acceleration, dtime))
end
entity:_add_loaded()
if entity.on_step then
entity:on_step(dtime)
end
end
end)

View File

@ -9,15 +9,13 @@ minetest.register_node("pipeworks:trashcan", {
"pipeworks_trashcan_side.png",
"pipeworks_trashcan_side.png",
},
groups = { snappy = 3, tubedevice = 1, tubedevice_receiver = 1 },
groups = {snappy = 3, tubedevice = 1, tubedevice_receiver = 1},
tube = {
insert_object = function(pos, node, stack, direction)
return ItemStack("")
end,
can_insert = function(pos, node, stack, direction)
return true
end,
connect_sides = { left = 1, right = 1, front = 1, back = 1, top = 1, bottom = 1 },
end,
connect_sides = {left = 1, right = 1, front = 1, back = 1, top = 1, bottom = 1},
priority = 1, -- Lower than anything else
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)

271
tubes.lua Normal file → Executable file
View File

@ -20,7 +20,7 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
end
for _, v in ipairs(connects) do
pipeworks.add_node_box(outboxes, pipeworks.tube_boxes[v])
table.extend(outboxes, pipeworks.tube_boxes[v])
table.insert(outsel, pipeworks.tube_selectboxes[v])
outimgs[vti[v]] = noctrs[v]
end
@ -31,13 +31,13 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
outimgs[vti[v]] = ends[v]
end
local tgroups = {snappy = 3, tube = 1, not_in_creative_inventory = 1}
local tgroups = {snappy = 3, tube = 1, tubedevice = 1, not_in_creative_inventory = 1}
local tubedesc = desc.." "..dump(connects).."... You hacker, you."
local iimg = plain[1]
local wscale = {x = 1, y = 1, z = 1}
if #connects == 0 then
tgroups = {snappy = 3, tube = 1}
tgroups = {snappy = 3, tube = 1, tubedevice = 1}
tubedesc = desc
iimg=inv
outimgs = {
@ -50,7 +50,8 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
wscale = {x = 1, y = 1, z = 0.01}
end
table.insert(pipeworks.tubenodes, name.."_"..tname)
local rname = name.."_"..tname
table.insert(pipeworks.tubenodes, rname)
local nodedef = {
description = tubedesc,
@ -62,7 +63,7 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
wield_scale = wscale,
paramtype = "light",
selection_box = {
type = "fixed",
type = "fixed",
fixed = outsel
},
node_box = {
@ -77,26 +78,22 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
style = style,
drop = name.."_"..dropname,
tubelike = 1,
tube = {connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1}},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("tubelike", 1)
if minetest.registered_nodes[name.."_"..tname].on_construct_ then
minetest.registered_nodes[name.."_"..tname].on_construct_(pos)
end
end,
after_place_node = function(pos)
tube = {
connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1},
priority = 50
},
--[[after_place_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
if minetest.registered_nodes[name.."_"..tname].after_place_node_ then
minetest.registered_nodes[name.."_"..tname].after_place_node_(pos)
if minetest.registered_nodes[rname].after_place_node_ then
minetest.registered_nodes[rname].after_place_node_(pos)
end
end,
after_dig_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
if minetest.registered_nodes[name.."_"..tname].after_dig_node_ then
minetest.registered_nodes[name.."_"..tname].after_dig_node_(pos)
if minetest.registered_nodes[rname].after_dig_node_ then
minetest.registered_nodes[rname].after_dig_node_(pos)
end
end
end]]
}
if style == "6d" then
nodedef.paramtype2 = "facedir"
@ -105,9 +102,9 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
if special == nil then special = {} end
for key, value in pairs(special) do
if key == "on_construct" or key == "after_dig_node" or key == "after_place_node" then
nodedef[key.."_"] = value
elseif key == "groups" then
--if key == "after_dig_node" or key == "after_place_node" then
-- nodedef[key.."_"] = value
if key == "groups" then
for group, val in pairs(value) do
nodedef.groups[group] = val
end
@ -115,19 +112,12 @@ local register_one_tube = function(name, tname, dropname, desc, plain, noctrs, e
for key, val in pairs(value) do
nodedef.tube[key] = val
end
elseif type(value) == "table" then
nodedef[key] = pipeworks.replace_name(value, "#id", tname)
elseif type(value) == "string" then
nodedef[key] = string.gsub(value, "#id", tname)
else
nodedef[key] = value
nodedef[key] = table.recursive_replace(value, "#id", tname)
end
end
local prefix = ":"
if string.find(name, "pipeworks:") then prefix = "" end
minetest.register_node(prefix..name.."_"..tname, nodedef)
minetest.register_node(rname, nodedef)
end
pipeworks.register_tube = function(name, desc, plain, noctrs, ends, short, inv, special, old_registration)
@ -181,22 +171,18 @@ pipeworks.register_tube = function(name, desc, plain, noctrs, ends, short, inv,
wield_image = inv,
paramtype = "light",
sunlight_propagates = true,
description = desc.." (legacy)",
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("tubelike", 1)
end,
after_place_node = function(pos)
description = "Pneumatic tube segment (legacy)",
--[[after_place_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
if minetest.registered_nodes[name.."_1"].after_place_node_ then
minetest.registered_nodes[name.."_1"].after_place_node_(pos)
end
end,
groups = {not_in_creative_inventory = 1, tube_to_update = 1},
end,]]
groups = {not_in_creative_inventory = 1, tube_to_update = 1, tube = 1},
tube = {connect_sides = {front = 1, back = 1, left = 1, right = 1, top = 1, bottom = 1}},
drop = name.."_1",
})
table.insert(pipeworks.tubenodes,cname)
table.insert(pipeworks.tubenodes, cname)
for xm = 0, 1 do
for xp = 0, 1 do
for ym = 0, 1 do
@ -221,8 +207,8 @@ if REGISTER_COMPATIBILITY then
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local minp = {x = pos.x-1, y = pos.y-1, z = pos.z-1}
local maxp = {x = pos.x+1, y = pos.y+1, z = pos.z+1}
local minp = vector.subtract(pos, 1)
local maxp = vector.add(pos, 1)
if table.getn(minetest.find_nodes_in_area(minp, maxp, "ignore")) == 0 then
pipeworks.scan_for_tube_objects(pos)
end
@ -241,7 +227,7 @@ local end_textures = {"pipeworks_tube_end.png", "pipeworks_tube_end.png", "pipew