local Event = require 'utils.event'
local Public = require 'maps.mountain_fortress_v3.table'
local Biters = require 'modules.wave_defense.biter_rolls'

local random = math.random
local abs = math.abs
local floor = math.floor
local ceil = math.ceil

local is_modded = Public.is_modded
local is_modded_pt2 = Public.is_modded_pt2

local zone_settings = Public.zone_settings
local worm_level_modifier = 0.19
local base_tile = 'grass-1'

local vanilla_start_ground_tiles =
{
    'grass-1',
    'grass-1',
    'grass-2',
    'sand-2',
    'grass-1',
    'grass-4',
    'sand-2',
    'grass-3',
    'grass-4',
    'grass-2',
    'sand-3',
    'grass-4'
}

local start_ground_tiles =
{
    'volcanic-jagged-ground',
    'volcanic-folds-warm',
    'volcanic-soil-light',
    'volcanic-smooth-stone-warm',
    'volcanic-folds-warm',
    'volcanic-jagged-ground',
    'volcanic-soil-dark',
    'volcanic-smooth-stone-warm',
    'volcanic-soil-dark',
    'volcanic-folds-warm',
    'volcanic-soil-light',
    'volcanic-smooth-stone-warm',
}

local start_fulgora_tiles =
{
    'fulgoran-machinery',
    'fulgoran-conduit',
    'fulgoran-machinery',
    'fulgoran-paving',
    'fulgoran-walls',
    'fulgoran-machinery',
    'fulgoran-conduit',
    'fulgoran-walls',
    'fulgoran-conduit',
    'fulgoran-walls',
    'fulgoran-paving',
    'fulgoran-walls',
}

local start_aquilo_tiles =
{
    'snow-flat',
    'snow-lumpy',
    'snow-crests',
    'snow-flat',
    'snow-crests',
    'snow-patchy',
    'snow-lumpy',
    'snow-crests',
    'snow-flat',
    'snow-patchy',
    'snow-lumpy',
    'snow-patchy',
}

if not has_space_age() then
    start_ground_tiles = vanilla_start_ground_tiles
end

local out_of_map_tile = 'out-of-map'
if is_modded then
    out_of_map_tile = 'void-tile'
end

local custom_stone_wall = 'stone-wall'
if is_modded then
    custom_stone_wall = 'steel-wall'
end

local nuclear_tiles =
{
    'nuclear-ground',
    'nuclear-ground',
    'black-refined-concrete',
    'tutorial-grid',
    'nuclear-ground',
    'red-refined-concrete',
    'tutorial-grid',
    'red-refined-concrete',
    'black-refined-concrete',
    'red-refined-concrete'
}

local wagon_raffle =
{
    'cargo-wagon',
    'locomotive',
    'fluid-wagon'
}

local rock_raffle =
{
    'big-sand-rock',
    'big-sand-rock',
    'big-rock',
    'big-rock',
    'big-rock',
    'big-rock',
    'big-rock',
    'big-rock',
    'big-rock',
    'big-rock',
    'huge-rock'
}

if is_modded then
    rock_raffle =
    {
        'huge-rock-crisp',
        'big-rock-crisp',
        'big-sand-rock-crisp',
        'huge-rock-crisp-1',
        'big-rock-crisp-1',
        'big-sand-rock-crisp-1',
        'huge-rock-crisp-2',
        'big-rock-crisp-2',
        'big-sand-rock-crisp-2',

    }
end

if is_modded_pt2 then
    rock_raffle =
    {
        'huge-rock-crisp-stra-1',
        'big-rock-crisp-stra-1',
        'huge-rock-crisp-stra-2',
        'big-rock-crisp-stra-2',
        'huge-rock-crisp-stra-3',
        'big-rock-crisp-stra-3',
        'huge-rock-crisp-volc-1',
        'big-rock-crisp-volc-1',
    }
end
local size_of_rock_raffle = #rock_raffle

local snowy_rock_raffle =
{
    'huge-rock-snowy',
    'big-rock-snowy',
    'big-sand-rock-snowy',
}

local size_of_snowy_rock_raffle = #snowy_rock_raffle


local aquilo_rock_raffle =
{
    'lithium-iceberg-big',
    'lithium-iceberg-huge',
    'lithium-iceberg-big',
    'lithium-iceberg-huge',
}
local size_of_aquilo_rock_raffle = #aquilo_rock_raffle

local gleba_rock_raffle = rock_raffle
local size_of_gleba_rock_raffle = #rock_raffle

local tree_raffle =
{
    'dry-tree',
    'tree-01',
    'tree-02-red',
    'tree-04',
    'tree-08-brown'
}
if is_modded_pt2 then
    tree_raffle =
    {
        'stingfrond',
        'teflilly',
        'boompuff',
        'sunnycomb',
        'stingfrond',
    }
end

local size_of_tree_raffle = #tree_raffle

local scrap_entities =
{
    'crash-site-spaceship-wreck-small-1',
    'crash-site-spaceship-wreck-small-6',
    'crash-site-spaceship-wreck-small-1',
    'crash-site-spaceship-wreck-small-6',
    'crash-site-spaceship-wreck-small-1',
    'crash-site-spaceship-wreck-small-6',
    'crash-site-spaceship-wreck-small-1',
    'crash-site-spaceship-wreck-small-6'
}

local scrap_entities_index = #scrap_entities

local scrap_entities_friendly =
{
    'crash-site-chest-1',
    'crash-site-chest-2',
    'crash-site-chest-1',
    'crash-site-chest-2',
    'crash-site-chest-1',
    'crash-site-chest-2'
}

local scrap_entities_friendly_index = #scrap_entities_friendly

local spawner_raffle =
{
    'biter-spawner',
    'biter-spawner',
    'biter-spawner',
    'spitter-spawner'
}

local trees =
{
    'dead-tree-desert',
    'dead-dry-hairy-tree',
    'dry-hairy-tree',
    'tree-06',
    'tree-06-brown',
    'dry-tree'
}

local callback =
{
    [1] = { callback = Public.refill_turret_callback, data = Public.firearm_magazine_ammo },
    [2] = { callback = Public.refill_turret_callback, data = Public.piercing_rounds_magazine_ammo },
    [3] = { callback = Public.refill_turret_callback, data = Public.uranium_rounds_magazine_ammo },
    [4] = { callback = Public.refill_turret_callback, data = Public.uranium_rounds_magazine_ammo },
    [5] = { callback = Public.refill_liquid_turret_callback, data = Public.light_oil_ammo },
    [6] = { callback = Public.refill_artillery_turret_callback, data = Public.artillery_shell_ammo }
}

local turret_list = {}

local function make_weights(zones)
    local weights = {}
    for _, entry in ipairs(zones) do
        weights[entry.zone] = { cb = callback[entry.cb], prob = entry.prob }
    end
    return weights
end

