init.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. --Okay, so we're making a Rubik's Cube!
  2. --Let's start with the basics.
  3. local colors = {
  4. 'green', -- +Y
  5. 'blue', -- -Y
  6. 'red', -- +X
  7. 'orange', -- -X
  8. 'white', -- +Z
  9. 'yellow', -- -Z
  10. }
  11. local materials = {} --what you craft the spawner with
  12. local tiles = {} --Base colors
  13. local spawntex = {} --what is on the spawner
  14. local cubetex = {} --what is on the cubelets
  15. for color = 1, #colors do
  16. materials[color] = 'wool:'..colors[color]
  17. tiles[color] = 'wool_'..colors[color]..'.png'
  18. spawntex[color] = tiles[color]..'^rubiks_three.png'
  19. cubetex[color] = tiles[color]..'^rubiks_outline.png'
  20. end
  21. --is this the center of a face, on the edge, or is it a corner?
  22. local function get_axesoff(pos)
  23. axesoff = 0
  24. dir = {x=0, y=0, z=0}
  25. center = {unpack(pos)}
  26. local meta = minetest.env:get_meta(pos)
  27. local string = meta:get_string('cube_center')
  28. if string ~= nil then
  29. center = minetest.string_to_pos(string)
  30. if center ~= nil then
  31. dir = {x=pos.x-center.x, y=pos.y-center.y, z=pos.z-center.z}
  32. axesoff = (dir.x ~= 0 and 1 or 0)
  33. + (dir.y ~= 0 and 1 or 0)
  34. + (dir.z ~= 0 and 1 or 0)
  35. end
  36. end
  37. return axesoff, dir, center
  38. end
  39. --this isn't in the cubelets' on_construct
  40. --because the meta already needs to be set
  41. local function set_cubelet_formspec(pos, size)
  42. axesoff, dir, center = get_axesoff(pos)
  43. if axesoff == 1 then
  44. local meta = minetest.env:get_meta(pos)
  45. meta:set_string("formspec",
  46. "size["..size..","..size.."]"..
  47. "image_button_exit[0,0;1,1;"..minetest.inventorycube(
  48. tiles[1]..'^rubiks_four.png',
  49. tiles[6]..'^rubiks_four.png',
  50. tiles[3]..'^rubiks_four.png')..
  51. ";larger;]"..
  52. "image_button_exit[0,1;1,1;"..minetest.inventorycube(
  53. spawntex[1],
  54. spawntex[6],
  55. spawntex[3])..
  56. ";reset;]"..
  57. --"image_button_exit[0,2;1,1;rubiks_scramble.png;scramble;]"..
  58. "image_button_exit[0,2;1,1;"..minetest.inventorycube(
  59. tiles[1]..'^rubiks_two.png',
  60. tiles[6]..'^rubiks_two.png',
  61. tiles[3]..'^rubiks_two.png')..
  62. ";smaller;]"..
  63. "image_button_exit[1,0;1,1;"..minetest.inventorycube(
  64. spawntex[1],
  65. spawntex[4],
  66. spawntex[6])..
  67. ";L3;]"..
  68. "image_button_exit[1,1;1,1;"..minetest.inventorycube(
  69. spawntex[1],
  70. tiles[6]..'^rubiks_with_orange.png^rubiks_three.png',
  71. tiles[3]..'^rubiks_with_yellow.png^rubiks_three.png')..
  72. ";L1;]"..
  73. "image_button_exit[1,2;1,1;"..minetest.inventorycube(
  74. spawntex[1],
  75. tiles[6]..'^rubiks_with_orange.png^rubiks_three.png^[transformR180',
  76. tiles[3]..'^rubiks_with_yellow.png^rubiks_three.png^[transformR180')..
  77. ";L2;]"..
  78. "image_button_exit[2,0;1,1;"..minetest.inventorycube(
  79. spawntex[1],
  80. spawntex[3],
  81. spawntex[5])..
  82. ";R3;]"..
  83. "image_button_exit[2,1;1,1;"..minetest.inventorycube(
  84. spawntex[1],
  85. tiles[6]..'^rubiks_with_red.png^rubiks_three.png',
  86. tiles[3]..'^rubiks_with_white.png^rubiks_three.png')..
  87. ";R1;]"..
  88. "image_button_exit[2,2;1,1;"..minetest.inventorycube(
  89. spawntex[1],
  90. tiles[6]..'^rubiks_with_red.png^rubiks_three.png^[transformR180',
  91. tiles[3]..'^rubiks_with_white.png^rubiks_three.png^[transformR180')..
  92. ";R2;]"..
  93. '')
  94. end
  95. end
  96. local function expand_cube(pos, spawn)
  97. for x = pos.x-1, pos.x+1 do
  98. for y = pos.y-1, pos.y+1 do
  99. for z = pos.z-1, pos.z+1 do
  100. pos2 = {x=x, y=y, z=z}
  101. if spawn then --create
  102. --don't overwrite the spawner
  103. if not(pos2.x==pos.x and pos2.y==pos.y and pos2.z==pos.z) then
  104. --always starts the same direction
  105. name = 'rubiks:cubelet'
  106. minetest.env:add_node(pos2, {name = name})
  107. --keep track of center for the purpose of rotating the cube
  108. local meta = minetest.env:get_meta(pos2)
  109. meta:set_string('cube_center',
  110. minetest.pos_to_string(pos)
  111. )
  112. set_cubelet_formspec(pos2, 3)
  113. end
  114. else --delete
  115. minetest.env:remove_node(pos2)
  116. end
  117. end
  118. end
  119. end
  120. if create then
  121. --keep a record so you can't get two cubes from one, or something like that
  122. local meta = minetest.env:get_meta(pos)
  123. meta:set_int('has_spawned', 1)
  124. end
  125. end
  126. --can't make a rubik's cube without the cube
  127. minetest.register_node('rubiks:cube', {
  128. --spawner because I don't get the uv pos yet
  129. description = "Rubik's Cube",
  130. tiles = spawntex,
  131. --show green, yellow, red sides to look 3d in inventory
  132. inventory_image = minetest.inventorycube(spawntex[1], spawntex[6], spawntex[3]),
  133. --want it to be diggable, quickly
  134. groups = {crumbly=3},
  135. on_punch = function(pos, node, puncher)
  136. for x = pos.x-1, pos.x+1 do
  137. for y = pos.y-1, pos.y+1 do
  138. for z = pos.z-1, pos.z+1 do
  139. if not(pos.x==x and pos.y==y and pos.z==z) then
  140. if minetest.env:get_node({x=x, y=y, z=z}).name ~= 'air' then
  141. --put it on a pedestal then remove the pedestal
  142. minetest.chat_send_player(puncher:get_player_name(), "Clear some space for Rubik's cube to expand")
  143. return
  144. end
  145. end
  146. end
  147. end
  148. end
  149. --surrounded by air, so
  150. expand_cube(pos, true)
  151. end,
  152. can_dig = function(pos, digger)
  153. --digging the center of a spawned cube yields
  154. --an extra cube without this - don't cheat when flying
  155. local meta = minetest.env:get_meta(pos)
  156. if meta:get_int('has_spawned') == 1 then
  157. return false
  158. end
  159. return true
  160. end,
  161. })
  162. --100% wool, need a way to get wool now.
  163. minetest.register_craft({
  164. type = "shapeless",
  165. output = "rubiks:cube",
  166. recipe = materials,
  167. })
  168. local function rotate_cube(pos, dir, clockwise, layer)
  169. --save cube to rotate without losing data
  170. cube = {}
  171. for x = -1, 1 do cube[x] = {}
  172. for y = -1, 1 do cube[x][y] = {}
  173. for z = -1, 1 do
  174. --read absolute position, save relative position
  175. pos2 = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
  176. cube[x][y][z] = {
  177. node = minetest.env:get_node(pos2),
  178. meta = minetest.env:get_meta(pos2):to_table()
  179. }
  180. end
  181. end
  182. end
  183. --what side of the cube will be rotated on what axes
  184. loadpos, axes = {0, 0, 0}, {}
  185. if dir.x ~= 0 then
  186. loadpos[1] = dir.x
  187. for l=1, layer-1 do
  188. loadpos[1] = loadpos[1] - dir.x
  189. end
  190. axes[1] = 3--z
  191. axes[2] = 2--y
  192. end
  193. if dir.y ~= 0 then
  194. loadpos[2] = dir.y
  195. for l=1, layer-1 do
  196. loadpos[2] = loadpos[2] - dir.y
  197. end
  198. axes[1] = 1--x
  199. axes[2] = 3--z
  200. end
  201. if dir.z ~= 0 then
  202. loadpos[3] = dir.z
  203. for l=1, layer-1 do
  204. loadpos[3] = loadpos[3] - dir.z
  205. end
  206. axes[1] = 2--y
  207. axes[2] = 1--x
  208. end
  209. sign = true
  210. if dir.x == -1 or dir.y == -1 or dir.z == -1 then
  211. clockwise = not clockwise
  212. --still clockwise, just from the opposite perspective
  213. sign = false
  214. end
  215. --start rotating
  216. for firstaxis = -1, 1 do loadpos[axes[1]] = firstaxis
  217. for secondaxis = -1, 1 do loadpos[axes[2]] = secondaxis
  218. --don't lose data here either
  219. writepos = {unpack(loadpos)}
  220. --rotate around center of face
  221. writepos[axes[1]] = loadpos[axes[2]] * (clockwise and 1 or -1)
  222. writepos[axes[2]] = loadpos[axes[1]] * (clockwise and -1 or 1)
  223. --get absolute position
  224. pos2 = {x=pos.x+writepos[1], y=pos.y+writepos[2], z=pos.z+writepos[3]}
  225. --rotate cubelet itself
  226. loadcubelet = cube[loadpos[1]][loadpos[2]][loadpos[3]]
  227. name = loadcubelet.node.name
  228. if name ~= 'rubiks:cube' then--continue end
  229. --turnaxis = dir.x and 1 or dir.y and 2 or dir.z and 3
  230. if dir.x ~= 0 then turnaxis = 1
  231. elseif dir.y ~= 0 then turnaxis = 2
  232. else turnaxis = 3 end
  233. --print(minetest.registered_nodes['rubiks:cubelet'].tiles[getface(loadcubelet.node.param2, turnaxis, negative)])
  234. --place it
  235. minetest.env:add_node(pos2, {name = name, param2 =
  236. axisRotate(loadcubelet.node.param2, turnaxis, clockwise and 90 or -90)
  237. })
  238. --
  239. --print(colors[getface(loadcubelet.node.param2, turnaxis, sign)])
  240. --
  241. local meta = minetest.env:get_meta(pos2)
  242. meta:from_table(loadcubelet.meta)
  243. end
  244. end
  245. end
  246. end
  247. local function start_rotation(pos, clockwise, layer)
  248. axesoff, dir, center = get_axesoff(pos)
  249. if axesoff == 1 then --center
  250. if layer == 6 then
  251. for layer = 1, 3 do
  252. rotate_cube(center, dir, clockwise, layer)
  253. end
  254. else
  255. rotate_cube(center, dir, clockwise, layer)
  256. end
  257. elseif axesoff == 2 then --edge
  258. else --corner
  259. end
  260. end
  261. local function register_cubelets()
  262. minetest.register_node('rubiks:cubelet', {
  263. description = "Rubik's Cubelet",
  264. tiles = cubetex,
  265. inventory_image = minetest.inventorycube(cubetex[1], cubetex[6], cubetex[3]),
  266. groups = {crumbly=2, not_in_creative_inventory = 1},
  267. after_dig_node = function(pos, oldnode, oldmeta, digger)
  268. local string = oldmeta.fields.cube_center
  269. if string ~= nil then
  270. pos = minetest.string_to_pos(string)
  271. expand_cube(pos, false)
  272. end
  273. end,
  274. drop = 'rubiks:cube',
  275. on_punch = function(pos, node, puncher)
  276. start_rotation(pos, true, 1)
  277. end,
  278. --cubelets not in the center of the face never get formspecs
  279. on_receive_fields = function(pos, formname, fields, sender)
  280. if fields.L1 then
  281. start_rotation(pos, false, 1)
  282. elseif fields.L2 then
  283. start_rotation(pos, false, 3)
  284. elseif fields.L3 then
  285. start_rotation(pos, false, 6)
  286. elseif fields.R1 then
  287. start_rotation(pos, true, 1)
  288. elseif fields.R2 then
  289. start_rotation(pos, true, 3)
  290. elseif fields.R3 then
  291. start_rotation(pos, true, 6)
  292. elseif fields.larger then
  293. minetest.chat_send_player(sender:get_player_name(),
  294. 'TODO: make the cube have more layers'
  295. )
  296. elseif fields.smaller then
  297. minetest.chat_send_player(sender:get_player_name(),
  298. 'TODO: make the cube have less layers'
  299. )
  300. else --reset
  301. minetest.chat_send_player(sender:get_player_name(),
  302. 'TODO: toggle between reset/scramble'
  303. )
  304. end
  305. end,
  306. paramtype2 = 'facedir',
  307. })
  308. end register_cubelets()
  309. --temporary aliases to update cleanly
  310. for rotations = 1, 6 do
  311. minetest.register_alias('rubiks:cubelet'..rotations, 'rubiks:cubelet')
  312. end
  313. --Stealable Code
  314. --You may edit this for coding style
  315. --Do not use this in your mod. This is for sharing only.
  316. --Put this somewhere where all modders can get to it
  317. -------------------------------------------------------------------------------
  318. function axisRotate(facedir, turnaxis, turnrot)
  319. turnrot = math.floor(turnrot / 90) % 4
  320. axis = math.floor(facedir / 4)
  321. rot = facedir % 4
  322. if turnaxis == 1 then --x
  323. if 3 == axis or axis == 4 then
  324. if axis == 4 then turnrot = -turnrot end
  325. rot = (rot + turnrot) % 4
  326. else
  327. for r = 0, turnrot-1 do
  328. if axis == 0 then axis = 1
  329. elseif axis == 1 then axis = 5
  330. rot=(rot+2)%4
  331. elseif axis == 5 then axis = 2
  332. rot=(rot-2)%4
  333. elseif axis == 2 then axis = 0
  334. else
  335. error("axisRotate: my bad")
  336. end
  337. end
  338. end
  339. elseif turnaxis == 2 then --y
  340. if 0 == axis or axis == 5 then
  341. if axis == 5 then turnrot = -turnrot end
  342. rot = (rot + turnrot) % 4
  343. else
  344. for r = 0, turnrot-1 do
  345. if axis == 1 then axis = 3
  346. elseif axis == 3 then axis = 2
  347. elseif axis == 2 then axis = 4
  348. elseif axis == 4 then axis = 1
  349. else
  350. error("axisRotate: my bad")
  351. end rot = (rot + 1) % 4
  352. end
  353. end
  354. elseif turnaxis == 3 then --z
  355. if 1 == axis or axis == 2 then
  356. if axis == 2 then turnrot = -turnrot end
  357. rot = (rot + turnrot) % 4
  358. else
  359. for r = 0, turnrot-1 do
  360. if axis == 0 then axis = 4
  361. elseif axis == 4 then axis = 5
  362. elseif axis == 5 then axis = 3
  363. elseif axis == 3 then axis = 0
  364. else
  365. error("axisRotate: my bad")
  366. end
  367. end
  368. end
  369. else
  370. error("axisRotate: turnaxis not 1-3")
  371. end
  372. facedir = axis * 4 + rot
  373. return facedir
  374. end
  375. local function rotfaces(faces, turnaxis, turnrot)
  376. turnrot = turnrot % 4
  377. for r = 0, turnrot-1 do
  378. if turnaxis == 1 then --x
  379. torot = {1, 5, 2, 6}
  380. elseif turnaxis == 2 then --y
  381. torot = {6, 4, 5, 3}
  382. elseif turnaxis == 3 then --z
  383. torot = {1, 4, 2, 3}
  384. else
  385. error("rotfaces: turnaxis: my bad")
  386. end
  387. wraparound = faces[torot[3]]
  388. faces[torot[3]] = faces[torot[2]]
  389. faces[torot[2]] = faces[torot[1]]
  390. faces[torot[1]] = wraparound
  391. end
  392. return faces
  393. end
  394. function getfaces(facedir)
  395. --FIXME?
  396. --tiles ±Y±X±Z
  397. --facedir axes +Y±Z±X-Y
  398. axis = math.floor(facedir / 4)
  399. rot = facedir % 4
  400. -- +Y -Y +X -X +Z -Z
  401. faces = {1, 2, 3, 4, 5, 6}
  402. if axis == 0 then -- +Y
  403. turnaxis = 2
  404. elseif axis == 1 then -- +Z
  405. faces = rotfaces(faces, 1, 1) -- +X
  406. turnaxis = 3
  407. elseif axis == 2 then -- -Z
  408. faces = rotfaces(faces, 1, -1) -- -X
  409. turnaxis = 3
  410. rot = -rot
  411. elseif axis == 3 then -- +X
  412. faces = rotfaces(faces, 3, -1) -- -Z
  413. turnaxis = 1
  414. elseif axis == 4 then -- -X
  415. faces = rotfaces(faces, 3, 1) -- +Z
  416. turnaxis = 1
  417. rot = -rot
  418. elseif axis == 5 then -- -Y
  419. faces = rotfaces(faces, 3, 2)-- ±Z
  420. turnaxis = 2
  421. rot = -rot
  422. else
  423. error("getfaces: bad facedir: "..facedir..' '..axis..' '..rot)
  424. end
  425. return rotfaces(faces, turnaxis, rot)
  426. end
  427. function getface(facedir, axis, sign)
  428. faces = getfaces(facedir)
  429. return faces[
  430. axis == 1 and (sign and 3 or 4) or (
  431. axis == 2 and (sign and 1 or 2) or (
  432. axis == 3 and (sign and 5 or 6)
  433. )
  434. )
  435. ]
  436. end