123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- --Okay, so we're making a Rubik's Cube!
- --Let's start with the basics.
- local colors = {
- 'green', -- +Y
- 'blue', -- -Y
- 'red', -- +X
- 'orange', -- -X
- 'white', -- +Z
- 'yellow', -- -Z
- }
- local materials = {} --what you craft the spawner with
- local tiles = {} --Base colors
- local spawntex = {} --what is on the spawner
- local cubetex = {} --what is on the cubelets
- for color = 1, #colors do
- materials[color] = 'wool:'..colors[color]
- tiles[color] = 'wool_'..colors[color]..'.png'
- spawntex[color] = tiles[color]..'^rubiks_three.png'
- cubetex[color] = tiles[color]..'^rubiks_outline.png'
- end
- --is this the center of a face, on the edge, or is it a corner?
- local function get_axesoff(pos)
- axesoff = 0
- dir = {x=0, y=0, z=0}
- center = {unpack(pos)}
- local meta = minetest.env:get_meta(pos)
- local string = meta:get_string('cube_center')
- if string ~= nil then
- center = minetest.string_to_pos(string)
- if center ~= nil then
- dir = {x=pos.x-center.x, y=pos.y-center.y, z=pos.z-center.z}
- axesoff = (dir.x ~= 0 and 1 or 0)
- + (dir.y ~= 0 and 1 or 0)
- + (dir.z ~= 0 and 1 or 0)
- end
- end
- return axesoff, dir, center
- end
- --this isn't in the cubelets' on_construct
- --because the meta already needs to be set
- local function set_cubelet_formspec(pos, size)
- axesoff, dir, center = get_axesoff(pos)
- if axesoff == 1 then
- local meta = minetest.env:get_meta(pos)
- meta:set_string("formspec",
- "size["..size..","..size.."]"..
-
- "image_button_exit[0,0;1,1;"..minetest.inventorycube(
- tiles[1]..'^rubiks_four.png',
- tiles[6]..'^rubiks_four.png',
- tiles[3]..'^rubiks_four.png')..
- ";larger;]"..
- "image_button_exit[0,1;1,1;"..minetest.inventorycube(
- spawntex[1],
- spawntex[6],
- spawntex[3])..
- ";reset;]"..
- --"image_button_exit[0,2;1,1;rubiks_scramble.png;scramble;]"..
- "image_button_exit[0,2;1,1;"..minetest.inventorycube(
- tiles[1]..'^rubiks_two.png',
- tiles[6]..'^rubiks_two.png',
- tiles[3]..'^rubiks_two.png')..
- ";smaller;]"..
- "image_button_exit[1,0;1,1;"..minetest.inventorycube(
- spawntex[1],
- spawntex[4],
- spawntex[6])..
- ";L3;]"..
- "image_button_exit[1,1;1,1;"..minetest.inventorycube(
- spawntex[1],
- tiles[6]..'^rubiks_with_orange.png^rubiks_three.png',
- tiles[3]..'^rubiks_with_yellow.png^rubiks_three.png')..
- ";L1;]"..
- "image_button_exit[1,2;1,1;"..minetest.inventorycube(
- spawntex[1],
- tiles[6]..'^rubiks_with_orange.png^rubiks_three.png^[transformR180',
- tiles[3]..'^rubiks_with_yellow.png^rubiks_three.png^[transformR180')..
- ";L2;]"..
-
- "image_button_exit[2,0;1,1;"..minetest.inventorycube(
- spawntex[1],
- spawntex[3],
- spawntex[5])..
- ";R3;]"..
- "image_button_exit[2,1;1,1;"..minetest.inventorycube(
- spawntex[1],
- tiles[6]..'^rubiks_with_red.png^rubiks_three.png',
- tiles[3]..'^rubiks_with_white.png^rubiks_three.png')..
- ";R1;]"..
- "image_button_exit[2,2;1,1;"..minetest.inventorycube(
- spawntex[1],
- tiles[6]..'^rubiks_with_red.png^rubiks_three.png^[transformR180',
- tiles[3]..'^rubiks_with_white.png^rubiks_three.png^[transformR180')..
- ";R2;]"..
- '')
- end
- end
- local function expand_cube(pos, spawn)
- for x = pos.x-1, pos.x+1 do
- for y = pos.y-1, pos.y+1 do
- for z = pos.z-1, pos.z+1 do
- pos2 = {x=x, y=y, z=z}
- if spawn then --create
- --don't overwrite the spawner
- if not(pos2.x==pos.x and pos2.y==pos.y and pos2.z==pos.z) then
- --always starts the same direction
- name = 'rubiks:cubelet'
- minetest.env:add_node(pos2, {name = name})
- --keep track of center for the purpose of rotating the cube
- local meta = minetest.env:get_meta(pos2)
- meta:set_string('cube_center',
- minetest.pos_to_string(pos)
- )
- set_cubelet_formspec(pos2, 3)
- end
- else --delete
- minetest.env:remove_node(pos2)
- end
- end
- end
- end
- if create then
- --keep a record so you can't get two cubes from one, or something like that
- local meta = minetest.env:get_meta(pos)
- meta:set_int('has_spawned', 1)
- end
- end
- --can't make a rubik's cube without the cube
- minetest.register_node('rubiks:cube', {
- --spawner because I don't get the uv pos yet
- description = "Rubik's Cube",
- tiles = spawntex,
- --show green, yellow, red sides to look 3d in inventory
- inventory_image = minetest.inventorycube(spawntex[1], spawntex[6], spawntex[3]),
- --want it to be diggable, quickly
- groups = {crumbly=3},
- on_punch = function(pos, node, puncher)
- for x = pos.x-1, pos.x+1 do
- for y = pos.y-1, pos.y+1 do
- for z = pos.z-1, pos.z+1 do
- if not(pos.x==x and pos.y==y and pos.z==z) then
- if minetest.env:get_node({x=x, y=y, z=z}).name ~= 'air' then
- --put it on a pedestal then remove the pedestal
- minetest.chat_send_player(puncher:get_player_name(), "Clear some space for Rubik's cube to expand")
- return
- end
- end
- end
- end
- end
- --surrounded by air, so
- expand_cube(pos, true)
- end,
- can_dig = function(pos, digger)
- --digging the center of a spawned cube yields
- --an extra cube without this - don't cheat when flying
- local meta = minetest.env:get_meta(pos)
- if meta:get_int('has_spawned') == 1 then
- return false
- end
- return true
- end,
- })
- --100% wool, need a way to get wool now.
- minetest.register_craft({
- type = "shapeless",
- output = "rubiks:cube",
- recipe = materials,
- })
- local function rotate_cube(pos, dir, clockwise, layer)
- --save cube to rotate without losing data
- cube = {}
- for x = -1, 1 do cube[x] = {}
- for y = -1, 1 do cube[x][y] = {}
- for z = -1, 1 do
- --read absolute position, save relative position
- pos2 = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
- cube[x][y][z] = {
- node = minetest.env:get_node(pos2),
- meta = minetest.env:get_meta(pos2):to_table()
- }
- end
- end
- end
- --what side of the cube will be rotated on what axes
- loadpos, axes = {0, 0, 0}, {}
- if dir.x ~= 0 then
- loadpos[1] = dir.x
- for l=1, layer-1 do
- loadpos[1] = loadpos[1] - dir.x
- end
- axes[1] = 3--z
- axes[2] = 2--y
- end
- if dir.y ~= 0 then
- loadpos[2] = dir.y
- for l=1, layer-1 do
- loadpos[2] = loadpos[2] - dir.y
- end
- axes[1] = 1--x
- axes[2] = 3--z
- end
- if dir.z ~= 0 then
- loadpos[3] = dir.z
- for l=1, layer-1 do
- loadpos[3] = loadpos[3] - dir.z
- end
- axes[1] = 2--y
- axes[2] = 1--x
- end
- sign = true
- if dir.x == -1 or dir.y == -1 or dir.z == -1 then
- clockwise = not clockwise
- --still clockwise, just from the opposite perspective
- sign = false
- end
- --start rotating
- for firstaxis = -1, 1 do loadpos[axes[1]] = firstaxis
- for secondaxis = -1, 1 do loadpos[axes[2]] = secondaxis
- --don't lose data here either
- writepos = {unpack(loadpos)}
- --rotate around center of face
- writepos[axes[1]] = loadpos[axes[2]] * (clockwise and 1 or -1)
- writepos[axes[2]] = loadpos[axes[1]] * (clockwise and -1 or 1)
- --get absolute position
- pos2 = {x=pos.x+writepos[1], y=pos.y+writepos[2], z=pos.z+writepos[3]}
- --rotate cubelet itself
- loadcubelet = cube[loadpos[1]][loadpos[2]][loadpos[3]]
- name = loadcubelet.node.name
- if name ~= 'rubiks:cube' then--continue end
- --turnaxis = dir.x and 1 or dir.y and 2 or dir.z and 3
- if dir.x ~= 0 then turnaxis = 1
- elseif dir.y ~= 0 then turnaxis = 2
- else turnaxis = 3 end
- --print(minetest.registered_nodes['rubiks:cubelet'].tiles[getface(loadcubelet.node.param2, turnaxis, negative)])
- --place it
- minetest.env:add_node(pos2, {name = name, param2 =
- axisRotate(loadcubelet.node.param2, turnaxis, clockwise and 90 or -90)
- })
- --
- --print(colors[getface(loadcubelet.node.param2, turnaxis, sign)])
- --
- local meta = minetest.env:get_meta(pos2)
- meta:from_table(loadcubelet.meta)
- end
- end
- end
- end
- local function start_rotation(pos, clockwise, layer)
- axesoff, dir, center = get_axesoff(pos)
- if axesoff == 1 then --center
- if layer == 6 then
- for layer = 1, 3 do
- rotate_cube(center, dir, clockwise, layer)
- end
- else
- rotate_cube(center, dir, clockwise, layer)
- end
- elseif axesoff == 2 then --edge
- else --corner
- end
- end
- local function register_cubelets()
- minetest.register_node('rubiks:cubelet', {
- description = "Rubik's Cubelet",
- tiles = cubetex,
- inventory_image = minetest.inventorycube(cubetex[1], cubetex[6], cubetex[3]),
- groups = {crumbly=2, not_in_creative_inventory = 1},
- after_dig_node = function(pos, oldnode, oldmeta, digger)
- local string = oldmeta.fields.cube_center
- if string ~= nil then
- pos = minetest.string_to_pos(string)
- expand_cube(pos, false)
- end
- end,
- drop = 'rubiks:cube',
- on_punch = function(pos, node, puncher)
- start_rotation(pos, true, 1)
- end,
- --cubelets not in the center of the face never get formspecs
- on_receive_fields = function(pos, formname, fields, sender)
- if fields.L1 then
- start_rotation(pos, false, 1)
- elseif fields.L2 then
- start_rotation(pos, false, 3)
- elseif fields.L3 then
- start_rotation(pos, false, 6)
- elseif fields.R1 then
- start_rotation(pos, true, 1)
- elseif fields.R2 then
- start_rotation(pos, true, 3)
- elseif fields.R3 then
- start_rotation(pos, true, 6)
- elseif fields.larger then
- minetest.chat_send_player(sender:get_player_name(),
- 'TODO: make the cube have more layers'
- )
- elseif fields.smaller then
- minetest.chat_send_player(sender:get_player_name(),
- 'TODO: make the cube have less layers'
- )
- else --reset
- minetest.chat_send_player(sender:get_player_name(),
- 'TODO: toggle between reset/scramble'
- )
- end
- end,
- paramtype2 = 'facedir',
- })
- end register_cubelets()
- --temporary aliases to update cleanly
- for rotations = 1, 6 do
- minetest.register_alias('rubiks:cubelet'..rotations, 'rubiks:cubelet')
- end
- --Stealable Code
- --You may edit this for coding style
- --Do not use this in your mod. This is for sharing only.
- --Put this somewhere where all modders can get to it
- -------------------------------------------------------------------------------
- function axisRotate(facedir, turnaxis, turnrot)
- turnrot = math.floor(turnrot / 90) % 4
- axis = math.floor(facedir / 4)
- rot = facedir % 4
- if turnaxis == 1 then --x
- if 3 == axis or axis == 4 then
- if axis == 4 then turnrot = -turnrot end
- rot = (rot + turnrot) % 4
- else
- for r = 0, turnrot-1 do
- if axis == 0 then axis = 1
- elseif axis == 1 then axis = 5
- rot=(rot+2)%4
- elseif axis == 5 then axis = 2
- rot=(rot-2)%4
- elseif axis == 2 then axis = 0
- else
- error("axisRotate: my bad")
- end
- end
- end
- elseif turnaxis == 2 then --y
- if 0 == axis or axis == 5 then
- if axis == 5 then turnrot = -turnrot end
- rot = (rot + turnrot) % 4
- else
- for r = 0, turnrot-1 do
- if axis == 1 then axis = 3
- elseif axis == 3 then axis = 2
- elseif axis == 2 then axis = 4
- elseif axis == 4 then axis = 1
- else
- error("axisRotate: my bad")
- end rot = (rot + 1) % 4
- end
- end
- elseif turnaxis == 3 then --z
- if 1 == axis or axis == 2 then
- if axis == 2 then turnrot = -turnrot end
- rot = (rot + turnrot) % 4
- else
- for r = 0, turnrot-1 do
- if axis == 0 then axis = 4
- elseif axis == 4 then axis = 5
- elseif axis == 5 then axis = 3
- elseif axis == 3 then axis = 0
- else
- error("axisRotate: my bad")
- end
- end
- end
- else
- error("axisRotate: turnaxis not 1-3")
- end
- facedir = axis * 4 + rot
- return facedir
- end
- local function rotfaces(faces, turnaxis, turnrot)
- turnrot = turnrot % 4
- for r = 0, turnrot-1 do
- if turnaxis == 1 then --x
- torot = {1, 5, 2, 6}
- elseif turnaxis == 2 then --y
- torot = {6, 4, 5, 3}
- elseif turnaxis == 3 then --z
- torot = {1, 4, 2, 3}
- else
- error("rotfaces: turnaxis: my bad")
- end
- wraparound = faces[torot[3]]
- faces[torot[3]] = faces[torot[2]]
- faces[torot[2]] = faces[torot[1]]
- faces[torot[1]] = wraparound
- end
- return faces
- end
- function getfaces(facedir)
- --FIXME?
- --tiles ±Y±X±Z
- --facedir axes +Y±Z±X-Y
-
- axis = math.floor(facedir / 4)
- rot = facedir % 4
- -- +Y -Y +X -X +Z -Z
- faces = {1, 2, 3, 4, 5, 6}
- if axis == 0 then -- +Y
- turnaxis = 2
- elseif axis == 1 then -- +Z
- faces = rotfaces(faces, 1, 1) -- +X
- turnaxis = 3
- elseif axis == 2 then -- -Z
- faces = rotfaces(faces, 1, -1) -- -X
- turnaxis = 3
- rot = -rot
- elseif axis == 3 then -- +X
- faces = rotfaces(faces, 3, -1) -- -Z
- turnaxis = 1
- elseif axis == 4 then -- -X
- faces = rotfaces(faces, 3, 1) -- +Z
- turnaxis = 1
- rot = -rot
- elseif axis == 5 then -- -Y
- faces = rotfaces(faces, 3, 2)-- ±Z
- turnaxis = 2
- rot = -rot
- else
- error("getfaces: bad facedir: "..facedir..' '..axis..' '..rot)
- end
- return rotfaces(faces, turnaxis, rot)
- end
- function getface(facedir, axis, sign)
- faces = getfaces(facedir)
- return faces[
- axis == 1 and (sign and 3 or 4) or (
- axis == 2 and (sign and 1 or 2) or (
- axis == 3 and (sign and 5 or 6)
- )
- )
- ]
- end
|