cartographer/scanner.lua

223 lines
6.2 KiB
Lua
Raw Normal View History

2020-06-01 13:59:34 +02:00
-- Arguments
-- map_data: The cartographer map data table
local map_data, chunk = ...;
2020-02-16 18:55:07 +01:00
2020-06-09 02:03:44 +02:00
local scan_queue = {};
2020-04-11 20:02:12 +02:00
-- Register a new tile in map data
-- x: The x position in map coordinates
-- y: The y position in map coordinates
-- biome: The tile's biome id
-- height: The tile's height
-- (Optional): manual_scan: Indicates if this was a 'manual' (non-generated)
-- scan. Manual scans are overridden by generated
-- scans under normal circumstances.
local function register_tile(x, y, biome, height, manual_scan)
2020-06-01 13:59:34 +02:00
if not map_data.generated[x] then
map_data.generated[x] = {
[y] = {
biome = biome,
height = height,
}
2020-02-16 18:55:07 +01:00
};
2020-04-11 20:02:12 +02:00
if manual_scan ~= nil then
2020-06-01 13:59:34 +02:00
map_data.generated[x][y].manual_scan = manual_scan;
2020-04-11 20:02:12 +02:00
end
2020-06-01 13:59:34 +02:00
elseif not map_data.generated[x][y] or map_data.generated[x][y].height < height then
map_data.generated[x][y] = {
biome = biome,
height = height,
};
2020-06-01 13:59:34 +02:00
if manual_scan ~= nil or map_data.generated[x][y].manual_scan then
map_data.generated[x][y].manual_scan = manual_scan;
2020-04-11 20:02:12 +02:00
end
2020-02-16 18:55:07 +01:00
end
end
local function get_mapgen_biome(min, max, mmin, mmax)
local UNDERGROUND = minetest.get_biome_id("underground");
local DEFAULT = minetest.get_biome_id("default");
local biomes = minetest.get_mapgen_object("biomemap");
local heights = minetest.get_mapgen_object("heightmap");
2020-02-16 18:55:07 +01:00
local xx = max.x - min.x;
local zz = max.z - min.z;
local xxx = mmax.x - mmin.x;
local startx = min.x - mmin.x;
local startz = min.z - mmin.z;
2020-02-16 18:55:07 +01:00
local scan_biomes = {};
local scan_heights = {};
2020-02-16 18:55:07 +01:00
for i = startx,startx + xx,1 do
for k = startz,startz + zz,1 do
local b = biomes[i + (k * (xxx + 1))];
2020-02-16 18:55:07 +01:00
if b ~= nil and b ~= UNDERGROUND and b ~= DEFAULT then
scan_biomes[b] = (scan_biomes[b] or 0) + 1;
scan_heights[b] = (scan_heights[b] or 0) + heights[i + (k * (xxx + 1))];
2020-02-16 18:55:07 +01:00
end
end
end
local biome = nil;
local high = 0;
for k,v in pairs(scan_biomes) do
if v > high then
biome = k;
high = v;
end
end
local avg_height = 0;
if high > 0 then
avg_height = scan_heights[biome] / high;
end
return biome, avg_height;
2020-02-16 18:55:07 +01:00
end
local function get_biome(min, max)
local UNDERGROUND = minetest.get_biome_id("underground");
local DEFAULT = minetest.get_biome_id("default");
local WATER_SOURCE = minetest.registered_aliases["mapgen_water_source"];
local scan_biomes = {};
local scan_heights = {};
2020-02-16 18:55:07 +01:00
for i = min.x,max.x,1 do
for j = min.y,max.y,1 do
for k = min.z,max.z,1 do
local pos = { x=i, y=j, z=k };
local b = minetest.get_biome_data(pos).biome;
local node = minetest.get_node(pos).name;
if b ~= nil and b ~= UNDERGROUND and b ~= DEFAULT and node ~= "air" and node ~= WATER_SOURCE then
pos.y = pos.y + 1;
node = minetest.get_node(pos).name;
if node == "air" or node == WATER_SOURCE then
scan_biomes[b] = (scan_biomes[b] or 0) + 1;
scan_heights[b] = (scan_heights[b] or 0) + j;
2020-02-16 18:55:07 +01:00
end
end
end
end
end
local biome = nil;
local high = 0;
for k,v in pairs(scan_biomes) do
if v > high then
biome = k;
high = v;
end
end
local avg_height = 0;
if high > 0 then
avg_height = scan_heights[biome] / high;
end
return biome, avg_height;
2020-02-16 18:55:07 +01:00
end
2020-06-02 00:22:08 +02:00
local function on_generated(min, max, _)
for i = chunk.to(min.x),chunk.to(max.x),1 do
for j = chunk.to(min.z),chunk.to(max.z),1 do
2020-02-16 18:55:07 +01:00
local sub_min = {
x = chunk.from(i),
2020-02-16 18:55:07 +01:00
y = min.y,
z = chunk.from(j),
2020-02-16 18:55:07 +01:00
};
local sub_max = {
x = chunk.from(i + 1),
2020-02-16 18:55:07 +01:00
y = max.y,
z = chunk.from(j + 1),
2020-02-16 18:55:07 +01:00
};
local biome, height = get_mapgen_biome(sub_min, sub_max, min, max);
2020-02-16 18:55:07 +01:00
if biome ~= nil then
2020-04-11 20:02:12 +02:00
register_tile(i, j, biome, height)
2020-02-16 18:55:07 +01:00
end
end
end
end
minetest.register_on_generated(on_generated);
2020-04-11 20:02:12 +02:00
-- Is the scan of this position already handled?
-- x: The x position, in map coordinates
-- y: The y position, in world coordinates
-- x: The z position, in map coordinates
--
-- Returns true if the position is handled by the current map data
local function is_scan_handled(x, y, z)
2020-06-01 13:59:34 +02:00
if not map_data.generated[x] then
2020-04-11 20:02:12 +02:00
return false;
end
2020-06-01 13:59:34 +02:00
local tile = map_data.generated[x][z];
2020-04-11 20:02:12 +02:00
return tile and ((not tile.manual_scan and tile.height > 0) or tile.height >= y);
end
local scanner = {};
2020-04-11 20:02:12 +02:00
-- Queue a tile for manual scanning
-- pos: The position as a table, in world coordinates
function scanner.queue_region(pos)
2020-02-16 18:55:07 +01:00
local converted = {
x = chunk.from(chunk.to(pos.x)),
y = chunk.from(chunk.to(pos.y)),
z = chunk.from(chunk.to(pos.z)),
2020-02-16 18:55:07 +01:00
};
if is_scan_handled(chunk.to(pos.x), pos.y, chunk.to(pos.z)) then
2020-02-16 18:55:07 +01:00
return;
end
2020-06-09 02:03:44 +02:00
for _,queued_pos in ipairs(scan_queue) do
2020-02-16 18:55:07 +01:00
if vector.equals(converted, queued_pos) then
return;
end
end
2020-06-09 02:03:44 +02:00
scan_queue[#scan_queue + 1] = converted;
2020-02-16 18:55:07 +01:00
end
2020-04-11 20:02:12 +02:00
-- Scan the next tile on the queue, and remove it
function scanner.scan_regions()
2020-06-09 02:03:44 +02:00
local len = #scan_queue;
2020-02-16 18:55:07 +01:00
if len == 0 then
return;
end
2020-06-09 02:03:44 +02:00
local startpos = scan_queue[1];
local chunk_x = chunk.to(startpos.x);
local chunk_y = chunk.to(startpos.y);
local chunk_z = chunk.to(startpos.z);
2020-02-16 18:55:07 +01:00
local endpos = {
x = chunk.from(chunk_x + 1),
y = chunk.from(chunk_y + 1),
z = chunk.from(chunk_z + 1),
2020-02-16 18:55:07 +01:00
};
if is_scan_handled(chunk_x, startpos.y, chunk_z) then
2020-06-09 02:03:44 +02:00
table.remove(scan_queue, 1);
2020-02-16 18:55:07 +01:00
return;
end
local biome,height = get_biome(startpos, endpos);
2020-02-16 18:55:07 +01:00
if biome ~= nil then
register_tile(chunk_x, chunk_z, biome, height, true)
2020-02-16 18:55:07 +01:00
end
2020-06-09 02:03:44 +02:00
table.remove(scan_queue, 1);
2020-02-16 18:55:07 +01:00
end
return scanner;