279 lines
7.6 KiB
Lua
279 lines
7.6 KiB
Lua
-- "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)
|