--[[ nodes.lua - basic node blocks for Inside The Box ]]-- -- -- visual marker for where breakable nodes can be placed -- local function nm(s) local ss = string.gsub(s, "_", " ") return ss:gsub("^%l", string.upper) end -- tilecache API nodes = {} local tilecache = {} function nodes.get_tiles(name) if tilecache[name] then return tilecache[name] else local def = minetest.registered_nodes[name] local tex if def.inventory_image and def.inventory_image ~= "" then tex = def.inventory_image elseif def.drawtype ~= "normal" then tex = def.tiles[1] and def.tiles[1].name or def.tiles[1] else tex = minetest.inventorycube( def.tiles[1] and def.tiles[1].name or def.tiles[1], def.tiles[3] and def.tiles[3].name or def.tiles[3] or def.tiles[2] and def.tiles[2].name or def.tiles[2] or def.tiles[1] and def.tiles[1].name or def.tiles[1], def.tiles[3] and def.tiles[3].name or def.tiles[3] or def.tiles[2] and def.tiles[2].name or def.tiles[2] or def.tiles[1] and def.tiles[1].name or def.tiles[1] ) end tilecache[name] = tex return tex end end -- -- handle placement for breakable nodes -- local on_place_breakable = function(itemstack, placer, pointed_thing) -- not a node? if not pointed_thing or pointed_thing.type ~= "node" then return itemstack end -- pass through? local under = minetest.get_node(pointed_thing.under) local udef = minetest.registered_nodes[under.name] if udef.on_rightclick then return udef.on_rightclick(pointed_thing.under, under, placer, itemstack, pointed_thing) end -- placeable here? local pos = pointed_thing.above local node = minetest.get_node(pos) local place = itemstack:get_name() local name = placer:get_player_name() -- editing or playing? if boxes.players_editing_boxes[name] then -- holding SHIFT? if placer:get_player_control().sneak then -- param2 local def = minetest.registered_nodes[place] local param2 = 0 if def.place_param2 ~= nil then param2 = def.place_param2 elseif def.paramtype2 == "wallmounted" then param2 = minetest.dir_to_wallmounted(vector.subtract( pointed_thing.under, pointed_thing.above)) elseif def.paramtype2 == "facedir" then param2 = minetest.dir_to_facedir(vector.subtract( pointed_thing.under, pointed_thing.above)) end minetest.set_node(pos, {name = place, param2 = param2}) return itemstack end if node.name ~= "air" and node.name ~= "nodes:placeholder" then minetest.chat_send_player(name, "Can only place this node at an empty node. Clear the space first") return itemstack end -- place placeholder information if node.name ~= "nodes:placeholder" then minetest.set_node(pos, {name = "nodes:placeholder"}) end local meta = minetest.get_meta(pos) local placeable = meta:get_string("placeable") if placeable == "" then meta:set_string("placeable", minetest.write_json({[place] = true})) else local t = minetest.parse_json(placeable) t[place] = true meta:set_string("placeable", minetest.write_json(t)) end elseif boxes.players_in_boxes[name] then -- check meta if we can place here local meta = minetest.get_meta(pos) local placeable = meta:get_string("placeable") if placeable == "" then return itemstack end local t = minetest.parse_json(placeable) if not t[place] then return itemstack end -- place and remove one from itemstack itemstack:take_item() -- param2 local def = minetest.registered_nodes[place] local param2 = 0 if def.place_param2 ~= nil then param2 = def.place_param2 elseif def.paramtype2 == "wallmounted" then param2 = minetest.dir_to_wallmounted(vector.subtract( pointed_thing.under, pointed_thing.above)) elseif def.paramtype2 == "facedir" then param2 = minetest.dir_to_facedir(vector.subtract( pointed_thing.under, pointed_thing.above)) end minetest.set_node(pos, {name = place, param2 = param2}) minetest.check_for_falling(pos) -- preserve metadata meta = minetest.get_meta(pos) meta:set_string("placeable", placeable) return itemstack else minetest.log("info", name .. " attempted to place a " .. place .. " outside a box") return itemstack end end local function after_dig_node_breakable(pos, oldnode, oldmetadata, digger) -- preserve metadata minetest.set_node(pos, {name = "nodes:placeholder"}) if not oldmetadata.fields or not oldmetadata.fields.placeable then return end local meta = minetest.get_meta(pos) meta:set_string("placeable", oldmetadata.fields.placeable) end local function on_destruct_breakable(pos) -- preserve metadata local placeable = minetest.get_meta(pos):get_string("placeable") minetest.after(0, function(p) local node = minetest.get_node(pos) if node.name == "air" then -- put the placeholder back if the node was removed minetest.set_node(pos, {name = "nodes:placeholder"}) end local meta = minetest.get_meta(pos) meta:set_string("placeable", p) end, placeable) end local function placeholder_particles(pos) local meta = minetest.get_meta(pos) local placeable = meta:get_string("placeable") if placeable == "" then return false end local nodelist = minetest.parse_json(placeable) for _, obj in pairs(minetest.get_objects_inside_radius(pos, 3)) do if obj:is_player() then local pname = obj:get_player_name() local itemstack = obj:get_wielded_item() local name = itemstack:get_name() if itemstack and nodelist[name] then minetest.add_particle({ pos = pos, expirationtime = 0.55, size = 5, texture = nodes.get_tiles(name), glow = 14, playername = pname, }) end end end return true end minetest.register_node("nodes:placeholder", { description = "Placeable node location placeholder", inventory_image = "air.png", wield_image = "air.png", drawtype = "airlike", paramtype = "light", sunlight_propagates = true, walkable = false, pointable = false, diggable = false, buildable_to = true, floodable = true, air_equivalent = true, drop = "", groups = {not_in_creative_inventory=1}, on_timer = placeholder_particles, on_construct = function(pos) minetest.get_node_timer(pos):start(0.5) end, on_destruct = on_destruct_breakable, after_box_construct = function(pos) minetest.get_node_timer(pos):start(0.5) end, }) -- table format: -- [1] name, also used for texture -- [2] true for stair/slab -- [3] tool if diggable version needs to be added -- [4] sounds -- [5] falling node local nodelist = { {"bricks_clay", true, "pickaxe", sounds.stone}, {"bricks_limestone", true, "pickaxe", sounds.stone}, {"bricks_marble", true, "pickaxe", sounds.stone}, {"bricks_sandstone", true, "pickaxe", sounds.stone}, {"bricks_stone", true, "pickaxe", sounds.stone}, {"bricks_stone_moss", true, "pickaxe", sounds.stone}, {"cobble", true, "pickaxe", sounds.stone}, {"cobble_moss", true, "pickaxe", sounds.stone}, {"limestone", true, "pickaxe", sounds.stone}, {"marble", true, "pickaxe", sounds.stone}, {"sandstone", true, "pickaxe", sounds.stone}, {"stone", true, "pickaxe", sounds.stone}, {"stone_moss", true, "pickaxe", sounds.stone}, {"obsidian", true, false, sounds.stone}, {"bedrock", true, false, sounds.stone}, {"redrock", true, false, sounds.stone}, {"bronze", false, "pickaxe", sounds.metal}, {"gold", false, "pickaxe", sounds.metal}, {"emerald", false, "pickaxe", sounds.stone}, {"iron", false, "pickaxe", sounds.metal}, {"clay", false, "shovel", sounds.dirt}, {"dirt", false, "shovel", sounds.dirt}, {"gravel", false, "shovel", sounds.gravel, 1}, {"sand", false, "shovel", sounds.sand, 1}, {"wood_dark", true, "axe", sounds.wood}, {"wood_light", true, "axe", sounds.wood}, {"wood_medium", true, "axe", sounds.wood}, {"grass", false, false, sounds.grass}, {"snow", false, "shovel", sounds.snow}, {"ice", false, "pickaxe", sounds.stone}, {"ore_black", false, "pickaxe", sounds.stone}, {"ore_blue", false, "pickaxe", sounds.stone}, {"ore_brown", false, "pickaxe", sounds.stone}, {"ore_cyan", false, "pickaxe", sounds.stone}, {"ore_gold", false, "pickaxe", sounds.stone}, {"ore_green", false, "pickaxe", sounds.stone}, {"ore_red", false, "pickaxe", sounds.stone}, } local function make_stair_slab(name, groups, b1, b2, snd, tex) if not tex then tex = name end minetest.register_node("nodes:" .. name .. b1 .. "_stairs", { description = nm(name .. " stairs" .. b2), tiles = {tex .. ".png"}, drawtype = "mesh", mesh = "stairs_stair.obj", paramtype = "light", paramtype2 = "facedir", groups = groups, selection_box = { type = "fixed", fixed = { {-1/2, -1/2, -1/2, 1/2, 0, 1/2}, {-1/2, 0, 0, 1/2, 1/2, 1/2}, }, }, collision_box = { type = "fixed", fixed = { {-1/2, -1/2, -1/2, 1/2, 0, 1/2}, {-1/2, 0, 0, 1/2, 1/2, 1/2}, }, }, sounds = snd, }) minetest.register_node("nodes:" .. name .. b1 .. "_slab", { description = nm(name .. " slab" .. b2), tiles = {tex .. ".png"}, drawtype = "nodebox", paramtype = "light", paramtype2 = "facedir", groups = groups, node_box = { type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, 0, 1/2}, }, sounds = snd, }) end for n, v in pairs(nodelist) do local tex = "blocks_tiles.png^[sheet:8x8:" .. (n - 1) % 8 .. "," .. math.floor((n - 1) / 8) local groups = {node = 1} if v[5] then groups.falling_node = 1 end if v[4] == sounds.stone then groups.stone = 1 elseif v[4] == sounds.wood then groups.wood = 1 end if v[3] then -- register diggable node version local gg = table.copy(groups) gg[v[3]] = 3 minetest.register_node("nodes:" .. v[1] .. "_breakable", { description = nm(v[1]), tiles = {tex}, groups = gg, sounds = v[4], }) if v[2] then make_stair_slab(v[1], gg, "_breakable", "", v[4], tex) end frame.register("nodes:" .. v[1] .. "_breakable") end groups.unbreakable = 1 minetest.register_node("nodes:" .. v[1], { description = nm(v[1]), tiles = {tex}, groups = groups, sounds = v[4], }) if v[2] then make_stair_slab(v[1], groups, "", "", v[4], tex) if groups.stone then walls.register("nodes:" .. v[1] .. "_wall", v[1] .. " Wall", tex, v[4], groups, {"group:stone"}) else walls.register("nodes:" .. v[1] .. "_wall", v[1] .. " Wall", tex, v[4], groups, {"group:wood"}) end end end minetest.register_node("nodes:marble_pillar", { description = "Marble pillar", paramtype2 = "facedir", tiles = {"marble_pillar_top.png", "marble_pillar_bottom.png", "marble_pillar_side.png"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.stone, }) minetest.register_node("nodes:marbleb", { description = "Marble border", tiles = {"blocks_tiles.png^[sheet:8x8:1,1"}, unpushable = true, groups = {not_in_creative_inventory = 1}, sounds = sounds.stone, }) minetest.register_node("nodes:bronzeb", { description = "Bronze border", tiles = {"blocks_tiles.png^[sheet:8x8:0,2"}, unpushable = true, groups = {not_in_creative_inventory = 1}, sounds = sounds.metal, }) -- -- wool -- for n, v in pairs({"yellow", "white", "violet", "red", "pink", "orange", "magenta", "grey", "green", "dark_grey", "dark_green", "cyan", "brown", "blue", "black"}) do local tex = "wool_tiles.png^[sheet:4x4:" .. (n - 1) % 4 .. "," .. math.floor((n - 1) / 4) minetest.register_node("nodes:wool_" .. v, { description = nm(v .. " wool"), tiles = {tex}, groups = {node = 1, unbreakable = 1}, sounds = sounds.cloth, }) minetest.register_node("nodes:carpet_" .. v, { description = nm(v .. " carpet"), tiles = {tex}, groups = {node = 1, unbreakable = 1}, sounds = sounds.cloth, sunlight_propagates = true, paramtype = "light", paramtype2 = "facedir", drawtype = "nodebox", node_box = { type = "fixed", fixed = { {-7/16, -1/2, -7/16, 7/16, -7/16, 7/16}, }, }, }) make_stair_slab("wool_" .. v, {node = 1, unbreakable = 1}, "", "", sounds.cloth, tex) minetest.register_node("nodes:wool_" .. v .. "_breakable", { description = nm(v .. " wool"), tiles = {tex}, groups = {axe = 1, node = 1}, sounds = sounds.cloth, }) make_stair_slab("wool_" .. v, {axe = 1, node = 1}, "_breakable", "", sounds.cloth, tex) frame.register("nodes:wool_" .. v .. "_breakable") end -- hardened clay for n, v in pairs({"black", "violet", "brown", "cyan", "gray", "green", "blue", "lime", "pink", "orange", "magenta", "purple", "red", "silver", "light_brown", "cream", "yellow"}) do local tex = "hardened_clay_tiles.png^[sheet:4x5:" .. (n - 1) % 4 .. "," .. math.floor((n - 1) / 4) minetest.register_node("nodes:hardened_clay_" .. v, { description = nm(v .. " hardened clay"), tiles = {tex}, groups = {node = 1, unbreakable = 1}, sounds = sounds.clay, }) make_stair_slab("hardened_clay_" .. v, {node = 1, unbreakable = 1}, "", "", sounds.cloth, tex) minetest.register_node("nodes:hardened_clay_" .. v .. "_breakable", { description = nm(v .. " hardened clay"), tiles = {tex}, groups = {pickaxe = 1, node = 1}, sounds = sounds.cloth, }) make_stair_slab("hardened_clay_" .. v, {axe = 1, node = 1}, "_breakable", "", sounds.cloth, tex) frame.register("nodes:hardened_clay_" .. v .. "_breakable") end -- trunks and leaves for _, v in pairs({ {"light", 1}, {"medium", 1}, {"dark", 1}, {"fall"}, {"jungle"}, }) do if v[2] then minetest.register_node("nodes:trunk_" .. v[1] .. "_breakable", { description = nm(v[1] .. " trunk"), tiles = {"trunk_" .. v[1] .. "_top.png", "trunk_" .. v[1] .. "_top.png", "trunk_" .. v[1] .. ".png"}, groups = {axe = 1, node = 1, wood = 1}, paramtype2 = "facedir", sounds = sounds.wood, }) minetest.register_node("nodes:trunk_" .. v[1] .. "", { description = nm(v[1] .. " trunk"), tiles = {"trunk_" .. v[1] .. "_top.png", "trunk_" .. v[1] .. "_top.png", "trunk_" .. v[1] .. ".png"}, groups = {node = 1, unbreakable = 1, wood = 1}, paramtype2 = "facedir", sounds = sounds.wood, }) frame.register("nodes:trunk_" .. v[1] .. "_breakable") end minetest.register_node("nodes:leaves_" .. v[1] .. "_breakable", { description = nm(v[1] .. " leaves"), drawtype = "allfaces_optional", paramtype = "light", tiles = {"leaves_" .. v[1] .. ".png"}, groups = {node = 1, axe = 1, leaves = 1}, sounds = sounds.leaves, }) minetest.register_node("nodes:leaves_" .. v[1], { description = nm(v[1] .. " leaves"), drawtype = "allfaces_optional", paramtype = "light", tiles = {"leaves_" .. v[1] .. ".png"}, groups = {node = 1, unbreakable = 1, leaves = 1}, sounds = sounds.leaves, }) end -- barrier minetest.register_node("nodes:barrier", { description = "Barrier", pointable = true, -- make it easy to understand that it's a barrier drawtype = "airlike", inventory_image = "barrier.png", sunlight_propagates = true, paramtype = "light", unpushable = true, groups = {not_in_creative_inventory = 1, fall_damage_add_percent = 1000}, collision_box = { type = "fixed", -- oversized to prevent crawling through fixed = {-1,-0.5,-1,1,1,1}, }, }) local function lamp_on_untrigger(pos) local meta = minetest.get_meta(pos) local node = minetest.get_node(pos) meta:set_string("on_name", node.name) node.name = "nodes:lamp_bar_0" minetest.swap_node(pos, node) end -- glass minetest.register_node("nodes:glass", { description = "glass", drawtype = "glasslike_framed_optional", sunlight_propagates = true, paramtype = "light", tiles = {"glass.png"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.glass, }) minetest.register_node("nodes:glass_breakable", { description = "glass", drawtype = "glasslike_framed_optional", sunlight_propagates = true, paramtype = "light", tiles = {"glass.png"}, groups = {node = 1, pickaxe = 1}, sounds = sounds.glass, }) frame.register("nodes:glass_breakable") -- light fixtures for _, v in pairs({14, 11, 8, 0}) do local on_trigger = function(pos) end local on_untrigger = function(pos) end if v == 0 then on_trigger = function(pos) local meta = minetest.get_meta(pos) local node = minetest.get_node(pos) local new_name = meta:get_string("on_name") if new_name ~= "" then node.name = new_name minetest.swap_node(pos, node) if string.find(new_name, "broken") then minetest.get_node_timer(pos):start(math.random(50)/10) end end end else on_untrigger = lamp_on_untrigger end minetest.register_node("nodes:lamp_bar_" .. v, { description = "A Lamp (" .. v .. ")", light_source = v, sunlight_propagates = true, tiles = {"lamp_bar.png"}, paramtype = "light", paramtype2 = "facedir", walkable = false, drawtype = "nodebox", node_box = { type = "fixed", fixed = {-1, 1/4, 1/4, 1, 1/2, 1/2}, }, groups = {node = 1, unbreakable = 1}, sounds = sounds.glass, on_rotate = screwdriver.rotate_simple, on_trigger = on_trigger, on_untrigger = on_untrigger, after_dig_node = mech.after_dig, }) end minetest.register_node("nodes:lamp_bar_broken", { description = "Broken lamp (flickering)", light_source = 8, sunlight_propagates = true, tiles = {"lamp_bar.png"}, paramtype = "light", paramtype2 = "facedir", walkable = false, drawtype = "nodebox", node_box = { type = "fixed", fixed = {-1, 1/4, 1/4, 1, 1/2, 1/2}, }, groups = {node = 1, unbreakable = 1}, sounds = sounds.glass, on_rotate = screwdriver.rotate_simple, on_trigger = function(pos) end, on_untrigger = lamp_on_untrigger, on_timer = function(pos) local node = minetest.get_node(pos) minetest.set_node(pos, {name = "nodes:lamp_bar_broken_off", param2 = node.param2}) end, on_construct = function(pos) minetest.get_node_timer(pos):start(math.random(50)/10) end, after_dig_node = mech.after_dig, }) minetest.register_node("nodes:lamp_bar_broken_off", { description = "Broken lamp (flickering, off)", sunlight_propagates = true, tiles = {"lamp_bar.png"}, paramtype = "light", paramtype2 = "facedir", walkable = false, drawtype = "nodebox", node_box = { type = "fixed", fixed = {-1, 1/4, 1/4, 1, 1/2, 1/2}, }, groups = {node = 1, unbreakable = 1}, sounds = sounds.glass, on_rotate = screwdriver.rotate_simple, on_trigger = function(pos) end, on_untrigger = lamp_on_untrigger, on_timer = function(pos) local node = minetest.get_node(pos) minetest.set_node(pos, {name = "nodes:lamp_bar_broken", param2 = node.param2}) end, on_construct = function(pos) minetest.get_node_timer(pos):start(math.random(50)/10) end, after_dig_node = mech.after_dig, }) for n, wood in ipairs({"wood_dark", "wood_light", "wood_medium"}) do fences.register_fence("nodes:fence_" .. wood, { description = nm(wood .. " fence"), texture = "blocks_tiles.png^[sheet:8x8:" .. n - 1 ..",3", inventory_image = "default_fence_overlay.png^" .. "blocks_tiles.png^[sheet:8x8:" .. n - 1 ..",3" .. "^default_fence_overlay.png^[makealpha:255,126,126", wield_image = "default_fence_overlay.png^" .. "blocks_tiles.png^[sheet:8x8:" .. n - 1 ..",3" .. "^default_fence_overlay.png^[makealpha:255,126,126", groups = {node = 1, unbreakable = 1, wood = 1}, sounds = sounds.wood, }) end minetest.register_node("nodes:bookshelf", { description = "Bookshelf", tiles = {"blocks_tiles.png^[sheet:8x8:2,3", "blocks_tiles.png^[sheet:8x8:2,3", "bookshelf.png", "bookshelf.png", "blocks_tiles.png^[sheet:8x8:2,3"}, sounds = sounds.wood, paramtype2 = "facedir", on_rotate = screwdriver.rotate_simple, groups = {node = 1, unbreakable = 1, wood = 1}, }) minetest.register_node("nodes:workbench", { description = "Workbench", tiles = {"workbench_top.png", "workbench_top.png", "workbench_side.png"}, sounds = sounds.wood, paramtype2 = "facedir", groups = {node = 1, unbreakable = 1, wood = 1}, }) minetest.register_node("nodes:furnace", { description = "Furnace (off)", tiles = {"furnace_top.png", "furnace_top.png", "furnace_side.png", "furnace_side.png", "furnace_side.png", "furnace_front_off.png"}, sounds = sounds.stone, paramtype2 = "facedir", groups = {node = 1, unbreakable = 1, stone = 1, mech = 1}, on_trigger = function(pos) local node = minetest.get_node(pos) node.name = "nodes:furnace_on" minetest.swap_node(pos, node) end, }) minetest.register_node("nodes:furnace_on", { description = "Furnace (on)", tiles = {"furnace_top.png", "furnace_top.png", "furnace_side.png", "furnace_side.png", "furnace_side.png", "furnace_front_on.png"}, sounds = sounds.stone, paramtype2 = "facedir", light_source = 5, groups = {node = 1, unbreakable = 1, stone = 1, mech = 1}, on_trigger = function() end, on_untrigger = function(pos) local node = minetest.get_node(pos) node.name = "nodes:furnace" minetest.swap_node(pos, node) end }) -- melon, pumpkin, hay minetest.register_node("nodes:melon", { description = "Melon", tiles = {"melon_top.png", "melon_top.png", "melon_side.png"}, sounds = sounds.wood, paramtype2 = "facedir", groups = {node = 1, unbreakable = 1}, }) minetest.register_node("nodes:melon_breakable", { description = "Melon", tiles = {"melon_top.png", "melon_top.png", "melon_side.png"}, sounds = sounds.wood, paramtype2 = "facedir", groups = {node = 1, hand = 1}, }) frame.register("nodes:melon_breakable") minetest.register_node("nodes:pumpkin", { description = "Pumpkin", tiles = {"pumpkin_top.png", "pumpkin_top.png", "pumpkin_side.png"}, sounds = sounds.wood, paramtype2 = "facedir", groups = {node = 1, unbreakable = 1}, }) minetest.register_node("nodes:pumpkin_breakable", { description = "Pumpkin", tiles = {"pumpkin_top.png", "pumpkin_top.png", "pumpkin_side.png"}, sounds = sounds.wood, paramtype2 = "facedir", groups = {node = 1, hand = 1}, }) frame.register("nodes:pumpkin_breakable") minetest.register_node("nodes:hay", { description = "Hay", tiles = {"hay_top.png", "hay_top.png", "hay_side.png"}, sounds = sounds.grass, paramtype2 = "facedir", groups = {node = 1, unbreakable = 1}, }) minetest.register_node("nodes:hay_breakable", { description = "Hay", tiles = {"hay_top.png", "hay_top.png", "hay_side.png"}, sounds = sounds.grass, paramtype2 = "facedir", groups = {node = 1, hand = 1}, }) frame.register("nodes:hay_breakable") -- ladders, rope & vines for _, n in pairs({ {"ladder", "signlike"}, {"rope", "plantlike"}, {"vine", "signlike"}, }) do local tex if n[1] == "vine" then tex = "plant_tiles.png^[sheet:8x8:2,5" else tex = n[1] .. ".png" end minetest.register_node("nodes:" .. n[1], { description = nm(n[1]), drawtype = n[2], tiles = {tex}, inventory_image = tex, paramtype = "light", paramtype2 = "wallmounted", sunlight_propagates = true, walkable = false, climbable = true, selection_box = n[2] == "signlike" and {type = "wallmounted"} or { type = "fixed", fixed = {-3/16, -1/2, -3/16, 3/16, 1/2, 3/16} }, groups = {node = 1, unbreakable = 1}, sounds = sounds.wood, }) minetest.register_node("nodes:" .. n[1] .. "_breakable", { description = nm(n[1]), drawtype = n[2], tiles = {tex}, inventory_image = tex, paramtype = "light", paramtype2 = "wallmounted", sunlight_propagates = true, walkable = false, climbable = true, selection_box = n[2] == "signlike" and {type = "wallmounted"} or { type = "fixed", fixed = {-3/16, -1/2, -3/16, 3/16, 1/2, 3/16} }, groups = {node = 1, axe = 1}, sounds = sounds.wood, }) frame.register("nodes:" .. n[1] .. "_breakable") end minetest.register_node("nodes:waterlily", { description = "Waterlily", drawtype = "nodebox", sunlight_propagates = true, tiles = {"plant_tiles.png^[sheet:8x8:3,5"}, walkable = true, node_box = { type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -7/16, 1/2} }, liquids_pointable = true, paramtype = "light", paramtype2 = "facedir", groups = { node = 1 }, on_place = function(itemstack, placer, pointed_thing) local pos = pointed_thing.under if not pos then return itemstack end -- fixme pass through on_rightclick if present (item frame) local node = minetest.get_node(pos) if not node then return itemstack end local name = itemstack:get_name() local rng = PseudoRandom(pos.x % 16 + ((pos.z % 16) * 16) + ((pos.y % 16) * 256)) minetest.set_node(pointed_thing.above, {name = name, param2 = rng:next(0, 3)}) end, }) --frame.register("nodes:waterlily") -- Trampolines for _, v in pairs({20, 40, 60, 80}) do minetest.register_node("nodes:trampoline_" .. v , { description = "Trampoline (" .. v .. "%)", tiles = {"trampoline_top.png", "trampoline_top.png", "trampoline_side.png"}, paramtype = "light", paramtype2 = "facedir", groups = {node = 1, bouncy = v, fall_damage_add_percent = -v}, }) end -- Sponge (cushion, nojump broken in 0.4.16) minetest.register_node("nodes:sponge", { description = "Sponge", tiles = {"sponge.png"}, paramtype = "light", groups = {node = 1, fall_damage_add_percent = -25, disable_jump = 1}, -- fall ~25 nodes, normal node = 1 heart left, sponge = 5 hearts left }) -- TNT local function tnt_explode(pos) minetest.remove_node(pos) minetest.sound_play("tnt_explosion", {pos = pos, max_hear_distance = 64, gain = 1.0}) minetest.add_particlespawner({ amount = 128, time = 1.0, minpos = vector.add(pos, -2.5), maxpos = vector.add(pos, 2.5), minvel = {x = 0, y = 0, z = 0}, maxvel = {x = 0, y = 0, z = 0}, minacc = {x = 0, y = 0, z = 0}, maxacc = {x = 0, y = 0, z = 0}, minexptime = 0.4, maxexptime = 0.4, minsize = 8, maxsize = 16, texture = "explosion_effect_animated.png", glow = 14, animation = { type = "sheet_2d", frames_w = 8, frames_h = 1, frame_length = 0.05, }, }) end minetest.register_node("nodes:tnt", { description = "TNT", tiles = {"itb_tnt_top.png", "itb_tnt_bottom.png", "itb_tnt_side.png"}, paramtype = "light", groups = {node = 1}, on_trigger = tnt_explode, }) minetest.register_node("nodes:tnt_diggable", { description = "TNT", tiles = {"itb_tnt_top.png", "itb_tnt_bottom.png", "itb_tnt_side.png"}, paramtype = "light", groups = {node = 1, hand = 1}, on_trigger = tnt_explode, }) frame.register("nodes:tnt_diggable") -- special nodes -- liquids minetest.register_node("nodes:water_source", { description = "Water source", drawtype = "liquid", tiles = { { name = "water_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2.0, }, }, }, special_tiles = { -- New-style water source material (mostly unused) { name = "water_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 2.0, }, backface_culling = false, }, }, alpha = 160, paramtype = "light", walkable = false, pointable = false, diggable = false, buildable_to = true, is_ground_content = false, drop = "", drowning = 2, liquidtype = "source", liquid_alternative_flowing = "nodes:water_flowing", liquid_alternative_source = "nodes:water_source", liquid_viscosity = 1, post_effect_color = {a = 203, r = 30, g = 60, b = 90}, groups = {water = 3, liquid = 3, puts_out_fire = 1, cools_lava = 1}, sounds = sounds.water, }) minetest.register_node("nodes:water_flowing", { description = "Flowing water", drawtype = "flowingliquid", tiles = {"water.png"}, special_tiles = { { name = "water_flowing_animated.png", backface_culling = false, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.2, }, }, { name = "water_flowing_animated.png", backface_culling = true, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.2, }, }, }, alpha = 160, paramtype = "light", paramtype2 = "flowingliquid", walkable = false, pointable = false, diggable = false, buildable_to = true, is_ground_content = false, drop = "", drowning = 2, liquidtype = "flowing", liquid_alternative_flowing = "nodes:water_flowing", liquid_alternative_source = "nodes:water_source", liquid_viscosity = 1, post_effect_color = {a = 203, r = 30, g = 60, b = 90}, groups = {water = 3, liquid = 3, puts_out_fire = 1, not_in_creative_inventory = 1, cools_lava = 1}, sounds = sounds.water, }) minetest.register_node("nodes:river_water_source", { description = "River water source", drawtype = "liquid", tiles = { { name = "river_water_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.2, }, }, }, special_tiles = { { name = "river_water_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.2, }, backface_culling = false, }, }, alpha = 160, paramtype = "light", walkable = false, pointable = false, diggable = false, buildable_to = true, is_ground_content = false, drop = "", drowning = 2, liquidtype = "source", liquid_alternative_flowing = "nodes:river_water_flowing", liquid_alternative_source = "nodes:river_water_source", liquid_viscosity = 1, liquid_renewable = false, liquid_range = 2, post_effect_color = {a = 203, r = 30, g = 76, b = 90}, groups = {water = 3, liquid = 3, puts_out_fire = 1, cools_lava = 1}, sounds = sounds.water, }) minetest.register_node("nodes:river_water_flowing", { description = "Flowing river water", drawtype = "flowingliquid", tiles = {"river_water.png"}, special_tiles = { { name = "river_water_flowing_animated.png", backface_culling = false, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 0.8, }, }, { name = "river_water_flowing_animated.png", backface_culling = true, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 0.8, }, }, }, alpha = 160, paramtype = "light", paramtype2 = "flowingliquid", walkable = false, pointable = false, diggable = false, buildable_to = true, is_ground_content = false, drop = "", drowning = 2, liquidtype = "flowing", liquid_alternative_flowing = "nodes:river_water_flowing", liquid_alternative_source = "nodes:river_water_source", liquid_viscosity = 1, liquid_renewable = false, liquid_range = 2, post_effect_color = {a = 203, r = 30, g = 76, b = 90}, groups = {water = 3, liquid = 3, puts_out_fire = 1, not_in_creative_inventory = 1, cools_lava = 1}, sounds = sounds.water, }) minetest.register_node("nodes:lava_source", { description = "Lava source", drawtype = "liquid", tiles = { { name = "lava_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.0, }, }, }, special_tiles = { -- New-style lava source material (mostly unused) { name = "lava_source_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.0, }, backface_culling = false, }, }, paramtype = "light", light_source = 8, walkable = false, pointable = false, diggable = false, buildable_to = true, is_ground_content = false, drop = "", drowning = 1, liquidtype = "source", liquid_alternative_flowing = "nodes:lava_flowing", liquid_alternative_source = "nodes:lava_source", liquid_viscosity = 7, liquid_renewable = false, damage_per_second = 4 * 2, post_effect_color = {a = 191, r = 255, g = 64, b = 0}, groups = {lava = 3, liquid = 2, igniter = 1}, sounds = sounds.water, }) minetest.register_node("nodes:lava_flowing", { description = "Flowing lava", drawtype = "flowingliquid", tiles = {"lava.png"}, special_tiles = { { name = "lava_flowing_animated.png", backface_culling = false, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.3, }, }, { name = "lava_flowing_animated.png", backface_culling = true, animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 3.3, }, }, }, paramtype = "light", paramtype2 = "flowingliquid", light_source = 8, walkable = false, pointable = false, diggable = false, buildable_to = true, is_ground_content = false, drop = "", drowning = 1, liquidtype = "flowing", liquid_alternative_flowing = "nodes:lava_flowing", liquid_alternative_source = "nodes:lava_source", liquid_viscosity = 7, liquid_renewable = false, damage_per_second = 4 * 2, post_effect_color = {a = 191, r = 255, g = 64, b = 0}, groups = {lava = 3, liquid = 2, igniter = 1, not_in_creative_inventory = 1}, sounds = sounds.water, }) -- lava cooling ABM lava_check_pos = { {x = 0, y = 1, z = 0}, {x = 1, y = 0, z = 0}, {x = 0, y = 0, z = 1}, {x = -1, y = 0, z = 0}, {x = 0, y = 0, z = -1}, {x = 0, y = -1, z = 0}, } minetest.register_abm({ label = "lava cooling abm", nodenames = {"group:lava"}, neighbors = {"group:cools_lava"}, interval = 1, -- as fast as server allows, usually 1 chance = 1, action = function(pos) for k, v in pairs(lava_check_pos) do local node = minetest.get_node(vector.add(pos, v)) local def = minetest.registered_nodes[node.name] if def.groups.cools_lava then if k == 1 and minetest.get_node(pos).name == "nodes:lava_source" then minetest.set_node(pos, {name = "nodes:obsidian"}) else minetest.set_node(pos, {name = "nodes:cobble_breakable"}) end return end end end, }) -- -- particles -- minetest.register_abm({ label = "smoke", nodenames = {"group:torch", "nodes:fire"}, neighbors = {"air"}, interval = 0.1, chance = 100, catch_up = false, action = function(pos, node) pos.y = pos.y + 0.5 minetest.add_particle({ pos = vector.add(pos, math.random(50)/100 - 0.25), velocity = {x = math.random(20)/100 - 0.1, y = 0.3, z = math.random(20)/100 - 0.1}, expirationtime = 2.3, size = math.random(10)/5 + 2, texture = "smoke_animated.png", animation = { type = "sheet_2d", frames_w = 8, frames_h = 1, frame_length = 0.3, }, }) end, }) minetest.register_abm({ label = "water_bubble", nodenames = {"group:water"}, interval = 0.2, chance = 200, catch_up = false, action = function(pos, node, a, b) minetest.add_particle({ pos = vector.add(pos, math.random(50)/100 - 0.25), velocity = {x = 0, y = 0.3, z = 0}, expirationtime = 0.78, size = math.random(10)/5 + 1, texture = "bubble_effect_animated.png", animation = { type = "sheet_2d", frames_w = 8, frames_h = 1, frame_length = 0.1, }, }) end, }) minetest.register_abm({ label = "lava_bubble", nodenames = {"group:lava"}, neighbors = {"air"}, interval = 0.1, chance = 200, catch_up = false, action = function(pos, node, a, b) pos.y = pos.y + 0.5 minetest.add_particle({ pos = vector.add(pos, math.random(50)/100 - 0.25), velocity = {x = 0, y = 0.2, z = 0}, expirationtime = 0.78, size = math.random(10)/5 + 1, texture = "lava_bubble_animated.png", glow = 13, animation = { type = "sheet_2d", frames_w = 8, frames_h = 1, frame_length = 0.1, }, }) end, }) -- tnt -- fire minetest.register_node("nodes:fire", { description = "Fire", drawtype = "firelike", tiles = { { name = "fire_animated.png", animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1.6, }, }, }, inventory_image = "fire.png", paramtype = "light", light_source = 13, walkable = false, buildable_to = true, sunlight_propagates = true, damage_per_second = 4, groups = {igniter = 2}, selection_box = {type = "fixed", fixed = {-7/16, -1/2, -7/16, 7/16, 6/16, 7/16}}, drop = "", }) frame.register("nodes:fire") -- chests: -- empty (fake) chest -- chest-with-key -- chest-with-tool for _, name in ipairs({ "nothing", "boxes:nexus", "tools:axe", "tools:pickaxe", "tools:shovel", "tools:sword", "tools:flint_and_steel", }) do minetest.register_node("nodes:chest_with_" .. string.gsub(name, ":", "_"), { description = "Chest with " .. name, drawtype = "mesh", paramtype = "light", paramtype2 = "facedir", mesh = "chest_close.obj", tiles = {"chest.png"}, groups = { node = 1, trigger = 1 }, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) minetest.sound_play("chest_open", {pos = pos}) node.name = "nodes:chest_open" mech.trigger(pos) minetest.after(1.0, mech.untrigger, pos) minetest.swap_node(pos, node) minetest.get_node_timer(pos):start(3.0) if name ~= "nothing" then if itemstack:item_fits(name) then itemstack:add_item(name) return itemstack end clicker:get_inventory():add_item("main", name) end return itemstack end, }) end minetest.register_alias("nodes:chest_with_boxes_findme_item", "nodes:chest_with_boxes_nexus") minetest.register_node("nodes:chest_open", { description = "Chest", drawtype = "mesh", paramtype = "light", paramtype2 = "facedir", mesh = "chest_open.obj", tiles = {"chest.png"}, on_timer = function(pos) minetest.sound_play("chest_close", {pos = pos}) local node = minetest.get_node(pos) minetest.swap_node(pos, {name = "nodes:chest_with_nothing", param2 = node.param2}) end, groups = {node = 0}, }) -- grass variants -- flowers -- farming plants -- flowerpot combos for _, n in pairs({ {"grass_1", "3,3", 56}, {"grass_2", "4,3", 56}, {"grass_3", "5,3", 56}, {"grass_4", "6,3", 56}, {"grass_5", "7,3", 56}, {"dead_bush", "1,3", 56}, {"rose", "6,4", 9}, {"dandelion", "0,3", 9}, {"white_tulip", "4,5", 9}, {"allium", "6,2", 9}, {"orchid", "3,4", 9}, {"daisy", "7,2", 9}, {"houstonia", "0,4", 9}, {"paeonia", "4,4", 9}, {"wheat_stage_0", "0,1", 11}, {"wheat_stage_1", "1,1", 11}, {"wheat_stage_2", "2,1", 11}, {"wheat_stage_3", "3,1", 11}, {"wheat_stage_4", "4,1", 11}, {"wheat_stage_5", "5,1", 11}, {"wheat_stage_6", "6,1", 11}, {"wheat_stage_7", "7,1", 11}, {"potatoes_stage_0", "4,0", 10}, {"potatoes_stage_1", "5,0", 10}, {"potatoes_stage_2", "6,0", 10}, {"potatoes_stage_3", "7,0", 10}, {"carrots_stage_0", "0,0", 8}, {"carrots_stage_1", "1,0", 8}, {"carrots_stage_2", "2,0", 8}, {"carrots_stage_3", "3,0", 8}, {"sapling_1", "0,2", 8, true}, {"sapling_2", "1,2", 8, true}, {"sapling_3", "2,2", 8, true}, {"sapling_4", "3,2", 8, true}, {"sapling_5", "4,2", 8, true}, {"sapling_6", "5,2", 8, true}, {"mushroom_red", "2,4", 10, true}, {"mushroom_brown", "1,4", 10, true}, {"fern", "2,3", 9}, {"reeds", "5,4", 0}, }) do local tex = "plant_tiles.png^[sheet:8x8:" .. n[2] minetest.register_node("nodes:" .. n[1], { description = nm(n[1]), drawtype = "plantlike", place_param2 = n[3], tiles = {tex}, paramtype = "light", paramtype2 = "meshoptions", sunlight_propagates = true, walkable = false, buildable_to = true, floodable = true, selection_box = { type = "fixed", fixed = {-1/4, -1/2, -1/4, 1/4, 1/4, 1/4}, }, sounds = sounds.grass, groups = {node = 1}, }) if n[4] then minetest.register_node("nodes:" .. n[1] .. "_breakable", { description = nm(n[1]), drawtype = "plantlike", place_param2 = n[3], tiles = {tex}, paramtype = "light", paramtype2 = "meshoptions", sunlight_propagates = true, walkable = false, buildable_to = true, floodable = true, selection_box = { type = "fixed", fixed = {-1/4, -1/2, -1/4, 1/4, 1/4, 1/4}, }, sounds = sounds.grass, groups = {node = 1, hand = 1}, }) frame.register("nodes:" .. n[1] .. "_breakable") else frame.register("nodes:" .. n[1]) end minetest.register_node("nodes:flowerpot_" ..n[1], { description = nm("Pot with " .. n[1]), drawtype = "mesh", mesh = "flowerpot.obj", tiles = { {name = "pot.png"}, {name = tex}, }, paramtype = "light", paramtype2 = "facedir", on_rotate = screwdriver.rotate_simple, sunlight_propagates = true, collision_box = { type = "fixed", fixed = {-1/4, -1/2, -1/4, 1/4, -1/8, 1/4}, }, selection_box = { type = "fixed", fixed = {-1/4, -1/2, -1/4, 1/4, 1/2, 1/4}, }, sounds = sounds.wood, groups = {node = 1}, }) end -- seeds for _, m in pairs({ {"melon", "0,5"}, {"wheat", "1,5"}, {"pumpkin", "7,4"}, }) do local n = m[1] local tex = "plant_tiles.png^[sheet:8x8:" .. m[2] minetest.register_node("nodes:" .. n .. "_seeds", { description = nm(n .. " seeds"), drawtype = "signlike", tiles = {tex}, inventory_image = tex, paramtype = "light", paramtype2 = "wallmounted", sunlight_propagates = true, walkable = false, buildable_to = true, floodable = true, selection_box = { type = "fixed", fixed = {-7/16, -1/2, -7/16, 7/16, -7/16, 7/16}, }, sounds = sounds.grass, groups = {node = 1}, }) minetest.register_node("nodes:" .. n .. "_seeds_breakable", { description = nm(n .. " seeds"), drawtype = "signlike", tiles = {tex}, inventory_image = tex, paramtype = "light", paramtype2 = "wallmounted", sunlight_propagates = true, walkable = false, buildable_to = true, floodable = true, selection_box = { type = "fixed", fixed = {-7/16, -1/2, -7/16, 7/16, -7/16, 7/16}, }, sounds = sounds.grass, groups = {node = 1, hand = 1}, }) frame.register("nodes:" .. n .. "_seeds_breakable") end -- empty flowerpot minetest.register_node("nodes:flowerpot_empty", { description = "Pot (empty)", drawtype = "mesh", mesh = "flowerpot.obj", tiles = { {name = "pot.png"}, {name = "itb_blank.png"}, }, paramtype = "light", sunlight_propagates = true, collision_box = { type = "fixed", fixed = {-1/4, -1/2, -1/4, 1/4, -1/8, 1/4}, }, selection_box = { type = "fixed", fixed = {-1/4, -1/2, -1/4, 1/4, 1/2, 1/4}, }, sounds = sounds.wood, groups = {node = 1}, }) -- soil for _, v in pairs({ "soil", "soil_wet", }) do -- register diggable node version minetest.register_node("nodes:" .. v .. "_breakable", { description = nm(v), tiles = {v .. ".png", "blocks_tiles.png^[sheet:8x8:5,2"}, groups = {node = 1, shovel = 1}, sounds = sounds.dirt, }) minetest.register_node("nodes:" .. v, { description = nm(v), tiles = {v .. ".png", "blocks_tiles.png^[sheet:8x8:5,2"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.dirt, }) frame.register("nodes:" .. v .. "_breakable") end -- dirt with grass, minetest.register_node("nodes:dirt_with_grass_breakable", { description = "Dirt with grass", tiles = {"blocks_tiles.png^[sheet:8x8:3,3", "blocks_tiles.png^[sheet:8x8:5,2", "grass_side.png"}, groups = {node = 1, shovel = 1}, sounds = sounds.dirt, }) minetest.register_node("nodes:dirt_with_grass", { description = "Dirt with grass", tiles = {"blocks_tiles.png^[sheet:8x8:3,3", "blocks_tiles.png^[sheet:8x8:5,2", "grass_side.png"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.dirt, }) frame.register("nodes:dirt_with_grass_breakable") -- dirt with podzol minetest.register_node("nodes:dirt_with_podzol_breakable", { description = "Dirt with podzol", tiles = {"podzol.png", "blocks_tiles.png^[sheet:8x8:5,2", "podzol_side.png"}, groups = {node = 1, shovel = 1}, sounds = sounds.dirt, }) minetest.register_node("nodes:dirt_with_podzol", { description = "Dirt with podzol", tiles = {"podzol.png", "blocks_tiles.png^[sheet:8x8:5,2", "podzol_side.png"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.dirt, }) frame.register("nodes:dirt_with_podzol_breakable") -- mycelium minetest.register_node("nodes:mycelium_breakable", { description = "mycelium", tiles = {"mycelium_top.png", "blocks_tiles.png^[sheet:8x8:5,2", "mycelium_side.png"}, groups = {node = 1, shovel = 1}, sounds = sounds.dirt, }) minetest.register_node("nodes:mycelium", { description = "mycelium", tiles = {"mycelium_top.png", "blocks_tiles.png^[sheet:8x8:5,2", "mycelium_side.png"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.dirt, }) frame.register("nodes:mycelium_breakable") -- dirt with snow minetest.register_node("nodes:dirt_with_snow_breakable", { description = "Dirt with snow", tiles = {"blocks_tiles.png^[sheet:8x8:4,3", "blocks_tiles.png^[sheet:8x8:5,2", "grass_side_snowed.png"}, groups = {node = 1, shovel = 1}, sounds = sounds.snow, }) minetest.register_node("nodes:dirt_with_snow", { description = "Dirt with snow", tiles = {"blocks_tiles.png^[sheet:8x8:4,3", "blocks_tiles.png^[sheet:8x8:5,2", "grass_side_snowed.png"}, groups = {node = 1, unbreakable = 1}, sounds = sounds.snow, }) frame.register("nodes:dirt_with_snow_breakable") minetest.register_node("nodes:snow_ledge", { description = "Snow ledge", paramtype = "light", drop = {}, groups = {node = 1, hand = 1, dig_immediate = 3, attached_node = 1, falling_node = 1}, sounds = sounds.snow, tiles = {"blocks_tiles.png^[sheet:8x8:4,3"}, drawtype = "nodebox", node_box = { type = "fixed", fixed = { {-1/2, -1/2, -1/2, 1/2, -1/4, 1/2}, }, }, }) for i, v in ipairs({[1] = "blue", [2] = "gray", [3] = "green", [4] = "rail", [5] = "red"}) do minetest.register_node("nodes:line_" .. v, { description = "Line (" .. v .. ")", drawtype = "raillike", paramtype = "light", sunlight_propagates = true, walkable = false, tiles = { "line_tiles.png^[sheet:4x5:2," .. i-1, "line_tiles.png^[sheet:4x5:1," .. i-1, "line_tiles.png^[sheet:4x5:3," .. i-1, "line_tiles.png^[sheet:4x5:0," .. i-1, }, selection_box = {type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -7/16, 1/2}, }, groups = {node = 1, unbreakable = 1}, sounds = sounds.wood, }) end -- attach breakable callbacks to everything for name, def in pairs(minetest.registered_nodes) do assert(def.groups) if def.groups.shovel or def.groups.axe or def.groups.pickaxe or def.groups.hand then local toolverb = "obtained" if def.groups.shovel then toolverb = "dug with a shovel" elseif def.groups.axe then toolverb = "chopped with an axe" elseif def.groups.pickaxe then toolverb = "dug with a pickaxe" elseif def.groups.hand then toolverb = "picked up" end local desc = def.description minetest.override_item(name, { description = desc .. "\nCan be " .. toolverb .. " by the player", on_place = on_place_breakable, on_destruct = on_destruct_breakable, after_dig_node = after_dig_node_breakable, node_placement_prediction = "", }) end end