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

589 lines
19 KiB
Lua
Raw Normal View History

2018-09-08 18:29:27 +02:00
--[[-- info
Provides the ability to collapse caves when digging.
]]
-- dependencies
require 'utils.list_utils'
local Event = require 'utils.event'
2018-09-14 22:12:55 +02:00
local Template = require 'map_gen.Diggy.Template'
local ScoreTable = require 'map_gen.Diggy.ScoreTable'
local Debug = require 'map_gen.Diggy.Debug'
2018-09-20 20:04:24 +02:00
local Task = require 'utils.Task'
local Token = require 'utils.global_token'
local Global = require 'utils.global'
local Game = require 'utils.game'
local insert = table.insert
local random = math.random
local floor = math.floor
local abs = math.abs
2018-09-08 18:29:27 +02:00
-- this
local DiggyCaveCollapse = {}
2018-09-20 20:04:24 +02:00
local config = {}
local n = 9
local radius = 0
local radius_sq = 0
local center_radius_sq = 0
local disc_radius_sq = 0
local center_weight
local disc_weight
local ring_weight
local disc_blur_sum = 0
local center_value = 0
local disc_value = 0
local ring_value = 0
local enable_stress_grid = 0
local stress_map_add
local mask_disc_blur
local stress_map_check_stress_in_threshold
local support_beam_entities
local on_surface_created
2018-10-07 20:51:07 +02:00
2018-10-16 18:05:25 +02:00
local stress_threshold_causing_collapse = 3.57
2018-10-07 20:51:07 +02:00
local deconstruction_alert_message_shown = {}
local stress_map_storage = {}
local new_tile_map = {}
local collapse_positions_storage = {}
local cave_collapse_disabled
Global.register({
new_tile_map = new_tile_map,
stress_map_storage = stress_map_storage,
deconstruction_alert_message_shown = deconstruction_alert_message_shown,
collapse_positions_storage = collapse_positions_storage,
cave_collapse_disabled = cave_collapse_disabled,
}, function(tbl)
new_tile_map = tbl.new_tile_map
stress_map_storage = tbl.stress_map_storage
deconstruction_alert_message_shown = tbl.deconstruction_alert_message_shown
collapse_positions_storage = tbl.collapse_positions_storage
cave_collapse_disabled = tbl.cave_collapse_disabled
end)
2018-10-07 20:51:07 +02:00
local defaultValue = 0
DiggyCaveCollapse.events = {
--[[--
When stress at certain position is above the collapse threshold
- position LuaPosition
- surface LuaSurface
]]
on_collapse_triggered = script.generate_event_name()
}
local function create_collapse_template(positions, surface)
local entities = {}
2018-10-11 20:13:42 +02:00
local find_entities_filtered = surface.find_entities_filtered
2018-10-15 13:53:22 +02:00
for _, position in pairs(positions) do
local x = position.x
local y = position.y
2018-10-15 21:30:05 +02:00
local do_insert = true
2018-10-15 13:53:22 +02:00
2018-10-27 14:47:14 +02:00
for _, entity in pairs(find_entities_filtered{area = {{x, y}, {x + 1, y + 1}}}) do
2018-10-15 13:53:22 +02:00
pcall(function()
local strength = support_beam_entities[entity.name]
if strength then
2018-10-15 21:30:05 +02:00
do_insert = false
else
entity.die()
2018-10-15 13:53:22 +02:00
end
end)
end
2018-10-15 21:30:05 +02:00
if do_insert then
insert(entities, {position = {x = x, y = y}, name = 'sand-rock-big'})
2018-10-15 21:30:05 +02:00
end
end
2018-10-15 13:53:22 +02:00
return entities
end
2018-09-12 23:13:54 +02:00
2018-10-16 16:38:58 +02:00
local function create_collapse_alert(surface, position)
local target = surface.create_entity{position = position, name = "sand-rock-big"}
for _,player in pairs(game.connected_players) do
player.add_custom_alert(target, {type="item", name="stone"}, "Cave collapsed!", true)
end
target.destroy()
end
2018-10-07 17:05:59 +02:00
local function collapse(args)
local position = args.position
local surface = args.surface
local positions = {}
2018-10-14 12:19:36 +02:00
local strength = config.collapse_threshold_total_strength
2018-10-16 16:38:58 +02:00
create_collapse_alert(surface, position)
mask_disc_blur(
2018-10-07 20:51:07 +02:00
position.x, position.y,
2018-10-14 12:19:36 +02:00
strength,
function(x, y, value)
stress_map_check_stress_in_threshold(
surface,
{x = x, y = y},
value,
function(_, position)
insert(positions, position)
end
)
end
)
2018-10-15 13:53:22 +02:00
local entities = create_collapse_template(positions, surface)
Template.insert(surface, {}, entities)
ScoreTable.increment('Cave collapse')
2018-09-08 18:29:27 +02:00
end
local on_collapse_timeout_finished = Token.register(collapse)
2018-09-20 20:04:24 +02:00
local function spawn_cracking_sound_text(surface, position)
local text = config.cracking_sounds[random(1, #config.cracking_sounds)]
2018-09-21 11:44:40 +02:00
local color = {
r = 1,
2018-11-06 19:48:38 +02:00
g = random(1, 100) * 0.01,
b = 0
2018-09-21 11:44:40 +02:00
}
local create_entity = surface.create_entity
2018-09-21 11:44:40 +02:00
for i = 1, #text do
local x_offset = (i - #text / 2 - 1) / 3
local char = text:sub(i, i)
create_entity {
name = 'flying-text',
color = color,
text = char,
2018-11-06 19:48:38 +02:00
position = {x = position.x + x_offset, y = position.y - ((i + 1) % 2) * 0.25}
}.active = true
end
end
local function on_collapse_triggered(event)
if cave_collapse_disabled then return end --kill switch
2018-10-11 20:13:42 +02:00
local surface = event.surface
local position = event.position
local x = position.x
local y = position.y
local x_t = new_tile_map[x]
if x_t and x_t[y] then
Template.insert(surface, {}, {{position = position, name = 'sand-rock-big'}})
return
end
spawn_cracking_sound_text(surface, position)
Task.set_timeout(
config.collapse_delay,
on_collapse_timeout_finished,
{surface = surface, position = position}
)
end
local function on_built_tile(surface, new_tile, tiles)
local new_tile_strength = support_beam_entities[new_tile.name]
for _, tile in pairs(tiles) do
if new_tile_strength then
stress_map_add(surface, tile.position, -1 * new_tile_strength, true)
end
local old_tile_strength = support_beam_entities[tile.old_tile.name]
if (old_tile_strength) then
stress_map_add(surface, tile.position, old_tile_strength, true)
end
end
end
local function on_robot_mined_tile(event)
2018-10-11 20:13:42 +02:00
local surface
for _, tile in pairs(event.tiles) do
local strength = support_beam_entities[tile.old_tile.name]
if strength then
2018-10-11 20:13:42 +02:00
surface = surface or event.robot.surface
stress_map_add(surface, tile.position, strength, true)
end
end
end
local function on_player_mined_tile(event)
local surface = game.surfaces[event.surface_index]
for _, tile in pairs(event.tiles) do
local strength = support_beam_entities[tile.old_tile.name]
if strength then
stress_map_add(surface, tile.position, strength, true)
end
end
end
local function on_mined_entity(event)
2018-10-11 20:13:42 +02:00
local entity = event.entity
local strength = support_beam_entities[entity.name]
if strength then
stress_map_add(entity.surface, entity.position, strength)
end
end
local function on_built_entity(event)
2018-10-11 20:13:42 +02:00
local entity = event.created_entity
local strength = support_beam_entities[entity.name]
if strength then
stress_map_add(entity.surface, entity.position, -1 * strength)
end
end
local function on_placed_entity(event)
local strength = support_beam_entities[event.entity.name]
if strength then
stress_map_add(event.entity.surface, event.entity.position, -1 * strength)
end
end
2018-10-07 20:51:07 +02:00
local on_new_tile_timeout_finished = Token.register(function(args)
local x_t = new_tile_map[args.x]
if x_t then
x_t[args.y] = nil --reset new tile status. This tile can cause a chain collapse now
end
end)
local function on_void_removed(event)
local strength = support_beam_entities['out-of-map']
local position = event.position
if strength then
stress_map_add(event.surface, position, strength)
end
2018-10-07 20:51:07 +02:00
local x = position.x
local y = position.y
--To avoid room collapse:
local x_t = new_tile_map[x]
if x_t then
x_t[y] = true
else
x_t = {
[y] = true
}
new_tile_map[x] = x_t
end
Task.set_timeout(3, on_new_tile_timeout_finished, {x = x, y = y})
end
2018-09-08 18:29:27 +02:00
--[[--
Registers all event handlers.]
2018-09-21 11:44:40 +02:00
@param global_config Table {@see Diggy.Config}.
2018-09-08 18:29:27 +02:00
]]
2018-10-07 17:05:59 +02:00
function DiggyCaveCollapse.register(cfg)
config = cfg
support_beam_entities = config.support_beam_entities
ScoreTable.reset('Cave collapse')
Event.add(DiggyCaveCollapse.events.on_collapse_triggered, on_collapse_triggered)
Event.add(defines.events.on_robot_built_entity, on_built_entity)
Event.add(defines.events.on_robot_built_tile, function (event)
on_built_tile(event.robot.surface, event.item, event.tiles)
end)
Event.add(defines.events.on_player_built_tile, function (event)
on_built_tile(game.surfaces[event.surface_index], event.item, event.tiles)
end)
Event.add(defines.events.on_robot_mined_tile, on_robot_mined_tile)
Event.add(defines.events.on_player_mined_tile, on_player_mined_tile)
Event.add(defines.events.on_robot_mined_entity, on_mined_entity)
Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(Template.events.on_placed_entity, on_placed_entity)
Event.add(defines.events.on_entity_died, on_mined_entity)
Event.add(defines.events.on_player_mined_entity, on_mined_entity)
Event.add(Template.events.on_void_removed, on_void_removed)
2018-10-07 20:51:07 +02:00
Event.add(defines.events.on_surface_created, on_surface_created)
Event.add(defines.events.on_marked_for_deconstruction, function (event)
if (nil ~= support_beam_entities[event.entity.name]) then
event.entity.cancel_deconstruction(Game.get_player_by_index(event.player_index).force)
end
end)
Event.add(defines.events.on_pre_player_mined_item, function(event)
local player_index = event.player_index
if (nil ~= deconstruction_alert_message_shown[player_index]) then
return
end
if (nil ~= support_beam_entities[event.entity.name]) then
require 'popup'.player(
Game.get_player_by_index(player_index),[[
Mining entities such as walls, stone paths, concrete
and rocks, can cause a cave-in, be careful miner!
Foreman's advice: Place a wall every 4th tile to
prevent a cave-in. Use stone paths and concrete
to reinforce it further.
]]
)
deconstruction_alert_message_shown[player_index] = true
end
end)
enable_stress_grid = config.enable_stress_grid
2018-10-07 20:51:07 +02:00
on_surface_created({surface_index = 1})
mask_init(config)
if (config.enable_mask_debug) then
local surface = game.surfaces.nauvis
2018-10-07 20:51:07 +02:00
mask_disc_blur(0, 0, 10, function(x, y, fraction)
Debug.print_grid_value(fraction, surface, {x = x, y = y})
end)
2018-09-25 21:34:36 +02:00
end
if config.enable_debug_commands then
commands.add_command('test-tile-support-range', '<tilename> <range> creates a square of tiles with length <range>. It is spawned one <range> north of the player.', function(cmd)
local params = {}
for param in string.gmatch(cmd.parameter, '%S+') do
table.insert(params, param)
end
local tilename = params[1]
local range = tonumber(params[2])
local position = {x = math.floor(game.player.position.x), y = math.floor(game.player.position.y) - 5 * range - 1}
local surface = game.player.surface
local tiles = {}
local entities = {}
for x = position.x, position.x + range * 5 do
for y = position.y, position.y + range * 5 do
if y % range + x % range == 0 then
insert(entities,{name = "stone-wall", position = {x=x,y=y}})
end
insert(tiles, {position = {x = x, y = y}, name = tilename})
local strength = support_beam_entities[tilename]
if strength then
stress_map_add(surface, {x =x, y=y}, - strength)
end
for _, entity in pairs(surface.find_entities_filtered({position = {x=x,y=y}})) do
pcall(function()
local strength = support_beam_entities[entity.name]
local position = entity.position
entity.die()
if strength then
stress_map_add(surface, position, strength)
end
end
)
end
end
end
Template.insert(surface, tiles, entities)
end
)
end
commands.add_command('toggle-cave-collapse', 'Toggles cave collapse (admins only).', function()
pcall(function() --better safe than sorry
if not game.player or game.player.admin then
cave_collapse_disabled = not cave_collapse_disabled
if cave_collapse_disabled then
game.print("Cave collapse: Disabled.")
else
game.print("Cave collapse: Enabled.")
end
end
end)
end)
end
2018-09-25 21:34:36 +02:00
--
--STRESS MAP
--
--[[--
Adds a fraction to a given location on the stress_map. Returns the new
fraction value of that position.
2018-09-08 18:29:27 +02:00
2018-10-07 20:51:07 +02:00
@param stress_map Table of {x,y}
@param position Table with x and y
@param number fraction
2018-09-08 18:29:27 +02:00
@return number sum of old fraction + new fraction
]]
local function add_fraction(stress_map, x, y, fraction)
2018-11-06 19:48:38 +02:00
x = 2 * floor(x * 0.5)
y = 2 * floor(y * 0.5)
2018-10-20 00:59:57 +02:00
local x_t = stress_map[x]
if not x_t then
x_t = {}
2018-10-20 00:59:57 +02:00
stress_map[x] = x_t
end
local value = x_t[y]
if not value then
value = defaultValue
end
value = value + fraction
x_t[y] = value
2018-10-14 12:19:36 +02:00
if (fraction > 0 and value > stress_threshold_causing_collapse) then
script.raise_event(
DiggyCaveCollapse.events.on_collapse_triggered,
{surface = game.surfaces[stress_map.surface_index], position = {x = x, y = y}}
)
end
if (enable_stress_grid) then
local surface = game.surfaces[stress_map.surface_index]
Debug.print_grid_value(value, surface, {x = x, y = y}, 4, 0.5)
end
return value
end
2018-10-07 20:51:07 +02:00
on_surface_created = function(event)
stress_map_storage[event.surface_index] = {}
local map = stress_map_storage[event.surface_index]
map['surface_index'] = event.surface_index
2018-10-09 01:26:24 +02:00
map[1] = {index = 1}
map[2] = {index = 2}
map[3] = {index = 3}
map[4] = {index = 4}
2018-10-07 20:51:07 +02:00
end
2018-09-08 18:29:27 +02:00
--[[--
Checks whether a tile's pressure is within a given threshold and calls the handler if not.
@param surface LuaSurface
@param position Position with x and y
@param number threshold
@param callback
]]
stress_map_check_stress_in_threshold = function(surface, position, threshold, callback)
2018-10-07 20:51:07 +02:00
local stress_map = stress_map_storage[surface.index]
local value = add_fraction(stress_map, position.x, position.y, 0)
2018-09-08 18:29:27 +02:00
if (value >= stress_threshold_causing_collapse - threshold) then
callback(surface, position)
end
end
2018-09-08 18:29:27 +02:00
stress_map_add = function(surface, position, factor, no_blur)
local x_start = floor(position.x)
local y_start = floor(position.y)
2018-10-07 20:51:07 +02:00
local stress_map = stress_map_storage[surface.index]
if not stress_map then
return
end
if no_blur then
add_fraction(stress_map, x_start, y_start, factor)
return
end
2018-10-20 00:59:57 +02:00
for x = -radius, radius do
for y = -radius, radius do
local value = 0
local distance_sq = x * x + y * y
if distance_sq <= center_radius_sq then
value = center_value
elseif distance_sq <= disc_radius_sq then
value = disc_value
elseif distance_sq <= radius_sq then
value = ring_value
end
2018-10-20 00:59:57 +02:00
if abs(value) > 0.001 then
add_fraction(stress_map, x + x_start, y + y_start, value * factor)
end
end
end
end
2018-09-08 18:29:27 +02:00
DiggyCaveCollapse.stress_map_add = stress_map_add
2018-09-08 18:29:27 +02:00
--
-- MASK
--
2018-09-08 18:29:27 +02:00
function mask_init(config)
n = config.mask_size
ring_weight = config.mask_relative_ring_weights[1]
disc_weight = config.mask_relative_ring_weights[2]
center_weight = config.mask_relative_ring_weights[3]
2018-11-06 19:48:38 +02:00
radius = floor(n * 0.5)
radius_sq = (radius + 0.2) * (radius + 0.2)
center_radius_sq = radius_sq / 9
disc_radius_sq = radius_sq * 4 / 9
for x = -radius, radius do
for y = -radius, radius do
local distance_sq = x * x + y * y
if distance_sq <= center_radius_sq then
disc_blur_sum = disc_blur_sum + center_weight
elseif distance_sq <= disc_radius_sq then
disc_blur_sum = disc_blur_sum + disc_weight
elseif distance_sq <= radius_sq then
disc_blur_sum = disc_blur_sum + ring_weight
end
end
end
center_value = center_weight / disc_blur_sum
disc_value = disc_weight / disc_blur_sum
ring_value = ring_weight / disc_blur_sum
2018-09-08 18:29:27 +02:00
end
--[[--
Applies a blur
Applies the disc in 3 discs: center, (middle) disc and (outer) ring.
The relative weights for tiles in a disc are:
center: 3/3
disc: 2/3
ring: 1/3
The sum of all values is 1
@param x_start number center point
@param y_start number center point
@param factor the factor to multiply the cell value with (value = cell_value * factor)
@param callback function to execute on each tile within the mask callback(x, y, value)
2018-09-08 18:29:27 +02:00
]]
mask_disc_blur = function(x_start, y_start, factor, callback)
x_start = floor(x_start)
y_start = floor(y_start)
for x = -radius, radius do
for y = -radius, radius do
local value = 0
local distance_sq = x * x + y * y
if distance_sq <= center_radius_sq then
value = center_value
elseif distance_sq <= disc_radius_sq then
value = disc_value
elseif distance_sq <= radius_sq then
value = ring_value
end
if abs(value) > 0.001 then
callback(x_start + x, y_start + y, value * factor)
end
end
end
2018-09-08 18:29:27 +02:00
end
2018-10-07 17:05:59 +02:00
function DiggyCaveCollapse.get_extra_map_info(config)
return [[Alien Spawner, aliens might spawn when mining!
Place stone walls, stone paths and (refined) concrete to reinforce the mine. If you see cracks appear, run!]]
end
2018-09-08 18:29:27 +02:00
return DiggyCaveCollapse