cx384
2024-01-22 a08ba2bb93d7683b619a0e6b0bf00e3afd614ae4
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
0f6bdb 91 local function table_empty(what)
S 92     for _ in pairs(what) do
808d38 93         return false
N 94     end
95     return true
c06cdf 96 end
478407 97
0f6bdb 98 local function add_table(what, toadd)
e1c995 99     local i = 1
c06cdf 100     while true do
0f6bdb 101         local o = what[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
0f6bdb 106     what[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
a8daa4 401         local prop = {
68f7d3 402             is_visible = true,
488f80 403             textures = { node.name },
68f7d3 404         }
N 405         self.object:set_properties(prop)
406     end,
407
408     get_staticdata = function(self)
409         return self.node.name
410     end,
411
412     on_activate = function(self, staticdata)
488f80 413         self.object:set_armor_groups({ immortal = 1 })
414         self:set_node({ name = staticdata })
68f7d3 415     end,
488f80 416
68f7d3 417     dig = function(self)
d5df30 418         minetest.handle_node_drops(self.object:get_pos(), { ItemStack("technic:frame_111111") }, self.last_puncher)
DL 419         local pos = vector.round(self.object:get_pos())
8fba5e 420         frames_pos[pos_to_string(pos)] = nil
68f7d3 421         self.object:remove()
N 422     end,
488f80 423
68f7d3 424     on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
d5df30 425         local pos = self.object:get_pos()
68f7d3 426         if self.damage_object == nil then
N 427             self.damage_object = minetest.add_entity(pos, "technic:damage_entity")
428             self.damage_object:get_luaentity().remaining_time = 0.25
429             self.damage_object:get_luaentity().frame_object = self
430             self.damage_object:get_luaentity().texture_index = 0
431             self.damage_object:get_luaentity().texture_change_time = 0.15
432         else
433             self.damage_object:get_luaentity().remaining_time = 0.25
434         end
488f80 435
68f7d3 436         self.last_puncher = puncher
d5df30 437         local ppos = puncher:get_pos()
68f7d3 438         local pvect = puncher:get_look_dir()
488f80 439         local pface = get_face(pos, ppos, pvect)
68f7d3 440         if pface == nil then return end
N 441         local nodename = self.node.name
488f80 442         local newstate = tostring(1 - tonumber(string.sub(nodename, pface - 7, pface - 7)))
443
68f7d3 444         if pface <= 5 then
488f80 445             nodename = string.sub(nodename, 1, pface - 7 - 1)..newstate..string.sub(nodename, pface - 7 + 1)
68f7d3 446         else
N 447             nodename = string.sub(nodename, 1, -2)..newstate
448         end
488f80 449
68f7d3 450         self.node.name = nodename
N 451         self:set_node(self.node)
452     end,
488f80 453
68f7d3 454     on_rightclick = function(self, clicker)
d5df30 455         local pos = self.object:get_pos()
DL 456         local ppos = clicker:get_pos()
68f7d3 457         local pvect = clicker:get_look_dir()
N 458         local pface = get_face(pos, ppos, pvect)
488f80 459
460         if pface == nil then
461             return
462         end
463
464         local pos_under = vector.round(pos)
465         local pos_above = { x = pos_under.x, y = pos_under.y, z = pos_under.z }
466         local index = ({ "x", "y", "z" })[math.floor((pface + 1) / 2)]
467         pos_above[index] = pos_above[index] + 2 * ((pface + 1)%2) - 1
468         local pointed_thing = { type = "node", under = pos_under, above = pos_above }
68f7d3 469         local itemstack = clicker:get_wielded_item()
N 470         local itemdef = minetest.registered_items[itemstack:get_name()]
488f80 471
68f7d3 472         if itemdef ~= nil then
N 473             itemdef.on_place(itemstack, clicker, pointed_thing)
474         end
475     end,
476 })
477
478 local crack = "crack_anylength.png^[verticalframe:5:0"
479 minetest.register_entity("technic:damage_entity", {
480     initial_properties = {
481         visual = "cube",
488f80 482         visual_size = { x = 1.01, y = 1.01 },
483         textures = { crack, crack, crack, crack, crack, crack },
484         collisionbox = { 0, 0, 0, 0, 0, 0 },
68f7d3 485         physical = false,
N 486     },
487     on_step = function(self, dtime)
488         if self.remaining_time == nil then
489             self.object:remove()
490             self.frame_object.damage_object = nil
491         end
492         self.remaining_time = self.remaining_time - dtime
493         if self.remaining_time < 0 then
494             self.object:remove()
495             self.frame_object.damage_object = nil
496         end
497         self.texture_change_time = self.texture_change_time - dtime
498         if self.texture_change_time < 0 then
499             self.texture_change_time = self.texture_change_time + 0.15
500             self.texture_index = self.texture_index + 1
501             if self.texture_index == 5 then
502                 self.object:remove()
503                 self.frame_object.damage_object = nil
504                 self.frame_object:dig()
505             end
506             local ct = "crack_anylength.png^[verticalframe:5:"..self.texture_index
488f80 507             self.object:set_properties({ textures = { ct, ct, ct, ct, ct, ct } })
68f7d3 508         end
N 509     end,
510 })
c06cdf 511
8da4d0 512 mesecon.register_mvps_unmov("technic:frame_entity")
VE 513 mesecon.register_mvps_unmov("technic:damage_entity")
514 mesecon.register_on_mvps_move(function(moved_nodes)
52e701 515     local to_move = {}
N 516     for _, n in ipairs(moved_nodes) do
517         if frames_pos[pos_to_string(n.oldpos)] ~= nil then
488f80 518             to_move[#to_move + 1] = {
519                 pos = n.pos,
520                 oldpos = n.oldpos,
521                 name = frames_pos[pos_to_string(n.oldpos)]
522             }
52e701 523             frames_pos[pos_to_string(n.oldpos)] = nil
N 524         end
525     end
526     if #to_move > 0 then
527         for _, t in ipairs(to_move) do
528             frames_pos[pos_to_string(t.pos)] = t.name
529             local objects = minetest.get_objects_inside_radius(t.oldpos, 0.1)
530             for _, obj in ipairs(objects) do
531                 local entity = obj:get_luaentity()
488f80 532                 if entity and (entity.name == "technic:frame_entity" or
533                         entity.name == "technic:damage_entity") then
d5df30 534                     obj:set_pos(t.pos)
52e701 535                 end
N 536             end
537         end
538     end
539 end)
540
541 minetest.register_on_dignode(function(pos, node)
542     if frames_pos[pos_to_string(pos)] ~= nil then
488f80 543         minetest.set_node(pos, { name = frames_pos[pos_to_string(pos)] })
52e701 544         frames_pos[pos_to_string(pos)] = nil
N 545         local objects = minetest.get_objects_inside_radius(pos, 0.1)
546         for _, obj in ipairs(objects) do
547             local entity = obj:get_luaentity()
548             if entity and (entity.name == "technic:frame_entity" or entity.name == "technic:damage_entity") then
549                 obj:remove()
550             end
551         end
552     end
553 end)
554
c06cdf 555 -- Frame motor
488f80 556 local function connected(pos, c, adj)
557     for _, vect in ipairs(adj) do
558         local pos1 = vector.add(pos, vect)
559         local nodename = minetest.get_node(pos1).name
8fba5e 560         if frames_pos[pos_to_string(pos1)] then
N 561             nodename = frames_pos[pos_to_string(pos1)]
562         end
488f80 563         if not pos_in_list(c, pos1) and nodename ~= "air" and
564                 (minetest.registered_nodes[nodename].frames_can_connect == nil or
565                 minetest.registered_nodes[nodename].frames_can_connect(pos1, vect)) then
566             c[#c + 1] = pos1
567             if minetest.registered_nodes[nodename].frame == 1 then
568                 local adj = minetest.registered_nodes[nodename].frame_connect_all(nodename)
569                 connected(pos1, c, adj)
c06cdf 570             end
N 571         end
572     end
573 end
574
575 local function get_connected_nodes(pos)
a8daa4 576     local c = { pos }
488f80 577     local nodename = minetest.get_node(pos).name
8fba5e 578     if frames_pos[pos_to_string(pos)] then
N 579         nodename = frames_pos[pos_to_string(pos)]
580     end
488f80 581     connected(pos, c, minetest.registered_nodes[nodename].frame_connect_all(nodename))
c06cdf 582     return c
N 583 end
584
585 local function frame_motor_on(pos, node)
488f80 586     local dirs = {
587         { x = 0, y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
588         { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
589         { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
590     }
591     local nnodepos = vector.add(pos, dirs[math.floor(node.param2 / 4) + 1])
48ea6f 592     local dir = minetest.facedir_to_dir(node.param2)
488f80 593     local nnode = minetest.get_node(nnodepos)
594
8fba5e 595     if frames_pos[pos_to_string(nnodepos)] then
N 596         nnode.name = frames_pos[pos_to_string(nnodepos)]
597     end
488f80 598
a579ee 599     local meta = minetest.get_meta(pos)
6a0807 600     if meta:get_int("last_moved") == minetest.get_gametime() then
N 601         return
602     end
488f80 603
a579ee 604     local owner = meta:get_string("owner")
488f80 605     if minetest.registered_nodes[nnode.name].frame == 1 then
606         local connected_nodes = get_connected_nodes(nnodepos)
607         move_nodes_vect(connected_nodes, dir, pos, owner)
478407 608     end
488f80 609
6a0807 610     minetest.get_meta(vector.add(pos, dir)):set_int("last_moved", minetest.get_gametime())
478407 611 end
R 612
488f80 613 minetest.register_node("technic:frame_motor", {
354ee6 614     description = S("Frame Motor"),
488f80 615     tiles = {
616         "pipeworks_filter_top.png^[transformR90", "technic_lv_cable.png", "technic_lv_cable.png",
617         "technic_lv_cable.png", "technic_lv_cable.png", "technic_lv_cable.png"
618     },
619     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, mesecon = 2 },
478407 620     paramtype2 = "facedir",
488f80 621     mesecons = { effector = { action_on = frame_motor_on } },
622
a579ee 623     after_place_node = function(pos, placer, itemstack)
N 624         local meta = minetest.get_meta(pos)
625         meta:set_string("owner", placer:get_player_name())
626     end,
488f80 627
628     frames_can_connect = function(pos, dir)
48ea6f 629         local node = minetest.get_node(pos)
488f80 630         local dir2 = ({
631             { x= 0 , y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
632             { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
633             { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
634         })[math.floor(node.param2 / 4) + 1]
635         return dir2.x ~= -dir.x or dir2.y ~= -dir.y or dir2.z ~= -dir.z
478407 636     end
R 637 })
638
639
c06cdf 640
N 641 -- Templates
488f80 642 local function template_connected(pos, c, connectors)
643     local vects = {
644         { x = 0, y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
645         { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
646         { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
647     }
648     for _, vect in ipairs(vects) do
649         local pos1 = vector.add(pos, vect)
650         local nodename = minetest.get_node(pos1).name
651         if not pos_in_list(c, pos1) and (nodename == "technic:template" or
652                 nodename == "technic:template_connector") then
a73d56 653             local meta = minetest.get_meta(pos1)
N 654             if meta:get_string("connected") == "" then
488f80 655                 c[#c + 1] = pos1
656                 template_connected(pos1, c, connectors)
808d38 657                 if nodename == "technic:template_connector" then
488f80 658                     connectors[#connectors + 1] = pos1
808d38 659                 end
a73d56 660             end
a579ee 661         end
478407 662     end
R 663 end
664
c06cdf 665 local function get_templates(pos)
488f80 666     local c = { pos }
808d38 667     local connectors
N 668     if minetest.get_node(pos).name == "technic:template_connector" then
488f80 669         connectors = { pos }
808d38 670     else
N 671         connectors = {}
672     end
488f80 673     template_connected(pos, c, connectors)
808d38 674     return c, connectors
N 675 end
676
677 local function swap_template(pos, new)
678     local meta = minetest.get_meta(pos)
679     local saved_node = meta:get_string("saved_node")
680     meta:set_string("saved_node", "")
f3d8b4 681     technic.swap_node(pos, new)
a8daa4 682     meta = minetest.get_meta(pos)
808d38 683     meta:set_string("saved_node", saved_node)
478407 684 end
R 685
c06cdf 686 local function save_node(pos)
N 687     local node = minetest.get_node(pos)
808d38 688     if node.name == "air" then
488f80 689         minetest.set_node(pos, { name = "technic:template" })
808d38 690         return
N 691     end
692     if node.name == "technic:template" then
693         swap_template(pos, "technic:template_connector")
694         local meta = minetest.get_meta(pos)
695         meta:set_string("connected", "")
696         return
697     end
488f80 698
c06cdf 699     local meta = minetest.get_meta(pos)
N 700     local meta0 = meta:to_table()
701     for _, list in pairs(meta0.inventory) do
702         for key, stack in pairs(list) do
703             list[key] = stack:to_string()
704         end
478407 705     end
488f80 706
c06cdf 707     node.meta = meta0
488f80 708     minetest.set_node(pos, { name = "technic:template" })
c06cdf 709     return node
478407 710 end
R 711
c06cdf 712 local function restore_node(pos, node)
N 713     minetest.set_node(pos, node)
714     local meta = minetest.get_meta(pos)
715     for _, list in pairs(node.meta.inventory) do
716         for key, stack in pairs(list) do
717             list[key] = ItemStack(stack)
718         end
719     end
720     meta:from_table(node.meta)
721 end
722
723 local function expand_template(pos)
724     local meta = minetest.get_meta(pos)
725     local c = meta:get_string("connected")
488f80 726
c06cdf 727     if c == "" then return end
N 728     c = minetest.deserialize(c)
488f80 729
c06cdf 730     for _, vect in ipairs(c) do
808d38 731         local pos1 = vector.add(pos, vect)
N 732         local saved_node = save_node(pos1)
733         local meta1 = minetest.get_meta(pos1)
734         if saved_node ~= nil then
735             meta1:set_string("saved_node", minetest.serialize(saved_node))
736         end
737     end
738 end
739
740 local function compress_templates(pos)
741     local templates, connectors = get_templates(pos)
488f80 742
808d38 743     if #connectors == 0 then
488f80 744         connectors = { pos }
808d38 745     end
488f80 746
808d38 747     for _, cn in ipairs(connectors) do
N 748         local meta = minetest.get_meta(cn)
749         local c = {}
488f80 750         for _, p in ipairs(templates) do
808d38 751             local np = vector.subtract(p, cn)
488f80 752             if not pos_in_list(c, np) then
753                 c[#c + 1] = np
478407 754             end
808d38 755         end
N 756         local cc = {}
488f80 757         for _, p in ipairs(connectors) do
808d38 758             local np = vector.subtract(p, cn)
488f80 759             if np.x ~= 0 or np.y ~= 0 or np.z ~= 0 then
808d38 760                 cc[pos_to_string(np)] = true
N 761             end
762         end
763         swap_template(cn, "technic:template")
764         meta:set_string("connected", minetest.serialize(c))
765         meta:set_string("connectors_connected", minetest.serialize(cc))
766     end
488f80 767
768     for _, p in ipairs(templates) do
808d38 769         if not pos_in_list(connectors, p) then
488f80 770             minetest.set_node(p, { name = "air" })
478407 771         end
R 772     end
773 end
774
48d571 775 local function template_drops(pos, node, oldmeta, digger)
N 776     local c = oldmeta.fields.connected
808d38 777     local cc = oldmeta.fields.connectors_connected
48d571 778     local drops
488f80 779
48d571 780     if c == "" or c == nil then
488f80 781         drops = { "technic:template 1" }
48d571 782     else
808d38 783         if cc == "" or cc == nil then
488f80 784             drops = { "technic:template 1" }
808d38 785         else
N 786             local dcc = minetest.deserialize(cc)
787             if not table_empty(dcc) then
788                 drops = {}
789                 for sp, _ in pairs(dcc) do
790                     local ssp = pos_from_string(sp)
791                     local p = vector.add(ssp, pos)
792                     local meta = minetest.get_meta(p)
793                     local d = minetest.deserialize(meta:get_string("connectors_connected"))
794                     if d ~= nil then
488f80 795                         d[pos_to_string({ x = -ssp.x, y = -ssp.y, z = -ssp.z })] = nil
808d38 796                         meta:set_string("connectors_connected", minetest.serialize(d))
N 797                     end
798                 end
799             else
800                 local stack_max = 99
488f80 801                 local num = #minetest.deserialize(c)
808d38 802                 drops = {}
N 803                 while num > stack_max do
488f80 804                     drops[#drops + 1] = "technic:template "..stack_max
808d38 805                     num = num - stack_max
N 806                 end
488f80 807                 drops[#drops + 1] = "technic:template "..num
808d38 808             end
48d571 809         end
N 810     end
488f80 811
48d571 812     minetest.handle_node_drops(pos, drops, digger)
808d38 813 end
N 814
815 local function template_on_destruct(pos, node)
816     local meta = minetest.get_meta(pos)
817     local saved_node = meta:get_string("saved_node")
818     if saved_node ~= "" then
819         local nnode = minetest.deserialize(saved_node)
820         minetest.after(0, restore_node, pos, nnode)
821     end
48d571 822 end
N 823
488f80 824 minetest.register_node("technic:template", {
354ee6 825     description = S("Template"),
488f80 826     tiles = { "technic_mv_cable.png" },
48d571 827     drop = "",
488f80 828     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2 },
808d38 829     on_destruct = template_on_destruct,
48d571 830     after_dig_node = template_drops,
488f80 831     on_punch = function(pos, node, puncher)
808d38 832         swap_template(pos, "technic:template_disabled")
c06cdf 833     end
N 834 })
835
488f80 836 minetest.register_node("technic:template_disabled", {
354ee6 837     description = S("Template"),
488f80 838     tiles = { "technic_hv_cable.png" },
48d571 839     drop = "",
488f80 840     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, not_in_creative_inventory = 1 },
808d38 841     on_destruct = template_on_destruct,
48d571 842     after_dig_node = template_drops,
488f80 843     on_punch = function(pos, node, puncher)
a8daa4 844     local _ = minetest.get_meta(pos)
808d38 845         swap_template(pos, "technic:template_connector")
N 846     end
847 })
848
488f80 849 minetest.register_node("technic:template_connector", {
354ee6 850     description = S("Template"),
488f80 851     tiles = { "technic_lv_cable.png" },
808d38 852     drop = "",
488f80 853     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, not_in_creative_inventory = 1 },
808d38 854     on_destruct = template_on_destruct,
N 855     after_dig_node = template_drops,
488f80 856     on_punch = function(pos, node, puncher)
808d38 857         swap_template(pos, "technic:template")
c06cdf 858     end
N 859 })
860
488f80 861 minetest.register_craftitem("technic:template_replacer", {
354ee6 862     description = S("Template (replacing)"),
c06cdf 863     inventory_image = "technic_template_replacer.png",
N 864     on_place = function(itemstack, placer, pointed_thing)
865         local p = pointed_thing.under
866         if minetest.is_protected and minetest.is_protected(p, placer:get_player_name()) then
867             return nil
868         end
869         local node = minetest.get_node(p)
870         if node.name == "technic:template" then return end
871         local saved_node = save_node(p)
872         itemstack:take_item()
873         if saved_node ~= nil then
874             local meta = minetest.get_meta(p)
875             meta:set_string("saved_node", minetest.serialize(saved_node))
876         end
877         return itemstack
878     end
879 })
880
488f80 881 minetest.register_tool("technic:template_tool", {
39c41a 882     description = S("Template Tool"),
c06cdf 883     inventory_image = "technic_template_tool.png",
N 884     on_use = function(itemstack, puncher, pointed_thing)
885         local pos = pointed_thing.under
488f80 886         if pos == nil or minetest.is_protected and minetest.is_protected(pos, puncher:get_player_name()) then
c06cdf 887             return nil
N 888         end
889         local node = minetest.get_node(pos)
808d38 890         if node.name ~= "technic:template" and node.name ~= "technic:template_connector" then return end
c06cdf 891         local meta = minetest.get_meta(pos)
N 892         local c2 = meta:get_string("connected")
893         if c2 ~= "" then
894             expand_template(pos)
808d38 895         else
N 896             compress_templates(pos)
c06cdf 897         end
488f80 898
c06cdf 899     end
N 900 })
901
902
903
904 -- Template motor
905 local function get_template_nodes(pos)
906     local meta = minetest.get_meta(pos)
907     local connected = meta:get_string("connected")
908     if connected == "" then return {} end
909     local adj = minetest.deserialize(connected)
910     local c = {}
488f80 911     for _, vect in ipairs(adj) do
912         local pos1 = vector.add(pos, vect)
913         local nodename = minetest.get_node(pos1).name
914         if not(pos_in_list(c, pos1)) and nodename ~= "air" then
915             c[#c + 1] = pos1
c06cdf 916         end
N 917     end
918     return c
919 end
920
921 local function template_motor_on(pos, node)
488f80 922     local dirs = {
923         { x = 0, y = 1, z = 0 }, { x = 0, y = 0, z = 1 },
924         { x = 0, y = 0, z = -1 }, { x = 1, y = 0, z = 0 },
925         { x = -1, y = 0, z = 0 }, { x = 0, y = -1, z = 0 }
926     }
927     local nnodepos = vector.add(pos, dirs[math.floor(node.param2 / 4) + 1])
c06cdf 928     local dir = minetest.facedir_to_dir(node.param2)
488f80 929     local nnode = minetest.get_node(nnodepos)
3cf0d3 930     local meta = minetest.get_meta(pos)
6a0807 931     if meta:get_int("last_moved") == minetest.get_gametime() then
N 932         return
933     end
3cf0d3 934     local owner = meta:get_string("owner")
c06cdf 935     if nnode.name == "technic:template" then
488f80 936         local connected_nodes = get_template_nodes(nnodepos)
937         move_nodes_vect(connected_nodes, dir, pos, owner)
c06cdf 938     end
6a0807 939     minetest.get_meta(vector.add(pos, dir)):set_int("last_moved", minetest.get_gametime())
c06cdf 940 end
N 941
488f80 942 minetest.register_node("technic:template_motor", {
39c41a 943     description = S("Template Motor"),
488f80 944     tiles = {
945         "pipeworks_filter_top.png^[transformR90",
946         "technic_lv_cable.png",
947         "technic_lv_cable.png",
948         "technic_lv_cable.png",
949         "technic_lv_cable.png",
950         "technic_lv_cable.png"
951     },
952     groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, mesecon = 2 },
c06cdf 953     paramtype2 = "facedir",
488f80 954     mesecons = { effector = { action_on = template_motor_on } },
3cf0d3 955     after_place_node = function(pos, placer, itemstack)
N 956         local meta = minetest.get_meta(pos)
957         meta:set_string("owner", placer:get_player_name())
958     end,
c06cdf 959 })
768794 960
N 961 -- Crafts
962 minetest.register_craft({
963     output = 'technic:frame_111111',
964     recipe = {
488f80 965         { '',              'default:stick',       '' },
44cb8d 966         { 'default:stick', 'basic_materials:brass_ingot', 'default:stick' },
488f80 967         { '',              'default:stick',       '' },
768794 968     }
N 969 })
970
971 minetest.register_craft({
972     output = 'technic:frame_motor',
973     recipe = {
488f80 974         { '',                                  'technic:frame_111111', '' },
44cb8d 975         { 'group:mesecon_conductor_craftable', 'basic_materials:motor',        'group:mesecon_conductor_craftable' },
488f80 976         { '',                                  'technic:frame_111111', '' },
768794 977     }
N 978 })
979
980 minetest.register_craft({
981     output = 'technic:template 10',
982     recipe = {
44cb8d 983         { '',                    'basic_materials:brass_ingot',  '' },
VD 984         { 'basic_materials:brass_ingot', 'default:mese_crystal', 'basic_materials:brass_ingot' },
985         { '',                    'basic_materials:brass_ingot',  '' },
768794 986     }
N 987 })
988
989 minetest.register_craft({
990     output = 'technic:template_replacer',
488f80 991     recipe = { { 'technic:template' } }
768794 992 })
N 993
994 minetest.register_craft({
995     output = 'technic:template',
488f80 996     recipe = { { 'technic:template_replacer' } }
768794 997 })
N 998
999 minetest.register_craft({
1000     output = 'technic:template_motor',
1001     recipe = {
488f80 1002         { '',                                  'technic:template', '' },
44cb8d 1003         { 'group:mesecon_conductor_craftable', 'basic_materials:motor',    'group:mesecon_conductor_craftable' },
488f80 1004         { '',                                  'technic:template', '' },
768794 1005     }
N 1006 })
1007
1008 minetest.register_craft({
1009     output = 'technic:template_tool',
1010     recipe = {
488f80 1011         { '',                     'technic:template', '' },
1012         { 'default:mese_crystal', 'default:stick',    'default:mese_crystal' },
1013         { '',                     'default:stick',    '' },
768794 1014     }
N 1015 })