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