David Leal
2019-09-14 d5df30c3ff5ad0b9486288521abca5b517651936
commit | author | age
354ee6 1 local S = technic.getter
S 2
c06cdf 3 frames = {}
1524a9 4
488f80 5 local infinite_stacks = minetest.settings:get_bool("creative_mode")
6     and minetest.get_modpath("unified_inventory") == nil
b28001 7
8fba5e 8 local frames_pos = {}
N 9
c06cdf 10 -- Helpers
N 11
488f80 12 local function get_face(pos, ppos, pvect)
c06cdf 13     -- Raytracer to get which face has been clicked
488f80 14     ppos = { x = ppos.x - pos.x, y = ppos.y - pos.y + 1.5, z = ppos.z - pos.z }
15
16     if pvect.x > 0 then
17         local t = (-0.5 - ppos.x) / pvect.x
18         local y_int = ppos.y + t * pvect.y
19         local z_int = ppos.z + t * pvect.z
20         if y_int > -0.45 and y_int < 0.45 and z_int > -0.45 and z_int < 0.45 then
21             return 1
22         end
23     elseif pvect.x < 0 then
24         local t = (0.5 - ppos.x) / pvect.x
25         local y_int = ppos.y + t * pvect.y
26         local z_int = ppos.z + t * pvect.z
27         if y_int > -0.45 and y_int < 0.45 and z_int > -0.45 and z_int < 0.45 then
28             return 2
29         end
478407 30     end
488f80 31
32     if pvect.y > 0 then
33         local t = (-0.5 - ppos.y) / pvect.y
34         local x_int = ppos.x + t * pvect.x
35         local z_int = ppos.z + t * pvect.z
36         if x_int > -0.45 and x_int < 0.45 and z_int > -0.45 and z_int < 0.45 then
37             return 3
38         end
39     elseif pvect.y < 0 then
40         local t = (0.5 - ppos.y) / pvect.y
41         local x_int = ppos.x + t * pvect.x
42         local z_int = ppos.z + t * pvect.z
43         if x_int > -0.45 and x_int < 0.45 and z_int > -0.45 and z_int < 0.45 then
44             return 4
45         end
478407 46     end
488f80 47
48     if pvect.z > 0 then
49         local t = (-0.5 - ppos.z) / pvect.z
50         local x_int = ppos.x + t * pvect.x
51         local y_int = ppos.y + t * pvect.y
52         if x_int > -0.45 and x_int < 0.45 and y_int > -0.45 and y_int < 0.45 then
53             return 5
54         end
55     elseif pvect.z < 0 then
56         local t = (0.5 - ppos.z) / pvect.z
57         local x_int = ppos.x + t * pvect.x
58         local y_int = ppos.y + t * pvect.y
59         if x_int > -0.45 and x_int < 0.45 and y_int > -0.45 and y_int < 0.45 then
60             return 6
61         end
478407 62     end
R 63 end
64
5cf765 65 local function lines(str)
808d38 66     local t = {}
N 67     local function helper(line) table.insert(t, line) return "" end
488f80 68     helper(str:gsub("(.-)\r?\n", helper))
808d38 69     return t
N 70 end
71
72 local function pos_to_string(pos)
73     if pos.x == 0 then pos.x = 0 end -- Fix for signed 0
74     if pos.y == 0 then pos.y = 0 end -- Fix for signed 0
75     if pos.z == 0 then pos.z = 0 end -- Fix for signed 0
76     return tostring(pos.x).."\n"..tostring(pos.y).."\n"..tostring(pos.z)
77 end
78
79 local function pos_from_string(str)
80     local l = lines(str)
488f80 81     return { x = tonumber(l[1]), y = tonumber(l[2]), z = tonumber(l[3]) }
808d38 82 end
N 83
488f80 84 local function pos_in_list(l, pos)
85     for _, p in ipairs(l) do
86         if p.x == pos.x and p.y == pos.y and p.z == pos.z then
87             return true
88         end
c06cdf 89     end
N 90     return false
808d38 91 end
N 92
93 local function table_empty(table)
94     for _, __ in pairs(table) do
95         return false
96     end
97     return true
c06cdf 98 end
478407 99
488f80 100 local function add_table(table, toadd)
e1c995 101     local i = 1
c06cdf 102     while true do
e1c995 103         o = table[i]
N 104         if o == toadd then return end
105         if o == nil then break end
488f80 106         i = i + 1
c06cdf 107     end
e1c995 108     table[i] = toadd
c06cdf 109 end
N 110
488f80 111 local function move_nodes_vect(poslist, vect, must_not_move, owner)
3cf0d3 112     if minetest.is_protected then
488f80 113         for _, pos in ipairs(poslist) do
114             local npos = vector.add(pos, vect)
3cf0d3 115             if minetest.is_protected(pos, owner) or minetest.is_protected(npos, owner) then
N 116                 return
117             end
118         end
119     end
488f80 120
121     for _, pos in ipairs(poslist) do
122         local npos = vector.add(pos, vect)
8c1be3 123         local name = minetest.get_node(npos).name
488f80 124         if (name ~= "air" and minetest.registered_nodes[name].liquidtype == "none" or
125                 frames_pos[pos_to_string(npos)]) and not pos_in_list(poslist, npos) then
c06cdf 126             return
N 127         end
128     end
488f80 129
e1c995 130     local nodelist = {}
N 131     for _, pos in ipairs(poslist) do
132         local node = minetest.get_node(pos)
133         local meta = minetest.get_meta(pos):to_table()
d74c25 134         local timer = minetest.get_node_timer(pos)
488f80 135         nodelist[#nodelist + 1] = {
d74c25 136             oldpos = pos,
137             pos = vector.add(pos, vect),
138             node = node,
139             meta = meta,
140             timer = {
141                 timeout = timer:get_timeout(),
142                 elapsed = timer:get_elapsed()
143             }
144         }
c06cdf 145     end
488f80 146
e1c995 147     local objects = {}
N 148     for _, pos in ipairs(poslist) do
488f80 149         for _, object in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
e1c995 150             local entity = object:get_luaentity()
8da4d0 151             if not entity or not mesecon.is_mvps_unmov(entity.name) then
e1c995 152                 add_table(objects, object)
N 153             end
c06cdf 154         end
N 155     end
488f80 156
e1c995 157     for _, obj in ipairs(objects) do
d5df30 158         obj:set_pos(vector.add(obj:get_pos(), vect))
c06cdf 159     end
488f80 160
161     for _, n in ipairs(nodelist) do
e1c995 162         local npos = n.pos
N 163         minetest.set_node(npos, n.node)
164         local meta = minetest.get_meta(npos)
c06cdf 165         meta:from_table(n.meta)
d74c25 166         local timer = minetest.get_node_timer(npos)
167         if n.timer.timeout ~= 0 or n.timer.elapsed ~= 0 then
168             timer:set(n.timer.timeout, n.timer.elapsed)
169         end
488f80 170         for __, pos in ipairs(poslist) do
e1c995 171             if npos.x == pos.x and npos.y == pos.y and npos.z == pos.z then
c06cdf 172                 table.remove(poslist, __)
N 173                 break
174             end
175         end
176     end
488f80 177
e1c995 178     for __, pos in ipairs(poslist) do
8c1be3 179         minetest.remove_node(pos)
c06cdf 180     end
488f80 181
e1c995 182     for _, callback in ipairs(mesecon.on_mvps_move) do
N 183         callback(nodelist)
184     end
c06cdf 185 end
N 186
8fba5e 187 local function is_supported_node(name)
488f80 188     return string.find(name, "tube") and string.find(name, "pipeworks")
8fba5e 189 end
c06cdf 190
N 191 -- Frames
488f80 192 for xm = 0, 1 do
193 for xp = 0, 1 do
194 for ym = 0, 1 do
195 for yp = 0, 1 do
196 for zm = 0, 1 do
197 for zp = 0, 1 do
478407 198
488f80 199     local a = 8 / 16
200     local b = 7 / 16
201     local nodeboxes = {
202         { -a, -a, -a, -b,  a, -b },
203         { -a, -a,  b, -b,  a,  a },
478407 204
488f80 205         {  b, -a,  b,  a,  a,  a },
206         {  b, -a, -a,  a,  a, -b },
478407 207
488f80 208         { -b,  b, -a,  b,  a, -b },
209         { -b, -a, -a,  b, -b, -b },
478407 210
488f80 211         { -b,  b,  b,  b,  a,  a },
212         { -b, -a,  b,  b, -b,  a },
213
214         {  b,  b, -b,  a,  a,  b },
215         {  b, -a, -b,  a, -b,  b },
216
217         { -a,  b, -b, -b,  a,  b },
218         { -a, -a, -b, -b, -b,  b },
478407 219     }
R 220
488f80 221     if yp == 0 then
222         table.insert(nodeboxes, { -b, b, -b, b, a, b })
223     end
224     if ym == 0 then
225         table.insert(nodeboxes, { -b, -a, -b, b, -b, b })
226     end
227     if xp == 0 then
228         table.insert(nodeboxes, { b, b, b, a, -b, -b })
229     end
230     if xm == 0 then
231         table.insert(nodeboxes, { -a, -b, -b, -b, b, b })
232     end
233     if zp == 0 then
234         table.insert(nodeboxes, { -b, -b, b, b, b, a })
235     end
236     if zm == 0 then
237         table.insert(nodeboxes, { -b, -b, -a, b, b, -b })
238     end
239
240     local nameext = string.format("%d%d%d%d%d%d", xm, xp, ym, yp, zm, zp)
241     local groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2 }
242     if nameext ~= "111111" then groups.not_in_creative_inventory = 1 end
243
244
245     minetest.register_node("technic:frame_"..nameext, {
354ee6 246         description = S("Frame"),
488f80 247         tiles = { "technic_frame.png" },
248         groups = groups,
478407 249         drawtype = "nodebox",
R 250         node_box = {
251             type = "fixed",
488f80 252             fixed = nodeboxes,
478407 253         },
3a5215 254         selection_box = {
488f80 255             type = "fixed",
256             fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }
3a5215 257         },
478407 258         paramtype = "light",
488f80 259         frame = 1,
260         drop = "technic:frame_111111",
b28001 261         sunlight_propagates = true,
488f80 262
263         frame_connect_all = function(nodename)
264             l2 = {}
265             l1 = {
266                 { x = -1, y = 0, z = 0 }, { x = 1, y = 0, z = 0 },
267                 { x = 0, y = -1, z = 0 }, { x = 0, y = 1, z = 0 },
268                 { x = 0, y = 0, z = -1 }, { x = 0, y = 0, z = 1 }
269             }
270             for i, dir in ipairs(l1) do
271                 if string.sub(nodename, -7 + i, -7 + i) == "1" then
272                     l2[#l2 + 1] = dir
478407 273                 end
R 274             end
275             return l2
276         end,
488f80 277
278         on_punch = function(pos, node, puncher)
d5df30 279             local ppos = puncher:get_pos()
488f80 280             local pvect = puncher:get_look_dir()
281             local pface = get_face(pos, ppos, pvect)
282
283             if pface == nil then return end
284
285             local nodename = node.name
286             local newstate = tostring(1 - tonumber(string.sub(nodename, pface - 7, pface - 7)))
287             if pface <= 5 then
288                 nodename = string.sub(nodename, 1, pface - 7 - 1)..newstate..string.sub(nodename, pface - 7 + 1)
478407 289             else
488f80 290                 nodename = string.sub(nodename, 1, -2)..newstate
478407 291             end
488f80 292
293             node.name = nodename
294             minetest.set_node(pos, node)
68f7d3 295         end,
488f80 296
68f7d3 297         on_place = function(itemstack, placer, pointed_thing)
N 298             local pos = pointed_thing.above
488f80 299
52e701 300             if minetest.is_protected(pos, placer:get_player_name()) then
N 301                 minetest.log("action", placer:get_player_name()
302                     .. " tried to place " .. itemstack:get_name()
303                     .. " at protected position "
304                     .. minetest.pos_to_string(pos))
305                 minetest.record_protection_violation(pos, placer:get_player_name())
306                 return itemstack
307             end
488f80 308
68f7d3 309             if pos == nil then return end
488f80 310
68f7d3 311             local node = minetest.get_node(pos)
N 312             if node.name ~= "air" then
8fba5e 313                 if is_supported_node(node.name) then
N 314                     obj = minetest.add_entity(pos, "technic:frame_entity")
488f80 315                     obj:get_luaentity():set_node({ name = itemstack:get_name() })
8fba5e 316                 end
68f7d3 317             else
488f80 318                 minetest.set_node(pos, { name = itemstack:get_name() })
52e701 319             end
488f80 320
b28001 321             if not infinite_stacks then
N 322                 itemstack:take_item()
323             end
52e701 324             return itemstack
N 325         end,
488f80 326
b28001 327         on_rightclick = function(pos, node, placer, itemstack, pointed_thing)
52e701 328             if is_supported_node(itemstack:get_name()) then
N 329                 if minetest.is_protected(pos, placer:get_player_name()) then
330                     minetest.log("action", placer:get_player_name()
331                         .. " tried to place " .. itemstack:get_name()
332                         .. " at protected position "
333                         .. minetest.pos_to_string(pos))
334                     minetest.record_protection_violation(pos, placer:get_player_name())
335                     return itemstack
336                 end
488f80 337
338                 minetest.set_node(pos, { name = itemstack:get_name() })
339
52e701 340                 local take_item = true
N 341                 local def = minetest.registered_items[itemstack:get_name()]
342                 -- Run callback
343                 if def.after_place_node then
344                     -- Copy place_to because callback can modify it
488f80 345                     local pos_copy = vector.new(pos)
52e701 346                     if def.after_place_node(pos_copy, placer, itemstack) then
N 347                         take_item = false
348                     end
349                 end
350
351                 -- Run script hook
352                 local _, callback
353                 for _, callback in ipairs(minetest.registered_on_placenodes) do
354                     -- Copy pos and node because callback can modify them
488f80 355                     local pos_copy = { x = pos.x, y = pos.y, z = pos.z }
356                     local newnode_copy = { name = def.name, param1 = 0, param2 = 0 }
357                     local oldnode_copy = { name = "air", param1 = 0, param2 = 0 }
52e701 358                     if callback(pos_copy, newnode_copy, placer, oldnode_copy, itemstack) then
N 359                         take_item = false
360                     end
361                 end
362
363                 if take_item then
364                     itemstack:take_item()
365                 end
488f80 366
52e701 367                 obj = minetest.add_entity(pos, "technic:frame_entity")
488f80 368                 obj:get_luaentity():set_node({ name = node.name })
369
52e701 370                 return itemstack
b28001 371             else
488f80 372                 --local pointed_thing = { type = "node", under = pos }
b28001 373                 if pointed_thing then
63efc3 374                     return minetest.item_place_node(itemstack, placer, pointed_thing)
b28001 375                 end
68f7d3 376             end
N 377         end,
478407 378     })
R 379
380 end
381 end
382 end
383 end
384 end
385 end
386
68f7d3 387 minetest.register_entity("technic:frame_entity", {
N 388     initial_properties = {
389         physical = true,
488f80 390         collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 },
68f7d3 391         visual = "wielditem",
N 392         textures = {},
488f80 393         visual_size = { x = 0.667, y = 0.667 },
68f7d3 394     },
c06cdf 395
68f7d3 396     node = {},
c06cdf 397
68f7d3 398     set_node = function(self, node)
N 399         self.node = node
488f80 400         local pos = vector.round(self.object:getpos())
8fba5e 401         frames_pos[pos_to_string(pos)] = node.name
488f80 402
68f7d3 403         local stack = ItemStack(node.name)
N 404         local itemtable = stack:to_table()
405         local itemname = nil
488f80 406
68f7d3 407         if itemtable then
N 408             itemname = stack:to_table().name
409         end
488f80 410
68f7d3 411         local item_texture = nil
N 412         local item_type = ""
413         if minetest.registered_items[itemname] then
414             item_texture = minetest.registered_items[itemname].inventory_image
415             item_type = minetest.registered_items[itemname].type
416         end
417         prop = {
418             is_visible = true,
488f80 419             textures = { node.name },
68f7d3 420         }
N 421         self.object:set_properties(prop)
422     end,
423
424     get_staticdata = function(self)
425         return self.node.name
426     end,
427
428     on_activate = function(self, staticdata)
488f80 429         self.object:set_armor_groups({ immortal = 1 })
430         self:set_node({ name = staticdata })
68f7d3 431     end,
488f80 432
68f7d3 433     dig = function(self)
d5df30 434         minetest.handle_node_drops(self.object:get_pos(), { ItemStack("technic:frame_111111") }, self.last_puncher)
DL 435         local pos = vector.round(self.object:get_pos())
8fba5e 436         frames_pos[pos_to_string(pos)] = nil
68f7d3 437         self.object:remove()
N 438     end,
488f80 439
68f7d3 440     on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
d5df30 441         local pos = self.object:get_pos()
68f7d3 442         if self.damage_object == nil then
N 443             self.damage_object = minetest.add_entity(pos, "technic:damage_entity")
444             self.damage_object:get_luaentity().remaining_time = 0.25
445             self.damage_object:get_luaentity().frame_object = self
446             self.damage_object:get_luaentity().texture_index = 0
447             self.damage_object:get_luaentity().texture_change_time = 0.15
448         else
449             self.damage_object:get_luaentity().remaining_time = 0.25
450         end
488f80 451
68f7d3 452         self.last_puncher = puncher
d5df30 453         local ppos = puncher:get_pos()
68f7d3 454         local pvect = puncher:get_look_dir()
488f80 455         local pface = get_face(pos, ppos, pvect)
68f7d3 456         if pface == nil then return end
N 457         local nodename = self.node.name
488f80 458         local newstate = tostring(1 - tonumber(string.sub(nodename, pface - 7, pface - 7)))
459
68f7d3 460         if pface <= 5 then
488f80 461             nodename = string.sub(nodename, 1, pface - 7 - 1)..newstate..string.sub(nodename, pface - 7 + 1)
68f7d3 462         else
N 463             nodename = string.sub(nodename, 1, -2)..newstate
464         end
488f80 465
68f7d3 466         self.node.name = nodename
N 467         self:set_node(self.node)
468     end,
488f80 469
68f7d3 470     on_rightclick = function(self, clicker)
d5df30 471         local pos = self.object:get_pos()
DL 472         local ppos = clicker:get_pos()
68f7d3 473         local pvect = clicker:get_look_dir()
N 474         local pface = get_face(pos, ppos, pvect)
488f80 475
476         if pface == nil then
477             return
478         end
479
480         local pos_under = vector.round(pos)
481         local pos_above = { x = pos_under.x, y = pos_under.y, z = pos_under.z }
482         local index = ({ "x", "y", "z" })[math.floor((pface + 1) / 2)]
483         pos_above[index] = pos_above[index] + 2 * ((pface + 1)%2) - 1
484         local pointed_thing = { type = "node", under = pos_under, above = pos_above }
68f7d3 485         local itemstack = clicker:get_wielded_item()
N 486         local itemdef = minetest.registered_items[itemstack:get_name()]
488f80 487
68f7d3 488         if itemdef ~= nil then
N 489             itemdef.on_place(itemstack, clicker, pointed_thing)
490         end
491     end,
492 })
493
494 local crack = "crack_anylength.png^[verticalframe:5:0"
495 minetest.register_entity("technic:damage_entity", {
496     initial_properties = {
497         visual = "cube",
488f80 498         visual_size = { x = 1.01, y = 1.01 },
499         textures = { crack, crack, crack, crack, crack, crack },
500         collisionbox = { 0, 0, 0, 0, 0, 0 },
68f7d3 501         physical = false,
N 502     },
503     on_step = function(self, dtime)
504         if self.remaining_time == nil then
505             self.object:remove()
506             self.frame_object.damage_object = nil
507         end
508         self.remaining_time = self.remaining_time - dtime
509         if self.remaining_time < 0 then
510             self.object:remove()
511             self.frame_object.damage_object = nil
512         end
513         self.texture_change_time = self.texture_change_time - dtime
514         if self.texture_change_time < 0 then
515             self.texture_change_time = self.texture_change_time + 0.15
516             self.texture_index = self.texture_index + 1
517             if self.texture_index == 5 then
518                 self.object:remove()
519                 self.frame_object.damage_object = nil
520                 self.frame_object:dig()
521             end
522             local ct = "crack_anylength.png^[verticalframe:5:"..self.texture_index
488f80 523             self.object:set_properties({ textures = { ct, ct, ct, ct, ct, ct } })
68f7d3 524         end
N 525     end,
526 })
c06cdf 527
8da4d0 528 mesecon.register_mvps_unmov("technic:frame_entity")
VE 529 mesecon.register_mvps_unmov("technic:damage_entity")
530 mesecon.register_on_mvps_move(function(moved_nodes)
52e701 531     local to_move = {}
N 532     for _, n in ipairs(moved_nodes) do
533         if frames_pos[pos_to_string(n.oldpos)] ~= nil then
488f80 534             to_move[#to_move + 1] = {
535                 pos = n.pos,
536                 oldpos = n.oldpos,
537                 name = frames_pos[pos_to_string(n.oldpos)]
538             }
52e701 539             frames_pos[pos_to_string(n.oldpos)] = nil
N 540         end
541     end
542     if #to_move > 0 then
543         for _, t in ipairs(to_move) do
544             frames_pos[pos_to_string(t.pos)] = t.name
545             local objects = minetest.get_objects_inside_radius(t.oldpos, 0.1)
546             for _, obj in ipairs(objects) do
547                 local entity = obj:get_luaentity()
488f80 548                 if entity and (entity.name == "technic:frame_entity" or
549                         entity.name == "technic:damage_entity") then
d5df30 550                     obj:set_pos(t.pos)
52e701 551                 end
N 552             end
553         end
554     end
555 end)
556
557 minetest.register_on_dignode(function(pos, node)
558     if frames_pos[pos_to_string(pos)] ~= nil then
488f80 559         minetest.set_node(pos, { name = frames_pos[pos_to_string(pos)] })
52e701 560         frames_pos[pos_to_string(pos)] = nil
N 561         local objects = minetest.get_objects_inside_radius(pos, 0.1)
562         for _, obj in ipairs(objects) do
563             local entity = obj:get_luaentity()
564             if entity and (entity.name == "technic:frame_entity" or entity.name == "technic:damage_entity") then
565                 obj:remove()
566             end
567         end
568     end
569 end)
570
c06cdf 571 -- Frame motor
488f80 572 local function connected(pos, c, adj)
573     for _, vect in ipairs(adj) do
574         local pos1 = vector.add(pos, vect)
575         local nodename = minetest.get_node(pos1).name
8fba5e 576         if frames_pos[pos_to_string(pos1)] then
N 577             nodename = frames_pos[pos_to_string(pos1)]
578         end
488f80 579         if not pos_in_list(c, pos1) and nodename ~= "air" and
580                 (minetest.registered_nodes[nodename].frames_can_connect == nil or
581                 minetest.registered_nodes[nodename].frames_can_connect(pos1, vect)) then
582             c[#c + 1] = pos1
583             if minetest.registered_nodes[nodename].frame == 1 then
584                 local adj = minetest.registered_nodes[nodename].frame_connect_all(nodename)
585                 connected(pos1, c, adj)
c06cdf 586             end
N 587         end
588     end
589 end
590
591 local function get_connected_nodes(pos)
488f80 592     c = { pos }
593     local nodename = minetest.get_node(pos).name
8fba5e 594     if frames_pos[pos_to_string(pos)] then
N 595         nodename = frames_pos[pos_to_string(pos)]
596     end
488f80 597     connected(pos, c, minetest.registered_nodes[nodename].frame_connect_all(nodename))
c06cdf 598     return c
N 599 end
600
601 local function frame_motor_on(pos, node)
488f80 602     local dirs = {
603         { x = 0, y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
604         { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
605         { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
606     }
607     local nnodepos = vector.add(pos, dirs[math.floor(node.param2 / 4) + 1])
48ea6f 608     local dir = minetest.facedir_to_dir(node.param2)
488f80 609     local nnode = minetest.get_node(nnodepos)
610
8fba5e 611     if frames_pos[pos_to_string(nnodepos)] then
N 612         nnode.name = frames_pos[pos_to_string(nnodepos)]
613     end
488f80 614
a579ee 615     local meta = minetest.get_meta(pos)
6a0807 616     if meta:get_int("last_moved") == minetest.get_gametime() then
N 617         return
618     end
488f80 619
a579ee 620     local owner = meta:get_string("owner")
488f80 621     if minetest.registered_nodes[nnode.name].frame == 1 then
622         local connected_nodes = get_connected_nodes(nnodepos)
623         move_nodes_vect(connected_nodes, dir, pos, owner)
478407 624     end
488f80 625
6a0807 626     minetest.get_meta(vector.add(pos, dir)):set_int("last_moved", minetest.get_gametime())
478407 627 end
R 628
488f80 629 minetest.register_node("technic:frame_motor", {
354ee6 630     description = S("Frame Motor"),
488f80 631     tiles = {
632         "pipeworks_filter_top.png^[transformR90", "technic_lv_cable.png", "technic_lv_cable.png",
633         "technic_lv_cable.png", "technic_lv_cable.png", "technic_lv_cable.png"
634     },
635     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, mesecon = 2 },
478407 636     paramtype2 = "facedir",
488f80 637     mesecons = { effector = { action_on = frame_motor_on } },
638
a579ee 639     after_place_node = function(pos, placer, itemstack)
N 640         local meta = minetest.get_meta(pos)
641         meta:set_string("owner", placer:get_player_name())
642     end,
488f80 643
644     frames_can_connect = function(pos, dir)
48ea6f 645         local node = minetest.get_node(pos)
488f80 646         local dir2 = ({
647             { x= 0 , y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
648             { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
649             { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
650         })[math.floor(node.param2 / 4) + 1]
651         return dir2.x ~= -dir.x or dir2.y ~= -dir.y or dir2.z ~= -dir.z
478407 652     end
R 653 })
654
655
c06cdf 656
N 657 -- Templates
488f80 658 local function template_connected(pos, c, connectors)
659     local vects = {
660         { x = 0, y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
661         { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
662         { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
663     }
664     for _, vect in ipairs(vects) do
665         local pos1 = vector.add(pos, vect)
666         local nodename = minetest.get_node(pos1).name
667         if not pos_in_list(c, pos1) and (nodename == "technic:template" or
668                 nodename == "technic:template_connector") then
a73d56 669             local meta = minetest.get_meta(pos1)
N 670             if meta:get_string("connected") == "" then
488f80 671                 c[#c + 1] = pos1
672                 template_connected(pos1, c, connectors)
808d38 673                 if nodename == "technic:template_connector" then
488f80 674                     connectors[#connectors + 1] = pos1
808d38 675                 end
a73d56 676             end
a579ee 677         end
478407 678     end
R 679 end
680
c06cdf 681 local function get_templates(pos)
488f80 682     local c = { pos }
808d38 683     local connectors
N 684     if minetest.get_node(pos).name == "technic:template_connector" then
488f80 685         connectors = { pos }
808d38 686     else
N 687         connectors = {}
688     end
488f80 689     template_connected(pos, c, connectors)
808d38 690     return c, connectors
N 691 end
692
693 local function swap_template(pos, new)
694     local meta = minetest.get_meta(pos)
695     local saved_node = meta:get_string("saved_node")
696     meta:set_string("saved_node", "")
f3d8b4 697     technic.swap_node(pos, new)
808d38 698     local meta = minetest.get_meta(pos)
N 699     meta:set_string("saved_node", saved_node)
478407 700 end
R 701
c06cdf 702 local function save_node(pos)
N 703     local node = minetest.get_node(pos)
808d38 704     if node.name == "air" then
488f80 705         minetest.set_node(pos, { name = "technic:template" })
808d38 706         return
N 707     end
708     if node.name == "technic:template" then
709         swap_template(pos, "technic:template_connector")
710         local meta = minetest.get_meta(pos)
711         meta:set_string("connected", "")
712         return
713     end
488f80 714
c06cdf 715     local meta = minetest.get_meta(pos)
N 716     local meta0 = meta:to_table()
717     for _, list in pairs(meta0.inventory) do
718         for key, stack in pairs(list) do
719             list[key] = stack:to_string()
720         end
478407 721     end
488f80 722
c06cdf 723     node.meta = meta0
488f80 724     minetest.set_node(pos, { name = "technic:template" })
c06cdf 725     return node
478407 726 end
R 727
c06cdf 728 local function restore_node(pos, node)
N 729     minetest.set_node(pos, node)
730     local meta = minetest.get_meta(pos)
731     for _, list in pairs(node.meta.inventory) do
732         for key, stack in pairs(list) do
733             list[key] = ItemStack(stack)
734         end
735     end
736     meta:from_table(node.meta)
737 end
738
739 local function expand_template(pos)
740     local meta = minetest.get_meta(pos)
741     local c = meta:get_string("connected")
488f80 742
c06cdf 743     if c == "" then return end
N 744     c = minetest.deserialize(c)
488f80 745
c06cdf 746     for _, vect in ipairs(c) do
808d38 747         local pos1 = vector.add(pos, vect)
N 748         local saved_node = save_node(pos1)
749         local meta1 = minetest.get_meta(pos1)
750         if saved_node ~= nil then
751             meta1:set_string("saved_node", minetest.serialize(saved_node))
752         end
753     end
754 end
755
756 local function compress_templates(pos)
757     local templates, connectors = get_templates(pos)
488f80 758
808d38 759     if #connectors == 0 then
488f80 760         connectors = { pos }
808d38 761     end
488f80 762
808d38 763     for _, cn in ipairs(connectors) do
N 764         local meta = minetest.get_meta(cn)
765         local c = {}
488f80 766         for _, p in ipairs(templates) do
808d38 767             local np = vector.subtract(p, cn)
488f80 768             if not pos_in_list(c, np) then
769                 c[#c + 1] = np
478407 770             end
808d38 771         end
N 772         local cc = {}
488f80 773         for _, p in ipairs(connectors) do
808d38 774             local np = vector.subtract(p, cn)
488f80 775             if np.x ~= 0 or np.y ~= 0 or np.z ~= 0 then
808d38 776                 cc[pos_to_string(np)] = true
N 777             end
778         end
779         swap_template(cn, "technic:template")
780         meta:set_string("connected", minetest.serialize(c))
781         meta:set_string("connectors_connected", minetest.serialize(cc))
782     end
488f80 783
784     for _, p in ipairs(templates) do
808d38 785         if not pos_in_list(connectors, p) then
488f80 786             minetest.set_node(p, { name = "air" })
478407 787         end
R 788     end
789 end
790
48d571 791 local function template_drops(pos, node, oldmeta, digger)
N 792     local c = oldmeta.fields.connected
808d38 793     local cc = oldmeta.fields.connectors_connected
48d571 794     local drops
488f80 795
48d571 796     if c == "" or c == nil then
488f80 797         drops = { "technic:template 1" }
48d571 798     else
808d38 799         if cc == "" or cc == nil then
488f80 800             drops = { "technic:template 1" }
808d38 801         else
N 802             local dcc = minetest.deserialize(cc)
803             if not table_empty(dcc) then
804                 drops = {}
805                 for sp, _ in pairs(dcc) do
806                     local ssp = pos_from_string(sp)
807                     local p = vector.add(ssp, pos)
808                     local meta = minetest.get_meta(p)
809                     local d = minetest.deserialize(meta:get_string("connectors_connected"))
810                     if d ~= nil then
488f80 811                         d[pos_to_string({ x = -ssp.x, y = -ssp.y, z = -ssp.z })] = nil
808d38 812                         meta:set_string("connectors_connected", minetest.serialize(d))
N 813                     end
814                 end
815             else
816                 local stack_max = 99
488f80 817                 local num = #minetest.deserialize(c)
808d38 818                 drops = {}
N 819                 while num > stack_max do
488f80 820                     drops[#drops + 1] = "technic:template "..stack_max
808d38 821                     num = num - stack_max
N 822                 end
488f80 823                 drops[#drops + 1] = "technic:template "..num
808d38 824             end
48d571 825         end
N 826     end
488f80 827
48d571 828     minetest.handle_node_drops(pos, drops, digger)
808d38 829 end
N 830
831 local function template_on_destruct(pos, node)
832     local meta = minetest.get_meta(pos)
833     local saved_node = meta:get_string("saved_node")
834     if saved_node ~= "" then
835         local nnode = minetest.deserialize(saved_node)
836         minetest.after(0, restore_node, pos, nnode)
837     end
48d571 838 end
N 839
488f80 840 minetest.register_node("technic:template", {
354ee6 841     description = S("Template"),
488f80 842     tiles = { "technic_mv_cable.png" },
48d571 843     drop = "",
488f80 844     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2 },
808d38 845     on_destruct = template_on_destruct,
48d571 846     after_dig_node = template_drops,
488f80 847     on_punch = function(pos, node, puncher)
808d38 848         swap_template(pos, "technic:template_disabled")
c06cdf 849     end
N 850 })
851
488f80 852 minetest.register_node("technic:template_disabled", {
354ee6 853     description = S("Template"),
488f80 854     tiles = { "technic_hv_cable.png" },
48d571 855     drop = "",
488f80 856     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, not_in_creative_inventory = 1 },
808d38 857     on_destruct = template_on_destruct,
48d571 858     after_dig_node = template_drops,
488f80 859     on_punch = function(pos, node, puncher)
808d38 860     local meta = minetest.get_meta(pos)
N 861         swap_template(pos, "technic:template_connector")
862     end
863 })
864
488f80 865 minetest.register_node("technic:template_connector", {
354ee6 866     description = S("Template"),
488f80 867     tiles = { "technic_lv_cable.png" },
808d38 868     drop = "",
488f80 869     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, not_in_creative_inventory = 1 },
808d38 870     on_destruct = template_on_destruct,
N 871     after_dig_node = template_drops,
488f80 872     on_punch = function(pos, node, puncher)
808d38 873         swap_template(pos, "technic:template")
c06cdf 874     end
N 875 })
876
488f80 877 minetest.register_craftitem("technic:template_replacer", {
354ee6 878     description = S("Template (replacing)"),
c06cdf 879     inventory_image = "technic_template_replacer.png",
N 880     on_place = function(itemstack, placer, pointed_thing)
881         local p = pointed_thing.under
882         if minetest.is_protected and minetest.is_protected(p, placer:get_player_name()) then
883             return nil
884         end
885         local node = minetest.get_node(p)
886         if node.name == "technic:template" then return end
887         local saved_node = save_node(p)
888         itemstack:take_item()
889         if saved_node ~= nil then
890             local meta = minetest.get_meta(p)
891             meta:set_string("saved_node", minetest.serialize(saved_node))
892         end
893         return itemstack
894     end
895 })
896
488f80 897 minetest.register_tool("technic:template_tool", {
39c41a 898     description = S("Template Tool"),
c06cdf 899     inventory_image = "technic_template_tool.png",
N 900     on_use = function(itemstack, puncher, pointed_thing)
901         local pos = pointed_thing.under
488f80 902         if pos == nil or minetest.is_protected and minetest.is_protected(pos, puncher:get_player_name()) then
c06cdf 903             return nil
N 904         end
905         local node = minetest.get_node(pos)
808d38 906         if node.name ~= "technic:template" and node.name ~= "technic:template_connector" then return end
c06cdf 907         local meta = minetest.get_meta(pos)
N 908         local c2 = meta:get_string("connected")
909         if c2 ~= "" then
910             expand_template(pos)
808d38 911         else
N 912             compress_templates(pos)
c06cdf 913         end
488f80 914
c06cdf 915     end
N 916 })
917
918
919
920 -- Template motor
921 local function get_template_nodes(pos)
922     local meta = minetest.get_meta(pos)
923     local connected = meta:get_string("connected")
924     if connected == "" then return {} end
925     local adj = minetest.deserialize(connected)
926     local c = {}
488f80 927     for _, vect in ipairs(adj) do
928         local pos1 = vector.add(pos, vect)
929         local nodename = minetest.get_node(pos1).name
930         if not(pos_in_list(c, pos1)) and nodename ~= "air" then
931             c[#c + 1] = pos1
c06cdf 932         end
N 933     end
934     return c
935 end
936
937 local function template_motor_on(pos, node)
488f80 938     local dirs = {
939         { x = 0, y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
940         { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
941         { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
942     }
943     local nnodepos = vector.add(pos, dirs[math.floor(node.param2 / 4) + 1])
c06cdf 944     local dir = minetest.facedir_to_dir(node.param2)
488f80 945     local nnode = minetest.get_node(nnodepos)
3cf0d3 946     local meta = minetest.get_meta(pos)
6a0807 947     if meta:get_int("last_moved") == minetest.get_gametime() then
N 948         return
949     end
3cf0d3 950     local owner = meta:get_string("owner")
c06cdf 951     if nnode.name == "technic:template" then
488f80 952         local connected_nodes = get_template_nodes(nnodepos)
953         move_nodes_vect(connected_nodes, dir, pos, owner)
c06cdf 954     end
6a0807 955     minetest.get_meta(vector.add(pos, dir)):set_int("last_moved", minetest.get_gametime())
c06cdf 956 end
N 957
488f80 958 minetest.register_node("technic:template_motor", {
39c41a 959     description = S("Template Motor"),
488f80 960     tiles = {
961         "pipeworks_filter_top.png^[transformR90",
962         "technic_lv_cable.png",
963         "technic_lv_cable.png",
964         "technic_lv_cable.png",
965         "technic_lv_cable.png",
966         "technic_lv_cable.png"
967     },
968     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, mesecon = 2 },
c06cdf 969     paramtype2 = "facedir",
488f80 970     mesecons = { effector = { action_on = template_motor_on } },
3cf0d3 971     after_place_node = function(pos, placer, itemstack)
N 972         local meta = minetest.get_meta(pos)
973         meta:set_string("owner", placer:get_player_name())
974     end,
c06cdf 975 })
768794 976
N 977 -- Crafts
978 minetest.register_craft({
979     output = 'technic:frame_111111',
980     recipe = {
488f80 981         { '',              'default:stick',       '' },
44cb8d 982         { 'default:stick', 'basic_materials:brass_ingot', 'default:stick' },
488f80 983         { '',              'default:stick',       '' },
768794 984     }
N 985 })
986
987 minetest.register_craft({
988     output = 'technic:frame_motor',
989     recipe = {
488f80 990         { '',                                  'technic:frame_111111', '' },
44cb8d 991         { 'group:mesecon_conductor_craftable', 'basic_materials:motor',        'group:mesecon_conductor_craftable' },
488f80 992         { '',                                  'technic:frame_111111', '' },
768794 993     }
N 994 })
995
996 minetest.register_craft({
997     output = 'technic:template 10',
998     recipe = {
44cb8d 999         { '',                    'basic_materials:brass_ingot',  '' },
VD 1000         { 'basic_materials:brass_ingot', 'default:mese_crystal', 'basic_materials:brass_ingot' },
1001         { '',                    'basic_materials:brass_ingot',  '' },
768794 1002     }
N 1003 })
1004
1005 minetest.register_craft({
1006     output = 'technic:template_replacer',
488f80 1007     recipe = { { 'technic:template' } }
768794 1008 })
N 1009
1010 minetest.register_craft({
1011     output = 'technic:template',
488f80 1012     recipe = { { 'technic:template_replacer' } }
768794 1013 })
N 1014
1015 minetest.register_craft({
1016     output = 'technic:template_motor',
1017     recipe = {
488f80 1018         { '',                                  'technic:template', '' },
44cb8d 1019         { 'group:mesecon_conductor_craftable', 'basic_materials:motor',    'group:mesecon_conductor_craftable' },
488f80 1020         { '',                                  'technic:template', '' },
768794 1021     }
N 1022 })
1023
1024 minetest.register_craft({
1025     output = 'technic:template_tool',
1026     recipe = {
488f80 1027         { '',                     'technic:template', '' },
1028         { 'default:mese_crystal', 'default:stick',    'default:mese_crystal' },
1029         { '',                     'default:stick',    '' },
768794 1030     }
N 1031 })