--luacheck: ignore
local Public = {}
local LootRaffle = require 'functions.loot_raffle'
local BiterRaffle = require 'functions.biter_raffle'
local bb_config = require 'maps.biter_battles_v2.config'

local table_insert = table.insert
local math_floor = math.floor
local math_random = math.random
local math_abs = math.abs
local math_sqrt = math.sqrt

local GetNoise = require 'utils.get_noise'
local simplex_noise = require 'utils.simplex_noise'.d2
local spawn_circle_size = 39
local ores = {'copper-ore', 'iron-ore', 'stone', 'coal'}
local rocks = {'sand-rock-big', 'sand-rock-big', 'rock-big', 'rock-big', 'rock-big', 'rock-big', 'rock-huge'}

local chunk_tile_vectors = {}
for x = 0, 31, 1 do
    for y = 0, 31, 1 do
        chunk_tile_vectors[#chunk_tile_vectors + 1] = {x, y}
    end
end
local size_of_chunk_tile_vectors = #chunk_tile_vectors

local loading_chunk_vectors = {}
for k, v in pairs(chunk_tile_vectors) do
    if v[1] == 0 or v[1] == 31 or v[2] == 0 or v[2] == 31 then
        table_insert(loading_chunk_vectors, v)
    end
end

local wrecks = {
    'crash-site-spaceship-wreck-big-1',
    'crash-site-spaceship-wreck-big-2',
    'crash-site-spaceship-wreck-medium-1',
    'crash-site-spaceship-wreck-medium-2',
    'crash-site-spaceship-wreck-medium-3'
}
local size_of_wrecks = #wrecks
local valid_wrecks = {}
for _, wreck in pairs(wrecks) do
    valid_wrecks[wreck] = true
end
local loot_blacklist = {
    ['automation-science-pack'] = true,
    ['logistic-science-pack'] = true,
    ['military-science-pack'] = true,
    ['chemical-science-pack'] = true,
    ['production-science-pack'] = true,
    ['utility-science-pack'] = true,
    ['space-science-pack'] = true,
    ['loader'] = true,
    ['fast-loader'] = true,
    ['express-loader'] = true
}

local function shuffle(tbl)
    local size = #tbl
    for i = size, 1, -1 do
        local rand = math_random(size)
        tbl[i], tbl[rand] = tbl[rand], tbl[i]
    end
    return tbl
end

local function get_noise(name, pos)
    local seed = game.surfaces['bb_source'].map_gen_settings.seed
    local noise_seed_add = 25000
    if name == 1 then
        local noise = simplex_noise(pos.x * 0.0042, pos.y * 0.0042, seed)
        seed = seed + noise_seed_add
        noise = noise + simplex_noise(pos.x * 0.031, pos.y * 0.031, seed) * 0.08
        seed = seed + noise_seed_add
        noise = noise + simplex_noise(pos.x * 0.1, pos.y * 0.1, seed) * 0.025
        return noise
    end

    if name == 2 then
        local noise = simplex_noise(pos.x * 0.011, pos.y * 0.011, seed)
        seed = seed + noise_seed_add
        noise = noise + simplex_noise(pos.x * 0.08, pos.y * 0.08, seed) * 0.2
        return noise
    end

    if name == 3 then
        local noise = simplex_noise(pos.x * 0.005, pos.y * 0.005, seed)
        noise = noise + simplex_noise(pos.x * 0.02, pos.y * 0.02, seed) * 0.3
        noise = noise + simplex_noise(pos.x * 0.15, pos.y * 0.15, seed) * 0.025
        return noise
    end
end

local function create_mirrored_tile_chain(surface, tile, count, straightness)
    if not surface then
        return
    end
    if not tile then
        return
    end
    if not count then
        return
    end

    local position = {x = tile.position.x, y = tile.position.y}

    local modifiers = {
        {x = 0, y = -1},
        {x = -1, y = 0},
        {x = 1, y = 0},
        {x = 0, y = 1},
        {x = -1, y = 1},
        {x = 1, y = -1},
        {x = 1, y = 1},
        {x = -1, y = -1}
    }
    modifiers = shuffle(modifiers)

    for a = 1, count, 1 do
        local tile_placed = false

        if math_random(0, 100) > straightness then
            modifiers = shuffle(modifiers)
        end
        for b = 1, 4, 1 do
            local pos = {x = position.x + modifiers[b].x, y = position.y + modifiers[b].y}
            if surface.get_tile(pos).name ~= tile.name then
                surface.set_tiles({{name = 'landfill', position = pos}}, true)
                surface.set_tiles({{name = tile.name, position = pos}}, true)
                --surface.set_tiles({{name = "landfill", position = {pos.x * -1, (pos.y * -1) - 1}}}, true)
                --surface.set_tiles({{name = tile.name, position = {pos.x * -1, (pos.y * -1) - 1}}}, true)
                position = {x = pos.x, y = pos.y}
                tile_placed = true
                break
            end
        end

        if not tile_placed then
            position = {x = position.x + modifiers[1].x, y = position.y + modifiers[1].y}
        end
    end
end

local function get_replacement_tile(surface, position)
    for i = 1, 128, 1 do
        local vectors = {{0, i}, {0, i * -1}, {i, 0}, {i * -1, 0}}
        table.shuffle_table(vectors)
        for k, v in pairs(vectors) do
            local tile = surface.get_tile(position.x + v[1], position.y + v[2])
            if not tile.collides_with('resource-layer') then
                if tile.name ~= 'stone-path' then
                    return tile.name
                end
            end
        end
    end
    return 'grass-1'
end

local function draw_noise_ore_patch(position, name, surface, radius, richness)
    if not position then
        return
    end
    if not name then
        return
    end
    if not surface then
        return
    end
    if not radius then
        return
    end
    if not richness then
        return
    end
    local seed = game.surfaces['bb_source'].map_gen_settings.seed
    local noise_seed_add = 25000
    local richness_part = richness / radius
    for y = radius * -3, radius * 3, 1 do
        for x = radius * -3, radius * 3, 1 do
            local pos = {x = x + position.x + 0.5, y = y + position.y + 0.5}
            local noise_1 = simplex_noise(pos.x * 0.0125, pos.y * 0.0125, seed)
            local noise_2 = simplex_noise(pos.x * 0.1, pos.y * 0.1, seed + 25000)
            local noise = noise_1 + noise_2 * 0.12
            local distance_to_center = math_sqrt(x ^ 2 + y ^ 2)
            local a = richness - richness_part * distance_to_center
            if distance_to_center < radius - math_abs(noise * radius * 0.85) and a > 1 then
                if surface.can_place_entity({name = name, position = pos, amount = a}) then
                    surface.create_entity {name = name, position = pos, amount = a}
                    for _, e in pairs(surface.find_entities_filtered({position = pos, name = {'wooden-chest', 'stone-wall', 'gun-turret'}})) do
                        e.destroy()
                    end
                end
            end
        end
    end
end

function is_within_spawn_circle(pos)
    if math_abs(pos.x) > spawn_circle_size then
        return false
    end
    if math_abs(pos.y) > spawn_circle_size then
        return false
    end
    if math_sqrt(pos.x ^ 2 + pos.y ^ 2) > spawn_circle_size then
        return false
    end
    return true
end

local river_y_1 = bb_config.border_river_width * -1.5
local river_y_2 = bb_config.border_river_width * 1.5
local river_width_half = math_floor(bb_config.border_river_width * -0.5)
function is_horizontal_border_river(pos)
    if pos.y < river_y_1 then
        return false
    end
    if pos.y > river_y_2 then
        return false
    end
    if pos.y >= river_width_half - (math_abs(get_noise(1, pos)) * 4) then
        return true
    end
    return false
end

local function generate_starting_area(pos, distance_to_center, surface)
    -- assert(distance_to_center >= spawn_circle_size) == true
    local spawn_wall_radius = 116
    local noise_multiplier = 15
    local min_noise = -noise_multiplier * 1.25

    -- Avoid calculating noise, see comment below
    if (distance_to_center + min_noise - spawn_wall_radius) > 4.5 then
        return
    end

    local noise = get_noise(2, pos) * noise_multiplier
    local distance_from_spawn_wall = distance_to_center + noise - spawn_wall_radius
    -- distance_from_spawn_wall is the difference between the distance_to_center (with added noise)
    -- and our spawn_wall radius (spawn_wall_radius=116), i.e. how far are we from the ring with radius spawn_wall_radius.
    -- The following shows what happens depending on distance_from_spawn_wall:
    --   	min     max
    --  	N/A     -10	    => replace water
    -- if noise_2 > -0.5:
    --      -1.75    0 	    => wall
    -- else:
    --   	-6      -3 	 	=> 1/16 chance of turret or turret-remnants
    --   	-1.95    0 	 	=> wall
    --    	 0       4.5    => chest-remnants with 1/3, chest with 1/(distance_from_spawn_wall+2)
    --
    -- => We never do anything for (distance_to_center + min_noise - spawn_wall_radius) > 4.5

    if distance_from_spawn_wall < 0 then
        if math_random(1, 100) > 23 then
            for _, tree in pairs(surface.find_entities_filtered({type = 'tree', area = {{pos.x, pos.y}, {pos.x + 1, pos.y + 1}}})) do
                tree.destroy()
            end
        end
    end

    if distance_from_spawn_wall < -10 and not is_horizontal_border_river(pos) then
        local tile_name = surface.get_tile(pos).name
        if tile_name == 'water' or tile_name == 'deepwater' then
            surface.set_tiles({{name = get_replacement_tile(surface, pos), position = pos}}, true)
        end
        return
    end

    if surface.can_place_entity({name = 'wooden-chest', position = pos}) and surface.can_place_entity({name = 'coal', position = pos}) then
        local noise_2 = get_noise(3, pos)
        if noise_2 < 0.40 then
            if noise_2 > -0.40 then
                if distance_from_spawn_wall > -1.75 and distance_from_spawn_wall < 0 then
                    local e = surface.create_entity({name = 'stone-wall', position = pos, force = 'neutral'})
                    e.active = false
                end
            else
                if distance_from_spawn_wall > -1.95 and distance_from_spawn_wall < 0 then
                    local e = surface.create_entity({name = 'stone-wall', position = pos, force = 'neutral'})
                    e.active = false
                elseif distance_from_spawn_wall > 0 and distance_from_spawn_wall < 4.5 then
                    local name = 'wooden-chest'
                    local r_max = math_floor(math.abs(distance_from_spawn_wall)) + 2
                    if math_random(1, 3) == 1 and not is_horizontal_border_river(pos) then
                        name = name .. '-remnants'
                    end
                    if math_random(1, r_max) == 1 then
                        local e = surface.create_entity({name = name, position = pos, force = 'neutral'})
                        e.active = false
                    end
                elseif distance_from_spawn_wall > -6 and distance_from_spawn_wall < -3 then
                    if math_random(1, 16) == 1 then
                        if surface.can_place_entity({name = 'gun-turret', position = pos}) then
                            local e = surface.create_entity({name = 'gun-turret', position = pos, force = 'neutral'})
                            e.insert({name = 'firearm-magazine', count = math_random(6, 12)})
                            e.active = false
                        end
                    else
                        if math_random(1, 24) == 1 and not is_horizontal_border_river(pos) then
                            if surface.can_place_entity({name = 'gun-turret', position = pos}) then
                                surface.create_entity({name = 'gun-turret-remnants', position = pos, force = 'neutral'})
                            end
                        end
                    end
                end
            end
        end
    end
end

local function generate_river(surface, left_top_x, left_top_y)
    if left_top_y ~= -32 then
        return
    end
    for x = 0, 31, 1 do
        for y = 0, 31, 1 do
            local pos = {x = left_top_x + x, y = left_top_y + y}
            local distance_to_center = math_sqrt(pos.x ^ 2 + pos.y ^ 2)
            if is_horizontal_border_river(pos) and distance_to_center > spawn_circle_size - 2 then
                surface.set_tiles({{name = 'deepwater', position = pos}})
                if math_random(1, 64) == 1 then
                    local e = surface.create_entity({name = 'fish', position = pos})
                    e.active = false
                end
            end
        end
    end
end

local scrap_vectors = {}
for x = -8, 8, 1 do
    for y = -8, 8, 1 do
        if math_sqrt(x ^ 2 + y ^ 2) <= 8 then
            scrap_vectors[#scrap_vectors + 1] = {x, y}
        end
    end
end
local size_of_scrap_vectors = #scrap_vectors

local function generate_extra_worm_turrets(surface, left_top)
    local chunk_distance_to_center = math_sqrt(left_top.x ^ 2 + left_top.y ^ 2)
    if bb_config.bitera_area_distance > chunk_distance_to_center then
        return
    end

    local amount = (chunk_distance_to_center - bb_config.bitera_area_distance) * 0.0005
    if amount < 0 then
        return
    end
    local floor_amount = math_floor(amount)
    local r = math.round(amount - floor_amount, 3) * 1000
    if math_random(0, 999) <= r then
        floor_amount = floor_amount + 1
    end

    if floor_amount > 64 then
        floor_amount = 64
    end

    for _ = 1, floor_amount, 1 do
        local worm_turret_name = BiterRaffle.roll('worm', chunk_distance_to_center * 0.00015)
        local v = chunk_tile_vectors[math_random(1, size_of_chunk_tile_vectors)]
        local position = surface.find_non_colliding_position(worm_turret_name, {left_top.x + v[1], left_top.y + v[2]}, 8, 1)
        if position then
            local worm = surface.create_entity({name = worm_turret_name, position = position, force = 'enemy'})
            worm.active = false

            -- add some scrap
            for c = 1, math_random(0, 4), 1 do
                local vector = scrap_vectors[math_random(1, size_of_scrap_vectors)]
                local position = {worm.position.x + vector[1], worm.position.y + vector[2]}
                local name = wrecks[math_random(1, size_of_wrecks)]
                position = surface.find_non_colliding_position(name, position, 16, 1)
                if position then
                    local e = surface.create_entity({name = name, position = position, force = 'neutral'})
                    e.active = false
                end
            end
        end
    end
end

local bitera_area_distance = bb_config.bitera_area_distance * -1
local biter_area_angle = 0.45

local function is_biter_area(position)
    local a = bitera_area_distance - (math_abs(position.x) * biter_area_angle)
    if position.y - 70 > a then
        return false
    end
    if position.y + 70 < a then
        return true
    end
    if position.y + (get_noise(3, position) * 64) > a then
        return false
    end
    return true
end

local function draw_biter_area(surface, left_top_x, left_top_y)
    if not is_biter_area({x = left_top_x, y = left_top_y - 96}) then
        return
    end

    local seed = game.surfaces['bb_source'].map_gen_settings.seed

    local out_of_map = {}
    local tiles = {}
    local i = 1

    for x = 0, 31, 1 do
        for y = 0, 31, 1 do
            local position = {x = left_top_x + x, y = left_top_y + y}
            if is_biter_area(position) then
                local index = math_floor(GetNoise('bb_biterland', position, seed) * 48) % 7 + 1
                out_of_map[i] = {name = 'out-of-map', position = position}
                tiles[i] = {name = 'dirt-' .. index, position = position}
                i = i + 1
            end
        end
    end

    surface.set_tiles(out_of_map, false)
    surface.set_tiles(tiles, true)

    for _ = 1, 4, 1 do
        local v = chunk_tile_vectors[math_random(1, size_of_chunk_tile_vectors)]
        local position = {x = left_top_x + v[1], y = left_top_y + v[2]}
        if is_biter_area(position) and surface.can_place_entity({name = 'spitter-spawner', position = position}) then
            if math_random(1, 4) == 1 then
                local e = surface.create_entity({name = 'spitter-spawner', position = position, force = 'enemy'})
                e.active = false
            else
                local e = surface.create_entity({name = 'biter-spawner', position = position, force = 'enemy'})
                e.active = false
            end
        end
    end

    local e = (math_abs(left_top_y) - bb_config.bitera_area_distance) * 0.0015
    for _ = 1, math_random(5, 10), 1 do
        local v = chunk_tile_vectors[math_random(1, size_of_chunk_tile_vectors)]
        local position = {x = left_top_x + v[1], y = left_top_y + v[2]}
        local worm_turret_name = BiterRaffle.roll('worm', e)
        if is_biter_area(position) and surface.can_place_entity({name = worm_turret_name, position = position}) then
            surface.create_entity({name = worm_turret_name, position = position, force = 'enemy'})
        end
    end
end

local function mixed_ore(surface, left_top_x, left_top_y)
    local seed = game.surfaces['bb_source'].map_gen_settings.seed

    local noise = GetNoise('bb_ore', {x = left_top_x + 16, y = left_top_y + 16}, seed)

    --Draw noise text values to determine which chunks are valid for mixed ore.
    --rendering.draw_text{text = noise, surface = game.surfaces.biter_battles, target = {x = left_top_x + 16, y = left_top_y + 16}, color = {255, 255, 255}, scale = 2, font = "default-game"}

    --Skip chunks that are too far off the ore noise value.
    if noise < 0.42 then
        return
    end

    --Draw the mixed ore patches.
    for x = 0, 31, 1 do
        for y = 0, 31, 1 do
            local pos = {x = left_top_x + x, y = left_top_y + y}
            if surface.can_place_entity({name = 'iron-ore', position = pos}) then
                local noise = GetNoise('bb_ore', pos, seed)
                if noise > 0.72 then
                    local amount = math_random(800, 1000) + math_sqrt(pos.x ^ 2 + pos.y ^ 2) * 3
                    local i = math_floor(noise * 25 + math_abs(pos.x) * 0.05) % 4 + 1
                    surface.create_entity({name = ores[i], position = pos, amount = amount})
                end
            end
        end
    end

    if left_top_y == -32 and math_abs(left_top_x) <= 32 then
        for _, e in pairs(surface.find_entities_filtered({area = {{-12, -12}, {12, 12}}})) do
            e.destroy()
        end
    end
end

function Public.generate(event)
    local surface = event.surface
    local left_top = event.area.left_top
    local left_top_x = left_top.x
    local left_top_y = left_top.y

    if surface.name == 'biter_battles' then
        local tiles = {}
        if math_abs(left_top_x) > 64 or math_abs(left_top_y) > 64 then
            for k, v in pairs(loading_chunk_vectors) do
                tiles[k] = {name = 'out-of-map', position = {left_top_x + v[1], left_top_y + v[2]}}
            end
        end
        surface.set_tiles(tiles, false)
        return
    end

    if surface.name ~= 'bb_source' then
        return
    end

    if left_top_y == 0 then
        local tiles = {}
        local i = 0
        for x = 0, 31, 1 do
            for y = 0, 31, 1 do
                i = i + 1
                tiles[i] = {name = 'out-of-map', position = {left_top_x + x, left_top_y + y}}
            end
        end
        surface.set_tiles(tiles, false)
        return
    end

    if left_top_y > 0 then
        return
    end

    mixed_ore(surface, left_top_x, left_top_y)
    generate_river(surface, left_top_x, left_top_y)
    draw_biter_area(surface, left_top_x, left_top_y)
    generate_extra_worm_turrets(surface, left_top)
end

function Public.draw_spawn_circle(surface)
    local tiles = {}
    for x = spawn_circle_size * -1, -1, 1 do
        for y = spawn_circle_size * -1, -1, 1 do
            local pos = {x = x, y = y}
            local distance_to_center = math_sqrt(pos.x ^ 2 + pos.y ^ 2)
            if distance_to_center <= spawn_circle_size then
                table_insert(tiles, {name = 'deepwater', position = pos})

                if distance_to_center < 9.5 then
                    table_insert(tiles, {name = 'refined-concrete', position = pos})
                    if distance_to_center < 7 then
                        table_insert(tiles, {name = 'sand-1', position = pos})
                    end
                --	else
                --
                end
            end
        end
    end

    for i = 1, #tiles, 1 do
        table_insert(tiles, {name = tiles[i].name, position = {tiles[i].position.x * -1 - 1, tiles[i].position.y}})
    end

    surface.set_tiles(tiles, true)

    for i = 1, #tiles, 1 do
        if tiles[i].name == 'deepwater' then
            if math_random(1, 48) == 1 then
                local e = surface.create_entity({name = 'fish', position = tiles[i].position})
                e.active = false
            end
        end
    end
end

function Public.fill_water_tiles(surface)
    local chunk_r = 8
    local r = chunk_r * 32

    for x = r * -1, r, 1 do
        for y = r * -1, -4, 1 do
            local pos = {x = x, y = y}
            local distance_to_center = math_sqrt(pos.x ^ 2 + pos.y ^ 2)
            if distance_to_center < 550 and not is_horizontal_border_river(pos) then
                local tile_name = surface.get_tile(pos).name
                if tile_name == 'water' or tile_name == 'deepwater' then
                    surface.set_tiles({{name = get_replacement_tile(surface, pos), position = pos}}, true)
                end
            end
        end
    end
end

function Public.draw_spawn_area(surface)
    local chunk_r = 4
    local r = chunk_r * 32

    for x = r * -1, r, 1 do
        for y = r * -1, -4, 1 do
            local pos = {x = x, y = y}
            local distance_to_center = math_sqrt(pos.x ^ 2 + pos.y ^ 2)
            generate_starting_area(pos, distance_to_center, surface)
        end
    end

    surface.destroy_decoratives({})
    surface.regenerate_decorative()
end

function Public.generate_additional_spawn_ore(surface)
    local r = 130
    local area = {{r * -1, r * -1}, {r, 0}}
    local ores = {}
    ores['iron-ore'] = surface.count_entities_filtered({name = 'iron-ore', area = area})
    ores['copper-ore'] = surface.count_entities_filtered({name = 'copper-ore', area = area})
    ores['coal'] = surface.count_entities_filtered({name = 'coal', area = area})
    ores['stone'] = surface.count_entities_filtered({name = 'stone', area = area})
    for ore, ore_count in pairs(ores) do
        if ore_count < 1000 or ore_count == nil then
            local pos = {}
            for a = 1, 32, 1 do
                pos = {x = -96 + math_random(0, 192), y = -20 - math_random(0, 96)}
                if surface.can_place_entity({name = 'coal', position = pos, amount = 1}) then
                    break
                end
            end
            draw_noise_ore_patch(pos, ore, surface, math_random(18, 24), math_random(1500, 2000))
        end
    end
end

function Public.generate_additional_rocks(surface)
    local r = 130
    if surface.count_entities_filtered({type = 'simple-entity', area = {{r * -1, r * -1}, {r, 0}}}) >= 12 then
        return
    end
    local position = {x = -96 + math_random(0, 192), y = -40 - math_random(0, 96)}
    local rocks = {'rock-huge', 'rock-big', 'rock-big', 'rock-big', 'sand-rock-big'}
    for _ = 1, math_random(6, 10) do
        local name = rocks[math_random(1, 5)]
        local p = surface.find_non_colliding_position(name, {position.x + (-10 + math_random(0, 20)), position.y + (-10 + math_random(0, 20))}, 16, 1)
        if p and p.y < -16 then
            surface.create_entity({name = name, position = p})
        end
    end
end

function Public.generate_silo(surface)
    local pos = {x = -32 + math_random(0, 64), y = -72}
    local mirror_position = {x = pos.x * -1, y = pos.y * -1}

    for _, t in pairs(surface.find_tiles_filtered({area = {{pos.x - 6, pos.y - 6}, {pos.x + 6, pos.y + 6}}, name = {'water', 'deepwater'}})) do
        surface.set_tiles({{name = get_replacement_tile(surface, t.position), position = t.position}})
    end
    for _, t in pairs(
        surface.find_tiles_filtered({area = {{mirror_position.x - 6, mirror_position.y - 6}, {mirror_position.x + 6, mirror_position.y + 6}}, name = {'water', 'deepwater'}})
    ) do
        surface.set_tiles({{name = get_replacement_tile(surface, t.position), position = t.position}})
    end

    local silo =
        surface.create_entity(
        {
            name = 'rocket-silo',
            position = pos,
            force = 'neutral'
        }
    )
    silo.minable = false
    silo.active = false

    for i = 1, 32, 1 do
        create_mirrored_tile_chain(surface, {name = 'stone-path', position = silo.position}, 32, 10)
    end

    local p = silo.position
    for _, entity in pairs(surface.find_entities({{p.x - 4, p.y - 4}, {p.x + 4, p.y + 4}})) do
        if entity.type == 'simple-entity' or entity.type == 'tree' or entity.type == 'resource' then
            entity.destroy()
        end
    end

    if bb_config.spawn_turrets_at_silo then
        local turret1 = surface.create_entity({name = 'gun-turret', position = {x = pos.x, y = pos.y - 5}, force = 'neutral'})
        turret1.insert({name = 'firearm-magazine', count = 10})
        turret1.active = false
        local turret2 = surface.create_entity({name = 'gun-turret', position = {x = pos.x + 2, y = pos.y - 5}, force = 'neutral'})
        turret2.insert({name = 'firearm-magazine', count = 10})
        turret2.active = false
    end
end
--[[
function Public.generate_spawn_goodies(surface)
	local tiles = surface.find_tiles_filtered({name = "stone-path"})
	table.shuffle_table(tiles)
	local budget = 1500
	local min_roll = 30
	local max_roll = 600
	local blacklist = {
		["automation-science-pack"] = true,
		["logistic-science-pack"] = true,
		["military-science-pack"] = true,
		["chemical-science-pack"] = true,
		["production-science-pack"] = true,
		["utility-science-pack"] = true,
		["space-science-pack"] = true,
		["loader"] = true,
		["fast-loader"] = true,
		["express-loader"] = true,
	}
	local container_names = {"wooden-chest", "wooden-chest", "iron-chest"}
	for k, tile in pairs(tiles) do
		if budget <= 0 then return end
		if surface.can_place_entity({name = "wooden-chest", position = tile.position, force = "neutral"}) then
			local v = math_random(min_roll, max_roll)
			local item_stacks = LootRaffle.roll(v, 4, blacklist)
			local container = surface.create_entity({name = container_names[math_random(1, 3)], position = tile.position, force = "neutral"})
			for _, item_stack in pairs(item_stacks) do container.insert(item_stack)	end
			budget = budget - v
		end
	end
end
]]
function Public.minable_wrecks(event)
    local entity = event.entity
    if not entity then
        return
    end
    if not entity.valid then
        return
    end
    if not valid_wrecks[entity.name] then
        return
    end

    local surface = entity.surface
    local player = game.players[event.player_index]

    local loot_worth = math_floor(math_abs(entity.position.x * 0.02)) + math_random(16, 32)
    local blacklist = LootRaffle.get_tech_blacklist(math_abs(entity.position.x * 0.0001) + 0.10)
    for k, v in pairs(blacklist) do
        print(k)
    end
    for k, v in pairs(loot_blacklist) do
        blacklist[k] = true
    end
    local item_stacks = LootRaffle.roll(loot_worth, math_random(1, 3), blacklist)

    for k, stack in pairs(item_stacks) do
        local amount = stack.count
        local name = stack.name

        local inserted_count = player.insert({name = name, count = amount})
        if inserted_count ~= amount then
            local amount_to_spill = amount - inserted_count
            surface.spill_item_stack(entity.position, {name = name, count = amount_to_spill}, true)
        end

        surface.create_entity(
            {
                name = 'flying-text',
                position = {entity.position.x, entity.position.y - 0.5 * k},
                text = '+' .. amount .. ' [img=item/' .. name .. ']',
                color = {r = 0.98, g = 0.66, b = 0.22}
            }
        )
    end
end

--Landfill Restriction
function Public.restrict_landfill(surface, inventory, tiles)
    for _, t in pairs(tiles) do
        local distance_to_center = math_sqrt(t.position.x ^ 2 + t.position.y ^ 2)
        local check_position = t.position
        if check_position.y > 0 then
            check_position = {x = check_position.x * -1, y = (check_position.y * -1) - 1}
        end
        if is_horizontal_border_river(check_position) or distance_to_center < spawn_circle_size then
            surface.set_tiles({{name = t.old_tile.name, position = t.position}}, true)
            inventory.insert({name = 'landfill', count = 1})
        end
    end
end

--Construction Robot Restriction
local robot_build_restriction = {
    ['north'] = function(y)
        if y >= -10 then
            return true
        end
    end,
    ['south'] = function(y)
        if y <= 10 then
            return true
        end
    end
}

function Public.deny_construction_bots(event)
    if not robot_build_restriction[event.robot.force.name] then
        return
    end
    if not robot_build_restriction[event.robot.force.name](event.created_entity.position.y) then
        return
    end
    local inventory = event.robot.get_inventory(defines.inventory.robot_cargo)
    inventory.insert({name = event.created_entity.name, count = 1})
    event.robot.surface.create_entity({name = 'explosion', position = event.created_entity.position})
    game.print('Team ' .. event.robot.force.name .. "'s construction drone had an accident.", {r = 200, g = 50, b = 100})
    event.created_entity.destroy()
end

return Public
