-- "I'll stop calling it Minetest when it stops being one." minetest = core -- Override properties of a table from those of another table function extend(dst, src) for k, v in pairs(src) do dst[k] = v end return dst end -- PHP-style helper verb function include(file) return dofile(minetest.get_modpath(minetest.get_current_modname()).."/"..file) end function include_all(dir) for _, x in pairs(minetest.get_dir_list(minetest.get_modpath(minetest.get_current_modname()).."/"..dir)) do include(dir.."/"..x) end end function warn(str) minetest.log("warning", str) end function err(str) minetest.log("error", str.."\n"..debug.traceback()) end function tell(p, msg) minetest.chat_send_player(p, "# Server: "..msg) end function say(msg) minetest.chat_send_all("# Server: "..msg) end EventTarget = { init = function() local e = { listeners = {} } return setmetatable(e, {__index = EventTarget}) end, listen = function(e, channel, fn) if not e.listeners[channel] then e.listeners[channel] = {} end local l = e.listeners[channel] l[#l +1] = fn end, unlisten = function(e, channel, fn) if not e.listeners[channel] then return end local l = e.listeners[channel] local idx = table.indexof(l, fn) if idx < 0 then return end table.remove(l, idx) end, dispatch = function(e, channel, ...) local l = e.listeners[channel] if not l then return end for i = 1, #l do l[i](...) end end } setmetatable(EventTarget, { __call = function(_, ...) return EventTarget.init(...) end }) StateMachine = { init = function(obj, states) local super = EventTarget() local e = { states = states, active_states = {}, obj = obj } return setmetatable(e, {__index = super}) end, add_state = function(e, state) if e.active_states[state] then return false end e.states[state]:add(e.obj) e.active_states[state] = true return true end, tick = function(e) for k in pairs(e.active_states) do e.states[k]:tick(e.obj) end end, remove_state = function(e, state) if not e.active_states[state] then return false end e.states[state]:remove(e.obj) e.active_states[state] = nil return true end, } setmetatable(StateMachine, { __call = function(_, ...) return StateMachine.init(...) end, __index = EventTarget() }) rgt = { adjacent_neighbor_offests = { vector.new(0,0,1), vector.new(0,0,-1), vector.new(1,0,0), vector.new(-1,0,0), vector.new(1,0,1), vector.new(1,0,-1), vector.new(-1,0,1), vector.new(-1,0,-1), }, adjacent_horizontal_neighbor_offests = { vector.new(0,0,1), vector.new(0,0,-1), vector.new(1,0,0), vector.new(-1,0,0), }, nodes_to_content_ids = {}, content_ids_to_nodes = {}, vm_data = {}, } local ns = rgt function ns.register_node(name, def) def._name = name local alias if not name:find(":") then alias = name name = "red_glazed_terracotta:"..name end if def.groups then if def.groups.interactable then def.on_rightclick = function(pos, _, p) rgt.players[p:get_player_name()].interacting_with = pos end end end if def._variants then if type(def._variants) == "string" then rgt_world["register_"..def._variants](def) else for _, x in ipairs(def._variants) do rgt_world["register_"..x](def) end end end minetest.register_node(":"..name, def) local cid = minetest.get_content_id(name) ns.nodes_to_content_ids[name] = cid ns.content_ids_to_nodes[cid] = name if alias then minetest.register_alias(alias, name) end end function ns.register_item(name, def) def._name = name local alias if not name:find(":") then alias = name name = "red_glazed_terracotta:"..name end minetest.register_craftitem(":"..name, def) if alias then minetest.register_alias(alias, name) end end function ns.register_tool(name, def) def._name = name local alias if not name:find(":") then alias = name name = "red_glazed_terracotta:"..name end minetest.register_tool(":"..name, def) if alias then minetest.register_alias(alias, name) end end function ns.register_entity(name, def) minetest.register_entity(name, def) end -- Make node dig particles denser. minetest.register_on_dignode(function(pos, node, digger) local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81 local def = minetest.registered_nodes[node.name] minetest.add_particlespawner({ amount = 128, time = 0.001, minpos = vector.offset(pos, -0.35, -0.35, -0.35), maxpos = vector.offset(pos, 0.35, 0.35, 0.35), minvel = vector.new(-1.7, 0, -1.7), maxvel = vector.new(1.7, 3.5, 1.7), minacc = vector.new(0, -gravity *2, 0), maxacc = vector.new(0, -gravity *2, 0), minexptime = 0, maxexptime = 1, minsize = 0, maxsize = 0, -- random node = node, minsize = 0.5, maxsize = 1.4, blend = (def and def.use_texture_alpha == "blend") and "blend" or "clip", }) end) -- Fills the area from pos1 to pos2 with the node named `node`. function ns.fill_area(pos1, pos2, node) local minp = vector.new(math.min(pos1.x, pos2.x), math.min(pos1.y, pos2.y), math.min(pos1.z, pos2.z)) local maxp = vector.new(math.max(pos1.x, pos2.x), math.max(pos1.y, pos2.y), math.max(pos1.z, pos2.z)) local vm = minetest.get_voxel_manip(pos1, pos2) local min, max = vm:get_emerged_area() local va = VoxelArea(min, max) local data = ns.vm_data vm:get_data(data) local c_node = minetest.get_content_id(node) for i in va:iterp(minp, maxp) do data[i] = c_node end vm:set_data(data) vm:write_to_map() if vm.close then vm:close() end end -- Get a flat texture that may represent the given node (using the first tile). function ns.get_node_texture(node) local def = minetest.registered_nodes[node] if not def or not def.tiles then return "blank.png" end local tx = def.tiles[1] if type(tx) == "string" then return tx end return tx.name or "blank.png" end -- Out-of-line node metadata, allowing meta for a node to be accessed even when its containing mapblock is not loaded. local db = minetest.get_mod_storage() local NodeMetaRef = { set = function(e, key, value) return db:set_string(e._pos.."_"..key, value) end, get = function(e, key) return db:get(e._pos.."_"..key) end, } NodeMetaRef.__index = NodeMetaRef function ns.get_node_meta(pos) return setmetatable({_pos = tostring(minetest.hash_node_position(pos))}, NodeMetaRef) end -- Allow nodes to provide a callback to run on activation without -- needing to register a bunch of mostly identical LBMs. minetest.register_lbm { name = ":red_glazed_terracotta:on_activate", nodenames = {"group:run_on_activate"}, action = function(pos, node) minetest.registered_nodes[node.name].on_activate(pos) end } minetest.register_on_joinplayer(function(p) if p:get_player_name() == "singleplayer" then minetest.change_player_privs(p:get_player_name(), { fast = true, fly = true, noclip = true, server = true, give = true, }) end end)