2022-11-26 11:06:37 -05:00

213 lines
7.2 KiB
Lua

--[[
Copyright (C) 2021 Jude Melton-Houghton
(Copyright notices for borrowed code are later in this comment.)
This file is part of area_containers. It implements miscellaneous
functionality that doesn't fit in the other files, much of which is
shared between other files.
area_containers 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 3 of the License, or
(at your option) any later version.
area_containers 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 area_containers. If not, see <https://www.gnu.org/licenses/>.
The code for determining the mapchunk position in blockpos_in_range is based
on some code from the Minetest project itself, specifically
EmergeManager::getContainingChunk in src/emerge.cpp and getContainerPos in
src/util/numeric.h. Both these files are provided under the terms of the
GNU Lesser General Public License version 2.1 or any later version.
The relevant revision of src/emerge.cpp is copyrighted as follows:
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
The relevant revision of src/util/numeric.h is copyrighted as follows:
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
The source code of these files can be found at
<https://github.com/minetest/minetest/>.
]]
local exports = {}
local MAPGEN_LIMIT =
tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000
local CHUNKSIZE = tonumber(minetest.get_mapgen_setting("chunksize")) or 5
-- A randomly seeded instance of PcgRandom. I'm just trying to mix up a few
-- entropy sources here.
local rng = PcgRandom(math.random(0, 32767))
rng = PcgRandom(math.random(0, 32767) + rng:next())
rng = PcgRandom(os.time() + rng:next())
rng = PcgRandom(os.clock() + rng:next())
exports.storage = minetest.get_mod_storage()
exports.translate = minetest.get_translator("area_containers")
-- Converts a vector (with or without a metatable) into a plain table.
function exports.vec2table(v)
return {x = v.x, y = v.y, z = v.z}
end
-- Makes a new table containing the pairs of a and b, with b's overriding a's.
function exports.merged_table(a, b)
local merged = {}
for key, value in pairs(a) do
merged[key] = value
end
for key, value in pairs(b) do
merged[key] = value
end
return merged
end
-- Does and returns nothing.
function exports.null_func() end
-- Returns a (most likely) unique random ID string.
function exports.get_random_id()
-- Generate 64 random bits and encode them in hexadecimal:
return string.format("%08x%08x",
rng:next() + 2147483648,
rng:next(0, 131071) * 32768 + math.random(0, 32767))
end
-- Rounds the number down to the nearest multiple of the blocksize.
function exports.floor_blocksize(a)
return math.floor(a / 16) * 16
end
-- Gets a node. If get_node fails because the position is not loaded, the
-- position is loaded and get_node is again tried. If this fails, a table is
-- returned with name = "ignore".
function exports.get_node_maybe_load(pos)
local node = minetest.get_node_or_nil(pos)
if node then return node end
minetest.load_area(pos)
return minetest.get_node(pos) -- Might be "ignore"
end
-- Returns whether the block position (NOT node position) is in-range of the
-- map generation.
function exports.blockpos_in_range(blockpos)
local chunk_offset = -math.floor(CHUNKSIZE / 2)
local chunkpos = vector.floor(
vector.divide(
vector.subtract(blockpos, chunk_offset),
CHUNKSIZE))
-- The chunk's minimum position minus the one-block padding:
local min_pos = vector.multiply(
vector.add(
vector.multiply(chunkpos, CHUNKSIZE),
chunk_offset - 1),
16)
if min_pos.x < -MAPGEN_LIMIT or min_pos.y < -MAPGEN_LIMIT or
min_pos.z < -MAPGEN_LIMIT then
return false
end
-- One past chunk's maximum position:
local max_extent = vector.add(min_pos, (CHUNKSIZE + 1) * 16)
if max_extent.x > MAPGEN_LIMIT or max_extent.y > MAPGEN_LIMIT or
max_extent.z > MAPGEN_LIMIT then
return false
end
return true
end
exports.MCL_BLAST_RESISTANCE_INDESTRUCTIBLE = 1000000
-- The longest common prefix of all container node names.
exports.CONTAINER_NAME_PREFIX = "area_containers:container_"
-- The 16 container node names counting up from off to on in binary. The bits
-- from most to least significant are: +X, -X, +Z, -Z.
exports.ALL_CONTAINER_STATES = {}
local all_container_variants = {
"off", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
"1000", "1001", "1010", "1011", "1100", "1101", "1110", "on",
}
for i, variant in ipairs(all_container_variants) do
exports.ALL_CONTAINER_STATES[i] =
exports.CONTAINER_NAME_PREFIX .. variant
end
-- The mesecons on and off states or nil if they could not be found.
if minetest.global_exists("mesecon") and mesecon.state then
exports.MESECON_STATE_ON = mesecon.state.on
exports.MESECON_STATE_OFF = mesecon.state.off
end
-- The offsets of the exit and digiline nodes from the inside position
-- (the chamber wall position with the lowest x, y, and z.)
exports.EXIT_OFFSET = vector.new(0, 2, 1)
exports.DIGILINE_OFFSET = vector.new(3, 0, 3)
-- A mapping from port IDs to offsets from the inside position.
exports.PORT_OFFSETS = {
nx = vector.new(0, 2, 4), pz = vector.new(0, 2, 6),
px = vector.new(0, 2, 8), nz = vector.new(0, 2, 10),
py = vector.new(0, 2, 12), ny = vector.new(0, 2, 14),
}
-- A mapping from port IDs to unit vectors encoding the directions the
-- corresponding outside ports face.
exports.PORT_DIRS = {
nx = vector.new(-1, 0, 0), pz = vector.new(0, 0, 1),
px = vector.new(1, 0, 0), nz = vector.new(0, 0, -1),
py = vector.new(0, 1, 0), ny = vector.new(0, -1, 0),
}
-- The list of horizontal port IDs in the order they appear inside,
-- left to right.
exports.PORT_IDS_HORIZ = {"nx", "pz", "px", "nz"}
-- The longest common prefix of all port node names.
local PORT_NAME_PREFIX = "area_containers:port_"
exports.PORT_NAME_PREFIX = PORT_NAME_PREFIX
-- Maps a port node name to the corresponding port ID.
function exports.get_port_id_from_name(node_name)
local prefix_length = #PORT_NAME_PREFIX
return string.sub(node_name, prefix_length + 1, prefix_length + 2)
end
-- The names of all nodes that count as ports.
exports.ALL_PORT_STATES = {}
for _, id in ipairs(exports.PORT_IDS_HORIZ) do
table.insert(exports.ALL_PORT_STATES, PORT_NAME_PREFIX .. id .. "_on")
table.insert(exports.ALL_PORT_STATES, PORT_NAME_PREFIX .. id .. "_off")
end
table.insert(exports.ALL_PORT_STATES, PORT_NAME_PREFIX .. "py_off")
table.insert(exports.ALL_PORT_STATES, PORT_NAME_PREFIX .. "ny_off")
-- Maps a tube output direction parallel to exactly one axis to the best guess
-- of the port ID.
function exports.get_port_id_from_direction(dir)
if dir.x > 0 then
return "px"
elseif dir.x < 0 then
return "nx"
elseif dir.z > 0 then
return "pz"
elseif dir.z < 0 then
return "nz"
elseif dir.y > 0 then
return "py"
else
return "ny"
end
end
return exports