local function get_gun_weights(unlock_zone)
    local weights = {}
    local probs = { 100, 90, 70, 50, 30, 10 }
    for zone = unlock_zone, #probs do
        weights[#weights + 1] =
        {
            zone = zone,
            cb = math.min(zone, 4),
            prob = probs[zone]
        }
    end
    return weights
end

local function get_laser_weights(unlock_zone)
    local weights = {}
    local probs = { [2] = 10, [3] = 30, [4] = 50, [5] = 70, [6] = 90 }
    for zone = unlock_zone, 6 do
        if probs[zone] then
            weights[#weights + 1] =
            {
                zone = zone,
                cb = nil,
                prob = probs[zone]
            }
        end
    end
    return weights
end

if is_modded then
    for i = 1, 5 do
        turret_list[#turret_list + 1] =
        {
            name = "mtn-addon-gun-turret-" .. i,
            weights = make_weights(get_gun_weights(i))
        }
    end

    for i = 1, 9 do
        turret_list[#turret_list + 1] =
        {
            name = "mtn-addon-laser-turret-" .. i,
            weights = make_weights(get_laser_weights(i))
        }
    end
else
    turret_list =
    {
        {
            name = "gun-turret",
            weights = make_weights(
                {
                    { zone = 1, cb = 1, prob = 100 },
                    { zone = 2, cb = 2, prob = 90 },
                    { zone = 3, cb = 3, prob = 70 },
                    { zone = 4, cb = 4, prob = 50 },
                })
        }
    }
end

table.insert(turret_list,
    {
        name = "flamethrower-turret",
        weights = make_weights(
            {
                { zone = 0, cb = 5, prob = 0 },
                { zone = 1, cb = 5, prob = 0 },
                { zone = 2, cb = 5, prob = 20 },
                { zone = 3, cb = 5, prob = 25 },
                { zone = 4, cb = 5, prob = 30 },
                { zone = 5, cb = 5, prob = 40 },
                { zone = 6, cb = 5, prob = 60 },
                { zone = 7, cb = 5, prob = 90 },
            })
    })

table.insert(turret_list,
    {
        name = "artillery-turret",
        weights = make_weights(
            {
                { zone = 0, cb = 6, prob = 0 },
                { zone = 1, cb = 6, prob = 0 },
                { zone = 2, cb = 6, prob = 0 },
                { zone = 3, cb = 6, prob = 0 },
                { zone = 4, cb = 6, prob = 0 },
                { zone = 5, cb = 6, prob = 0 },
                { zone = 6, cb = 6, prob = 0 },
                { zone = 7, cb = 6, prob = 20 },
                { zone = 8, cb = 6, prob = 30 },
                { zone = 9, cb = 6, prob = 60 },
                { zone = 10, cb = 6, prob = 100 },
            })
    })

local function get_scrap_mineable_entities(p)
    local default_entities =
    {
        'crash-site-spaceship-wreck-small-1',
        'crash-site-spaceship-wreck-small-1',
        'crash-site-spaceship-wreck-small-2',
        'crash-site-spaceship-wreck-small-2',
        'crash-site-spaceship-wreck-small-3',
        'crash-site-spaceship-wreck-small-3',
        'crash-site-spaceship-wreck-small-4',
        'crash-site-spaceship-wreck-small-4',
        'crash-site-spaceship-wreck-small-5',
        'crash-site-spaceship-wreck-small-5',
        'crash-site-spaceship-wreck-small-6'
    }

    if not is_modded then
        return default_entities, #default_entities
    end

    local depth = abs(p.y)
    local zone_depth = zone_settings.zone_depth

    local thresholds =
    {
        { limit = 1.5, entity = 'mineable-wreckage-1' },
        { limit = 2.5, entity = 'mineable-wreckage-1' },
        { limit = 3.5, entity = 'mineable-wreckage-2' },
        { limit = 4.5, entity = 'mineable-wreckage-2' },
        { limit = 5.5, entity = 'mineable-wreckage-2' },
        { limit = 6.5, entity = 'mineable-wreckage-3' },
        { limit = 7.5, entity = 'mineable-wreckage-3' },
        { limit = 8.5, entity = 'mineable-wreckage-4' },
        { limit = 9.5, entity = 'mineable-wreckage-5' },
        { limit = 10.5, entity = 'mineable-wreckage-6' }
    }

    for _, t in ipairs(thresholds) do
        if depth < zone_depth * t.limit then
            return { t.entity }, 1
        end
    end

    return default_entities, #default_entities
end

local function get_closest_weight(weights, zone)
    local closest = nil
    for z, _ in pairs(weights) do
        if z <= zone and (not closest or z > closest) then
            closest = z
        end
    end
    return closest
end

local function get_weighted_turret(zone)
    local weighted_pool = {}

    for _, turret in ipairs(turret_list) do
        local fallback_zone = get_closest_weight(turret.weights, zone)
        if fallback_zone then
            local entry = turret.weights[fallback_zone]
            if entry and entry.prob > 0 then
                for _ = 1, entry.prob do
                    table.insert(weighted_pool,
                        {
                            name = turret.name,
                            callback = entry.cb
                        })
                end
            end
        end
    end

    if #weighted_pool == 0 then
        return
        {
            name = turret_list[1].name,
            callback = turret_list[1] and turret_list[1].weights and turret_list[1].weights[1] and turret_list[1].weights[1].cb or nil
        }
    end

    return weighted_pool[math.random(#weighted_pool)]
end

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

--- Returns a function that will generate the next value each time it is called.
--- It is configured by the list of such elements {value = '...', tags = {}, weight = 100}.
--- It ensures that consecutive values do not have the same tags (if such variants exist).
--- The probability of generating the next value is calculated based on the weight parameter.
local function generate_with_tags_and_weights(variants)
    local prev_tags = {}
    local prev_value = nil

    for _, variant in pairs(variants) do
        variant.inner_weight = variant.weight
    end

    local function intersect(arr1, arr2)
        for _, i1 in pairs(arr1) do
            for _, i2 in pairs(arr2) do
                if i1 == i2 then
                    return true
                end
            end
        end
        return false
    end

    local function next_value_provider()
        local candidates = {}
        local total_weight = 0

        for _, variant in pairs(variants) do
            local valid = true

            -- block same-tag repeats
            if intersect(variant.tags, prev_tags) then
                valid = false
            end

            -- auto-block self repeats
            if prev_value and variant.value == prev_value then
                valid = false
            end

            -- block forbidden_after rules
            if prev_value and variant.forbidden_after then
                for _, bad in ipairs(variant.forbidden_after) do
                    if bad == prev_value then
                        valid = false
                        break
                    end
                end
            end

            if valid then
                table.insert(candidates, variant)
                total_weight = total_weight + variant.inner_weight
            end
        end

        if #candidates == 0 then
            candidates = variants -- fallback to avoid deadlock
        end

        -- weighted random pick
        local chosen = candidates[1]
        local selection = math.random(0, total_weight)
        for _, candidate in pairs(candidates) do
            selection = selection - candidate.inner_weight
            if selection <= 0 then
                chosen = candidate
                break
            end
        end

        -- regen inner weights
        for _, variant in pairs(variants) do
            if variant.inner_weight < variant.weight then
                variant.inner_weight = math.min(variant.weight, math.floor(variant.inner_weight + variant.weight / #variants))
            end
        end

        prev_tags = chosen.tags
        prev_value = chosen.value
        chosen.inner_weight = 0

        return chosen.value
    end

    return next_value_provider
end

local function is_position_near(area, table_to_check)
    local status = false
    local function inside(pos)
        local lt = area.left_top
        local rb = area.right_bottom

        return pos.x >= lt.x and pos.y >= lt.y and pos.x <= rb.x and pos.y <= rb.y
    end

    for i = 1, #table_to_check do
        if inside(table_to_check[i]) then
            status = true
        end
    end

    return status
end

local function place_wagon(data, adjusted_zones)
    local final_battle = Public.get('final_battle')
    if final_battle then
        return
    end

    local x_min = (-zone_settings.zone_width / 2) + 10
    local x_max = (zone_settings.zone_width / 2) - 10

    if data.x < x_min then
        return
    end
    if data.x > x_max then
        return
    end

    local placed_trains_in_zone = Public.get('placed_trains_in_zone')
    if not placed_trains_in_zone.randomized then
        placed_trains_in_zone.limit = random(0, 2)
        placed_trains_in_zone.randomized = true
        placed_trains_in_zone = Public.get('placed_trains_in_zone')
    end

    if not data.new_zone then
        data.new_zone = 1
    end

    if data.new_zone == adjusted_zones.size then
        data.new_zone = 1
    end

    if data.current_zone == adjusted_zones.size then
        local new_zone = placed_trains_in_zone.zones[data.new_zone]
        if new_zone then
            new_zone.placed = 0
            new_zone.positions = {}
            data.new_zone = data.new_zone + 1
        end
    end

    local zone = placed_trains_in_zone.zones[data.current_zone]
    if not zone then
        placed_trains_in_zone.zones[data.current_zone] =
        {
            placed = 0,
            positions = {}
        }
        zone = placed_trains_in_zone.zones[data.current_zone]
    end

    if zone.placed >= placed_trains_in_zone.limit then
        return
    end

    local surface = data.surface
    local tiles = data.tiles
    local entities = data.entities
    local top_y = data.top_y
    local position = { x = data.x, y = top_y + random(4, 12) * 2 }
    local wagon_mineable =
    {
        callback = Public.disable_minable_and_ICW_callback,
        delayed_callback = Public.remove_colliding_entities_callback,
    }

    local rail_mineable =
    {
        callback = Public.disable_destructible_callback
    }

    local radius = 300
    local area =
    {
        left_top = { x = position.x - radius, y = position.y - radius },
        right_bottom = { x = position.x + radius, y = position.y + radius }
    }

    if is_position_near(area, zone.positions) then
        return
    end

    local location
    local direction

    local r1 = random(2, 4) * 2
    local r2 = random(2, 4) * 2

    if random(1, 2) == 1 then
        location = surface.find_tiles_filtered({ area = { { position.x, position.y - r1 }, { position.x + 2, position.y + r2 } } })
        direction = 0
    else
        location = surface.find_tiles_filtered({ area = { { position.x - r1, position.y }, { position.x + r2, position.y + 2 } } })
        direction = 4
    end

    for _, tile in pairs(location) do
        tiles[#tiles + 1] = { name = adjusted_zones.starting_tile, position = tile.position }
        if tile.position.y % 1 == 0 and tile.position.x % 1 == 0 then
            entities[#entities + 1] =
            {
                name = 'straight-rail',
                position = tile.position,
                force = 'player',
                direction = direction,
                callback = rail_mineable
            }
        end
    end
    entities[#entities + 1] =
    {
        name = wagon_raffle[random(1, #wagon_raffle)],
        position = position,
        force = 'player',
        callback = wagon_mineable
    }

    zone.placed = zone.placed + 1
    zone.positions[#zone.positions + 1] = position

    return true
end

local function get_oil_amount(p)
    return (abs(p.y) * 200 + 10000) * random(75, 125) * 0.01
end

local function spawn_turret(entities, p, probability)
    local direction
    local adjusted_zones = Public.get('adjusted_zones')
    if adjusted_zones.reversed then
        direction = defines.direction.north
    else
        direction = defines.direction.south
    end

    local turret = get_weighted_turret(probability)

    entities[#entities + 1] =
    {
        name = turret.name,
        position = p,
        force = 'enemy',
        callback = turret.callback or nil,
        direction = direction,
        collision = true,
        note = true
    }
end

local function wall(p, data, adjusted_zones)
    local tiles = data.tiles
    local entities = data.entities
    local surface = data.surface
    local treasure = data.treasure
    local stone_wall = { callback = Public.disable_minable_callback }
    local enable_arties = Public.get('enable_arties')
    local alert_zone_1 = Public.get('alert_zone_1')

    local seed = data.seed
    local y = data.yv

    local small_caves = Public.get_noise('small_caves', p, seed + seed)
    local cave_ponds = Public.get_noise('cave_rivers', p, seed + seed)
    if y > 9 + cave_ponds * 6 and y < 23 + small_caves * 6 then
        if small_caves > 0.02 or cave_ponds > 0.02 then
            if small_caves > 0.005 then
                tiles[#tiles + 1] = { name = 'water', position = p }
            else
                tiles[#tiles + 1] = { name = 'water-shallow', position = p }
                if random(1, 20) == 1 then
                    entities[#entities + 1] =
                    {
                        name = 'land-mine',
                        position = p,
                        force = 'enemy'
                    }
                end
            end
            if random(1, 48) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
        else
            local noise = Public.get_noise('dungeon_sewer', p, data.seed)
            local index = floor(noise * 32) % 9 + 1
            tiles[#tiles + 1] = { name = nuclear_tiles[index], position = p }

            if random(1, 4) ~= 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
                if random(1, 26) == 1 then
                    entities[#entities + 1] =
                    {
                        name = 'land-mine',
                        position = p,
                        force = 'enemy'
                    }
                end
            end
        end
    else
        local noise = Public.get_noise('dungeon_sewer', p, data.seed)
        local index = floor(noise * 32) % 9 + 1
        tiles[#tiles + 1] = { name = nuclear_tiles[index], position = p }

        if
            surface.can_place_entity(
                {
                    name = custom_stone_wall,
                    position = p,
                    force = 'enemy'
                }
            )
        then
            if random(1, 512) == 1 and y > 3 and y < 28 then
                if random(1, 2) == 1 then
                    treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
                else
                    treasure[#treasure + 1] = { position = p, chest = 'steel-chest' }
                end
            end
            if y < 4 or y > 24 then
                if y <= 22 then
                    if random(1, y + 1) == 1 then
                        entities[#entities + 1] =
                        {
                            name = custom_stone_wall,
                            position = p,
                            force = 'neutral',
                            callback = stone_wall
                        }

                        if not alert_zone_1 and data.y >= -zone_settings.zone_depth then
                            local x_min = -zone_settings.zone_width / 2
                            local x_max = zone_settings.zone_width / 2

                            if adjusted_zones.reversed then
                                Public.set('zone1_beam1', surface.create_entity({ name = 'electric-beam', position = { x_min, p.y + 30 }, source = { x_min, p.y + 30 }, target = { x_max, p.y + 30 } }))
                                Public.set('zone1_beam2', surface.create_entity({ name = 'electric-beam', position = { x_min, p.y + 30 }, source = { x_min, p.y + 30 }, target = { x_max, p.y + 30 } }))
                                Public.set('zone1_beam3', surface.create_entity({ name = 'electric-beam', position = { x_min, p.y + 30 }, source = { x_min, p.y + 30 }, target = { x_max, p.y + 30 } }))
                                Public.set('alert_zone_1', true)
                                Public.set(
                                    'zone1_text1',
                                    rendering.draw_text
                                    {
                                        text = ({ 'breached_wall.warning' }),
                                        surface = surface,
                                        target = { 0, p.y - 35 },
                                        color = { r = 255, g = 106, b = 0 },
                                        scale = 10,
                                        font = 'heading-1',
                                        alignment = 'center',
                                        scale_with_zoom = false
                                    }
                                )
                                Public.set(
                                    'zone1_text2',
                                    rendering.draw_text
                                    {
                                        text = ({ 'breached_wall.warning' }),
                                        surface = surface,
                                        target = { -180, p.y - 35 },
                                        color = { r = 255, g = 106, b = 0 },
                                        scale = 10,
                                        font = 'heading-1',
                                        alignment = 'center',
                                        scale_with_zoom = false
                                    }
                                )
                                Public.set(
                                    'zone1_text3',
                                    rendering.draw_text
                                    {
                                        text = ({ 'breached_wall.warning' }),
                                        surface = surface,
                                        target = { 180, p.y - 35 },
                                        color = { r = 255, g = 106, b = 0 },
                                        scale = 10,
                                        font = 'heading-1',
                                        alignment = 'center',
                                        scale_with_zoom = false
                                    }
                                )
                            else
                                Public.set('zone1_beam1', surface.create_entity({ name = 'electric-beam', position = { x_min, p.y }, source = { x_min, p.y }, target = { x_max, p.y } }))
                                Public.set('zone1_beam2', surface.create_entity({ name = 'electric-beam', position = { x_min, p.y }, source = { x_min, p.y }, target = { x_max, p.y } }))
                                Public.set('zone1_beam3', surface.create_entity({ name = 'electric-beam', position = { x_min, p.y }, source = { x_min, p.y }, target = { x_max, p.y } }))
                                Public.set('alert_zone_1', true)
                                Public.set(
                                    'zone1_text1',
                                    rendering.draw_text
                                    {
                                        text = ({ 'breached_wall.warning' }),
                                        surface = surface,
                                        target = { 0, p.y + 35 },
                                        color = { r = 255, g = 106, b = 0 },
                                        scale = 10,
                                        font = 'heading-1',
                                        alignment = 'center',
                                        scale_with_zoom = false
                                    }
                                )
                                Public.set(
                                    'zone1_text2',
                                    rendering.draw_text
                                    {
                                        text = ({ 'breached_wall.warning' }),
                                        surface = surface,
                                        target = { -180, p.y + 35 },
                                        color = { r = 255, g = 106, b = 0 },
                                        scale = 10,
                                        font = 'heading-1',
                                        alignment = 'center',
                                        scale_with_zoom = false
                                    }
                                )
                                Public.set(
                                    'zone1_text3',
                                    rendering.draw_text
                                    {
                                        text = ({ 'breached_wall.warning' }),
                                        surface = surface,
                                        target = { 180, p.y + 35 },
                                        color = { r = 255, g = 106, b = 0 },
                                        scale = 10,
                                        font = 'heading-1',
                                        alignment = 'center',
                                        scale_with_zoom = false
                                    }
                                )
                            end
                        end
                    end
                else
                    if random(1, 32 - y) == 1 then
                        entities[#entities + 1] =
                        {
                            name = custom_stone_wall,
                            position = p,
                            force = 'neutral',
                            callback = stone_wall
                        }
                    end
                end
            end
        end

        if random(1, 40) == 1 then
            if
                surface.can_place_entity(
                    {
                        name = 'medium-worm-turret',
                        position = p,
                        force = 'enemy'
                    }
                )
            then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name(),
                    position = p,
                    force = 'enemy',
                    note = true
                }
            end
        end

        if random(1, 25) == 1 then
            if abs(p.y) < zone_settings.zone_depth * 1.5 then
                if random(1, 16) == 1 then
                    spawn_turret(entities, p, 1)
                else
                    spawn_turret(entities, p, 2)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 2.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 1)
                else
                    spawn_turret(entities, p, 2)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 3.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 2)
                else
                    spawn_turret(entities, p, 3)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 4.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 4)
                else
                    spawn_turret(entities, p, 5)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 5.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 6)
                elseif random(1, 2) == 1 then
                    spawn_turret(entities, p, 7)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 6.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 8)
                elseif random(1, 2) == 1 then
                    spawn_turret(entities, p, 9)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 7.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 10)
                elseif random(1, 2) == 1 then
                    spawn_turret(entities, p, 11)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 8.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 12)
                elseif random(1, 2) == 1 then
                    spawn_turret(entities, p, 13)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 9.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 14)
                elseif random(1, 2) == 1 then
                    spawn_turret(entities, p, 15)
                end
            elseif abs(p.y) < zone_settings.zone_depth * 10.5 then
                if random(1, 4) == 1 then
                    spawn_turret(entities, p, 16)
                elseif random(1, 2) == 1 then
                    spawn_turret(entities, p, 17)
                end
            end
        elseif abs(p.y) > zone_settings.zone_depth * 5.5 then
            if random(1, 15) == 1 then
                spawn_turret(entities, p, random(3, enable_arties))
            end
        end
    end
end

local function zone_crystal_1(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local buildings = data.buildings
    local tiles = data.tiles
    local entities = data.entities
    local treasure = data.treasure
    local markets = data.markets
    data.crystal_zone = true

    local small_caves = Public.get_noise('dungeons', p, seed + 12345)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + 67890)
    local smol_areas = Public.get_noise('smol_areas', p, seed + 11111)
    local cave_rivers = Public.get_noise('cave_rivers', p, seed + 22222)
    local no_rocks = Public.get_noise('no_rocks', p, seed + 33333)
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'blue-refined-concrete', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p, zone_settings.zone_depth)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy'
            }
        end
        return
    end

    local scrap_mineable_entities, scrap_mineable_entities_index = get_scrap_mineable_entities(p)

    if noise_cave_ponds < 0.505 and noise_cave_ponds > -0.112 then
        if small_caves > 0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
        if small_caves < -0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if noise_cave_ponds > 0.64 then
        if noise_cave_ponds > 0.74 then
            tiles[#tiles + 1] = { name = 'cyan-refined-concrete', position = p }
            if random(1, 4) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 8) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                    position = p,
                    force = 'neutral'
                }
            end
            return
        end
        tiles[#tiles + 1] = { name = 'deepwater', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    if cave_rivers < 0.042 and cave_rivers > -0.062 then
        if noise_cave_ponds > 0.1 then
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
            if random(1, 64) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end
    end

    if noise_cave_ponds > 0.322 then
        if noise_cave_ponds > 0.542 then
            if cave_rivers > -0.302 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }
            end
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] =
            {
                name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                position = p,
                force = 'neutral'
            }
        end
        return
    end

    if no_rocks < 0.035 and no_rocks > -0.145 then
        if small_caves > 0.081 then
            tiles[#tiles + 1] = { name = 'blue-refined-concrete', position = p }
            if random(1, 250) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                    position = p,
                    force = 'neutral'
                }
            end
            if random(1, 96) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name(),
                    position = p,
                    force = 'enemy'
                }
            end
            return
        end
    end

    local maze_p = { x = floor(p.x - p.x % 10), y = floor(p.y - p.y % 10) }
    local maze_noise = Public.get_noise('no_rocks_2', maze_p, seed)
    if maze_noise > -0.35 and maze_noise < 0.35 then
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed)
        if random(1, 2) == 1 and no_rocks_2 > -0.5 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        if random(1, 1024) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 256) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    tiles[#tiles + 1] = { name = void_or_lab, position = p }
    if random(1, 128) == 1 then
        entities[#entities + 1] =
        {
            name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
            position = p,
            force = 'neutral'
        }
    end
end

local function zone_volcanic_1(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local buildings = data.buildings
    local entities = data.entities
    local markets = data.markets
    local treasure = data.treasure
    data.volcanic_zone = true


    local small_caves = Public.get_noise('dungeons', p, seed + 44444)
    local noise_large_caves = Public.get_noise('large_caves', p, seed + 55555)
    local smol_areas = Public.get_noise('smol_areas', p, seed + 66666)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + 77777)

    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    if noise_large_caves > -0.75 and noise_large_caves < 0.75 then
        if noise_cave_ponds < 0.15 and noise_cave_ponds > -0.15 then
            if small_caves > 0.32 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }
                return
            end
            if small_caves < -0.32 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }
                return
            end
        end

        if noise_cave_ponds > 0.80 then
            tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
            if random(1, 16) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end

        local cave_rivers = Public.get_noise('cave_rivers', p, seed + 88888)
        if cave_rivers < 0.037 and cave_rivers > -0.037 then
            if noise_cave_ponds < 0.1 then
                tiles[#tiles + 1] = { name = 'water-shallow', position = p }
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'fish', position = p }
                end
                return
            end
        end

        if noise_cave_ponds > 0.66 then
            tiles[#tiles + 1] = { name = 'dirt-' .. random(4, 6), position = p }
            return
        end

        if noise_cave_ponds < -0.80 then
            tiles[#tiles + 1] = { name = 'grass-' .. floor(noise_cave_ponds * 32) % 3 + 1, position = p }
            if random(1, 32) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 16) == 1 then
                entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
            end
            return
        end

        local no_rocks = Public.get_noise('no_rocks', p, seed + 99999)
        if no_rocks < 0.20 and no_rocks > -0.20 then
            if small_caves > 0.30 then
                tiles[#tiles + 1] = { name = 'dirt-' .. floor(noise_cave_ponds * 32) % 7 + 1, position = p }
                if random(1, 450) == 1 then
                    entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
                end
                if random(1, 64) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name('fire'),
                        position = p,
                        force = 'enemy'
                    }
                end
                return
            end
        end
    end

    local noise_1 = Public.get_noise('small_caves', p, seed)
    if noise_1 > -0.30 and noise_1 < 0.30 then
        if noise_1 > -0.14 and noise_1 < 0.14 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            if random(1, 256) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
        else
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
        end
        return
    end

    local iron_noise = Public.get_noise('ore', p, seed + 5000)
    if iron_noise > 0.75 then
        entities[#entities + 1] = { name = 'iron-ore', position = p, amount = 1000 + abs(iron_noise * 1000) }
    end
    local copper_noise = Public.get_noise('ore', p, seed + 10000)
    if copper_noise > 0.75 then
        entities[#entities + 1] = { name = 'copper-ore', position = p, amount = 1000 + abs(copper_noise * 1000) }
    end
    local coal_noise = Public.get_noise('ore', p, seed + 15000)
    if coal_noise > 0.75 then
        entities[#entities + 1] = { name = 'coal', position = p, amount = 1000 + abs(coal_noise * 1000) }
    end
    local stone_noise = Public.get_noise('ore', p, seed + 20000)
    if stone_noise > 0.75 then
        entities[#entities + 1] = { name = 'stone', position = p, amount = 1000 + abs(stone_noise * 1000) }
    end

    tiles[#tiles + 1] = { name = 'red-refined-concrete', position = p }
    if random(1, 128) == 1 then
        entities[#entities + 1] = { name = 'land-mine', position = p, force = 'enemy' }
    end
end

local rand_chests =
{
    'active-provider-chest',
    'passive-provider-chest',
    'storage-chest',
    'buffer-chest',
    'requester-chest',
    'roboport'
}

local function zone_tech_1(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local treasure = data.treasure
    data.tech_zone = true

    local tech_ruins = Public.get_noise('scrapyard', p, seed + 111111)

    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local small_caves = Public.get_noise('small_caves', p, seed)
    if noise_cave_ponds < 0.15 and noise_cave_ponds > -0.15 then
        if small_caves > 0.35 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end

        if small_caves < -0.35 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if tech_ruins < -0.25 or tech_ruins > 0.25 then
        if random(1, 256) == 1 then
            if random(1, 8) == 1 then
                spawn_turret(entities, p, 3)
            else
                spawn_turret(entities, p, 4)
            end
            if random(1, 2048) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'steel-chest' }
            end
        end
        tiles[#tiles + 1] = { name = 'hazard-concrete-right', position = p }
        if tech_ruins < -0.55 or tech_ruins > 0.55 then
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            return
        end
        if tech_ruins < -0.28 or tech_ruins > 0.28 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            if random(1, 128) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name(),
                    position = p,
                    force = 'enemy',
                    note = true
                }
            end
            if random(1, 96) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_entities[random(1, scrap_entities_index)],
                    position = p,
                    force = 'enemy'
                }
            end
            if random(1, 96) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_entities_friendly[random(1, scrap_entities_friendly_index)],
                    position = p,
                    force = 'neutral'
                }
            end

            local scrap_mineable_entities, scrap_mineable_entities_index = get_scrap_mineable_entities(p)

            if random(1, 5) > 1 then
                entities[#entities + 1] =
                {
                    name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                    position = p,
                    force = 'neutral'
                }
            end

            if random(1, 128) == 1 then
                entities[#entities + 1] = { name = 'land-mine', position = p, force = 'enemy' }
            end
            return
        end
        return
    end

    local cave_ponds = Public.get_noise('cave_ponds', p, seed)
    if cave_ponds < -0.6 and tech_ruins > -0.2 and tech_ruins < 0.2 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    tiles[#tiles + 1] = { name = 'hazard-concrete-right', position = p }
    if random(1, 128) == 1 then
        entities[#entities + 1] = { name = rand_chests[random(1, #rand_chests)], position = p, force = 'enemy' }
    end
end

local function zone_frostbite_1(x, y, data, void_or_lab)
    local p = { x = x, y = y }
    local seed = data.seed
    local buildings = data.buildings
    local tiles = data.tiles
    local entities = data.entities
    local markets = data.markets
    local treasure = data.treasure
    data.frostbite_zone = true

    local small_caves = Public.get_noise('dungeons', p, seed + 333333)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + 444444)
    local smol_areas = Public.get_noise('smol_areas', p, seed + 555555)
    local cave_rivers = Public.get_noise('cave_rivers', p, seed + 666666)

    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    if noise_cave_ponds < 0.101 and noise_cave_ponds > -0.102 then
        if small_caves > 0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
        if small_caves < -0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if noise_cave_ponds > 0.670 then
        if noise_cave_ponds > 0.750 then
            tiles[#tiles + 1] = { name = 'snow-tile', position = p }
            if random(1, 4) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 4) == 1 then
                entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
            end
            return
        end
        tiles[#tiles + 1] = { name = 'deepwater', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    if cave_rivers < 0.041 and cave_rivers > -0.042 then
        if noise_cave_ponds > 0 then
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
            if random(1, 64) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end
    end

    if noise_cave_ponds > 0.74 then
        tiles[#tiles + 1] = { name = 'snow-tile', position = p }
        if cave_rivers < -0.502 then
            tiles[#tiles + 1] = { name = 'refined-hazard-concrete-right', position = p }
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
        end
        return
    end

    local no_rocks = Public.get_noise('no_rocks', p, seed + 777777)
    if p.y < -64 + noise_cave_ponds * 10 then
        if no_rocks < 0.11 and no_rocks > -0.11 then
            if small_caves > 0.31 then
                tiles[#tiles + 1] = { name = 'snow-tile', position = p }
                if random(1, 450) == 1 then
                    entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
                end
                if random(1, 96) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name('fire'),
                        position = p,
                        force = 'enemy'
                    }
                end
                return
            end
        end
    end

    local maze_p = { x = floor(p.x - p.x % 10), y = floor(p.y - p.y % 10) }
    local maze_noise = Public.get_noise('no_rocks_2', maze_p, seed)
    if maze_noise > -0.35 and maze_noise < 0.35 then
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed)
        if random(1, 2) == 1 and no_rocks_2 > -0.5 then
            entities[#entities + 1] = { name = snowy_rock_raffle[random(1, size_of_snowy_rock_raffle)], position = p }
        end
        if random(1, 1024) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 256) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
    end

    tiles[#tiles + 1] = { name = 'ice-tile', position = p }
    if random(1, 128) == 1 then
        entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
    end
end

local function zone_frostbite_2(x, y, data, void_or_lab)
    local p = { x = x, y = y }
    local seed = data.seed
    local buildings = data.buildings
    local tiles = data.tiles
    local entities = data.entities
    local markets = data.markets
    local treasure = data.treasure
    data.frostbite_zone = true

    local small_caves = Public.get_noise('dungeons', p, seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed)
    local cave_rivers = Public.get_noise('cave_rivers', p, seed)

    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    if noise_cave_ponds < 0.101 and noise_cave_ponds > -0.102 then
        if small_caves > 0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
        if small_caves < -0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if noise_cave_ponds > 0.670 then
        if noise_cave_ponds > 0.750 then
            tiles[#tiles + 1] = { name = 'snow-tile', position = p }
            if random(1, 4) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 4) == 1 then
                entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
            end
            return
        elseif noise_cave_ponds > 0.850 then
            tiles[#tiles + 1] = { name = 'ice-tile', position = p }
            if random(1, 4) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 4) == 1 then
                entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
            end
            return
        end
        tiles[#tiles + 1] = { name = 'deepwater', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    if cave_rivers < 0.041 and cave_rivers > -0.042 then
        if noise_cave_ponds > 0 then
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
            if random(1, 64) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end
    end

    if noise_cave_ponds > 0.74 then
        if noise_cave_ponds > 0.850 then
            tiles[#tiles + 1] = { name = 'ice-tile', position = p }
        else
            tiles[#tiles + 1] = { name = 'snow-tile', position = p }
        end
        if cave_rivers < -0.502 then
            tiles[#tiles + 1] = { name = 'refined-hazard-concrete-right', position = p }
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
        end
        return
    end

    local no_rocks = Public.get_noise('no_rocks', p, seed + 777777)
    if p.y < -64 + noise_cave_ponds * 10 then
        if no_rocks < 0.11 and no_rocks > -0.11 then
            if small_caves > 0.31 then
                tiles[#tiles + 1] = { name = 'snow-tile', position = p }
                if random(1, 450) == 1 then
                    entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
                end
                if random(1, 96) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name('fire'),
                        position = p,
                        force = 'enemy'
                    }
                end
                return
            end
        end
    end

    local maze_p = { x = floor(p.x - p.x % 10), y = floor(p.y - p.y % 10) }
    local maze_noise = Public.get_noise('no_rocks_2', maze_p, seed)
    if maze_noise > -0.35 and maze_noise < 0.35 then
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed)
        if random(1, 2) == 1 and no_rocks_2 > -0.5 then
            entities[#entities + 1] = { name = snowy_rock_raffle[random(1, size_of_snowy_rock_raffle)], position = p }
        end
        if random(1, 1024) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 256) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
    end

    -- Default frostbite terrain
    if noise_cave_ponds > 0.75 then
        tiles[#tiles + 1] = { name = 'ice-tile', position = p }
    else
        tiles[#tiles + 1] = { name = 'snow-tile', position = p }
    end
    if random(1, 128) == 1 then
        entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
    end
end

local function zone_14(x, y, data, _, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local treasure = data.treasure
    data.forest_zone = true

    local small_caves = Public.get_noise('small_caves', p, seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Resource Spots
    if smol_areas < -0.71 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    if small_caves > -0.21 and small_caves < 0.21 then
        tiles[#tiles + 1] = { name = 'grass-3', position = p }
        if random(1, 768) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 2) == 1 then
            entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
        end
        return
    end

    if small_caves < -0.34 or small_caves > 0.34 then
        tiles[#tiles + 1] = { name = 'water', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if random(1, 256) == 1 then
            spawn_turret(entities, p, 4)
        end
        return
    end

    if small_caves > -0.41 and small_caves < 0.41 then
        if noise_cave_ponds > 0.35 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            tiles[#tiles + 1] = { name = 'grass-' .. random(1, 4), position = p }
            if random(1, 256) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            if random(1, 256) == 1 then
                entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
            end
            return
        end
        if noise_cave_ponds > 0.23 then
            tiles[#tiles + 1] = { name = 'grass-4', position = p }
            if random(1, 512) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
            end
            return
        end
    end

    tiles[#tiles + 1] = { name = 'water-shallow', position = p }
end

local function zone_13(x, y, data, _, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local treasure = data.treasure

    local small_caves = Public.get_noise('small_caves', p, seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    if small_caves > -0.22 and small_caves < 0.22 then
        tiles[#tiles + 1] = { name = 'dirt-3', position = p }
        if random(1, 768) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 2) == 1 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        return
    end

    if small_caves < -0.35 or small_caves > 0.35 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if random(1, 256) == 1 then
            spawn_turret(entities, p, 4)
        end
        return
    end

    if small_caves > -0.40 and small_caves < 0.40 then
        if noise_cave_ponds > 0.35 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            tiles[#tiles + 1] = { name = 'dirt-' .. random(1, 4), position = p }
            if random(1, 256) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            if random(1, 256) == 1 then
                entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
            end
            return
        end
        if noise_cave_ponds > 0.25 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 512) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            return
        end
    end

    tiles[#tiles + 1] = { name = 'water-shallow', position = p }
end

local function zone_12(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure

    local noise_1 = Public.get_noise('small_caves', p, seed)
    local noise_2 = Public.get_noise('no_rocks_2', p, seed + seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    if noise_1 > 0.65 then
        if random(1, 100) > 88 then
            entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
        else
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
        end
        if random(1, 48) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    if noise_1 < -0.72 then
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        tiles[#tiles + 1] = { name = void_or_lab, position = p }
        if random(1, 100) > 88 then
            entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
        end
        return
    end

    if noise_1 > -0.30 and noise_1 < 0.30 then
        if noise_1 > -0.14 and noise_1 < 0.14 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            if random(1, 256) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
        else
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
        end
        return
    end

    if random(1, 64) == 1 and noise_2 > 0.65 then
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'stone', position = p, amount = abs(p.y) + 1 }
        elseif random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'iron-ore', position = p, amount = abs(p.y) + 1 }
        elseif random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'copper-ore', position = p, amount = abs(p.y) + 1 }
        elseif random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'coal', position = p, amount = abs(p.y) + 1 }
        end
    end
    if random(1, 8192) == 1 then
        markets[#markets + 1] = p
    end
    if random(1, 1024) == 1 then
        entities[#entities + 1] =
        {
            name = 'crash-site-chest-' .. random(1, 2),
            position = p,
            force = 'neutral'
        }
    end

    tiles[#tiles + 1] = { name = 'tutorial-grid', position = p }
end

local function zone_11(x, y, data, _, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure

    local noise_1 = Public.get_noise('small_caves', p, seed)
    local noise_2 = Public.get_noise('no_rocks_2', p, seed + seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    if noise_1 > 0.7 then
        tiles[#tiles + 1] = { name = 'water', position = p }
        if random(1, 48) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    if noise_1 < -0.72 then
        tiles[#tiles + 1] = { name = 'lab-dark-1', position = p }
        entities[#entities + 1] = { name = 'uranium-ore', position = p, amount = abs(p.y) + 1 * 3 }
        return
    end

    if adjusted_zones.starting_planet == 'fulgora' then
        if noise_1 < -0.5 then
            entities[#entities + 1] = { name = 'scrap', position = p, amount = abs(p.y) + 1 * 3 }
            return
        end
    end

    if noise_1 > -0.30 and noise_1 < 0.30 then
        if noise_1 > -0.14 and noise_1 < 0.14 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            if random(1, 256) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
        else
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
        end
        return
    end

    if random(1, 64) == 1 and noise_2 > 0.65 then
        entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
    end
    if random(1, 8192) == 1 then
        markets[#markets + 1] = p
    end
    if random(1, 1024) == 1 then
        entities[#entities + 1] =
        {
            name = 'crash-site-chest-' .. random(1, 2),
            position = p,
            force = 'neutral'
        }
    end

    local success = place_wagon(data, adjusted_zones)
    if success then
        return
    end

    local noise_forest_location = Public.get_noise('forest_location', p, seed)
    if noise_forest_location > 0.095 then
        if noise_forest_location > 0.6 then
            if random(1, 100) > 42 then
                tiles[#tiles + 1] = { name = 'red-refined-concrete', position = p }
            end
        else
            if random(1, 100) > 42 then
                tiles[#tiles + 1] = { name = 'green-refined-concrete', position = p }
            end
        end
        return
    end

    if noise_forest_location < -0.095 then
        if noise_forest_location < -0.6 then
            if random(1, 100) > 42 then
                tiles[#tiles + 1] = { name = 'blue-refined-concrete', position = p }
            end
        else
            if random(1, 100) > 42 then
                tiles[#tiles + 1] = { name = 'red-refined-concrete', position = p }
            end
        end
        return
    end
end

local function zone_10(x, y, data, _, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local treasure = data.treasure
    data.forest_zone = true

    local scrapyard = Public.get_noise('scrapyard', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    if scrapyard < -0.70 or scrapyard > 0.70 then
        tiles[#tiles + 1] = { name = 'grass-3', position = p }
        if random(1, 40) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        return
    end

    if scrapyard < -0.65 or scrapyard > 0.65 then
        tiles[#tiles + 1] = { name = 'water-green', position = p }
        return
    end
    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    if abs(scrapyard) > 0.40 and abs(scrapyard) < 0.65 then
        if random(1, 64) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        tiles[#tiles + 1] = { name = 'water-mud', position = p }
        return
    end
    if abs(scrapyard) > 0.25 and abs(scrapyard) < 0.40 then
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if random(1, 256) == 1 then
            spawn_turret(entities, p, 4)
        end
        tiles[#tiles + 1] = { name = 'water-shallow', position = p }
        return
    end
    local noise_forest_location = Public.get_noise('forest_location', p, seed)
    if scrapyard > -0.15 and scrapyard < 0.15 then
        if noise_forest_location > 0.095 then
            if random(1, 256) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name('fire'),
                    position = p,
                    force = 'enemy',
                    note = true
                }
            end
            if noise_forest_location > 0.6 then
                if random(1, 100) > 42 then
                    entities[#entities + 1] = { name = 'tree-03', position = p }
                end
            else
                if random(1, 100) > 42 then
                    entities[#entities + 1] = { name = 'tree-01', position = p }
                end
            end
            return
        end

        if noise_forest_location < -0.095 then
            if random(1, 256) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name('fire'),
                    position = p,
                    force = 'enemy',
                    note = true
                }
            end
            if noise_forest_location < -0.6 then
                if random(1, 100) > 42 then
                    entities[#entities + 1] = { name = 'dry-tree', position = p }
                end
            else
                if random(1, 100) > 42 then
                    entities[#entities + 1] = { name = 'tree-02-red', position = p }
                end
            end
            return
        end
        tiles[#tiles + 1] = { name = 'dirt-6', position = p }
        return
    end
    tiles[#tiles + 1] = { name = 'grass-2', position = p }
end

local function zone_9(x, y, data, _, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure

    local maze_p = { x = floor(p.x - p.x % 10), y = floor(p.y - p.y % 10) }
    local maze_noise = Public.get_noise('no_rocks_2', maze_p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    if maze_noise > -0.35 and maze_noise < 0.35 then
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed)
        if random(1, 2) == 1 and no_rocks_2 > -0.5 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        if random(1, 1024) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 256) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    if maze_noise > 0 and maze_noise < 0.45 then
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        if random(1, 512) == 1 then
            markets[#markets + 1] = p
        end
        if random(1, 256) == 1 then
            entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
        end
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
        end
        return
    end

    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    if maze_noise < -0.5 or maze_noise > 0.5 then
        tiles[#tiles + 1] = { name = 'deepwater', position = p }
        if random(1, 96) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    tiles[#tiles + 1] = { name = 'water', position = p }
    if random(1, 96) == 1 then
        entities[#entities + 1] = { name = 'fish', position = p }
    end
end

--SCRAPYARD
local function zone_scrap_2(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local treasure = data.treasure
    data.scrap_zone = true

    local scrapyard_modified = Public.get_noise('scrapyard_modified', p, seed)
    local cave_rivers = Public.get_noise('cave_rivers', p, seed + seed)

    --Chasms
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local small_caves = Public.get_noise('small_caves', p, seed)
    if noise_cave_ponds < 0.15 and noise_cave_ponds > -0.15 then
        if small_caves > 0.35 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end

        if small_caves < -0.35 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if scrapyard_modified < -0.25 or scrapyard_modified > 0.25 then
        if random(1, 256) == 1 then
            if random(1, 8) == 1 then
                spawn_turret(entities, p, 3)
            else
                spawn_turret(entities, p, 4)
            end
            if random(1, 2048) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
        end
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if scrapyard_modified < -0.55 or scrapyard_modified > 0.55 then
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            return
        end
        if scrapyard_modified < -0.28 or scrapyard_modified > 0.28 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            if random(1, 128) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name(),
                    position = p,
                    force = 'enemy',
                    note = true
                }
            end
            if random(1, 96) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_entities[random(1, scrap_entities_index)],
                    position = p,
                    force = 'enemy'
                }
            end
            if random(1, 96) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_entities_friendly[random(1, scrap_entities_friendly_index)],
                    position = p,
                    force = 'neutral'
                }
            end

            local scrap_mineable_entities, scrap_mineable_entities_index = get_scrap_mineable_entities(p)

            if random(1, 5) > 1 then
                entities[#entities + 1] =
                {
                    name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                    position = p,
                    force = 'neutral'
                }
            end

            if random(1, 256) == 1 then
                entities[#entities + 1] = { name = 'land-mine', position = p, force = 'enemy' }
            end
            return
        end
        return
    end

    local cave_ponds = Public.get_noise('cave_ponds', p, seed)
    if cave_ponds < -0.6 and scrapyard_modified > -0.2 and scrapyard_modified < 0.2 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    --Resource Spots
    if cave_rivers < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    local large_caves = Public.get_noise('large_caves', p, seed)
    if scrapyard_modified > -0.15 and scrapyard_modified < 0.15 then
        if floor(large_caves * 10) % 4 < 3 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            if random(1, 2048) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            return
        end
    end

    if random(1, 64) == 1 and cave_ponds > 0.6 then
        entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
    end

    tiles[#tiles + 1] = { name = adjusted_zones.starting_tile, position = p }
    if random(1, 256) == 1 then
        entities[#entities + 1] = { name = 'land-mine', position = p, force = 'enemy' }
    end
end

--SCRAPYARD
local function zone_scrap_1(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local treasure = data.treasure
    data.scrap_zone = true

    local scrapyard = Public.get_noise('scrapyard', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Chasms
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local small_caves = Public.get_noise('small_caves', p, seed)
    if noise_cave_ponds < 0.15 and noise_cave_ponds > -0.15 then
        if small_caves > 0.35 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end

        if small_caves < -0.35 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if scrapyard < -0.25 or scrapyard > 0.25 then
        if random(1, 256) == 1 then
            if random(1, 8) == 1 then
                spawn_turret(entities, p, 3)
            else
                spawn_turret(entities, p, 4)
            end
            if random(1, 2048) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
        end
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if scrapyard < -0.55 or scrapyard > 0.55 then
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            return
        end
        if scrapyard < -0.28 or scrapyard > 0.28 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            if random(1, 128) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name(),
                    position = p,
                    force = 'enemy',
                    note = true
                }
            end
            if random(1, 96) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_entities[random(1, scrap_entities_index)],
                    position = p,
                    force = 'enemy'
                }
            end
            if random(1, 96) == 1 then
                entities[#entities + 1] =
                {
                    name = scrap_entities_friendly[random(1, scrap_entities_friendly_index)],
                    position = p,
                    force = 'neutral'
                }
            end

            local scrap_mineable_entities, scrap_mineable_entities_index = get_scrap_mineable_entities(p)

            if random(1, 5) > 1 then
                entities[#entities + 1] =
                {
                    name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                    position = p,
                    force = 'neutral'
                }
            end

            if random(1, 256) == 1 then
                entities[#entities + 1] = { name = 'land-mine', position = p, force = 'enemy' }
            end
            return
        end
        return
    end

    local cave_ponds = Public.get_noise('cave_ponds', p, seed)
    if cave_ponds < -0.6 and scrapyard > -0.2 and scrapyard < 0.2 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    local large_caves = Public.get_noise('large_caves', p, seed)
    if scrapyard > -0.15 and scrapyard < 0.15 then
        if floor(large_caves * 10) % 4 < 3 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
            if random(1, 2048) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            return
        end
    end

    if random(1, 64) == 1 and cave_ponds > 0.6 then
        entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
    end

    tiles[#tiles + 1] = { name = 'red-refined-concrete', position = p }
    if random(1, 256) == 1 then
        entities[#entities + 1] = { name = 'land-mine', position = p, force = 'enemy' }
    end
end

local function zone_7(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure

    local cave_rivers_3 = Public.get_noise('cave_rivers_2', p, seed)
    local cave_rivers_4 = Public.get_noise('cave_rivers_2', p, seed + seed)
    local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    if cave_rivers_3 > -0.025 and cave_rivers_3 < 0.025 and no_rocks_2 > -0.6 then
        tiles[#tiles + 1] = { name = 'water', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    if cave_rivers_4 > -0.025 and cave_rivers_4 < 0.025 and no_rocks_2 > -0.6 then
        tiles[#tiles + 1] = { name = 'water', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    local noise_ores = Public.get_noise('no_rocks_2', p, seed + seed)

    if cave_rivers_3 > -0.20 and cave_rivers_3 < 0.20 then
        tiles[#tiles + 1] = { name = 'grass-' .. floor(cave_rivers_3 * 32) % 3 + 1, position = p }
        if cave_rivers_3 > -0.10 and cave_rivers_3 < 0.10 then
            if random(1, 8) == 1 and no_rocks_2 > -0.25 then
                entities[#entities + 1] = { name = 'tree-01', position = p }
            end
            if random(1, 2048) == 1 then
                markets[#markets + 1] = p
            end
            if noise_ores < -0.5 and no_rocks_2 > -0.6 then
                if cave_rivers_3 > 0 and cave_rivers_3 < 0.07 then
                    entities[#entities + 1] = { name = 'iron-ore', position = p, amount = abs(p.y) + 1 }
                end
            end
        end
        if random(1, 64) == 1 and no_rocks_2 > 0.7 then
            entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
        end

        if random(1, 2048) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        return
    end

    if cave_rivers_4 > -0.20 and cave_rivers_4 < 0.20 then
        tiles[#tiles + 1] = { name = 'grass-' .. floor(cave_rivers_4 * 32) % 3 + 1, position = p }
        if cave_rivers_4 > -0.10 and cave_rivers_4 < 0.10 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            if random(1, 8) == 1 and no_rocks_2 > -0.25 then
                entities[#entities + 1] = { name = 'tree-02', position = p }
            end
            if random(1, 2048) == 1 then
                markets[#markets + 1] = p
            end
            if noise_ores < -0.5 and no_rocks_2 > -0.6 then
                if cave_rivers_4 > 0 and cave_rivers_4 < 0.07 then
                    entities[#entities + 1] = { name = 'copper-ore', position = p, amount = abs(p.y) + 1 }
                end
            end
        end
        if random(1, 64) == 1 and no_rocks_2 > 0.7 then
            entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
        end
        if random(1, 2048) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        return
    end

    --Chasms
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local small_caves = Public.get_noise('small_caves', p, seed)
    if noise_cave_ponds < 0.25 and noise_cave_ponds > -0.25 then
        if small_caves > 0.55 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end

        if small_caves < -0.55 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }

            return
        end
    end

    --Resource Spots
    if smol_areas < -0.72 then
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
    end

    tiles[#tiles + 1] = { name = 'dirt-7', position = p }
    if random(1, 100) > 15 then
        entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
    end
    if random(1, 256) == 1 then
        treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
    end
end

local function zone_forest_2(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure
    data.forest_zone = true

    local large_caves = Public.get_noise('large_caves', p, seed)
    local cave_rivers = Public.get_noise('cave_rivers', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Chasms
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local small_caves = Public.get_noise('small_caves', p, seed)
    if noise_cave_ponds < 0.45 and noise_cave_ponds > -0.45 then
        if small_caves > 0.45 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end

        if small_caves < -0.45 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if large_caves > -0.03 and large_caves < 0.03 and cave_rivers < 0.25 then
        tiles[#tiles + 1] = { name = 'water-green', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    --Resource Spots
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end

        return
    end
    local noise_forest_location = Public.get_noise('forest_location', p, seed)
    if cave_rivers > -0.1 and cave_rivers < 0.1 then
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        if random(1, 36) == 1 then
            entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if random(1, 256) == 1 then
            spawn_turret(entities, p, 4)
        end
    else
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if random(1, 100) > 15 then
            if noise_forest_location > 0.095 then
                if random(1, 256) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name('fire'),
                        position = p,
                        force = 'enemy',
                        note = true
                    }
                end
                if noise_forest_location > 0.6 then
                    if random(1, 100) > 42 then
                        entities[#entities + 1] = { name = 'tree-08-brown', position = p }
                    end
                else
                    if random(1, 100) > 42 then
                        entities[#entities + 1] = { name = 'tree-01', position = p }
                    end
                end
                return
            end

            if noise_forest_location < -0.095 then
                if random(1, 256) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name('fire'),
                        position = p,
                        force = 'enemy',
                        note = true
                    }
                end
                if noise_forest_location < -0.6 then
                    if random(1, 100) > 42 then
                        entities[#entities + 1] = { name = 'tree-04', position = p }
                    end
                else
                    if random(1, 100) > 42 then
                        entities[#entities + 1] = { name = 'tree-02-red', position = p }
                    end
                end

                return
            end
        end
        if random(1, 512) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 4096) == 1 then
            entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
        end
        if random(1, 8096) == 1 then
            markets[#markets + 1] = p
        end
    end
end

local function zone_5(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local treasure = data.treasure

    local small_caves = Public.get_noise('small_caves', p, seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    if small_caves > -0.24 and small_caves < 0.24 then
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if random(1, 768) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        if random(1, 2) == 1 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        return
    end

    if small_caves < -0.50 or small_caves > 0.50 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 128) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if random(1, 256) == 1 then
            spawn_turret(entities, p, 4)
        end
        return
    end

    --Resource Spots
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end

        return
    end

    if small_caves > -0.40 and small_caves < 0.40 then
        if noise_cave_ponds > 0.35 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            tiles[#tiles + 1] = { name = 'dirt-' .. random(1, 4), position = p }
            if random(1, 256) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            if random(1, 256) == 1 then
                entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
            end
            return
        end
        if noise_cave_ponds > 0.25 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            if random(1, 512) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end

            if random(1, 2) == 1 then
                entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
            end
        end
    end

    tiles[#tiles + 1] = { name = void_or_lab, position = p }
end

local function zone_4(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure

    local noise_large_caves = Public.get_noise('large_caves', p, seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local small_caves = Public.get_noise('dungeons', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    if abs(noise_large_caves) > 0.7 then
        tiles[#tiles + 1] = { name = 'water', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end
    if abs(noise_large_caves) > 0.6 then
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'tree-02', position = p }
        end
        if random(1, 32) == 1 then
            markets[#markets + 1] = p
        end
    end
    if abs(noise_large_caves) > 0.5 then
        tiles[#tiles + 1] = { name = 'grass-2', position = p }
        if random(1, 620) == 1 then
            entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
        end
        if random(1, 384) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        if random(1, 1024) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        return
    end
    if abs(noise_large_caves) > 0.475 then
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if random(1, 2) == 1 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        if random(1, 2048) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        return
    end

    --Chasms
    if noise_cave_ponds < 0.15 and noise_cave_ponds > -0.15 then
        if small_caves > 0.75 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end

        if small_caves < -0.75 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    if small_caves > -0.15 and small_caves < 0.15 then
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if random(1, 2) == 1 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        if random(1, 1024) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        return
    end

    --Resource Spots
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end

        return
    end

    if noise_large_caves > -0.2 and noise_large_caves < 0.2 then
        --Main Rock Terrain
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed + seed)
        if no_rocks_2 > 0.80 or no_rocks_2 < -0.80 then
            tiles[#tiles + 1] = { name = 'dirt-' .. floor(no_rocks_2 * 8) % 2 + 5, position = p }
            if random(1, 512) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end
            return
        end

        if random(1, 2048) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end

        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if random(1, 100) > 30 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        return
    end

    tiles[#tiles + 1] = { name = void_or_lab, position = p }
end

local function zone_3(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local entities = data.entities
    local buildings = data.buildings
    local markets = data.markets
    local treasure = data.treasure

    local small_caves = Public.get_noise('dungeons', p, seed + seed)
    local small_caves_2 = Public.get_noise('small_caves_2', p, seed + seed)
    local noise_large_caves = Public.get_noise('large_caves', p, seed + seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local cave_miner = Public.get_noise('cave_miner_01', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Resource Spots
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    --Market Spots
    if noise_cave_ponds < -0.77 then
        if noise_cave_ponds > -0.79 then
            tiles[#tiles + 1] = { name = 'dirt-7', position = p }
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        else
            tiles[#tiles + 1] = { name = 'grass-' .. floor(noise_cave_ponds * 32) % 3 + 1, position = p }
            if random(1, 32) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 16) == 1 then
                entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
            end
        end
        return
    end

    if noise_large_caves > -0.15 and noise_large_caves < 0.15 or small_caves_2 > 0 then
        --Green Water Ponds
        if noise_cave_ponds > 0.80 then
            tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
            if random(1, 16) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end

        --Chasms
        if noise_cave_ponds < 0.12 and noise_cave_ponds > -0.12 then
            if small_caves > 0.85 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }

                return
            end

            if small_caves < -0.85 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }

                return
            end
        end

        --Rivers
        local cave_rivers = Public.get_noise('cave_rivers', p, seed + seed)
        if cave_rivers < 0.024 and cave_rivers > -0.024 then
            if noise_cave_ponds > 0.2 then
                tiles[#tiles + 1] = { name = 'water-shallow', position = p }
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'fish', position = p }
                end
                return
            end
        end
        local cave_rivers_2 = Public.get_noise('cave_rivers_2', p, seed)
        if cave_rivers_2 < 0.024 and cave_rivers_2 > -0.024 then
            if noise_cave_ponds < 0.4 then
                tiles[#tiles + 1] = { name = 'water-shallow', position = p }
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'fish', position = p }
                end
                return
            end
        end

        if noise_cave_ponds > 0.725 then
            tiles[#tiles + 1] = { name = 'dirt-' .. random(4, 6), position = p }
            return
        end

        local no_rocks = Public.get_noise('no_rocks', p, seed + seed)
        --Worm oil Zones
        if no_rocks < 0.20 and no_rocks > -0.20 then
            if small_caves > 0.35 then
                tiles[#tiles + 1] = { name = 'dirt-' .. floor(noise_cave_ponds * 32) % 7 + 1, position = p }
                if random(1, 320) == 1 then
                    entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
                end
                if random(1, 50) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name(),
                        position = p,
                        force = 'enemy',
                        note = true
                    }
                end

                if random(1, 256) == 1 then
                    spawn_turret(entities, p, 3)
                end
                if random(1, 512) == 1 then
                    treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
                end
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'dead-tree-desert', position = p }
                end
                return
            end
        end

        --Main Rock Terrain
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed + seed)
        if no_rocks_2 > 0.80 or no_rocks_2 < -0.80 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            tiles[#tiles + 1] = { name = 'dirt-' .. floor(no_rocks_2 * 8) % 2 + 5, position = p }
            if random(1, 512) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end

            return
        end

        if random(1, 2048) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        tiles[#tiles + 1] = { name = 'dirt-7', position = p }
        if random(1, 100) > 30 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        return
    end

    if cave_miner < 0.32 and cave_miner > -0.32 then
        tiles[#tiles + 1] = { name = void_or_lab, position = p }
    else
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
    end
end

local function zone_2(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local tiles = data.tiles
    local buildings = data.buildings
    local entities = data.entities
    local markets = data.markets
    local treasure = data.treasure

    local small_caves = Public.get_noise('dungeons', p, seed)
    local noise_large_caves = Public.get_noise('large_caves', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Resource Spots
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    if noise_large_caves > -0.75 and noise_large_caves < 0.75 then
        local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)

        --Chasms
        if noise_cave_ponds < 0.15 and noise_cave_ponds > -0.15 then
            if small_caves > 0.32 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }

                return
            end
            if small_caves < -0.32 then
                tiles[#tiles + 1] = { name = void_or_lab, position = p }
                return
            end
        end

        --Green Water Ponds
        if noise_cave_ponds > 0.80 then
            tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
            if random(1, 16) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end

        --Rivers
        local cave_rivers = Public.get_noise('cave_rivers', p, seed + seed)
        if cave_rivers < 0.037 and cave_rivers > -0.037 then
            if noise_cave_ponds < 0.1 then
                tiles[#tiles + 1] = { name = 'water-shallow', position = p }
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'fish', position = p }
                end
                return
            end
        end

        if noise_cave_ponds > 0.66 then
            tiles[#tiles + 1] = { name = 'dirt-' .. random(4, 6), position = p }
            return
        end

        --Market Spots
        if noise_cave_ponds < -0.80 then
            tiles[#tiles + 1] = { name = 'grass-' .. floor(noise_cave_ponds * 32) % 3 + 1, position = p }
            if random(1, 32) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 16) == 1 then
                entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
            end
            return
        end

        local no_rocks = Public.get_noise('no_rocks', p, seed + seed)
        --Worm oil Zones
        if no_rocks < 0.20 and no_rocks > -0.20 then
            if small_caves > 0.30 then
                tiles[#tiles + 1] = { name = 'dirt-' .. floor(noise_cave_ponds * 32) % 7 + 1, position = p }
                if random(1, 450) == 1 then
                    entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
                end
                if random(1, 64) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name(),
                        position = p,
                        force = 'enemy',
                        note = true
                    }
                end

                if random(1, 256) == 1 then
                    spawn_turret(entities, p, 2)
                end
                if random(1, 1024) == 1 then
                    treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
                end
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'dead-tree-desert', position = p }
                end
                return
            end
        end

        --Main Rock Terrain
        local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed + seed)
        if no_rocks_2 > 0.80 or no_rocks_2 < -0.80 then
            local success = place_wagon(data, adjusted_zones)
            if success then
                return
            end
            tiles[#tiles + 1] = { name = 'grass-' .. random(1, 4), position = p }
            if random(1, 512) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
            end

            return
        end

        if random(1, 2048) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'wooden-chest' }
        end
        tiles[#tiles + 1] = { name = 'grass-' .. random(1, 4), position = p }
        if random(1, 100) > 25 then
            entities[#entities + 1] = { name = adjusted_zones.rock_raffle[random(1, adjusted_zones.size_of)], position = p }
        end
        return
    end

    tiles[#tiles + 1] = { name = void_or_lab, position = p }
end

local function zone_forest_1(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local buildings = data.buildings
    local tiles = data.tiles
    local entities = data.entities
    local markets = data.markets
    local treasure = data.treasure
    data.forest_zone = true

    local small_caves = Public.get_noise('dungeons', p, seed + seed)
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed)
    local smol_areas = Public.get_noise('smol_areas', p, seed + seed)

    --Resource Spots
    if smol_areas < 0.055 and smol_areas > -0.025 then
        tiles[#tiles + 1] = { name = 'deepwater-green', position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        return
    end

    --Chasms
    if noise_cave_ponds < 0.101 and noise_cave_ponds > -0.102 then
        if small_caves > 0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
        if small_caves < -0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    --Water Ponds
    if noise_cave_ponds > 0.670 then
        if noise_cave_ponds > 0.750 then
            tiles[#tiles + 1] = { name = 'nuclear-ground', position = p }
            if random(1, 4) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 4) == 1 then
                entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
            end
            return
        end
        tiles[#tiles + 1] = { name = 'deepwater', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    --Rivers
    local cave_rivers = Public.get_noise('cave_rivers', p, seed + seed)
    if cave_rivers < 0.041 and cave_rivers > -0.042 then
        if noise_cave_ponds > 0 then
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
            if random(1, 64) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end
    end

    if noise_cave_ponds > 0.74 then
        tiles[#tiles + 1] = { name = 'nuclear-ground', position = p }
        if cave_rivers < -0.502 then
            tiles[#tiles + 1] = { name = 'refined-hazard-concrete-right', position = p }
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
        end
        return
    end

    local no_rocks = Public.get_noise('no_rocks', p, seed + seed)
    --Worm oil Zones
    if p.y < -64 + noise_cave_ponds * 10 then
        if no_rocks < 0.11 and no_rocks > -0.11 then
            if small_caves > 0.31 then
                tiles[#tiles + 1] = { name = 'brown-refined-concrete', position = p }
                if random(1, 450) == 1 then
                    entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
                end
                if random(1, 96) == 1 then
                    Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                    entities[#entities + 1] =
                    {
                        name = Biters.wave_defense_roll_worm_name('fire'),
                        position = p,
                        force = 'enemy',
                        note = true
                    }
                end

                if random(1, 1024) == 1 then
                    treasure[#treasure + 1] = { position = p, chest = 'iron-chest' }
                end
                if random(1, 64) == 1 then
                    entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
                end
                return
            end
        end
    end

    --Main Rock Terrain
    local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed + seed)
    if no_rocks_2 > 0.64 or no_rocks_2 < -0.64 then
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        tiles[#tiles + 1] = { name = adjusted_zones.starting_tile, position = p }
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
        end

        if random(1, 512) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'iron-chest' }
        end
        return
    end

    if random(1, 2048) == 1 then
        treasure[#treasure + 1] = { position = p, chest = 'iron-chest' }
    end
    tiles[#tiles + 1] = { name = 'nuclear-ground', position = p }
    local noise_forest_location = Public.get_noise('forest_location', p, seed)
    if noise_forest_location > 0.095 then
        if random(1, 256) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if noise_forest_location > 0.6 then
            if random(1, 100) > 42 then
                entities[#entities + 1] = { name = 'tree-08-brown', position = p }
            end
        else
            if random(1, 100) > 42 then
                entities[#entities + 1] = { name = 'tree-01', position = p }
            end
        end
        return
    end

    if noise_forest_location < -0.095 then
        if random(1, 256) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name('fire'),
                position = p,
                force = 'enemy',
                note = true
            }
        end
        if noise_forest_location < -0.6 then
            if random(1, 100) > 42 then
                entities[#entities + 1] = { name = 'tree-04', position = p }
            end
        else
            if random(1, 100) > 42 then
                entities[#entities + 1] = { name = 'tree-02-red', position = p }
            end
        end
        return
    end
end

local function zone_1(x, y, data, void_or_lab, adjusted_zones)
    local p = { x = x, y = y }
    local seed = data.seed
    local buildings = data.buildings
    local tiles = data.tiles
    local entities = data.entities
    local markets = data.markets
    local treasure = data.treasure

    local small_caves = Public.get_noise('small_caves', p, seed + 34883)
    local noise_cave_ponds = Public.get_noise('cave_rivers_2', p, seed + 28939)
    local smol_areas = Public.get_noise('smol_areas', p, seed + 3992)
    local no_rocks_2 = Public.get_noise('no_rocks_2', p, seed + 1922)
    local cave_rivers = Public.get_noise('cave_rivers', p, seed + 31922)
    local no_rocks = Public.get_noise('dungeon_sewer', p, seed + seed)

    if smol_areas < 0.055 and smol_areas > -0.025 then
        entities[#entities + 1] = { name = rock_raffle[random(1, size_of_rock_raffle)], position = p }
        if random(1, 32) == 1 then
            Public.spawn_random_buildings(buildings, p, zone_settings.zone_depth)
        end
        if random(1, 128) == 1 then
            Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
            entities[#entities + 1] =
            {
                name = Biters.wave_defense_roll_worm_name(),
                position = p,
                force = 'enemy'
            }
        end
        return
    end

    --Chasms
    if noise_cave_ponds < 0.505 and noise_cave_ponds > -0.112 then
        if small_caves > 0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
        if small_caves < -0.52 then
            tiles[#tiles + 1] = { name = void_or_lab, position = p }
            return
        end
    end

    --Water Ponds
    if noise_cave_ponds < 0.24 and noise_cave_ponds > 0.04 then
        if noise_cave_ponds > 0.14 then
            tiles[#tiles + 1] = { name = 'acid-refined-concrete', position = p }
            if random(1, 4) == 1 then
                markets[#markets + 1] = p
            end
            if random(1, 4) == 1 then
                entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
            end
            return
        end
        tiles[#tiles + 1] = { name = 'deepwater', position = p }
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fish', position = p }
        end
        return
    end

    --Rivers
    if cave_rivers < 0.042 and cave_rivers > -0.062 then
        if noise_cave_ponds > 0.1 then
            tiles[#tiles + 1] = { name = 'water-shallow', position = p }
            if random(1, 64) == 1 then
                entities[#entities + 1] = { name = 'fish', position = p }
            end
            return
        end
    end


    --Worm oil Zones
    if no_rocks < 0.40 and no_rocks > -0.20 then
        if small_caves > 0.40 then
            tiles[#tiles + 1] = { name = 'brown-refined-concrete', position = p }
            if random(1, 450) == 1 then
                entities[#entities + 1] = { name = 'crude-oil', position = p, amount = get_oil_amount(p) }
            end
            if random(1, 96) == 1 then
                Biters.wave_defense_set_worm_raffle(abs(p.y) * worm_level_modifier)
                entities[#entities + 1] =
                {
                    name = Biters.wave_defense_roll_worm_name(),
                    position = p,
                    force = 'enemy'
                }
            end

            if random(1, 1024) == 1 then
                treasure[#treasure + 1] = { position = p, chest = 'iron-chest' }
            end
            if random(1, 64) == 1 then
                entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
            end
            return
        end
    end


    if noise_cave_ponds > 0.182 then
        if noise_cave_ponds > 0.542 then
            if cave_rivers > -0.302 then
                tiles[#tiles + 1] = { name = 'refined-hazard-concrete-right', position = p }
            end
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = tree_raffle[random(1, size_of_tree_raffle)], position = p }
        end
        return
    end

    --Main Rock Terrain
    if no_rocks_2 > 0.334 and no_rocks_2 < 0.544 then
        local success = place_wagon(data, adjusted_zones)
        if success then
            return
        end
        tiles[#tiles + 1] = { name = 'nuclear-ground', position = p }
        if random(1, 18) == 1 then
            entities[#entities + 1] = { name = 'tree-0' .. random(1, 9), position = p }
        end

        if random(1, 512) == 1 then
            treasure[#treasure + 1] = { position = p, chest = 'iron-chest' }
        end
        return
    end

    if random(1, 2048) == 1 then
        treasure[#treasure + 1] = { position = p, chest = 'iron-chest' }
    end
    tiles[#tiles + 1] = { name = 'nuclear-ground', position = p }
    if random(1, 125) > 25 then
        entities[#entities + 1] = { name = rock_raffle[random(1, size_of_rock_raffle)], position = p }
    end
end

local function fulgora_callback(x, y, data)
    local p = { x = x, y = y }
    local seed = data.seed + 10000
    local entities = data.entities
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + seed)

    if noise_cave_ponds > 0.65 then
        if noise_cave_ponds > 0.80 then
            return
        end
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-small', position = p }
            return
        end
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-medium', position = p }
            return
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-huge', position = p }
            return
        end
        return
    end
end

local function vulcanus_callback(x, y, data)
    local p = { x = x, y = y }
    local seed = data.seed + 10000
    local entities = data.entities
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + seed)

    if noise_cave_ponds > 0.65 then
        if noise_cave_ponds > 0.80 then
            return
        end
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-small', position = p }
            return
        end
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-medium', position = p }
            return
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-huge', position = p }
            return
        end
        return
    end
end

local function gleba_callback(x, y, data)
    local p = { x = x, y = y }
    local seed = data.seed + 10000
    local entities = data.entities
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + seed)

    if noise_cave_ponds > 0.65 then
        if noise_cave_ponds > 0.80 then
            return
        end
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-small', position = p }
            return
        end
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-medium', position = p }
            return
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-huge', position = p }
            return
        end
        return
    end
end

local function aquilo_callback(x, y, data)
    local p = { x = x, y = y }
    local seed = data.seed + 10000
    local entities = data.entities
    local noise_cave_ponds = Public.get_noise('cave_ponds', p, seed + seed)

    if noise_cave_ponds > 0.65 then
        if noise_cave_ponds > 0.80 then
            return
        end
        if random(1, 16) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-small', position = p }
            return
        end
        if random(1, 32) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-medium', position = p }
            return
        end
        if random(1, 64) == 1 then
            entities[#entities + 1] = { name = 'fulgoran-ruin-huge', position = p }
            return
        end
        return
    end
end

local function fortress_callback(x, y, data)
    if x then return end
    if y then return end
    if data then return end
    -- empty
end



local zones =
{
    -- Main zones (grouped under "main")
    zone_1 = { fn = zone_1, weight = 100, tags = { 'main' }, forbidden_after = { "zone_2" } },
    zone_2 = { fn = zone_2, weight = 100, tags = { 'main' } },
    zone_3 = { fn = zone_3, weight = 100, tags = { 'main' } },
    zone_4 = { fn = zone_4, weight = 100, tags = { 'main' } },
    zone_5 = { fn = zone_5, weight = 100, tags = { 'main' } },
    zone_7 = { fn = zone_7, weight = 100, tags = { 'main' } },
    zone_9 = { fn = zone_9, weight = 100, tags = { 'main' } },
    zone_11 = { fn = zone_11, weight = 100, tags = { 'main' } },
    zone_12 = { fn = zone_12, weight = 100, tags = { 'main' } },
    zone_13 = { fn = zone_13, weight = 100, tags = { 'main' } },

    -- Forest zones (grouped under "forest")
    zone_forest_1 = { fn = zone_forest_1, weight = 100, tags = { 'forest' } },
    zone_forest_2 = { fn = zone_forest_2, weight = 100, tags = { 'forest' } },
    zone_10 = { fn = zone_10, weight = 100, tags = { 'forest' } },
    zone_14 = { fn = zone_14, weight = 100, tags = { 'forest' } },

    -- Scrap zones (grouped under "scrap")
    zone_scrap_1 = { fn = zone_scrap_1, weight = 100, tags = { 'scrap' } },
    zone_scrap_2 = { fn = zone_scrap_2, weight = 100, tags = { 'scrap' } },

    -- Crystal zones (grouped under "crystal")
    zone_crystal_1 = { fn = zone_crystal_1, weight = 100, tags = { 'crystal' } },

    -- Volcanic zones (grouped under "volcanic")
    zone_volcanic_1 = { fn = zone_volcanic_1, weight = 100, tags = { 'volcanic' } },

    -- Tech zones (grouped under "tech")
    zone_tech_1 = { fn = zone_tech_1, weight = 100, tags = { 'tech' } },

    -- Frostbite zones (grouped under "frostbite")
    zone_frostbite_1 = { fn = zone_frostbite_1, weight = 100, tags = { 'frostbite' } },
    zone_frostbite_2 = { fn = zone_frostbite_2, weight = 100, tags = { 'frostbite' } },
}

local planets =
{
    fulgora = fulgora_callback,
    vulcanus = vulcanus_callback,
    gleba = gleba_callback,
    aquilo = aquilo_callback,
    fortress = fortress_callback
}

local function shuffle_terrains(adjusted_zones, new_zone)
    if not adjusted_zones.shuffled_terrains then
        shuffle(adjusted_zones.shuffled_zones)
        adjusted_zones.shuffled_terrains = new_zone
    end
end

local function is_out_of_map(p)
    if p.x < Public.zone_settings.zone_width / 2 and p.x >= -Public.zone_settings.zone_width / 2 then
        return
    end
    return true
end

local function init_terrain(adjusted_zones)
    if adjusted_zones.init_terrain then
        return
    end

    local zones_to_generate = {}
    for zone_name, zone_data in pairs(zones) do
        table.insert(
            zones_to_generate,
            {
                value = zone_name,
                tags = zone_data.tags,
                weight = zone_data.weight,
                forbidden_after = zone_data.forbidden_after or {}
            }
        )
    end

    local generated_zones = {}
    local next_provider = generate_with_tags_and_weights(zones_to_generate)

    local size = 132
    for i = 1, size do
        local next = next_provider()
        generated_zones[i] = next
        -- Server.output_script_data(next)
    end

    adjusted_zones.size = size
    adjusted_zones.shuffled_zones = generated_zones
    adjusted_zones.init_terrain = true
end

local function process_bits(p, data, adjusted_zones)
    local left_top_y = data.area.left_top.y

    local index = floor((abs(left_top_y / zone_settings.zone_depth)) % adjusted_zones.size) + 1

    shuffle_terrains(adjusted_zones, index)

    local depth
    if adjusted_zones.reversed then
        depth = left_top_y <= zone_settings.zone_depth
    else
        depth = left_top_y >= -zone_settings.zone_depth
    end

    local generate_zone
    if adjusted_zones.starting_zone and depth then
        generate_zone = zone_1
    else
        generate_zone = zones[adjusted_zones.shuffled_zones[index]].fn
        if not generate_zone then
            generate_zone = zones[adjusted_zones.shuffled_zones[adjusted_zones.size]].fn
        end
    end


    data.current_zone = index

    if data.forest_zone and not adjusted_zones.forest[index] then
        adjusted_zones.forest[index] = true
    end

    if data.scrap_zone and not adjusted_zones.scrap[index] then
        adjusted_zones.scrap[index] = true
    end

    if data.crystal_zone and not adjusted_zones.crystal[index] then
        adjusted_zones.crystal[index] = true
    end

    if data.frostbite_zone and not adjusted_zones.frostbite[index] then
        adjusted_zones.frostbite[index] = true
    end

    if data.volcanic_zone and not adjusted_zones.volcanic[index] then
        adjusted_zones.volcanic[index] = true
    end

    if data.tech_zone and not adjusted_zones.tech[index] then
        adjusted_zones.tech[index] = true
    end

    local starting_planet = Public.get_planet()
    local void_or_tile = Public.get('void_or_tile')

    adjusted_zones.starting_planet = starting_planet
    local planet_callback = planets[adjusted_zones.starting_planet]

    if starting_planet == 'fulgora' then
        adjusted_zones.starting_tile = 'oil-ocean-shallow'
        adjusted_zones.rock_raffle = rock_raffle
        adjusted_zones.size_of = size_of_rock_raffle
    elseif starting_planet == 'vulcanus' then
        adjusted_zones.rock_raffle = rock_raffle
        adjusted_zones.size_of = size_of_rock_raffle
        adjusted_zones.starting_tile = 'fulgoran-machinery'
    elseif starting_planet == 'gleba' then
        adjusted_zones.starting_tile = 'fulgoran-machinery'
        adjusted_zones.rock_raffle = gleba_rock_raffle
        adjusted_zones.size_of = size_of_gleba_rock_raffle
    elseif starting_planet == 'aquilo' then
        adjusted_zones.starting_tile = 'snow-crests'
        adjusted_zones.rock_raffle = aquilo_rock_raffle
        adjusted_zones.size_of = size_of_aquilo_rock_raffle
    else
        adjusted_zones.starting_tile = base_tile
        adjusted_zones.rock_raffle = rock_raffle
        adjusted_zones.size_of = size_of_rock_raffle
    end

    local x = p.x
    local y = p.y

    planet_callback(x, y, data)
    generate_zone(x, y, data, void_or_tile, adjusted_zones)
end

local function border_chunk(p, data, dec_tbl)
    local entities = data.entities
    local decoratives = data.decoratives
    local tiles = data.tiles
    local surface = data.surface

    local pos = p

    if data.reversed then
        if p.y < -74 then
            return
        end
    else
        if p.y > 74 then
            return
        end
    end

    if random(1, ceil(abs(pos.y) + abs(pos.y)) + 64) == 1 then
        entities[#entities + 1] = { name = trees[random(1, #trees)], position = pos }
    end

    game.forces.player.chart(surface, { { p.x - 32, p.y - 32 }, { p.x + 32, p.y + 32 } })

    local noise = Public.get_noise('cave_rivers_2', pos, data.seed)
    local index = floor(noise * 32) % 11 + 1
    local tile = surface.get_tile(pos)
    local starting_planet = Public.get_planet()
    if tile and tile.valid and tile.name ~= 'black-refined-concrete' then
        if starting_planet == 'fulgora' then
            tiles[#tiles + 1] = { name = start_fulgora_tiles[index], position = pos }
        elseif starting_planet == 'aquilo' then
            tiles[#tiles + 1] = { name = start_aquilo_tiles[index], position = pos }
        else
            tiles[#tiles + 1] = { name = start_ground_tiles[index], position = pos }
        end
    end



    local scrap_mineable_entities, scrap_mineable_entities_index = get_scrap_mineable_entities(p)

    if not is_out_of_map(pos) then
        if random(1, ceil(abs(pos.y) + abs(pos.y)) + 32) == 1 then
            entities[#entities + 1] =
            {
                name = scrap_mineable_entities[random(1, scrap_mineable_entities_index)],
                position = pos,
                force = 'neutral'
            }
        end

        for _, decorative in pairs(dec_tbl) do
            if random(1, abs(pos.y) + 3) == 1 then
                decoratives[#decoratives + 1] =
                {
                    name = decorative,
                    position = pos,
                    amount = 1
                }
            end
        end
    end
end

local function biter_chunk(p, data)
    local surface = data.surface
    local entities = data.entities
    local tile_positions = {}

    tile_positions[#tile_positions + 1] = p

    local disable_spawners =
    {
        callback = Public.deactivate_callback
    }
    local disable_worms =
    {
        callback = Public.active_not_destructible_callback
    }

    game.forces.player.chart(surface, { { p.x - 32, p.y - 32 }, { p.x + 32, p.y + 32 } })

    if random(1, 128) == 1 then
        local position = surface.find_non_colliding_position('biter-spawner', tile_positions[random(1, #tile_positions)], 16, 2)
        if position then
            entities[#entities + 1] =
            {
                name = spawner_raffle[random(1, #spawner_raffle)],
                position = position,
                force = 'enemy',
                callback = disable_spawners
            }
        end
    end

    if random(1, 128) == 1 then
        local position = surface.find_non_colliding_position('big-worm-turret', tile_positions[random(1, #tile_positions)], 16, 2)
        if position then
            entities[#entities + 1] =
            {
                name = 'big-worm-turret',
                position = position,
                force = 'enemy',
                callback = disable_worms
            }
        end
    end
end

local function out_of_map(p, data)
    local tiles = data.tiles
    tiles[#tiles + 1] = { name = out_of_map_tile, position = p }
end

local function get_decoratives()
    local decoratives_generated = Public.get('decoratives_generated')
    if decoratives_generated then
        return decoratives_generated
    end

    local game_decoratives = prototypes.decorative
    local dec_tbl = {}
    local names = {}

    for _, decorative in pairs(game_decoratives) do
        names[#names + 1] = decorative.name
    end

    local taken = {}
    while #dec_tbl < 3 and #names > 0 do
        local idx = math.random(#names)
        local name = names[idx]
        if not taken[name] then
            table.insert(dec_tbl, name)
            taken[name] = true
        end
    end

    Public.set('decoratives_generated', dec_tbl)
    return dec_tbl
end

function Public.heavy_functions(data)
    local top_y = data.top_y
    local surface = data.surface
    local starting_planet = Public.get_planet()
    if string.sub(surface.name, 0, #starting_planet) ~= starting_planet then
        return
    end
    local adjusted_zones = Public.get('adjusted_zones')
    data.reversed = adjusted_zones.reversed

    local p = data.position
    if is_out_of_map(p) then
        return out_of_map(p, data)
    end

    local dec_tbl = get_decoratives()

    local pre_final_battle = Public.get('pre_final_battle')
    if pre_final_battle then
        local tiles = data.tiles
        tiles[#tiles + 1] = { name = out_of_map_tile, position = p }
        return
    end

    if adjusted_zones.disable_terrain then
        return
    end

    init_terrain(adjusted_zones)

    if not data.seed then
        data.seed = Public.get('random_seed')
    end

    if data.reversed then
        if top_y % zone_settings.zone_depth == 0 and top_y > 0 then
            Public.set('left_top', data.left_top)
            return wall(p, data, adjusted_zones)
        end

        if top_y > -0 then
            return process_bits(p, data, adjusted_zones)
        end

        if top_y < -150 then
            return out_of_map(p, data)
        end

        if top_y < -100 then
            return biter_chunk(p, data)
        end

        if top_y <= -0 then
            return border_chunk(p, data, dec_tbl)
        end
    else
        if top_y % zone_settings.zone_depth == 0 and top_y < 0 then
            Public.set('left_top', data.left_top)
            return wall(p, data, adjusted_zones)
        end
        if top_y < 0 then
            return process_bits(p, data, adjusted_zones)
        end

        if top_y > 120 then
            return out_of_map(p, data)
        end

        if top_y > 75 then
            return biter_chunk(p, data)
        end

        if top_y >= 0 then
            return border_chunk(p, data, dec_tbl)
        end
    end
end

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 loading_chunk_vectors = {}
for _, v in pairs(chunk_tile_vectors) do
    if v[2] == 16 or v[2] == 17 then
        table.insert(loading_chunk_vectors, v)
    end
end

Event.add(
    defines.events.on_chunk_generated,
    function (e)
        local surface = e.surface
        local starting_planet = Public.get_planet()
        if string.sub(surface.name, 0, #starting_planet) ~= starting_planet then
            return
        end

        local adjusted_zones = Public.get('adjusted_zones')
        if adjusted_zones and adjusted_zones.disable_terrain then
            return
        end

        local area = e.area
        local left_top = area.left_top
        if not surface then
            return
        end
        if not surface.valid then
            return
        end

        local pre_final_battle = Public.get('pre_final_battle')
        if pre_final_battle then
            local tiles = {}
            for x = 0, 31 do
                for y = 0, 31 do
                    tiles[#tiles + 1] = { name = out_of_map_tile, position = { left_top.x + x, left_top.y + y } }
                end
            end
            surface.set_tiles(tiles, false)
            return
        end

        local tiles = {}
        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_out_of_map(position) then
                    tiles[#tiles + 1] = { name = 'out-of-map', position = position }
                end
            end
        end
        surface.set_tiles(tiles, true)

        local winter_mode = Public.get('winter_mode')
        if winter_mode then
            rendering.draw_sprite(
                {
                    sprite = 'tile/snow-tile',
                    x_scale = 32,
                    y_scale = 32,
                    target = left_top,
                    surface = surface,
                    tint = { r = 0.6, g = 0.6, b = 0.6, a = 0.6 },
                    render_layer = 'ground'
                }
            )
        end

        if adjusted_zones.reversed then
            if left_top.y == 128 and left_top.x == -128 then
                local locomotive = Public.get('locomotive')
                if locomotive and locomotive.valid then
                    local position = locomotive.position
                    for _, entity in pairs(surface.find_entities_filtered({ area = { { position.x - 5, position.y + 6 }, { position.x + 5, position.y - 10 } }, type = 'simple-entity' })) do
                        entity.destroy()
                    end
                end
            end

            if left_top.y > 32 then
                game.forces.player.chart(surface, { { left_top.x, left_top.y }, { left_top.x + 31, left_top.y + 31 } })
            end

            -- local oom_tiles = {}

            -- if left_top.y > 32 then
            --     for k, v in pairs(loading_chunk_vectors) do
            --         oom_tiles[k] = { name = out_of_map_tile, position = { left_top.x + v[1], left_top.y + v[2] } }
            --     end
            -- end
            -- surface.set_tiles(oom_tiles, false)
        else
            if left_top.y == -128 and left_top.x == -128 then
                local locomotive = Public.get('locomotive')
                if locomotive and locomotive.valid then
                    local position = locomotive.position
                    for _, entity in pairs(surface.find_entities_filtered({ area = { { position.x - 5, position.y - 6 }, { position.x + 5, position.y + 10 } }, type = 'simple-entity' })) do
                        entity.destroy()
                    end
                end
            end

            if left_top.y < -32 then
                game.forces.player.chart(surface, { { left_top.x, left_top.y }, { left_top.x + 31, left_top.y + 31 } })
            end

            -- local oom_tiles = {}

            -- if math.abs(left_top.y) > 128 then
            --     for k, v in pairs(loading_chunk_vectors) do
            --         oom_tiles[k] = { name = out_of_map_tile, position = { left_top.x + v[1], left_top.y + v[2] } }
            --     end
            -- end
            -- surface.set_tiles(oom_tiles, false)
        end
    end
)

return Public
