Fix incorrect power transmission of buttons
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons / util.lua
blobf6dac3f5fc67bee74ea3e312711b0bca5cd40b10
1 function mesecon.move_node(pos, newpos)
2 local node = minetest.get_node(pos)
3 local meta = minetest.get_meta(pos):to_table()
4 minetest.remove_node(pos)
5 minetest.set_node(newpos, node)
6 minetest.get_meta(pos):from_table(meta)
7 end
9 --Rules rotation Functions:
10 function mesecon.rotate_rules_right(rules)
11 local nr = {}
12 for i, rule in ipairs(rules) do
13 table.insert(nr, {
14 x = -rule.z,
15 y = rule.y,
16 z = rule.x,
17 name = rule.name,
18 spread = rule.spread,})
19 end
20 return nr
21 end
23 function mesecon.rotate_rules_left(rules)
24 local nr = {}
25 for i, rule in ipairs(rules) do
26 table.insert(nr, {
27 x = rule.z,
28 y = rule.y,
29 z = -rule.x,
30 name = rule.name,
31 spread = rule.spread,})
32 end
33 return nr
34 end
36 function mesecon.rotate_rules_down(rules)
37 local nr = {}
38 for i, rule in ipairs(rules) do
39 table.insert(nr, {
40 x = -rule.y,
41 y = rule.x,
42 z = rule.z,
43 name = rule.name,
44 spread = rule.spread,})
45 end
46 return nr
47 end
49 function mesecon.rotate_rules_up(rules)
50 local nr = {}
51 for i, rule in ipairs(rules) do
52 table.insert(nr, {
53 x = rule.y,
54 y = -rule.x,
55 z = rule.z,
56 name = rule.name,
57 spread = rule.spread,})
58 end
59 return nr
60 end
62 function mesecon.flattenrules(allrules)
63 --[[
66 {xyz},
67 {xyz},
70 {xyz},
71 {xyz},
74 --]]
75 if allrules[1] and
76 allrules[1].x then
77 return allrules
78 end
80 local shallowrules = {}
81 for _, metarule in ipairs( allrules) do
82 for _, rule in ipairs(metarule ) do
83 table.insert(shallowrules, rule)
84 end
85 end
86 return shallowrules
87 --[[
89 {xyz},
90 {xyz},
91 {xyz},
92 {xyz},
94 --]]
95 end
97 function mesecon.rule2bit(findrule, allrules)
98 --get the bit of the metarule the rule is in, or bit 1
99 if (allrules[1] and
100 allrules[1].x) or
101 not findrule then
102 return 1
104 for m,metarule in ipairs( allrules) do
105 for _, rule in ipairs(metarule ) do
106 if vector.equals(findrule, rule) then
107 return m
113 function mesecon.rule2metaindex(findrule, allrules)
114 --get the metarule the rule is in, or allrules
115 if allrules[1].x then
116 return nil
119 if not(findrule) then
120 return mesecon.flattenrules(allrules)
123 for m, metarule in ipairs( allrules) do
124 for _, rule in ipairs(metarule ) do
125 if vector.equals(findrule, rule) then
126 return m
132 function mesecon.rule2meta(findrule, allrules)
133 if #allrules == 0 then return {} end
135 local index = mesecon.rule2metaindex(findrule, allrules)
136 if index == nil then
137 if allrules[1].x then
138 return allrules
139 else
140 return {}
143 return allrules[index]
146 -- Returns the 6 immediate neighbors of pos
147 -- (nodes which touch the sides of pos).
148 -- NOT PART OF ORIGINAL MESECONS!
149 function mesecon.mcl_get_neighbors(pos)
150 local r = {
151 { x= 0, y= 0, z=-1 },
152 { x= 0, y= 0, z= 1 },
153 { x= 0, y=-1, z= 0 },
154 { x= 0, y= 1, z= 0 },
155 { x=-1, y= 0, z= 0 },
156 { x= 1, y= 0, z= 0 },
158 local e = {}
159 for i=1, #r do
160 table.insert(e, { pos = vector.add(pos, r[i]), link = r[i] })
162 return e
165 function mesecon.dec2bin(n)
166 local x, y = math.floor(n / 2), n % 2
167 if (n > 1) then
168 return mesecon.dec2bin(x)..y
169 else
170 return ""..y
174 function mesecon.getstate(nodename, states)
175 for state, name in ipairs(states) do
176 if name == nodename then
177 return state
180 error(nodename.." doesn't mention itself in "..dump(states))
183 function mesecon.getbinstate(nodename, states)
184 return mesecon.dec2bin(mesecon.getstate(nodename, states)-1)
187 function mesecon.get_bit(binary,bit)
188 bit = bit or 1
189 local c = binary:len()-(bit-1)
190 return binary:sub(c,c) == "1"
193 function mesecon.set_bit(binary,bit,value)
194 if value == "1" then
195 if not mesecon.get_bit(binary,bit) then
196 return mesecon.dec2bin(tonumber(binary,2)+math.pow(2,bit-1))
198 elseif value == "0" then
199 if mesecon.get_bit(binary,bit) then
200 return mesecon.dec2bin(tonumber(binary,2)-math.pow(2,bit-1))
203 return binary
207 function mesecon.invertRule(r)
208 return vector.multiply(r, -1)
211 function mesecon.tablecopy(table) -- deep table copy
212 if type(table) ~= "table" then return table end -- no need to copy
213 local newtable = {}
215 for idx, item in pairs(table) do
216 if type(item) == "table" then
217 newtable[idx] = mesecon.tablecopy(item)
218 else
219 newtable[idx] = item
223 return newtable
226 function mesecon.cmpAny(t1, t2)
227 if type(t1) ~= type(t2) then return false end
228 if type(t1) ~= "table" and type(t2) ~= "table" then return t1 == t2 end
230 for i, e in pairs(t1) do
231 if not mesecon.cmpAny(e, t2[i]) then return false end
234 return true
237 -- does not overwrite values; number keys (ipairs) are appended, not overwritten
238 function mesecon.mergetable(source, dest)
239 local rval = mesecon.tablecopy(dest)
241 for k, v in pairs(source) do
242 rval[k] = dest[k] or mesecon.tablecopy(v)
244 for i, v in ipairs(source) do
245 table.insert(rval, mesecon.tablecopy(v))
248 return rval
251 function mesecon.register_node(name, spec_common, spec_off, spec_on)
252 spec_common.drop = spec_common.drop or name .. "_off"
253 spec_common.__mesecon_basename = name
254 spec_on.__mesecon_state = "on"
255 spec_off.__mesecon_state = "off"
257 spec_on = mesecon.mergetable(spec_common, spec_on);
258 spec_off = mesecon.mergetable(spec_common, spec_off);
260 minetest.register_node(name .. "_on", spec_on)
261 minetest.register_node(name .. "_off", spec_off)
264 -- swap onstate and offstate nodes, returns new state
265 function mesecon.flipstate(pos, node)
266 local nodedef = minetest.registered_nodes[node.name]
267 local newstate
268 if (nodedef.__mesecon_state == "on") then newstate = "off" end
269 if (nodedef.__mesecon_state == "off") then newstate = "on" end
271 minetest.swap_node(pos, {name = nodedef.__mesecon_basename .. "_" .. newstate,
272 param2 = node.param2})
274 return newstate
277 -- File writing / reading utilities
278 local wpath = minetest.get_worldpath()
279 function mesecon.file2table(filename)
280 local f = io.open(wpath..DIR_DELIM..filename, "r")
281 if f == nil then return {} end
282 local t = f:read("*all")
283 f:close()
284 if t == "" or t == nil then return {} end
285 return minetest.deserialize(t)
288 function mesecon.table2file(filename, table)
289 local f = io.open(wpath..DIR_DELIM..filename, "w")
290 f:write(minetest.serialize(table))
291 f:close()
294 -- Block position "hashing" (convert to integer) functions for voxelmanip cache
295 local BLOCKSIZE = 16
297 -- convert node position --> block hash
298 local function hash_blockpos(pos)
299 return minetest.hash_node_position({
300 x = math.floor(pos.x/BLOCKSIZE),
301 y = math.floor(pos.y/BLOCKSIZE),
302 z = math.floor(pos.z/BLOCKSIZE)
306 -- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
307 -- table.
309 -- Contents of the table are:
310 -- “vm” → the VoxelManipulator
311 -- “va” → the VoxelArea
312 -- “data” → the data array
313 -- “param1” → the param1 array
314 -- “param2” → the param2 array
315 -- “dirty” → true if data has been modified
317 -- Nil if no VM-based transaction is in progress.
318 local vm_cache = nil
320 -- Starts a VoxelManipulator-based transaction.
322 -- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
323 -- cached copy of the world loaded via VoxelManipulators. That cache can later
324 -- be committed to the real map by means of vm_commit or discarded by means of
325 -- vm_abort.
326 function mesecon.vm_begin()
327 vm_cache = {}
330 -- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
331 -- and writing back any modified areas.
332 function mesecon.vm_commit()
333 for hash, tbl in pairs(vm_cache) do
334 if tbl.dirty then
335 local vm = tbl.vm
336 vm:set_data(tbl.data)
337 vm:write_to_map()
338 vm:update_map()
341 vm_cache = nil
344 -- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
345 -- away any modified areas.
346 function mesecon.vm_abort()
347 vm_cache = nil
350 -- Gets the cache entry covering a position, populating it if necessary.
351 local function vm_get_or_create_entry(pos)
352 local hash = hash_blockpos(pos)
353 local tbl = vm_cache[hash]
354 if not tbl then
355 local vm = minetest.get_voxel_manip(pos, pos)
356 local min_pos, max_pos = vm:get_emerged_area()
357 local va = VoxelArea:new{MinEdge = min_pos, MaxEdge = max_pos}
358 tbl = {vm = vm, va = va, data = vm:get_data(), param1 = vm:get_light_data(), param2 = vm:get_param2_data(), dirty = false}
359 vm_cache[hash] = tbl
361 return tbl
364 -- Gets the node at a given position during a VoxelManipulator-based
365 -- transaction.
366 function mesecon.vm_get_node(pos)
367 local tbl = vm_get_or_create_entry(pos)
368 local index = tbl.va:indexp(pos)
369 local node_value = tbl.data[index]
370 if node_value == core.CONTENT_IGNORE then
371 return nil
372 else
373 local node_param1 = tbl.param1[index]
374 local node_param2 = tbl.param2[index]
375 return {name = minetest.get_name_from_content_id(node_value), param1 = node_param1, param2 = node_param2}
379 -- Sets a node’s name during a VoxelManipulator-based transaction.
381 -- Existing param1, param2, and metadata are left alone.
382 function mesecon.vm_swap_node(pos, name)
383 local tbl = vm_get_or_create_entry(pos)
384 local index = tbl.va:indexp(pos)
385 tbl.data[index] = minetest.get_content_id(name)
386 tbl.dirty = true
389 -- Gets the node at a given position, regardless of whether it is loaded or
390 -- not, respecting a transaction if one is in progress.
392 -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
393 -- the server’s main map data cache and then accessed from there.
395 -- Inside a VM transaction, the transaction’s VM cache is used.
396 function mesecon.get_node_force(pos)
397 if vm_cache then
398 return mesecon.vm_get_node(pos)
399 else
400 local node = minetest.get_node_or_nil(pos)
401 if node == nil then
402 -- Node is not currently loaded; use a VoxelManipulator to prime
403 -- the mapblock cache and try again.
404 minetest.get_voxel_manip(pos, pos)
405 node = minetest.get_node_or_nil(pos)
407 return node
411 -- Swaps the node at a given position, regardless of whether it is loaded or
412 -- not, respecting a transaction if one is in progress.
414 -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
415 -- the server’s main map data cache and then accessed from there.
417 -- Inside a VM transaction, the transaction’s VM cache is used.
419 -- This function can only be used to change the node’s name, not its parameters
420 -- or metadata.
421 function mesecon.swap_node_force(pos, name)
422 if vm_cache then
423 return mesecon.vm_swap_node(pos, name)
424 else
425 -- This serves to both ensure the mapblock is loaded and also hand us
426 -- the old node table so we can preserve param2.
427 local node = mesecon.get_node_force(pos)
428 node.name = name
429 minetest.swap_node(pos, node)
433 -- Autoconnect Hooks
434 -- Nodes like conductors may change their appearance and their connection rules
435 -- right after being placed or after being dug, e.g. the default wires use this
436 -- to automatically connect to linking nodes after placement.
437 -- After placement, the update function will be executed immediately so that the
438 -- possibly changed rules can be taken into account when recalculating the circuit.
439 -- After digging, the update function will be queued and executed after
440 -- recalculating the circuit. The update function must take care of updating the
441 -- node at the given position itself, but also all of the other nodes the given
442 -- position may have (had) a linking connection to.
443 mesecon.autoconnect_hooks = {}
445 -- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function.
446 -- fct: The update function with parameters function(pos, node)
447 function mesecon.register_autoconnect_hook(name, fct)
448 mesecon.autoconnect_hooks[name] = fct
449 mesecon.queue:add_function("autoconnect_hook_"..name, fct)
452 function mesecon.execute_autoconnect_hooks_now(pos, node)
453 for _, fct in pairs(mesecon.autoconnect_hooks) do
454 fct(pos, node)
458 function mesecon.execute_autoconnect_hooks_queue(pos, node)
459 for name in pairs(mesecon.autoconnect_hooks) do
460 mesecon.queue:add_action(pos, "autoconnect_hook_"..name, {node})