blueprints/core.lua

261 lines
7.3 KiB
Lua

--
-- Minetest blueprints mod: Core functions
--
blueprints = {}
-- Default rules
-- These may be overriden on a per-node basis using '_blueprints' in the node's
-- definition.
blueprints.default_rules = {
-- allowed - Allows the node to be blueprint-ified. If this is 'default',
-- it will only allow nodes that are in the creative inventory, and will
-- be set to either true or false when blueprints.get_rules() is called.
allowed = 'default',
-- param2 - Allows param2 to be saved in the blueprint.
param2 = true,
-- meta - Specifies which meta strings to save. This can be 'true' to save
-- everything.
-- meta = {'formspec', 'infotext'},
meta = true,
-- pvmeta - Specifies which meta strings to mark as private.
pvmeta = {},
-- inv_lists - A list of inventory lists to be copied.
inv_lists = {},
-- item - The blueprint item to use.
item = 'blueprints:print'
}
-- Aliases - Allow multiple nodes to be treated internally as one.
local aliases = {}
blueprints.register_alias = function(old_node, new_node)
aliases[old_node] = new_node
end
blueprints.check_alias = function(node)
if aliases[node] then return aliases[node] end
local def = minetest.registered_nodes[node]
-- Mesecons integration
if def and def.__mesecon_state and def.__mesecon_basename then
node = def.__mesecon_basename .. '_off'
end
return node
end
blueprints.check_reverse_alias = function(new_node, old_node)
return blueprints.check_alias(old_node) == new_node
end
-- Get a list of rules for a node.
blueprints.get_rules = function(node)
if node and type(node) ~= 'string' then node = node.name end
-- Check aliases
node = blueprints.check_alias(node)
-- Unknown nodes cannot be blueprinted.
local def = false
if node and node ~= 'ignore' then
def = minetest.registered_nodes[node]
end
if not def then
return {
allowed = false,
param2 = false,
meta = {},
pvmeta = {},
inv_lists = {},
item = 'blueprints:blank'
}
end
-- Use the default rules
local node_rules = def._blueprints
if not node_rules then
if def.groups and def.groups.not_in_creative_inventory then
node_rules = {}
else
return blueprints.default_rules
end
end
-- Replace any omitted values with the defaults.
local rules = {}
for k, v in pairs(blueprints.default_rules) do
if node_rules[k] ~= nil then
rules[k] = node_rules[k]
else
rules[k] = v
end
end
-- Check for 'You hacker you!'
if rules.allowed == 'default' then
if def.groups and def.groups.not_in_creative_inventory then
rules.allowed = false
else
rules.allowed = true
end
end
return rules
end
-- Get a nicer inv_lists
local function get_inv_lists(rules)
local inv_lists = {}
if rules.inv_lists then
for _, list in ipairs(rules.inv_lists) do
inv_lists[list] = true
end
end
return inv_lists
end
-- Get a blueprint string for a node.
blueprints.get_blueprint = function(pos, raw, force)
-- Check aliases
local node = minetest.get_node(pos)
node.name = blueprints.check_alias(node.name)
-- Get the rules list
local rules
local rules = blueprints.get_rules(node)
if force then
rules.meta = true
elseif not rules.allowed then
return
end
-- Using a meta table allows ints/floats/etc to be copied nicely.
local blueprint = {name = node.name, pattern = rules.pattern}
local metatable = minetest.get_meta(pos):to_table()
-- Copy across allowed metadata fields
if (rules.meta == true or #rules.meta > 0) and metatable.fields then
if rules.meta == true then
blueprint.meta = metatable.fields
else
blueprint.meta = {}
for _, name in ipairs(rules.meta) do
blueprint.meta[name] = metatable.fields[name]
end
end
end
-- Get a nicer inv_lists
local inv_lists
if not force then inv_lists = get_inv_lists(rules) end
-- Copy allowed inventories
if metatable.inventory then
blueprint.inv = {}
for listname, list in pairs(metatable.inventory) do
blueprint.inv[listname] = {}
for id, itemstack in ipairs(list) do
if force or inv_lists[listname] then
blueprint.inv[listname][id] = itemstack:to_table() or ''
else
blueprint.inv[listname][id] = ''
end
end
end
end
-- Copy across param2
if rules.param2 then
blueprint.param2 = node.param2
end
-- Return the blueprint
if not raw then
blueprint = minetest.serialize(blueprint)
end
return blueprint
end
-- Apply blueprints (and double-check the allowed fields)
blueprints.apply_blueprint = function(pos, blueprint, only_if_exists, force)
-- Deserialize if required and get the rules
if type(blueprint) == 'string' then
blueprint = minetest.deserialize(blueprint)
end
if not blueprint then return end
-- Make sure the node exists
if only_if_exists then
local node = minetest.get_node(pos)
if node.name ~= blueprint.name then
-- "Un-alias" the blueprint.
if blueprints.check_reverse_alias(blueprint.name, node.name) then
blueprint.name = node.name
else
return
end
end
end
-- Get the rules
local rules = blueprints.get_rules(blueprint)
if not rules or (not rules.allowed and not force) then return end
-- Set the node (and param2)
local node = {name = blueprint.name}
if rules.param2 and blueprint.param2 then
node.param2 = blueprint.param2
end
minetest.set_node(pos, node)
local metatable = {fields = {}, inventory = {}}
-- Copy across allowed metadata fields
if blueprint.meta and rules.meta then
if rules.meta == true or force then
metatable.fields = blueprint.meta
else
for _, name in ipairs(rules.meta) do
metatable.fields[name] = blueprint.meta[name]
end
end
end
-- Copy allowed inventories
if blueprint.inv then
local inv_lists
if not force then inv_lists = get_inv_lists(rules) end
for name, inv in pairs(blueprint.inv) do
metatable.inventory[name] = {}
for id, item in ipairs(inv) do
if not force and not inv_lists[name] then item = '' end
metatable.inventory[name][id] = ItemStack(item)
end
end
end
-- Update the node meta
local meta = minetest.get_meta(pos)
meta:from_table(metatable)
meta:mark_as_private(rules.pvmeta)
return true
end
-- Protection check function
blueprints.check_protection = function(pos, name)
if type(name) ~= 'string' then
name = name:get_player_name()
end
if minetest.is_protected(pos, name) and
not minetest.check_player_privs(name, {protection_bypass=true}) then
minetest.record_protection_violation(pos, name)
return true
end
return false
end