1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2024-12-14 10:13:13 +02:00
RedMew/map_gen/shared/generate.lua

339 lines
8.7 KiB
Lua
Raw Normal View History

2019-01-04 22:02:55 +02:00
local Task = require 'utils.task'
2018-11-26 03:07:03 +02:00
local Token = require 'utils.token'
2018-05-16 12:38:51 +02:00
local Event = require 'utils.event'
2017-08-12 02:20:08 +02:00
2018-11-11 17:08:35 +02:00
local insert = table.insert
2020-09-02 13:26:19 +02:00
local raise_event = script.raise_event
2018-11-11 17:08:35 +02:00
2018-05-24 15:29:24 +02:00
local tiles_per_tick
2018-05-16 12:38:51 +02:00
local regen_decoratives
local surfaces = {}
2018-05-24 15:29:24 +02:00
local total_calls
local Public = {}
2019-01-16 23:32:57 +02:00
-- Set to false by modules that want to control the on_chunk_generated event themselves.
Public.enable_register_events = true
2020-09-02 13:26:19 +02:00
local on_chunk_generated = Event.generate_event_name('generate_on_chunk_generated')
Public.events = {
on_chunk_generated = on_chunk_generated
}
2019-01-16 23:32:57 +02:00
local function do_tile_inner(tiles, tile, pos)
if not tile then
insert(tiles, {name = 'out-of-map', position = pos})
elseif type(tile) == 'string' then
insert(tiles, {name = tile, position = pos})
2018-05-24 15:29:24 +02:00
end
2019-01-16 23:32:57 +02:00
end
2018-05-24 15:29:24 +02:00
2019-01-16 23:32:57 +02:00
local function do_tile(y, x, data, shape)
2018-05-24 15:29:24 +02:00
local pos = {x, y}
-- local coords need to be 'centered' to allow for correct rotation and scaling.
local tile = shape(x + 0.5, y + 0.5, data)
if type(tile) == 'table' then
2019-01-16 23:32:57 +02:00
do_tile_inner(data.tiles, tile.tile, pos)
2018-05-24 15:29:24 +02:00
2018-11-11 17:08:35 +02:00
local hidden_tile = tile.hidden_tile
if hidden_tile then
insert(data.hidden_tiles, {tile = hidden_tile, position = pos})
end
2018-05-24 15:29:24 +02:00
local entities = tile.entities
if entities then
for _, entity in ipairs(entities) do
if not entity.position then
entity.position = pos
end
2018-11-11 17:08:35 +02:00
insert(data.entities, entity)
2018-05-24 15:29:24 +02:00
end
end
local decoratives = tile.decoratives
if decoratives then
for _, decorative in ipairs(decoratives) do
2018-11-11 17:08:35 +02:00
insert(data.decoratives, decorative)
2018-05-24 15:29:24 +02:00
end
end
else
2019-01-16 23:32:57 +02:00
do_tile_inner(data.tiles, tile, pos)
2018-05-24 15:29:24 +02:00
end
end
local function do_row(row, data, shape)
local y = data.top_y + row
local top_x = data.top_x
2019-01-16 23:32:57 +02:00
local tiles = data.tiles
data.y = y
for x = top_x, top_x + 31 do
data.x = x
local pos = {data.x, data.y}
-- local coords need to be 'centered' to allow for correct rotation and scaling.
local tile = shape(x + 0.5, y + 0.5, data)
if type(tile) == 'table' then
2019-01-16 23:32:57 +02:00
do_tile_inner(tiles, tile.tile, pos)
local hidden_tile = tile.hidden_tile
if hidden_tile then
insert(data.hidden_tiles, {tile = hidden_tile, position = pos})
end
local entities = tile.entities
if entities then
for _, entity in ipairs(entities) do
if not entity.position then
entity.position = pos
end
insert(data.entities, entity)
end
end
local decoratives = tile.decoratives
if decoratives then
for _, decorative in ipairs(decoratives) do
if not decorative.position then
decorative.position = pos
end
insert(data.decoratives, decorative)
end
end
else
2019-01-16 23:32:57 +02:00
do_tile_inner(tiles, tile, pos)
end
end
end
local function do_place_tiles(data)
data.surface.set_tiles(data.tiles, true)
end
2018-11-11 17:08:35 +02:00
local function do_place_hidden_tiles(data)
local surface = data.surface
for _, t in ipairs(data.hidden_tiles) do
surface.set_hidden_tile(t.position, t.tile)
end
end
local function do_place_decoratives(data)
2018-05-16 12:38:51 +02:00
if regen_decoratives then
2019-02-24 23:58:06 +02:00
data.surface.regenerate_decorative(nil, {{data.top_x / 32, data.top_y / 32}})
end
2018-05-24 00:04:28 +02:00
local dec = data.decoratives
2018-05-24 20:05:32 +02:00
if #dec > 0 then
2018-05-24 00:04:28 +02:00
data.surface.create_decoratives({check_collision = true, decoratives = dec})
end
end
local function do_place_entities(data)
local surface = data.surface
for _, e in ipairs(data.entities) do
2018-06-07 17:42:02 +02:00
if e.always_place or surface.can_place_entity(e) then
2018-05-10 21:42:38 +02:00
local entity = surface.create_entity(e)
2018-06-07 17:42:02 +02:00
if entity and e.callback then
2018-05-10 21:42:38 +02:00
local callback = Token.get(e.callback)
2018-06-16 13:57:11 +02:00
callback(entity, e.data)
2018-05-10 21:42:38 +02:00
end
end
end
end
local function run_chart_update(data)
local x = data.top_x / 32
local y = data.top_y / 32
2018-02-23 14:11:39 +02:00
if game.forces.player.is_chunk_charted(data.surface, {x, y}) then
-- Don't use full area, otherwise adjacent chunks get charted
game.forces.player.chart(
2018-02-23 14:11:39 +02:00
data.surface,
{
{data.top_x, data.top_y},
{data.top_x + 1, data.top_y + 1}
}
)
end
end
local function map_gen_action(data)
local state = data.y
if state < 32 then
2018-06-07 14:09:28 +02:00
local shape = surfaces[data.surface.name]
if shape == nil then
return false
end
2018-05-24 15:29:24 +02:00
local count = tiles_per_tick
local y = state + data.top_y
local x = data.x
local max_x = data.top_x + 32
2018-05-24 15:29:24 +02:00
data.y = y
repeat
count = count - 1
2018-06-07 14:09:28 +02:00
do_tile(y, x, data, shape)
2018-05-24 15:29:24 +02:00
x = x + 1
if x == max_x then
2018-05-24 15:29:24 +02:00
y = y + 1
if y == data.top_y + 32 then
break
end
x = data.top_x
data.y = y
end
data.x = x
until count == 0
data.y = y - data.top_y
return true
elseif state == 32 then
2018-05-24 15:29:24 +02:00
do_place_tiles(data)
data.y = 33
return true
elseif state == 33 then
2018-11-11 17:08:35 +02:00
do_place_hidden_tiles(data)
2018-05-24 15:29:24 +02:00
data.y = 34
return true
elseif state == 34 then
2018-11-11 17:08:35 +02:00
do_place_entities(data)
2018-05-24 15:29:24 +02:00
data.y = 35
return true
elseif state == 35 then
2018-11-11 17:08:35 +02:00
do_place_decoratives(data)
data.y = 36
return true
elseif state == 36 then
2018-05-24 15:29:24 +02:00
run_chart_update(data)
2020-09-02 13:26:19 +02:00
raise_event(on_chunk_generated, data)
2018-05-24 15:29:24 +02:00
return false
end
end
local map_gen_action_token = Token.register(map_gen_action)
2019-02-04 23:17:00 +02:00
--- Adds generation of a Chunk of the map to the queue
-- @param event <table> the event table from on_chunk_generated
function Public.schedule_chunk(event)
2018-06-07 14:09:28 +02:00
local surface = event.surface
local shape = surfaces[surface.name]
if not shape then
return
end
local area = event.area
local data = {
2018-05-24 15:29:24 +02:00
y = 0,
x = area.left_top.x,
2018-05-24 15:29:24 +02:00
area = area,
top_x = area.left_top.x,
top_y = area.left_top.y,
2020-09-02 13:26:19 +02:00
position = event.position,
2018-06-07 14:09:28 +02:00
surface = surface,
tiles = {},
2018-11-11 17:08:35 +02:00
hidden_tiles = {},
entities = {},
decoratives = {}
}
Task.queue_task(map_gen_action_token, data, total_calls)
end
2019-02-04 23:17:00 +02:00
--- Generates a Chunk of map when called
-- @param event <table> the event table from on_chunk_generated
function Public.do_chunk(event)
local surface = event.surface
local shape = surfaces[surface.name]
if not shape then
return
end
local area = event.area
local data = {
area = area,
top_x = area.left_top.x,
top_y = area.left_top.y,
2020-09-02 13:26:19 +02:00
position = event.position,
surface = surface,
tiles = {},
hidden_tiles = {},
entities = {},
decoratives = {}
}
for row = 0, 31 do
do_row(row, data, shape)
end
do_place_tiles(data)
do_place_hidden_tiles(data)
do_place_entities(data)
do_place_decoratives(data)
2020-09-02 13:26:19 +02:00
raise_event(on_chunk_generated, data)
end
2019-02-04 23:17:00 +02:00
--- Sets the variables for the generate functions, should only be called from map_loader
-- @param args <table>
function Public.init(args)
2018-05-24 15:29:24 +02:00
tiles_per_tick = args.tiles_per_tick or 32
2018-05-16 12:38:51 +02:00
regen_decoratives = args.regen_decoratives or false
for surface_name, shape in pairs(args.surfaces or {}) do
surfaces[surface_name] = shape
end
2018-05-16 12:38:51 +02:00
2018-11-11 17:08:35 +02:00
total_calls = math.ceil(1024 / tiles_per_tick) + 5
end
local do_chunk = Public.do_chunk
local schedule_chunk = Public.schedule_chunk
2018-05-24 15:29:24 +02:00
local function on_chunk(event)
if event.tick == 0 then
do_chunk(event)
else
schedule_chunk(event)
end
end
2019-02-04 23:17:00 +02:00
--- Registers the event to generate our map when Chunks are generated, should only be called from map_loader
function Public.register()
2019-01-16 23:32:57 +02:00
if not Public.enable_register_events then
return
end
2018-05-09 00:46:49 +02:00
2019-01-16 23:32:57 +02:00
if _DEBUG then
Event.add(defines.events.on_chunk_generated, do_chunk)
else
Event.add(defines.events.on_chunk_generated, on_chunk)
end
end
2019-02-04 23:17:00 +02:00
--- Returns the surfaces that the generate functions will act on
-- Warning! Changing this table after on_init or on_load has run will cause desyncs!
2019-02-04 23:17:00 +02:00
-- @return dictionary of surface_name -> shape function
function Public.get_surfaces()
if _LIFECYCLE == 8 then
error('Calling Generate.get_surfaces after on_init() or on_load() has run is a desync risk.', 2)
end
return surfaces
end
return Public