Rename mod: walls → hades_walls
[minetest_hades/hades_revisited.git] / mods / pipeworks / item_transport.lua
blob11544b89c23958136942a48301164912be53d6f5
1 dofile(pipeworks.modpath.."/compat.lua")
3 --and an extra function for getting the right-facing vector
4 local function facedir_to_right_dir(facedir)
6 --find the other directions
7 local backdir = minetest.facedir_to_dir(facedir)
8 local topdir = ({[0]={x=0, y=1, z=0},
9 {x=0, y=0, z=1},
10 {x=0, y=0, z=-1},
11 {x=1, y=0, z=0},
12 {x=-1, y=0, z=0},
13 {x=0, y=-1, z=0}})[math.floor(facedir/4)]
15 --return a cross product
16 return {x=topdir.y*backdir.z - backdir.y*topdir.z,
17 y=topdir.z*backdir.x - backdir.z*topdir.x,
18 z=topdir.x*backdir.y - backdir.x*topdir.y}
19 end
21 local fakePlayer = {
22 get_player_name = function() return ":pipeworks" end,
23 -- any other player functions called by allow_metadata_inventory_take anywhere...
24 -- perhaps a custom metaclass that errors specially when fakePlayer.<property> is not found?
27 function pipeworks.tube_item(pos, item)
28 -- Take item in any format
29 local stack = ItemStack(item)
30 local obj = minetest.add_entity(pos, "pipeworks:tubed_item")
31 obj:get_luaentity():set_item(stack:to_string())
32 return obj
33 end
35 -- adding two tube functions
36 -- can_remove(pos,node,stack,dir) returns the maximum number of items of that stack that can be removed
37 -- remove_items(pos,node,stack,dir,count) removes count items and returns them
38 -- both optional w/ sensible defaults and fallback to normal allow_* function
39 -- XXX: possibly change insert_object to insert_item
41 -- sname = the current name to allow for, or nil if it allows anything
43 local function grabAndFire(frominv,frominvname,frompos,fromnode,sname,tube,idef,dir,all)
44 for spos,stack in ipairs(frominv:get_list(frominvname)) do
45 if (sname == nil and stack:get_name() ~= "") or stack:get_name() == sname then
46 local doRemove = stack:get_count()
47 if tube.can_remove then
48 doRemove = tube.can_remove(frompos, fromnode, stack, dir)
49 elseif idef.allow_metadata_inventory_take then
50 doRemove = idef.allow_metadata_inventory_take(frompos,"main",spos, stack, fakePlayer)
51 end
52 -- stupid lack of continue statements grumble
53 if doRemove > 0 then
54 local item
55 local count
56 if all then
57 count = math.min(stack:get_count(), doRemove)
58 else
59 count = 1
60 end
61 if tube.remove_items then
62 -- it could be the entire stack...
63 item = tube.remove_items(frompos, fromnode, stack, dir, count)
64 else
65 item = stack:take_item(count)
66 frominv:set_stack(frominvname, spos, stack)
67 if idef.on_metadata_inventory_take then
68 idef.on_metadata_inventory_take(frompos, "main", spos, item, fakePlayer)
69 end
70 end
71 local item1 = pipeworks.tube_item(vector.add(frompos, vector.multiply(dir, 1.4)), item)
72 item1:get_luaentity().start_pos = vector.add(frompos, dir)
73 item1:set_velocity(dir)
74 item1:set_acceleration({x=0, y=0, z=0})
75 return true-- only fire one item, please
76 end
77 end
78 end
79 return false
80 end
82 minetest.register_node("pipeworks:filter", {
83 description = "Filter",
84 tiles = {"pipeworks_filter_top.png", "pipeworks_filter_top.png", "pipeworks_filter_output.png",
85 "pipeworks_filter_input.png", "pipeworks_filter_side.png", "pipeworks_filter_top.png"},
86 paramtype2 = "facedir",
87 groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
88 sounds = hades_sounds.node_sound_wood_defaults(),
89 on_construct = function(pos)
90 local meta = minetest.get_meta(pos)
91 meta:set_string("formspec",
92 "size[10,6.5;]"..
93 "list[current_name;main;1,0;8,2;]"..
94 "list[current_player;main;0,2.5;10,4;]")
95 meta:set_string("infotext", "Filter")
96 local inv = meta:get_inventory()
97 inv:set_size("main", 10*4)
98 end,
99 can_dig = function(pos,player)
100 local meta = minetest.get_meta(pos);
101 local inv = meta:get_inventory()
102 return inv:is_empty("main")
103 end,
104 after_place_node = function(pos)
105 pipeworks.scan_for_tube_objects(pos)
106 end,
107 after_dig_node = function(pos)
108 pipeworks.scan_for_tube_objects(pos)
109 end,
110 mesecons={effector={action_on=function(pos,node)
111 minetest.registered_nodes[node.name].on_punch(pos,node,nil)
112 end}},
113 tube={connect_sides={right=1}},
114 on_punch = function (pos, node, puncher)
115 local meta = minetest.get_meta(pos);
116 local inv = meta:get_inventory()
117 local dir = facedir_to_right_dir(node.param2)
118 local frompos = {x=pos.x - dir.x, y=pos.y - dir.y, z=pos.z - dir.z}
119 local fromnode=minetest.get_node(frompos)
120 if not fromnode then return end
121 local idef = minetest.registered_nodes[fromnode.name]
122 -- assert(idef)
123 local tube = idef.tube
124 if not (tube and tube.input_inventory) then
125 return
127 if tube.before_filter then
128 tube.before_filter(frompos)
130 local frommeta = minetest.get_meta(frompos)
131 local frominvname = tube.input_inventory
132 local frominv = frommeta:get_inventory()
133 local sname
134 local fired = false
135 for _,filter in ipairs(inv:get_list("main")) do
136 sname = filter:get_name()
137 if sname ~= "" then
138 -- XXX: that's a lot of parameters
139 if grabAndFire(frominv, frominvname, frompos, fromnode, sname, tube, idef, dir) then
140 fired = true
141 break
145 if not fired and inv:is_empty("main") then
146 grabAndFire(frominv,frominvname,frompos,fromnode,nil,tube,idef,dir)
148 if tube.after_filter then
149 tube.after_filter(frompos)
151 end,
154 minetest.register_node("pipeworks:mese_filter", {
155 description = "Mese Filter",
156 tiles = {"pipeworks_mese_filter_top.png", "pipeworks_mese_filter_top.png", "pipeworks_mese_filter_output.png",
157 "pipeworks_mese_filter_input.png", "pipeworks_mese_filter_side.png", "pipeworks_mese_filter_top.png"},
158 paramtype2 = "facedir",
159 groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
160 sounds = hades_sounds.node_sound_wood_defaults(),
161 on_construct = function(pos)
162 local meta = minetest.get_meta(pos)
163 meta:set_string("formspec",
164 "size[8,6.5;]"..
165 "list[current_name;main;1,0;8,2;]"..
166 "list[current_player;main;0,2.5;10,4;]")
167 meta:set_string("infotext", "Mese Filter")
168 local inv = meta:get_inventory()
169 inv:set_size("main", 10*4)
170 end,
171 can_dig = function(pos,player)
172 local meta = minetest.get_meta(pos);
173 local inv = meta:get_inventory()
174 return inv:is_empty("main")
175 end,
176 after_place_node = function(pos)
177 pipeworks.scan_for_tube_objects(pos)
178 end,
179 after_dig_node = function(pos)
180 pipeworks.scan_for_tube_objects(pos)
181 end,
182 mesecons={effector={action_on=function(pos,node)
183 minetest.registered_nodes[node.name].on_punch(pos,node,nil)
184 end}},
185 tube={connect_sides={right=1}},
186 on_punch = function (pos, node, puncher)
187 local meta = minetest.get_meta(pos);
188 local inv = meta:get_inventory()
189 local dir = facedir_to_right_dir(node.param2)
190 local frompos = {x=pos.x - dir.x, y=pos.y - dir.y, z=pos.z - dir.z}
191 local fromnode=minetest.get_node(frompos)
192 local idef = minetest.registered_nodes[fromnode.name]
193 -- assert(idef)
194 local tube = idef.tube
195 if not (tube and tube.input_inventory) then
196 return
198 local frommeta = minetest.get_meta(frompos)
199 local frominvname = minetest.registered_nodes[fromnode.name].tube.input_inventory
200 local frominv = frommeta:get_inventory()
201 local sname
202 for _,filter in ipairs(inv:get_list("main")) do
203 sname = filter:get_name()
204 if sname ~= "" then
205 if grabAndFire(frominv, frominvname, frompos, fromnode, sname, tube, idef, dir, true) then return end
208 if inv:is_empty("main") then
209 grabAndFire(frominv, frominvname, frompos, fromnode, nil, tube, idef, dir, true)
211 end,
214 local function roundpos(pos)
215 return {x=math.floor(pos.x+0.5),y=math.floor(pos.y+0.5),z=math.floor(pos.z+0.5)}
218 local function addVect(pos,vect)
219 return {x=pos.x+vect.x,y=pos.y+vect.y,z=pos.z+vect.z}
222 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}}
224 function pipeworks.notvel(tbl, vel)
225 local tbl2={}
226 for _,val in ipairs(tbl) do
227 if val.x ~= -vel.x or val.y ~= -vel.y or val.z ~= -vel.z then table.insert(tbl2, val) end
229 return tbl2
232 local function go_next(pos, velocity, stack)
233 local chests = {}
234 local tubes = {}
235 local cnode = minetest.get_node(pos)
236 local cmeta = minetest.get_meta(pos)
237 local n
238 local can_go
239 local speed = math.abs(velocity.x + velocity.y + velocity.z)
240 local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed,speed=speed}
241 if speed >= 4.1 then
242 speed = 4
243 elseif speed >= 1.1 then
244 speed = speed-0.1
245 else
246 speed = 1
248 vel.speed = speed
249 if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then
250 can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack)
251 else
252 can_go = pipeworks.notvel(adjlist, vel)
254 local meta = nil
255 for _,vect in ipairs(can_go) do
256 local npos = addVect(pos,vect)
257 local node = minetest.get_node(npos)
258 local tube_receiver = minetest.get_item_group(node.name,"tubedevice_receiver")
259 meta = minetest.get_meta(npos)
260 local tubelike = meta:get_int("tubelike")
261 if tube_receiver == 1 then
262 if minetest.registered_nodes[node.name].tube and
263 minetest.registered_nodes[node.name].tube.can_insert and
264 minetest.registered_nodes[node.name].tube.can_insert(npos, node, stack, vect) then
265 local i = #chests + 1
266 chests[i] = {}
267 chests[i].pos = npos
268 chests[i].vect = vect
270 elseif tubelike == 1 then
271 local i = #tubes + 1
272 tubes[i] = {}
273 tubes[i].pos = npos
274 tubes[i].vect = vect
277 if chests[1] == nil then--no chests found
278 if tubes[1] == nil then
279 return 0
280 else
281 n = (cmeta:get_int("tubedir")%(#tubes)) + 1
282 if pipeworks.enable_cyclic_mode then
283 cmeta:set_int("tubedir",n)
285 velocity.x = tubes[n].vect.x*vel.speed
286 velocity.y = tubes[n].vect.y*vel.speed
287 velocity.z = tubes[n].vect.z*vel.speed
289 else
290 n = (cmeta:get_int("tubedir")%(#chests))+1
291 if pipeworks.enable_cyclic_mode then
292 cmeta:set_int("tubedir",n)
294 velocity.x = chests[n].vect.x*speed
295 velocity.y = chests[n].vect.y*speed
296 velocity.z = chests[n].vect.z*speed
298 return 1
301 minetest.register_entity("pipeworks:tubed_item", {
302 initial_properties = {
303 hp_max = 1,
304 physical = false,
305 -- collisionbox = {0,0,0,0,0,0},
306 collisionbox = {0.1,0.1,0.1,0.1,0.1,0.1},
307 visual = "sprite",
308 visual_size = {x=0.5, y=0.5},
309 textures = {""},
310 spritediv = {x=1, y=1},
311 initial_sprite_basepos = {x=0, y=0},
312 is_visible = false,
313 start_pos={},
314 route={}
317 itemstring = '',
318 physical_state = false,
320 set_item = function(self, itemstring)
321 self.itemstring = itemstring
322 local stack = ItemStack(itemstring)
323 local itemtable = stack:to_table()
324 local itemname = nil
325 if itemtable then
326 itemname = stack:to_table().name
328 local item_texture = nil
329 local item_type = ""
330 if minetest.registered_items[itemname] then
331 item_texture = minetest.registered_items[itemname].inventory_image
332 item_type = minetest.registered_items[itemname].type
334 prop = {
335 is_visible = true,
336 visual = "sprite",
337 textures = {"unknown_item.png"}
339 if item_texture and item_texture ~= "" then
340 prop.visual = "sprite"
341 prop.textures = {item_texture}
342 prop.visual_size = {x=0.3, y=0.3}
343 else
344 prop.visual = "wielditem"
345 prop.textures = {itemname}
346 prop.visual_size = {x=0.15, y=0.15}
348 self.object:set_properties(prop)
349 end,
351 get_staticdata = function(self)
352 if self.start_pos==nil then return end
353 local velocity=self.object:get_velocity()
354 self.object:set_pos(self.start_pos)
355 return minetest.serialize({
356 itemstring=self.itemstring,
357 velocity=velocity,
358 start_pos=self.start_pos
360 end,
362 on_activate = function(self, staticdata)
363 if staticdata=="" or staticdata==nil then return end
364 local item = minetest.deserialize(staticdata)
365 local stack = ItemStack(item.itemstring)
366 local itemtable = stack:to_table()
367 local itemname = nil
368 if itemtable then
369 itemname = stack:to_table().name
372 if itemname then
373 self.start_pos=item.start_pos
374 self.object:set_velocity(item.velocity)
375 self.object:set_acceleration({x=0, y=0, z=0})
376 self.object:set_pos(item.start_pos)
378 self:set_item(item.itemstring)
379 end,
381 on_step = function(self, dtime)
382 if self.start_pos==nil then
383 local pos = self.object:get_pos()
384 self.start_pos=roundpos(pos)
386 local pos = self.object:get_pos()
387 local node = minetest.get_node(pos)
388 local meta = minetest.get_meta(pos)
389 local tubelike = meta:get_int("tubelike")
390 local stack = ItemStack(self.itemstring)
391 local drop_pos = nil
393 local velocity=self.object:get_velocity()
395 if velocity == nil then return end
397 local velocitycopy = {x = velocity.x, y = velocity.y, z = velocity.z}
399 local moved = false
400 local speed = math.abs(velocity.x+velocity.y+velocity.z)
401 local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed, speed = speed}
403 if math.abs(vel.x) == 1 then
404 local next_node = math.abs(pos.x-self.start_pos.x)
405 if next_node >= 1 then
406 self.start_pos.x = self.start_pos.x+vel.x
407 moved = true
409 elseif math.abs(vel.y) == 1 then
410 local next_node = math.abs(pos.y-self.start_pos.y)
411 if next_node >= 1 then
412 self.start_pos.y = self.start_pos.y+vel.y
413 moved = true
414 end
415 elseif math.abs(vel.z) == 1 then
416 local next_node = math.abs(pos.z-self.start_pos.z)
417 if next_node >= 1 then
418 self.start_pos.z = self.start_pos.z+vel.z
419 moved = true
423 local sposcopy = {x = self.start_pos.x, y = self.start_pos.y, z = self.start_pos.z}
425 node = minetest.get_node(self.start_pos)
426 if moved and minetest.get_item_group(node.name, "tubedevice_receiver") == 1 then
427 local leftover = nil
428 if minetest.registered_nodes[node.name].tube and minetest.registered_nodes[node.name].tube.insert_object then
429 leftover = minetest.registered_nodes[node.name].tube.insert_object(self.start_pos, node, stack, vel)
430 else
431 leftover = stack
433 if leftover:is_empty() then
434 self.object:remove()
435 return
437 velocity.x = -velocity.x
438 velocity.y = -velocity.y
439 velocity.z = -velocity.z
440 self.object:set_velocity(velocity)
441 self:set_item(leftover:to_string())
442 return
445 if moved then
446 if go_next (self.start_pos, velocity, stack) == 0 then
447 drop_pos = minetest.find_node_near(vector.add(self.start_pos, velocity), 1, "air")
448 if drop_pos then
449 minetest.item_drop(stack, nil, drop_pos)
450 self.object:remove()
455 if velocity.x~=velocitycopy.x or velocity.y~=velocitycopy.y or velocity.z~=velocitycopy.z or
456 self.start_pos.x~=sposcopy.x or self.start_pos.y~=sposcopy.y or self.start_pos.z~=sposcopy.z then
457 self.object:set_pos(self.start_pos)
458 self.object:set_velocity(velocity)
463 if minetest.get_modpath("mesecons_mvps") ~= nil then
464 mesecon:register_mvps_unmov("pipeworks:tubed_item")
465 mesecon:register_on_mvps_move(function(moved_nodes)
466 local objects_to_move = {}
467 for _, n in ipairs(moved_nodes) do
468 local objects = minetest.get_objects_inside_radius(n.oldpos, 1)
469 for _, obj in ipairs(objects) do
470 local entity = obj:get_luaentity()
471 if entity and entity.name == "pipeworks:tubed_item" then
472 objects_to_move[#objects_to_move+1] = obj
476 if #objects_to_move > 0 then
477 local dir = vector.subtract(moved_nodes[1].pos, moved_nodes[1].oldpos)
478 for _, obj in ipairs(objects_to_move) do
479 local entity = obj:get_luaentity()
480 obj:set_pos(vector.add(obj:get_pos(), dir))
481 entity.start_pos = vector.add(entity.start_pos, dir)
484 end)