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
)
9 --Rules rotation Functions:
10 function mesecon
.rotate_rules_right(rules
)
12 for i
, rule
in ipairs(rules
) do
18 spread
= rule
.spread
,})
23 function mesecon
.rotate_rules_left(rules
)
25 for i
, rule
in ipairs(rules
) do
31 spread
= rule
.spread
,})
36 function mesecon
.rotate_rules_down(rules
)
38 for i
, rule
in ipairs(rules
) do
44 spread
= rule
.spread
,})
49 function mesecon
.rotate_rules_up(rules
)
51 for i
, rule
in ipairs(rules
) do
57 spread
= rule
.spread
,})
62 function mesecon
.flattenrules(allrules
)
80 local shallowrules
= {}
81 for _
, metarule
in ipairs( allrules
) do
82 for _
, rule
in ipairs(metarule
) do
83 table.insert(shallowrules
, rule
)
97 function mesecon
.rule2bit(findrule
, allrules
)
98 --get the bit of the metarule the rule is in, or bit 1
104 for m
,metarule
in ipairs( allrules
) do
105 for _
, rule
in ipairs(metarule
) do
106 if vector
.equals(findrule
, rule
) then
113 function mesecon
.rule2metaindex(findrule
, allrules
)
114 --get the metarule the rule is in, or allrules
115 if allrules
[1].x
then
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
132 function mesecon
.rule2meta(findrule
, allrules
)
133 if #allrules
== 0 then return {} end
135 local index
= mesecon
.rule2metaindex(findrule
, allrules
)
137 if allrules
[1].x
then
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
= mesecon
.rules
.alldirs
153 table.insert(e
, { pos
= vector
.add(pos
, r
[i
]), link
= r
[i
] })
158 function mesecon
.dec2bin(n
)
159 local x
, y
= math
.floor(n
/ 2), n
% 2
161 return mesecon
.dec2bin(x
)..y
167 function mesecon
.getstate(nodename
, states
)
168 for state
, name
in ipairs(states
) do
169 if name
== nodename
then
173 error(nodename
.." doesn't mention itself in "..dump(states
))
176 function mesecon
.getbinstate(nodename
, states
)
177 return mesecon
.dec2bin(mesecon
.getstate(nodename
, states
)-1)
180 function mesecon
.get_bit(binary
,bit
)
182 local c
= binary
:len()-(bit
-1)
183 return binary
:sub(c
,c
) == "1"
186 function mesecon
.set_bit(binary
,bit
,value
)
188 if not mesecon
.get_bit(binary
,bit
) then
189 return mesecon
.dec2bin(tonumber(binary
,2)+math
.pow(2,bit
-1))
191 elseif value
== "0" then
192 if mesecon
.get_bit(binary
,bit
) then
193 return mesecon
.dec2bin(tonumber(binary
,2)-math
.pow(2,bit
-1))
200 function mesecon
.invertRule(r
)
201 local spread
= r
.spread
202 r
= vector
.multiply(r
, -1)
209 function mesecon
.tablecopy(table) -- deep table copy
210 if type(table) ~= "table" then return table end -- no need to copy
213 for idx
, item
in pairs(table) do
214 if type(item
) == "table" then
215 newtable
[idx
] = mesecon
.tablecopy(item
)
224 function mesecon
.cmpAny(t1
, t2
)
225 if type(t1
) ~= type(t2
) then return false end
226 if type(t1
) ~= "table" and type(t2
) ~= "table" then return t1
== t2
end
228 for i
, e
in pairs(t1
) do
229 if not mesecon
.cmpAny(e
, t2
[i
]) then return false end
235 -- does not overwrite values; number keys (ipairs) are appended, not overwritten
236 function mesecon
.mergetable(source
, dest
)
237 local rval
= mesecon
.tablecopy(dest
)
239 for k
, v
in pairs(source
) do
240 rval
[k
] = dest
[k
] or mesecon
.tablecopy(v
)
242 for i
, v
in ipairs(source
) do
243 table.insert(rval
, mesecon
.tablecopy(v
))
249 function mesecon
.register_node(name
, spec_common
, spec_off
, spec_on
)
250 spec_common
.drop
= spec_common
.drop
or name
.. "_off"
251 spec_common
.on_blast
= spec_common
.on_blast
or mesecon
.on_blastnode
252 spec_common
.__mesecon_basename
= name
253 spec_on
.__mesecon_state
= "on"
254 spec_off
.__mesecon_state
= "off"
256 spec_on
= mesecon
.mergetable(spec_common
, spec_on
);
257 spec_off
= mesecon
.mergetable(spec_common
, spec_off
);
259 minetest
.register_node(name
.. "_on", spec_on
)
260 minetest
.register_node(name
.. "_off", spec_off
)
263 -- swap onstate and offstate nodes, returns new state
264 function mesecon
.flipstate(pos
, node
)
265 local nodedef
= minetest
.registered_nodes
[node
.name
]
267 if (nodedef
.__mesecon_state
== "on") then newstate
= "off" end
268 if (nodedef
.__mesecon_state
== "off") then newstate
= "on" end
270 minetest
.swap_node(pos
, {name
= nodedef
.__mesecon_basename
.. "_" .. newstate
,
271 param2
= node
.param2
})
276 -- File writing / reading utilities
277 local wpath
= minetest
.get_worldpath()
278 function mesecon
.file2table(filename
)
279 local f
= io
.open(wpath
..DIR_DELIM
..filename
, "r")
280 if f
== nil then return {} end
281 local t
= f
:read("*all")
283 if t
== "" or t
== nil then return {} end
284 return minetest
.deserialize(t
)
287 function mesecon
.table2file(filename
, table)
288 local f
= io
.open(wpath
..DIR_DELIM
..filename
, "w")
289 f
:write(minetest
.serialize(table))
293 -- Block position "hashing" (convert to integer) functions for voxelmanip cache
296 -- convert node position --> block hash
297 local function hash_blockpos(pos
)
298 return minetest
.hash_node_position({
299 x
= math
.floor(pos
.x
/BLOCKSIZE
),
300 y
= math
.floor(pos
.y
/BLOCKSIZE
),
301 z
= math
.floor(pos
.z
/BLOCKSIZE
)
305 -- Maps from a hashed mapblock position (as returned by hash_blockpos) to a
308 -- Contents of the table are:
309 -- “vm” → the VoxelManipulator
310 -- “va” → the VoxelArea
311 -- “data” → the data array
312 -- “param1” → the param1 array
313 -- “param2” → the param2 array
314 -- “dirty” → true if data has been modified
316 -- Nil if no VM-based transaction is in progress.
319 -- Starts a VoxelManipulator-based transaction.
321 -- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a
322 -- cached copy of the world loaded via VoxelManipulators. That cache can later
323 -- be committed to the real map by means of vm_commit or discarded by means of
325 function mesecon
.vm_begin()
329 -- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data
330 -- and writing back any modified areas.
331 function mesecon
.vm_commit()
332 for hash
, tbl
in pairs(vm_cache
) do
335 vm
:set_data(tbl
.data
)
343 -- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing
344 -- away any modified areas.
345 function mesecon
.vm_abort()
349 -- Gets the cache entry covering a position, populating it if necessary.
350 local function vm_get_or_create_entry(pos
)
351 local hash
= hash_blockpos(pos
)
352 local tbl
= vm_cache
[hash
]
354 local vm
= minetest
.get_voxel_manip(pos
, pos
)
355 local min_pos
, max_pos
= vm
:get_emerged_area()
356 local va
= VoxelArea
:new
{MinEdge
= min_pos
, MaxEdge
= max_pos
}
357 tbl
= {vm
= vm
, va
= va
, data
= vm
:get_data(), param1
= vm
:get_light_data(), param2
= vm
:get_param2_data(), dirty
= false}
363 -- Gets the node at a given position during a VoxelManipulator-based
365 function mesecon
.vm_get_node(pos
)
366 local tbl
= vm_get_or_create_entry(pos
)
367 local index
= tbl
.va
:indexp(pos
)
368 local node_value
= tbl
.data
[index
]
369 if node_value
== core
.CONTENT_IGNORE
then
372 local node_param1
= tbl
.param1
[index
]
373 local node_param2
= tbl
.param2
[index
]
374 return {name
= minetest
.get_name_from_content_id(node_value
), param1
= node_param1
, param2
= node_param2
}
378 -- Sets a node’s name during a VoxelManipulator-based transaction.
380 -- Existing param1, param2, and metadata are left alone.
381 function mesecon
.vm_swap_node(pos
, name
)
382 local tbl
= vm_get_or_create_entry(pos
)
383 local index
= tbl
.va
:indexp(pos
)
384 tbl
.data
[index
] = minetest
.get_content_id(name
)
388 -- Gets the node at a given position, regardless of whether it is loaded or
389 -- not, respecting a transaction if one is in progress.
391 -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
392 -- the server’s main map data cache and then accessed from there.
394 -- Inside a VM transaction, the transaction’s VM cache is used.
395 function mesecon
.get_node_force(pos
)
397 return mesecon
.vm_get_node(pos
)
399 local node
= minetest
.get_node_or_nil(pos
)
401 -- Node is not currently loaded; use a VoxelManipulator to prime
402 -- the mapblock cache and try again.
403 minetest
.get_voxel_manip(pos
, pos
)
404 node
= minetest
.get_node_or_nil(pos
)
410 -- Swaps the node at a given position, regardless of whether it is loaded or
411 -- not, respecting a transaction if one is in progress.
413 -- Outside a VM transaction, if the mapblock is not loaded, it is pulled into
414 -- the server’s main map data cache and then accessed from there.
416 -- Inside a VM transaction, the transaction’s VM cache is used.
418 -- This function can only be used to change the node’s name, not its parameters
420 function mesecon
.swap_node_force(pos
, name
)
422 return mesecon
.vm_swap_node(pos
, name
)
424 -- This serves to both ensure the mapblock is loaded and also hand us
425 -- the old node table so we can preserve param2.
426 local node
= mesecon
.get_node_force(pos
)
428 minetest
.swap_node(pos
, node
)
433 -- Nodes like conductors may change their appearance and their connection rules
434 -- right after being placed or after being dug, e.g. the default wires use this
435 -- to automatically connect to linking nodes after placement.
436 -- After placement, the update function will be executed immediately so that the
437 -- possibly changed rules can be taken into account when recalculating the circuit.
438 -- After digging, the update function will be queued and executed after
439 -- recalculating the circuit. The update function must take care of updating the
440 -- node at the given position itself, but also all of the other nodes the given
441 -- position may have (had) a linking connection to.
442 mesecon
.autoconnect_hooks
= {}
444 -- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function.
445 -- fct: The update function with parameters function(pos, node)
446 function mesecon
.register_autoconnect_hook(name
, fct
)
447 mesecon
.autoconnect_hooks
[name
] = fct
448 mesecon
.queue
:add_function("autoconnect_hook_"..name
, fct
)
451 function mesecon
.execute_autoconnect_hooks_now(pos
, node
)
452 for _
, fct
in pairs(mesecon
.autoconnect_hooks
) do
457 function mesecon
.execute_autoconnect_hooks_queue(pos
, node
)
458 for name
in pairs(mesecon
.autoconnect_hooks
) do
459 mesecon
.queue
:add_action(pos
, "autoconnect_hook_"..name
, {node
})