--[[ k-smallblocks is a Minetest mod that adds smaller blocks to minetest aswell as its own node placement prediction/system Copyright (C) 2019 Kurtzmusch This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA --]] -- bitmaps are a binary representation of which of the 8 divisions of each node are filled by the material -- origin bitmaps are the ones that can be rotated to make any other of the 255 possibilities -- many bitmaps can be mapped to a single origin_bitmap, and each origin is a node registration -- the origin bitmaps are generated by generate_origin_bitmaps.lua -- 22 bitmaps will be generated and 22 nodes for each material will be registered -- the mod creates bitmaps when placing nodes over other nodes of the same material, -- then finds the correct node name and facedir using the maps int_facedir_map and int_name_map -- all 'unnamed nodes' will be called "_" -- 'named nodes' are the ones craftable and have a distinc name like "stair_" -- example of named node "stair_stonebrick" -- exmaple of unnamed node "127_stonebrick" -- named nodes apear in the creative inventory and can be crafted -- unnamed nodes will not drop themselves when dug, but will drop the equivalent amount in smallblock_ -- example: when a cobble inner stair is dug, it will drop 7 smallblock_cobble -- inner and outer stairs are not craftable, but easily buildable -- the mod doesnt work with screwdriver, the idea is that you never need to rotate anything because you can manually -- dig a smallblock out of a node and replace it in other place in that node. textures always lign up aswell -- definitions of named nodes will contain a recipe, logic for correct orientation when placed and dug modpath = minetest.get_modpath("k_smallblocks") smallblocks = {} smallblocks.int_facedir_map = { [255]=0 } -- maps an int that represents a bitmap to a facedir smallblocks.int_name_map = { [255]="" } -- maps an int that represents a bitmap to a name. many ints can map to a single name. a name is a string of the interger that represents the bitmap smallblocks.name_int_map = {} -- inverse of the above map, might be usefull in the future -- definitions of named (and craftable) nodes smallblock = {} halfslab = {} slab = {} stair = {} common = {} -- common for all nodes common.on_right_click = function( position, node, clicker, itemstack, pointed_thing ) -- find world coordinates for smallblock removal local camera_position = clicker:get_pos() camera_position.y = camera_position.y + clicker:get_properties().eye_height local raycaster = Raycast( camera_position, vector.add( camera_position, vector.multiply(clicker:get_look_dir(), 5) ), false, false ) local intersected_thing = raycaster:next() if( intersected_thing == nil ) then return end --pushes the intersection inside the face to resolve ambiguity and get a rogh position in the world local small_normal = vector.divide( intersected_thing.intersection_normal, 4 ) minetest.chat_send_all( small_normal.x .." ".. small_normal.y .." ".. small_normal.z ) small_normal = vector.multiply( small_normal, -1 ) -- reverse vector so it points inside the face minetest.chat_send_all( small_normal.x .." ".. small_normal.y .." ".. small_normal.z ) local world_rough_position = vector.add( small_normal, intersected_thing.intersection_point ) local target_node_position = { x = math.floor(world_rough_position.x+0.5), y = math.floor(world_rough_position.y+0.5), z = math.floor(world_rough_position.z+0.5), } local point_within_node = util.worldPoint_to_nodePoint( world_rough_position ) local bitmap = util.nodePoint_to_bitmap( point_within_node ) -- bitmap representing the smallblock to be removed local underscore_index local targetNode_fullName = minetest.get_node( target_node_position ).name underscore_index = string.find( targetNode_fullName, "_", 3 ) local targetNode_materialName = string.sub( targetNode_fullName, underscore_index+1 ) minetest.log( "warning", targetNode_fullName ) minetest.log( "warning", targetNode_materialName ) local targetNode_bitmapName = string.sub( targetNode_fullName, 0, underscore_index-1 ) local colon_index = string.find( targetNode_fullName, ":" ) targetNode_bitmapName = string.sub( targetNode_bitmapName, colon_index+1 ) if( targetNode_bitmapName == "smallblock" ) then targetNode_bitmapName = "1" end minetest.chat_send_all( targetNode_bitmapName ) -- find bitmap from name and facedir local bitmap_list = {} -- list of bitmaps that are a rotation of targetNode bitmapName local index = 1 for key_bitmap_as_int, val_bitmap_name in pairs(smallblocks.int_name_map) do if( val_bitmap_name == targetNode_bitmapName ) then bitmap_list[index] = key_bitmap_as_int -- grabing keys that map to the target node name index = index + 1 minetest.chat_send_all( key_bitmap_as_int ) end end local targetNode_bitmap -- find the bitmap that corresponds to the targetNode facedir for i=1, index-1 do if( smallblocks.int_facedir_map[bitmap_list[i]] == minetest.get_node( target_node_position ).param2 ) then targetNode_bitmap = bitmap_list[i] minetest.chat_send_all( "TN bitmap: " .. targetNode_bitmap ) end end if( targetNode_bitmap ~= nil ) then --this should always happen --targetNode_bitmap = util.integer_to_bitmap( targetNode_bitmap ) local tnb = util.integer_to_bitmap( targetNode_bitmap ) minetest.log( "none", "oring "..targetNode_bitmap .. " with ".. util.bitmap_to_integer( bitmap ) ) local finalBitmap = util.xor_bitmap( tnb, bitmap ) if( util.bitmap_to_integer( finalBitmap ) == 0 ) then minetest.remove_node( target_node_position ) return end minetest.log("none", "result " .. util.bitmap_to_integer( finalBitmap ) ) local finalNode_name = smallblocks.int_name_map[util.bitmap_to_integer( finalBitmap )] if( finalNode_name == "1" ) then finalNode_name = "smallblock" end local finalNode_facedir = smallblocks.int_facedir_map[util.bitmap_to_integer( finalBitmap )] local finalNode = { name="k_smallblocks:"..finalNode_name.."_"..targetNode_materialName, param2=finalNode_facedir } minetest.swap_node( target_node_position, finalNode ) minetest.chat_send_all( "materials match" ) end end smallblock.on_use = function( itemstack, user, pointed_thing ) -- find world coordinates for smallblock placement local camera_position = user:get_pos() camera_position.y = camera_position.y + user:get_properties().eye_height local raycaster = Raycast( camera_position, vector.add( camera_position, vector.multiply(user:get_look_dir(), 5) ), false, false ) local intersected_thing = raycaster:next() if( intersected_thing == nil ) then return end --pushes the intersection away from the face to resolve ambiguity and get a rogh position in the world local small_normal = vector.divide( intersected_thing.intersection_normal, 4 ) local world_rough_position = vector.add( small_normal, intersected_thing.intersection_point ) local target_node_position = { x = math.floor(world_rough_position.x+0.5), y = math.floor(world_rough_position.y+0.5), z = math.floor(world_rough_position.z+0.5), } local point_within_node = util.worldPoint_to_nodePoint( world_rough_position ) minetest.chat_send_all( "x: "..point_within_node.x ) minetest.chat_send_all( "y: "..point_within_node.y ) minetest.chat_send_all( "z: "..point_within_node.z ) local bitmap = util.nodePoint_to_bitmap( point_within_node ) minetest.log("warning", "placing at" .. util.bitmap_to_integer( bitmap ) ) if( minetest.get_node( target_node_position ).name == "air" ) then minetest.log( smallblocks.int_name_map[util.bitmap_to_integer(bitmap)] ) local bitmap_string = "" for i = 0, 7, 1 do bitmap_string = bitmap_string .. bitmap[i] end minetest.log( bitmap_string ) minetest.set_node( target_node_position, { name="k_smallblocks:smallblock_stonebrick", param2=smallblocks.int_facedir_map[util.bitmap_to_integer(bitmap)] } ) else local underscore_index local wieldedNode_fullName = itemstack:get_name() underscore_index = string.find( wieldedNode_fullName, "_", 3 ) local wieldedNode_materialName = string.sub( wieldedNode_fullName, underscore_index+1 ) local targetNode_fullName = minetest.get_node( target_node_position ).name underscore_index = string.find( targetNode_fullName, "_", 3 ) local targetNode_materialName = string.sub( targetNode_fullName, underscore_index+1 ) if( targetNode_materialName == wieldedNode_materialName ) then local targetNode_bitmapName = string.sub( targetNode_fullName, 0, underscore_index-1 ) local colon_index = string.find( targetNode_fullName, ":" ) targetNode_bitmapName = string.sub( targetNode_bitmapName, colon_index+1 ) if( targetNode_bitmapName == "smallblock" ) then targetNode_bitmapName = "1" end minetest.chat_send_all( targetNode_bitmapName ) -- find bitmap from name and facedir local bitmap_list = {} -- list of bitmaps that are a rotation of targetNode bitmapName local index = 1 for key_bitmap_as_int, val_bitmap_name in pairs(smallblocks.int_name_map) do if( val_bitmap_name == targetNode_bitmapName ) then bitmap_list[index] = key_bitmap_as_int -- grabing keys that map to the target node name index = index + 1 minetest.chat_send_all( key_bitmap_as_int ) end end local targetNode_bitmap -- find the bitmap that corresponds to the targetNode facedir for i=1, index-1 do if( smallblocks.int_facedir_map[bitmap_list[i]] == minetest.get_node( target_node_position ).param2 ) then targetNode_bitmap = bitmap_list[i] minetest.chat_send_all( "TN bitmap: " .. targetNode_bitmap ) end end if( targetNode_bitmap ~= nil ) then --this should always happen --targetNode_bitmap = util.integer_to_bitmap( targetNode_bitmap ) local tnb = util.integer_to_bitmap( targetNode_bitmap ) minetest.log( "none", "oring "..targetNode_bitmap .. " with ".. util.bitmap_to_integer( bitmap ) ) local finalBitmap = util.or_bitmap( tnb, bitmap ) minetest.log("none", "result " .. util.bitmap_to_integer( finalBitmap ) ) local finalNode_name = smallblocks.int_name_map[util.bitmap_to_integer( finalBitmap )] local finalNode_facedir = smallblocks.int_facedir_map[util.bitmap_to_integer( finalBitmap )] local finalNode = { name="k_smallblocks:"..finalNode_name.."_"..targetNode_materialName, param2=finalNode_facedir } minetest.swap_node( target_node_position, finalNode ) minetest.chat_send_all( "materials match" ) end end end end dofile(modpath .. "/util.lua") test_bitmap = util.integer_to_bitmap( 182 ) test_int = util.bitmap_to_integer( test_bitmap ) minetest.log("warning", test_int ) for i = 0, 7, 1 do minetest.log("warning", test_bitmap[i] ) end dofile(modpath .. "/generate_shapes.lua") dofile(modpath .. "/generate_nodeboxes.lua") dofile(modpath .. "/generate_map.lua") -- maps int to shape, facedir smallblocks.name_int_map = util.table_invert( smallblocks.int_name_map ) for k,v in pairs(origin_bitmaps) do minetest.log("error", " ".. v) end for k, v in pairs(smallblocks.origin_bitmaps) do local shape_int = v local shape_nb = smallblocks.nodeboxes[k] if( v == 1 ) then minetest.register_node("k_smallblocks:smallblock_stonebrick", { description = "Stone Brick Smallblock", paramtype = "light", paramtype2 = "facedir", drawtype = "nodebox", tiles = { {name="default_stone_brick.png", align_style="world"} }, groups = { cracky=2 }, sunlight_propagates = true, node_box = { type = "fixed", fixed = shape_nb }, on_use = smallblock.on_use, on_rightclick = common.on_right_click, on_place = function( itemstack, placer, pointed_thing ) if( string.sub(minetest.get_node( pointed_thing.under ).name, 1, 13 ) == "k_smallblocks" ) then minetest.chat_send_all( "on_place" .. pointed_thing.under.x .. " ".. pointed_thing.under.y.." ".. pointed_thing.under.z ) minetest.item_place( itemstack, placer, pointed_thing, 0) -- doesnt actually place any item, but calls target node on_rightclick if its defined end end } ) else minetest.register_node("k_smallblocks:"..shape_int.."_".."stonebrick", { description = "Stone Brick", paramtype = "light", paramtype2 = "facedir", drawtype = "nodebox", tiles = { {name="default_stone_brick.png", align_style="world"} }, is_ground_content = false, groups = { cracky=2}, sunlight_propagates = true, node_box = { type = "fixed", fixed = shape_nb }, on_place = function() end, on_rightclick = common.on_right_click } ) end end