init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. local plg = {}
  2. plg.rules = {}
  3. local lcore = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/logic.lua")
  4. dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/tool.lua")(plg)
  5. plg.register_nodes = function(template)
  6. -- each loop is for one of the 4 IO ports
  7. for a = 0, 1 do
  8. for b = 0, 1 do
  9. for c = 0, 1 do
  10. for d = 0, 1 do
  11. local ndef = table.copy(template)
  12. local nodename = "mesecons_fpga:fpga"
  13. .. tostring(d) .. tostring(c) .. tostring(b) .. tostring(a)
  14. -- build top texture string
  15. local texture = "jeija_fpga_top.png"
  16. if a == 1 then texture = texture .. "^jeija_microcontroller_LED_A.png" end
  17. if b == 1 then texture = texture .. "^jeija_microcontroller_LED_B.png" end
  18. if c == 1 then texture = texture .. "^jeija_microcontroller_LED_C.png" end
  19. if d == 1 then texture = texture .. "^jeija_microcontroller_LED_D.png" end
  20. ndef.tiles[1] = texture
  21. ndef.inventory_image = texture
  22. if (a + b + c + d) > 0 then
  23. ndef.groups["not_in_creative_inventory"] = 1
  24. end
  25. -- interaction with mesecons (input / output)
  26. local rules_out = {}
  27. if a == 1 then table.insert(rules_out, {x = -1, y = 0, z = 0}) end
  28. if b == 1 then table.insert(rules_out, {x = 0, y = 0, z = 1}) end
  29. if c == 1 then table.insert(rules_out, {x = 1, y = 0, z = 0}) end
  30. if d == 1 then table.insert(rules_out, {x = 0, y = 0, z = -1}) end
  31. plg.rules[nodename] = rules_out
  32. local rules_in = {}
  33. if a == 0 then table.insert(rules_in, {x = -1, y = 0, z = 0}) end
  34. if b == 0 then table.insert(rules_in, {x = 0, y = 0, z = 1}) end
  35. if c == 0 then table.insert(rules_in, {x = 1, y = 0, z = 0}) end
  36. if d == 0 then table.insert(rules_in, {x = 0, y = 0, z = -1}) end
  37. ndef.mesecons.effector.rules = rules_in
  38. if (a + b + c + d) > 0 then
  39. ndef.mesecons.receptor = {
  40. state = mesecon.state.on,
  41. rules = rules_out,
  42. }
  43. end
  44. minetest.register_node(nodename, ndef)
  45. end
  46. end
  47. end
  48. end
  49. end
  50. plg.register_nodes({
  51. description = "FPGA",
  52. drawtype = "nodebox",
  53. tiles = {
  54. "", -- replaced later
  55. "jeija_microcontroller_bottom.png",
  56. "jeija_fpga_sides.png",
  57. "jeija_fpga_sides.png",
  58. "jeija_fpga_sides.png",
  59. "jeija_fpga_sides.png"
  60. },
  61. inventory_image = "", -- replaced later
  62. is_ground_content = false,
  63. sunlight_propagates = true,
  64. paramtype = "light",
  65. walkable = true,
  66. groups = {dig_immediate = 2, mesecon = 3, overheat = 1},
  67. drop = "mesecons_fpga:fpga0000",
  68. selection_box = {
  69. type = "fixed",
  70. fixed = { -8/16, -8/16, -8/16, 8/16, -5/16, 8/16 },
  71. },
  72. node_box = {
  73. type = "fixed",
  74. fixed = {
  75. { -8/16, -8/16, -8/16, 8/16, -7/16, 8/16 }, -- bottom slab
  76. { -5/16, -7/16, -5/16, 5/16, -6/16, 5/16 }, -- circuit board
  77. { -3/16, -6/16, -3/16, 3/16, -5/16, 3/16 }, -- IC
  78. }
  79. },
  80. on_construct = function(pos)
  81. local meta = minetest.get_meta(pos)
  82. local is = { {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} }
  83. meta:set_string("instr", lcore.serialize(is))
  84. meta:set_int("valid", 0)
  85. meta:set_string("formspec", plg.to_formspec_string(is))
  86. meta:set_string("infotext", "FPGA")
  87. end,
  88. on_receive_fields = function(pos, formname, fields, sender)
  89. if fields.program == nil then return end -- we only care when the user clicks "Program"
  90. local meta = minetest.get_meta(pos)
  91. local is = plg.from_formspec_fields(fields)
  92. meta:set_string("instr", lcore.serialize(is))
  93. plg.update_formspec(pos, is)
  94. end,
  95. sounds = default.node_sound_stone_defaults(),
  96. mesecons = {
  97. effector = {
  98. rules = {}, -- replaced later
  99. action_change = function(pos, node, rule, newstate)
  100. plg.ports_changed(pos, rule, newstate)
  101. plg.update(pos)
  102. end
  103. }
  104. },
  105. after_dig_node = function(pos, node)
  106. mesecon.receptor_off(pos, plg.rules[node.name])
  107. end,
  108. on_blast = mesecon.on_blastnode,
  109. on_rotate = function(pos, node, user, mode)
  110. local abcd1 = {"A", "B", "C", "D"}
  111. local abcd2 = {A = 1, B = 2, C = 3, D = 4}
  112. local ops = {"op1", "op2", "dst"}
  113. local dir = 0
  114. if mode == screwdriver.ROTATE_FACE then -- clock-wise
  115. dir = 1
  116. if user and user:is_player() then
  117. minetest.chat_send_player(user:get_player_name(),
  118. "FPGA ports have been rotated clockwise.")
  119. end
  120. elseif mode == screwdriver.ROTATE_AXIS then -- counter-clockwise
  121. dir = -1
  122. if user and user:is_player() then
  123. minetest.chat_send_player(user:get_player_name(),
  124. "FPGA ports have been rotated counter-clockwise.")
  125. end
  126. end
  127. local meta = minetest.get_meta(pos)
  128. local instr = lcore.deserialize(meta:get_string("instr"))
  129. for i = 1, #instr do
  130. for _, op in ipairs(ops) do
  131. local o = instr[i][op]
  132. if o and o.type == "io" then
  133. local num = abcd2[o.port]
  134. num = num + dir
  135. if num > 4 then num = 1
  136. elseif num < 1 then num = 4 end
  137. instr[i][op].port = abcd1[num]
  138. end
  139. end
  140. end
  141. meta:set_string("instr", lcore.serialize(instr))
  142. plg.update_formspec(pos, instr)
  143. return true
  144. end,
  145. })
  146. plg.to_formspec_string = function(is)
  147. local function dropdown_op(x, y, name, val)
  148. local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
  149. .. "0.75,0.5;" .. name .. ";" -- the height seems to be ignored?
  150. s = s .. " ,A,B,C,D,0,1,2,3,4,5,6,7,8,9;"
  151. if val == nil then
  152. s = s .. "0" -- actually selects no field at all
  153. elseif val.type == "io" then
  154. local mapping = {
  155. ["A"] = 1,
  156. ["B"] = 2,
  157. ["C"] = 3,
  158. ["D"] = 4,
  159. }
  160. s = s .. tostring(1 + mapping[val.port])
  161. else -- "reg"
  162. s = s .. tostring(6 + val.n)
  163. end
  164. return s .. "]"
  165. end
  166. local function dropdown_action(x, y, name, val)
  167. local s = "dropdown[" .. tostring(x) .. "," .. tostring(y) .. ";"
  168. .. "1.125,0.5;" .. name .. ";" -- the height seems to be ignored?
  169. s = s .. " , AND, OR, NOT, XOR,NAND, =,XNOR;"
  170. if val == nil then
  171. return s .. "0]" -- actually selects no field at all
  172. end
  173. local mapping = {
  174. ["and"] = 1,
  175. ["or"] = 2,
  176. ["not"] = 3,
  177. ["xor"] = 4,
  178. ["nand"] = 5,
  179. ["buf"] = 6,
  180. ["xnor"] = 7,
  181. }
  182. return s .. tostring(1 + mapping[val]) .. "]"
  183. end
  184. local s = "size[9,9]"..
  185. "label[3.4,-0.15;FPGA gate configuration]"..
  186. "button_exit[7,7.5;2,2.5;program;Program]"..
  187. "box[4.2,0.5;0.03,7;#ffffff]"..
  188. "label[0.25,0.25;op. 1]"..
  189. "label[1.0,0.25;gate type]"..
  190. "label[2.125,0.25;op. 2]"..
  191. "label[3.15,0.25;dest]"..
  192. "label[4.5,0.25;op. 1]"..
  193. "label[5.25,0.25;gate type]"..
  194. "label[6.375,0.25;op. 2]"..
  195. "label[7.4,0.25;dest]"
  196. local x = 1 - 0.75
  197. local y = 1 - 0.25
  198. for i = 1, 14 do
  199. local cur = is[i]
  200. s = s .. dropdown_op (x , y, tostring(i).."op1", cur.op1)
  201. s = s .. dropdown_action(x+0.75 , y, tostring(i).."act", cur.action)
  202. s = s .. dropdown_op (x+1.875, y, tostring(i).."op2", cur.op2)
  203. s = s .. "label[" .. tostring(x+2.625) .. "," .. tostring(y+0.1) .. "; ->]"
  204. s = s .. dropdown_op (x+2.9 , y, tostring(i).."dst", cur.dst)
  205. y = y + 1
  206. if i == 7 then
  207. x = 4.5
  208. y = 1 - 0.25
  209. end
  210. end
  211. return s
  212. end
  213. plg.from_formspec_fields = function(fields)
  214. local function read_op(s)
  215. if s == nil or s == " " then
  216. return nil
  217. elseif s == "A" or s == "B" or s == "C" or s == "D" then
  218. return {type = "io", port = s}
  219. else
  220. return {type = "reg", n = tonumber(s)}
  221. end
  222. end
  223. local function read_action(s)
  224. if s == nil or s == " " then
  225. return nil
  226. end
  227. local mapping = {
  228. ["AND"] = "and",
  229. ["OR"] = "or",
  230. ["NOT"] = "not",
  231. ["XOR"] = "xor",
  232. ["NAND"] = "nand",
  233. ["="] = "buf",
  234. ["XNOR"] = "xnor",
  235. }
  236. s = s:gsub("^%s*", "") -- remove leading spaces
  237. return mapping[s]
  238. end
  239. local is = {}
  240. for i = 1, 14 do
  241. local cur = {}
  242. cur.op1 = read_op(fields[tonumber(i) .. "op1"])
  243. cur.action = read_action(fields[tonumber(i) .. "act"])
  244. cur.op2 = read_op(fields[tonumber(i) .. "op2"])
  245. cur.dst = read_op(fields[tonumber(i) .. "dst"])
  246. is[#is + 1] = cur
  247. end
  248. return is
  249. end
  250. plg.update_formspec = function(pos, is)
  251. if type(is) == "string" then -- serialized string
  252. is = lcore.deserialize(is)
  253. end
  254. local meta = minetest.get_meta(pos)
  255. local form = plg.to_formspec_string(is)
  256. local err = lcore.validate(is)
  257. if err == nil then
  258. meta:set_int("valid", 1)
  259. meta:set_string("infotext", "FPGA (functional)")
  260. else
  261. meta:set_int("valid", 0)
  262. meta:set_string("infotext", "FPGA")
  263. local fmsg = minetest.colorize("#ff0000", minetest.formspec_escape(err.msg))
  264. form = form .. plg.red_box_around(err.i) ..
  265. "label[0.25,8.25;The gate configuration is erroneous in the marked area:]"..
  266. "label[0.25,8.5;" .. fmsg .. "]"
  267. end
  268. meta:set_string("formspec", form)
  269. -- reset ports and run programmed logic
  270. plg.setports(pos, false, false, false, false)
  271. plg.update(pos)
  272. end
  273. plg.red_box_around = function(i)
  274. local x, y
  275. if i > 7 then
  276. x = 4.5
  277. y = 0.75 + (i - 8)
  278. else
  279. x = 0.25
  280. y = 0.75 + (i - 1)
  281. end
  282. return string.format("box[%f,%f;3.8,0.8;#ff0000]", x-0.1, y-0.05)
  283. end
  284. plg.update = function(pos)
  285. local meta = minetest.get_meta(pos)
  286. if meta:get_int("valid") ~= 1 then
  287. return
  288. elseif mesecon.do_overheat(pos) then
  289. plg.setports(pos, false, false, false, false)
  290. meta:set_int("valid", 0)
  291. meta:set_string("infotext", "FPGA (overheated)")
  292. return
  293. end
  294. local is = lcore.deserialize(meta:get_string("instr"))
  295. local A, B, C, D = plg.getports(pos)
  296. A, B, C, D = lcore.interpret(is, A, B, C, D)
  297. plg.setports(pos, A, B, C, D)
  298. end
  299. plg.ports_changed = function(pos, rule, newstate)
  300. if rule == nil then return end
  301. local meta = minetest.get_meta(pos)
  302. local states
  303. local s = meta:get_string("portstates")
  304. if s == nil then
  305. states = {false, false, false, false}
  306. else
  307. states = {
  308. s:sub(1, 1) == "1",
  309. s:sub(2, 2) == "1",
  310. s:sub(3, 3) == "1",
  311. s:sub(4, 4) == "1",
  312. }
  313. end
  314. -- trick to transform rules (see register_node) into port number
  315. local portno = ({4, 1, nil, 3, 2})[3 + rule.x + 2*rule.z]
  316. states[portno] = (newstate == "on")
  317. meta:set_string("portstates",
  318. (states[1] and "1" or "0") .. (states[2] and "1" or "0") ..
  319. (states[3] and "1" or "0") .. (states[4] and "1" or "0")
  320. )
  321. end
  322. plg.getports = function(pos) -- gets merged states of INPUT & OUTPUT
  323. local sin, sout
  324. local s = minetest.get_meta(pos):get_string("portstates")
  325. if s == nil then
  326. sin = {false, false, false, false}
  327. else
  328. sin = {
  329. s:sub(1, 1) == "1",
  330. s:sub(2, 2) == "1",
  331. s:sub(3, 3) == "1",
  332. s:sub(4, 4) == "1",
  333. }
  334. end
  335. local name = minetest.get_node(pos).name
  336. assert(name:find("mesecons_fpga:fpga") == 1)
  337. local off = #"mesecons_fpga:fpga"
  338. sout = {
  339. name:sub(off+4, off+4) == "1",
  340. name:sub(off+3, off+3) == "1",
  341. name:sub(off+2, off+2) == "1",
  342. name:sub(off+1, off+1) == "1",
  343. }
  344. return unpack({
  345. sin[1] or sout[1],
  346. sin[2] or sout[2],
  347. sin[3] or sout[3],
  348. sin[4] or sout[4],
  349. })
  350. end
  351. plg.setports = function(pos, A, B, C, D) -- sets states of OUTPUT
  352. local base = "mesecons_fpga:fpga"
  353. local name = base
  354. .. (D and "1" or "0") .. (C and "1" or "0")
  355. .. (B and "1" or "0") .. (A and "1" or "0")
  356. minetest.swap_node(pos, {name = name, param2 = minetest.get_node(pos).param2})
  357. if A ~= nil then
  358. local ru = plg.rules[base .. "0001"]
  359. if A then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
  360. end
  361. if B ~= nil then
  362. local ru = plg.rules[base .. "0010"]
  363. if B then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
  364. end
  365. if C ~= nil then
  366. local ru = plg.rules[base .. "0100"]
  367. if C then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
  368. end
  369. if D ~= nil then
  370. local ru = plg.rules[base .. "1000"]
  371. if D then mesecon.receptor_on(pos, ru) else mesecon.receptor_off(pos, ru) end
  372. end
  373. end
  374. minetest.register_craft({
  375. output = "mesecons_fpga:fpga0000 2",
  376. recipe = {
  377. {'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
  378. {'mesecons_materials:silicon', 'mesecons_materials:silicon'},
  379. {'group:mesecon_conductor_craftable', 'group:mesecon_conductor_craftable'},
  380. }
  381. })