internal.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. -- Internal.lua - The core of mesecons
  2. --
  3. -- For more practical developer resources see http://mesecons.net/developers.php
  4. --
  5. -- Function overview
  6. -- mesecon.get_effector(nodename) --> Returns the mesecons.effector -specifictation in the nodedef by the nodename
  7. -- mesecon.get_receptor(nodename) --> Returns the mesecons.receptor -specifictation in the nodedef by the nodename
  8. -- mesecon.get_conductor(nodename) --> Returns the mesecons.conductor-specifictation in the nodedef by the nodename
  9. -- mesecon.get_any_inputrules (node) --> Returns the rules of a node if it is a conductor or an effector
  10. -- mesecon.get_any_outputrules (node) --> Returns the rules of a node if it is a conductor or a receptor
  11. -- RECEPTORS
  12. -- mesecon.is_receptor(nodename) --> Returns true if nodename is a receptor
  13. -- mesecon.is_receptor_on(nodename --> Returns true if nodename is an receptor with state = mesecon.state.on
  14. -- mesecon.is_receptor_off(nodename) --> Returns true if nodename is an receptor with state = mesecon.state.off
  15. -- mesecon.receptor_get_rules(node) --> Returns the rules of the receptor (mesecon.rules.default if none specified)
  16. -- EFFECTORS
  17. -- mesecon.is_effector(nodename) --> Returns true if nodename is an effector
  18. -- mesecon.is_effector_on(nodename) --> Returns true if nodename is an effector with nodedef.mesecons.effector.action_off
  19. -- mesecon.is_effector_off(nodename) --> Returns true if nodename is an effector with nodedef.mesecons.effector.action_on
  20. -- mesecon.effector_get_rules(node) --> Returns the input rules of the effector (mesecon.rules.default if none specified)
  21. -- SIGNALS
  22. -- mesecon.activate(pos, node, depth) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on), higher depths are executed later
  23. -- mesecon.deactivate(pos, node, depth) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off), higher depths are executed later
  24. -- mesecon.changesignal(pos, node, rulename, newstate, depth) --> Changes the effector node at the specific pos (calls nodedef.mesecons.effector.action_change), higher depths are executed later
  25. -- CONDUCTORS
  26. -- mesecon.is_conductor(nodename) --> Returns true if nodename is a conductor
  27. -- mesecon.is_conductor_on(node --> Returns true if node is a conductor with state = mesecon.state.on
  28. -- mesecon.is_conductor_off(node) --> Returns true if node is a conductor with state = mesecon.state.off
  29. -- mesecon.get_conductor_on(node_off) --> Returns the onstate nodename of the conductor
  30. -- mesecon.get_conductor_off(node_on) --> Returns the offstate nodename of the conductor
  31. -- mesecon.conductor_get_rules(node) --> Returns the input+output rules of a conductor (mesecon.rules.default if none specified)
  32. -- HIGH-LEVEL Internals
  33. -- mesecon.is_power_on(pos) --> Returns true if pos emits power in any way
  34. -- mesecon.is_power_off(pos) --> Returns true if pos does not emit power in any way
  35. -- mesecon.is_powered(pos) --> Returns true if pos is powered by a receptor or a conductor
  36. -- RULES ROTATION helpers
  37. -- mesecon.rotate_rules_right(rules)
  38. -- mesecon.rotate_rules_left(rules)
  39. -- mesecon.rotate_rules_up(rules)
  40. -- mesecon.rotate_rules_down(rules)
  41. -- These functions return rules that have been rotated in the specific direction
  42. -- General
  43. function mesecon.get_effector(nodename)
  44. if minetest.registered_nodes[nodename]
  45. and minetest.registered_nodes[nodename].mesecons
  46. and minetest.registered_nodes[nodename].mesecons.effector then
  47. return minetest.registered_nodes[nodename].mesecons.effector
  48. end
  49. end
  50. function mesecon.get_receptor(nodename)
  51. if minetest.registered_nodes[nodename]
  52. and minetest.registered_nodes[nodename].mesecons
  53. and minetest.registered_nodes[nodename].mesecons.receptor then
  54. return minetest.registered_nodes[nodename].mesecons.receptor
  55. end
  56. end
  57. function mesecon.get_conductor(nodename)
  58. if minetest.registered_nodes[nodename]
  59. and minetest.registered_nodes[nodename].mesecons
  60. and minetest.registered_nodes[nodename].mesecons.conductor then
  61. return minetest.registered_nodes[nodename].mesecons.conductor
  62. end
  63. end
  64. function mesecon.get_any_outputrules(node)
  65. if not node then return nil end
  66. if mesecon.is_conductor(node.name) then
  67. return mesecon.conductor_get_rules(node)
  68. elseif mesecon.is_receptor(node.name) then
  69. return mesecon.receptor_get_rules(node)
  70. end
  71. end
  72. function mesecon.get_any_inputrules(node)
  73. if not node then return nil end
  74. if mesecon.is_conductor(node.name) then
  75. return mesecon.conductor_get_rules(node)
  76. elseif mesecon.is_effector(node.name) then
  77. return mesecon.effector_get_rules(node)
  78. end
  79. end
  80. function mesecon.get_any_rules(node)
  81. return mesecon.mergetable(mesecon.get_any_inputrules(node) or {},
  82. mesecon.get_any_outputrules(node) or {})
  83. end
  84. -- Receptors
  85. -- Nodes that can power mesecons
  86. function mesecon.is_receptor_on(nodename)
  87. local receptor = mesecon.get_receptor(nodename)
  88. if receptor and receptor.state == mesecon.state.on then
  89. return true
  90. end
  91. return false
  92. end
  93. function mesecon.is_receptor_off(nodename)
  94. local receptor = mesecon.get_receptor(nodename)
  95. if receptor and receptor.state == mesecon.state.off then
  96. return true
  97. end
  98. return false
  99. end
  100. function mesecon.is_receptor(nodename)
  101. local receptor = mesecon.get_receptor(nodename)
  102. if receptor then
  103. return true
  104. end
  105. return false
  106. end
  107. function mesecon.receptor_get_rules(node)
  108. local receptor = mesecon.get_receptor(node.name)
  109. if receptor then
  110. local rules = receptor.rules
  111. if type(rules) == 'function' then
  112. return rules(node)
  113. elseif rules then
  114. return rules
  115. end
  116. end
  117. return mesecon.rules.default
  118. end
  119. -- Effectors
  120. -- Nodes that can be powered by mesecons
  121. function mesecon.is_effector_on(nodename)
  122. local effector = mesecon.get_effector(nodename)
  123. if effector and effector.action_off then
  124. return true
  125. end
  126. return false
  127. end
  128. function mesecon.is_effector_off(nodename)
  129. local effector = mesecon.get_effector(nodename)
  130. if effector and effector.action_on then
  131. return true
  132. end
  133. return false
  134. end
  135. function mesecon.is_effector(nodename)
  136. local effector = mesecon.get_effector(nodename)
  137. if effector then
  138. return true
  139. end
  140. return false
  141. end
  142. function mesecon.effector_get_rules(node)
  143. local effector = mesecon.get_effector(node.name)
  144. if effector then
  145. local rules = effector.rules
  146. if type(rules) == 'function' then
  147. return rules(node)
  148. elseif rules then
  149. return rules
  150. end
  151. end
  152. return mesecon.rules.default
  153. end
  154. -- #######################
  155. -- # Signals (effectors) #
  156. -- #######################
  157. -- Activation:
  158. mesecon.queue:add_function("activate", function (pos, rulename)
  159. local node = mesecon.get_node_force(pos)
  160. if not node then return end
  161. local effector = mesecon.get_effector(node.name)
  162. if effector and effector.action_on then
  163. effector.action_on(pos, node, rulename)
  164. end
  165. end)
  166. function mesecon.activate(pos, node, rulename, depth)
  167. if rulename == nil then
  168. for _,rule in ipairs(mesecon.effector_get_rules(node)) do
  169. mesecon.activate(pos, node, rule, depth + 1)
  170. end
  171. return
  172. end
  173. mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / depth)
  174. end
  175. -- Deactivation
  176. mesecon.queue:add_function("deactivate", function (pos, rulename)
  177. local node = mesecon.get_node_force(pos)
  178. if not node then return end
  179. local effector = mesecon.get_effector(node.name)
  180. if effector and effector.action_off then
  181. effector.action_off(pos, node, rulename)
  182. end
  183. end)
  184. function mesecon.deactivate(pos, node, rulename, depth)
  185. if rulename == nil then
  186. for _,rule in ipairs(mesecon.effector_get_rules(node)) do
  187. mesecon.deactivate(pos, node, rule, depth + 1)
  188. end
  189. return
  190. end
  191. mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / depth)
  192. end
  193. -- Change
  194. mesecon.queue:add_function("change", function (pos, rulename, changetype)
  195. local node = mesecon.get_node_force(pos)
  196. if not node then return end
  197. local effector = mesecon.get_effector(node.name)
  198. if effector and effector.action_change then
  199. effector.action_change(pos, node, rulename, changetype)
  200. end
  201. end)
  202. function mesecon.changesignal(pos, node, rulename, newstate, depth)
  203. if rulename == nil then
  204. for _,rule in ipairs(mesecon.effector_get_rules(node)) do
  205. mesecon.changesignal(pos, node, rule, newstate, depth + 1)
  206. end
  207. return
  208. end
  209. -- Include "change" in overwritecheck so that it cannot be overwritten
  210. -- by "active" / "deactivate" that will be called upon the node at the same time.
  211. local overwritecheck = {"change", rulename}
  212. mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, overwritecheck, 1 / depth)
  213. end
  214. -- Conductors
  215. function mesecon.is_conductor_on(node, rulename)
  216. if not node then return false end
  217. local conductor = mesecon.get_conductor(node.name)
  218. if conductor then
  219. if conductor.state then
  220. return conductor.state == mesecon.state.on
  221. end
  222. if conductor.states then
  223. if not rulename then
  224. return mesecon.getstate(node.name, conductor.states) ~= 1
  225. end
  226. local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node))
  227. local binstate = mesecon.getbinstate(node.name, conductor.states)
  228. return mesecon.get_bit(binstate, bit)
  229. end
  230. end
  231. return false
  232. end
  233. function mesecon.is_conductor_off(node, rulename)
  234. if not node then return false end
  235. local conductor = mesecon.get_conductor(node.name)
  236. if conductor then
  237. if conductor.state then
  238. return conductor.state == mesecon.state.off
  239. end
  240. if conductor.states then
  241. if not rulename then
  242. return mesecon.getstate(node.name, conductor.states) == 1
  243. end
  244. local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node))
  245. local binstate = mesecon.getbinstate(node.name, conductor.states)
  246. return not mesecon.get_bit(binstate, bit)
  247. end
  248. end
  249. return false
  250. end
  251. function mesecon.is_conductor(nodename)
  252. local conductor = mesecon.get_conductor(nodename)
  253. if conductor then
  254. return true
  255. end
  256. return false
  257. end
  258. function mesecon.get_conductor_on(node_off, rulename)
  259. local conductor = mesecon.get_conductor(node_off.name)
  260. if conductor then
  261. if conductor.onstate then
  262. return conductor.onstate
  263. end
  264. if conductor.states then
  265. local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_off))
  266. local binstate = mesecon.getbinstate(node_off.name, conductor.states)
  267. binstate = mesecon.set_bit(binstate, bit, "1")
  268. return conductor.states[tonumber(binstate,2)+1]
  269. end
  270. end
  271. return offstate
  272. end
  273. function mesecon.get_conductor_off(node_on, rulename)
  274. local conductor = mesecon.get_conductor(node_on.name)
  275. if conductor then
  276. if conductor.offstate then
  277. return conductor.offstate
  278. end
  279. if conductor.states then
  280. local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_on))
  281. local binstate = mesecon.getbinstate(node_on.name, conductor.states)
  282. binstate = mesecon.set_bit(binstate, bit, "0")
  283. return conductor.states[tonumber(binstate,2)+1]
  284. end
  285. end
  286. return onstate
  287. end
  288. function mesecon.conductor_get_rules(node)
  289. local conductor = mesecon.get_conductor(node.name)
  290. if conductor then
  291. local rules = conductor.rules
  292. if type(rules) == 'function' then
  293. return rules(node)
  294. elseif rules then
  295. return rules
  296. end
  297. end
  298. return mesecon.rules.default
  299. end
  300. -- some more general high-level stuff
  301. function mesecon.is_power_on(pos, rulename)
  302. local node = mesecon.get_node_force(pos)
  303. if node and (mesecon.is_conductor_on(node, rulename) or mesecon.is_receptor_on(node.name)) then
  304. return true
  305. end
  306. return false
  307. end
  308. function mesecon.is_power_off(pos, rulename)
  309. local node = mesecon.get_node_force(pos)
  310. if node and (mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name)) then
  311. return true
  312. end
  313. return false
  314. end
  315. -- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`.
  316. -- Breadth-first search. Map is abstracted away in a voxelmanip.
  317. -- Follow all all conductor paths replacing conductors that were already
  318. -- looked at, activating / changing all effectors along the way.
  319. function mesecon.turnon(pos, link)
  320. local frontiers = {{pos = pos, link = link}}
  321. local depth = 1
  322. while frontiers[1] do
  323. local f = table.remove(frontiers, 1)
  324. local node = mesecon.get_node_force(f.pos)
  325. if not node then
  326. -- Area does not exist; do nothing
  327. elseif mesecon.is_conductor_off(node, f.link) then
  328. local rules = mesecon.conductor_get_rules(node)
  329. -- Call turnon on neighbors
  330. for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
  331. local np = vector.add(f.pos, r)
  332. for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
  333. table.insert(frontiers, {pos = np, link = l})
  334. end
  335. end
  336. mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link))
  337. elseif mesecon.is_effector(node.name) then
  338. mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth)
  339. if mesecon.is_effector_off(node.name) then
  340. mesecon.activate(f.pos, node, f.link, depth)
  341. end
  342. end
  343. depth = depth + 1
  344. end
  345. end
  346. -- Turn on an equipotential section starting at `pos`, which outputs in the direction of `link`.
  347. -- Breadth-first search. Map is abstracted away in a voxelmanip.
  348. -- Follow all all conductor paths replacing conductors that were already
  349. -- looked at, deactivating / changing all effectors along the way.
  350. -- In case an onstate receptor is discovered, abort the process by returning false, which will
  351. -- cause `receptor_off` to discard all changes made in the voxelmanip.
  352. -- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only
  353. -- be called in the very end when we can be sure that no conductor was found along the path.
  354. --
  355. -- Signal table entry structure:
  356. -- {
  357. -- pos = position of effector,
  358. -- node = node descriptor (name, param1 and param2),
  359. -- link = link the effector is connected to,
  360. -- depth = indicates order in which signals wire fired, higher is later
  361. -- }
  362. function mesecon.turnoff(pos, link)
  363. local frontiers = {{pos = pos, link = link}}
  364. local signals = {}
  365. local depth = 1
  366. while frontiers[1] do
  367. local f = table.remove(frontiers, 1)
  368. local node = mesecon.get_node_force(f.pos)
  369. if not node then
  370. -- Area does not exist; do nothing
  371. elseif mesecon.is_conductor_on(node, f.link) then
  372. local rules = mesecon.conductor_get_rules(node)
  373. for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
  374. local np = vector.add(f.pos, r)
  375. -- Check if an onstate receptor is connected. If that is the case,
  376. -- abort this turnoff process by returning false. `receptor_off` will
  377. -- discard all the changes that we made in the voxelmanip:
  378. for _, l in ipairs(mesecon.rules_link_rule_all_inverted(f.pos, r)) do
  379. if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
  380. return false
  381. end
  382. end
  383. -- Call turnoff on neighbors
  384. for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
  385. table.insert(frontiers, {pos = np, link = l})
  386. end
  387. end
  388. mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link))
  389. elseif mesecon.is_effector(node.name) then
  390. table.insert(signals, {
  391. pos = f.pos,
  392. node = node,
  393. link = f.link,
  394. depth = depth
  395. })
  396. end
  397. depth = depth + 1
  398. end
  399. for _, sig in ipairs(signals) do
  400. mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth)
  401. if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then
  402. mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth)
  403. end
  404. end
  405. return true
  406. end
  407. -- Get all linking inputrules of inputnode (effector or conductor) that is connected to
  408. -- outputnode (receptor or conductor) at position `output` and has an output in direction `rule`
  409. function mesecon.rules_link_rule_all(output, rule)
  410. local input = vector.add(output, rule)
  411. local inputnode = mesecon.get_node_force(input)
  412. local inputrules = mesecon.get_any_inputrules(inputnode)
  413. if not inputrules then
  414. return {}
  415. end
  416. local rules = {}
  417. for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
  418. -- Check if input accepts from output
  419. if vector.equals(vector.add(input, inputrule), output) then
  420. table.insert(rules, inputrule)
  421. end
  422. end
  423. return rules
  424. end
  425. -- Get all linking outputnodes of outputnode (receptor or conductor) that is connected to
  426. -- inputnode (effector or conductor) at position `input` and has an input in direction `rule`
  427. function mesecon.rules_link_rule_all_inverted(input, rule)
  428. local output = vector.add(input, rule)
  429. local outputnode = mesecon.get_node_force(output)
  430. local outputrules = mesecon.get_any_outputrules(outputnode)
  431. if not outputrules then
  432. return {}
  433. end
  434. local rules = {}
  435. for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
  436. if vector.equals(vector.add(output, outputrule), input) then
  437. table.insert(rules, mesecon.invertRule(outputrule))
  438. end
  439. end
  440. return rules
  441. end
  442. function mesecon.is_powered(pos, rule)
  443. local node = mesecon.get_node_force(pos)
  444. local rules = mesecon.get_any_inputrules(node)
  445. if not rules then return false end
  446. -- List of nodes that send out power to pos
  447. local sourcepos = {}
  448. if not rule then
  449. for _, rule in ipairs(mesecon.flattenrules(rules)) do
  450. local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
  451. for _, rname in ipairs(rulenames) do
  452. local np = vector.add(pos, rname)
  453. local nn = mesecon.get_node_force(np)
  454. if (mesecon.is_conductor_on(nn, mesecon.invertRule(rname))
  455. or mesecon.is_receptor_on(nn.name)) then
  456. table.insert(sourcepos, np)
  457. end
  458. end
  459. end
  460. else
  461. local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
  462. for _, rname in ipairs(rulenames) do
  463. local np = vector.add(pos, rname)
  464. local nn = mesecon.get_node_force(np)
  465. if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname))
  466. or mesecon.is_receptor_on (nn.name)) then
  467. table.insert(sourcepos, np)
  468. end
  469. end
  470. end
  471. -- Return FALSE if not powered, return list of sources if is powered
  472. if (#sourcepos == 0) then return false
  473. else return sourcepos end
  474. end