From 336cc6c77d29fec9ffba632153830554539eb01f Mon Sep 17 00:00:00 2001
From: Gerkiz <fs@nvfs.se>
Date: Thu, 11 Nov 2021 01:56:03 +0100
Subject: [PATCH 1/5] wave defense - minor changes

Fixed a bug that made so spawners did not spawn.
Modified so biters have the ability to spawn within a radius.
---
 modules/wave_defense/main.lua          | 123 +++++++++++++++++++------
 modules/wave_defense/table.lua         |   5 +
 modules/wave_defense/threat_events.lua |  22 +++--
 3 files changed, 115 insertions(+), 35 deletions(-)

diff --git a/modules/wave_defense/main.lua b/modules/wave_defense/main.lua
index 941e712b..22fc0cd8 100644
--- a/modules/wave_defense/main.lua
+++ b/modules/wave_defense/main.lua
@@ -57,20 +57,39 @@ local function valid(userdata)
     return true
 end
 
+local function shuffle(tbl)
+    local size = #tbl
+    for i = size, 1, -1 do
+        local rand = math.random(size)
+        tbl[i], tbl[rand] = tbl[rand], tbl[i]
+    end
+    return tbl
+end
+
 local function find_initial_spot(surface, position)
     local spot = WD.get('spot')
     if not spot then
-        local pos = surface.find_non_colliding_position('rocket-silo', position, 128, 1)
+        local pos = surface.find_non_colliding_position('stone-furnace', position, 128, 1)
         if not pos then
-            pos = surface.find_non_colliding_position('rocket-silo', position, 148, 1)
+            pos = surface.find_non_colliding_position('stone-furnace', position, 148, 1)
         end
         if not pos then
-            pos = surface.find_non_colliding_position('rocket-silo', position, 164, 1)
+            pos = surface.find_non_colliding_position('stone-furnace', position, 164, 1)
         end
         if not pos then
             pos = position
         end
 
+        if math_random(1, 2) == 1 then
+            local random_pos = {{x = pos.x - 10, y = pos.y - 5}, {x = pos.x + 10, y = pos.y + 5}, {x = pos.x - 10, y = pos.y + 5}, {x = pos.x + 10, y = pos.y - 5}}
+            local actual_pos = shuffle(random_pos)
+            pos = {x = actual_pos[1].x, y = actual_pos[1].y}
+        end
+
+        if not pos then
+            pos = position
+        end
+
         WD.set('spot', pos)
         return pos
     else
@@ -181,7 +200,7 @@ local function get_spawn_pos()
     local initial_position = WD.get('spawn_position')
 
     local located_position = find_initial_spot(surface, initial_position)
-    local valid_position = surface.find_non_colliding_position('behemoth-biter', located_position, 32, 1)
+    local valid_position = surface.find_non_colliding_position('steel-chest', located_position, 32, 1)
     local debug = WD.get('debug')
     if debug then
         if valid_position then
@@ -338,6 +357,12 @@ local function set_main_target()
         end
     end
 
+    local unit_groups_size = WD.get('unit_groups_size')
+    if unit_groups_size < 0 then
+        unit_groups_size = 0
+    end
+    WD.set('unit_groups_size', unit_groups_size)
+
     local sec_target = SideTargets.get_side_target()
     if not sec_target then
         sec_target = get_random_character()
@@ -429,9 +454,14 @@ local function get_active_unit_groups_count()
                 count = count + 1
             else
                 g.destroy()
+                unit_groups[k] = nil
+                local unit_groups_size = WD.get('unit_groups_size')
+                WD.set('unit_groups_size', unit_groups_size - 1)
             end
         else
             unit_groups[k] = nil
+            local unit_groups_size = WD.get('unit_groups_size')
+            WD.set('unit_groups_size', unit_groups_size - 1)
             local unit_group_last_command = WD.get('unit_group_last_command')
             if unit_group_last_command[k] then
                 unit_group_last_command[k] = nil
@@ -447,10 +477,12 @@ local function get_active_unit_groups_count()
     return count
 end
 
-local function spawn_biter(surface, is_boss_biter)
-    if not is_boss_biter then
-        if not can_units_spawn() then
-            return
+local function spawn_biter(surface, position, forceSpawn, is_boss_biter)
+    if not forceSpawn then
+        if not is_boss_biter then
+            if not can_units_spawn() then
+                return
+            end
         end
     end
 
@@ -462,11 +494,17 @@ local function spawn_biter(surface, is_boss_biter)
     else
         name = BiterRolls.wave_defense_roll_biter_name()
     end
-    local position = get_spawn_pos()
+
+    local old_position = position
+
+    position = surface.find_non_colliding_position('steel-chest', position, 10, 1)
+    if not position then
+        position = old_position
+    end
 
     local biter = surface.create_entity({name = name, position = position, force = 'enemy'})
     biter.ai_settings.allow_destroy_when_commands_fail = true
-    biter.ai_settings.allow_try_return_to_spawner = true
+    biter.ai_settings.allow_try_return_to_spawner = false
     biter.ai_settings.do_separation = true
 
     local increase_health_per_wave = WD.get('increase_health_per_wave')
@@ -615,6 +653,8 @@ local function reform_group(group)
         debug_print('Creating new unit group, because old one was stuck.')
         local unit_groups = WD.get('unit_groups')
         unit_groups[new_group.group_number] = new_group
+        local unit_groups_size = WD.get('unit_groups_size')
+        WD.set('unit_groups_size', unit_groups_size + 1)
 
         return new_group
     else
@@ -631,6 +671,8 @@ local function reform_group(group)
                 positions[group.group_number] = nil
             end
             table.remove(unit_groups, group.group_number)
+            local unit_groups_size = WD.get('unit_groups_size')
+            WD.set('unit_groups_size', unit_groups_size - 1)
         end
         group.destroy()
     end
@@ -883,10 +925,14 @@ local function give_main_command_to_group()
     end
 end
 
-local function spawn_unit_group()
-    if not can_units_spawn() then
-        debug_print('spawn_unit_group - Cant spawn units?')
-        return
+local function spawn_unit_group(fs)
+    if fs then
+        debug_print('spawn_unit_group - forcing new biters')
+    else
+        if not can_units_spawn() then
+            debug_print('spawn_unit_group - Cant spawn units?')
+            return
+        end
     end
     local target = WD.get('target')
     if not valid(target) then
@@ -895,9 +941,13 @@ local function spawn_unit_group()
     end
 
     local max_active_unit_groups = WD.get('max_active_unit_groups')
-    if get_active_unit_groups_count() >= max_active_unit_groups then
-        debug_print('spawn_unit_group - unit_groups at max')
-        return
+    if fs then
+        debug_print('spawn_unit_group - forcing new biters')
+    else
+        if get_active_unit_groups_count() >= max_active_unit_groups then
+            debug_print('spawn_unit_group - unit_groups at max')
+            return
+        end
     end
     local surface_index = WD.get('surface_index')
     local remove_entities = WD.get('remove_entities')
@@ -932,15 +982,14 @@ local function spawn_unit_group()
     BiterRolls.wave_defense_set_unit_raffle(wave_number)
 
     debug_print('Spawning unit group at x' .. spawn_position.x .. ' y' .. spawn_position.y)
-    local position = spawn_position
 
     local unit_group_pos = WD.get('unit_group_pos')
-    local unit_group = surface.create_unit_group({position = position, force = 'enemy'})
+    local unit_group = surface.create_unit_group({position = spawn_position, force = 'enemy'})
     unit_group_pos.positions[unit_group.group_number] = {position = unit_group.position, index = 0}
     local average_unit_group_size = WD.get('average_unit_group_size')
     local group_size = math_floor(average_unit_group_size * group_size_modifier_raffle[math_random(1, group_size_modifier_raffle_size)])
     for _ = 1, group_size, 1 do
-        local biter = spawn_biter(surface)
+        local biter = spawn_biter(surface, spawn_position, fs)
         if not biter then
             debug_print('spawn_unit_group - No biters were found?')
             break
@@ -951,7 +1000,7 @@ local function spawn_unit_group()
     end
 
     local boss_wave = WD.get('boss_wave')
-    if boss_wave then
+    if not boss_wave then
         local count = math_random(1, math_floor(wave_number * 0.01) + 2)
         if count > 16 then
             count = 16
@@ -960,7 +1009,7 @@ local function spawn_unit_group()
             count = 4
         end
         for _ = 1, count, 1 do
-            local biter = spawn_biter(surface, true)
+            local biter = spawn_biter(surface, spawn_position, fs, true)
             if not biter then
                 debug_print('spawn_unit_group - No biters were found?')
                 break
@@ -972,8 +1021,10 @@ local function spawn_unit_group()
 
     local unit_groups = WD.get('unit_groups')
     unit_groups[unit_group.group_number] = unit_group
+    local unit_groups_size = WD.get('unit_groups_size')
+    WD.set('unit_groups_size', unit_groups_size + 1)
     if math_random(1, 2) == 1 then
-        WD.set('random_group', unit_group.group_number)
+        WD.set('random_group', unit_group)
     end
     WD.set('spot', 'nil')
     return true
@@ -1028,7 +1079,6 @@ end
 local tick_tasks = {
     [30] = set_main_target,
     [60] = set_enemy_evolution,
-    [90] = spawn_unit_group,
     [120] = give_main_command_to_group,
     [150] = ThreatEvent.build_nest,
     [180] = ThreatEvent.build_worm,
@@ -1037,7 +1087,7 @@ local tick_tasks = {
     [7200] = refresh_active_unit_threat
 }
 
-local function on_tick()
+local function t1()
     local tick = game.tick
     local game_lost = WD.get('game_lost')
     if game_lost then
@@ -1058,10 +1108,10 @@ local function on_tick()
     local t2 = tick % 18000
 
     if tick_tasks[t] then
-        tick_tasks[t]()
+        tick_tasks[t](true)
     end
     if tick_tasks[t2] then
-        tick_tasks[t2]()
+        tick_tasks[t2](true)
     end
 
     local resolve_pathing = WD.get('resolve_pathing')
@@ -1083,6 +1133,23 @@ local function on_tick()
     end
 end
 
-Event.on_nth_tick(30, on_tick)
+local function t2()
+    local game_lost = WD.get('game_lost')
+    if game_lost then
+        return
+    end
+
+    local paused = WD.get('paused')
+    if paused then
+        return
+    end
+
+    spawn_unit_group()
+end
+
+Public.spawn_unit_group = spawn_unit_group
+
+Event.on_nth_tick(30, t1)
+Event.on_nth_tick(130, t2)
 
 return Public
diff --git a/modules/wave_defense/table.lua b/modules/wave_defense/table.lua
index 28a32868..4db7b16a 100644
--- a/modules/wave_defense/table.lua
+++ b/modules/wave_defense/table.lua
@@ -18,6 +18,10 @@ function Public.debug_module()
     this.debug = true
 end
 
+function Public.enable_debug()
+    this.debug = true
+end
+
 function Public.reset_wave_defense()
     this.boss_wave = false
     this.boss_wave_warning = false
@@ -50,6 +54,7 @@ function Public.reset_wave_defense()
     this.threat_log = {}
     this.threat_log_index = 0
     this.unit_groups = {}
+    this.unit_groups_size = 0
     this.unit_group_pos = {
         positions = {}
     }
diff --git a/modules/wave_defense/threat_events.lua b/modules/wave_defense/threat_events.lua
index 2ded88cc..20476ea2 100644
--- a/modules/wave_defense/threat_events.lua
+++ b/modules/wave_defense/threat_events.lua
@@ -36,7 +36,10 @@ end
 local function place_nest_near_unit_group()
     local unit_groups = WD.get('unit_groups')
     local random_group = WD.get('random_group')
-    local group = unit_groups[random_group]
+    if not (random_group and random_group.valid) then
+        return
+    end
+    local group = unit_groups[random_group.group_number]
     if not group then
         return
     end
@@ -74,6 +77,7 @@ local function place_nest_near_unit_group()
         return
     end
     local spawner = unit.surface.create_entity({name = name, position = position, force = unit.force})
+    BiterHealthBooster.add_boss_unit(spawner, 4)
     local nests = WD.get('nests')
     nests[#nests + 1] = spawner
     unit.surface.create_entity({name = 'blood-explosion-huge', position = position})
@@ -90,8 +94,8 @@ function Public.build_nest()
     if threat < 1024 then
         return
     end
-    local index = WD.get('index')
-    if index == 0 then
+    local unit_groups_size = WD.get('unit_groups_size')
+    if unit_groups_size == 0 then
         return
     end
     for _ = 1, 2, 1 do
@@ -111,14 +115,17 @@ function Public.build_worm()
         return
     end
 
-    local index = WD.get('index')
-    if index == 0 then
+    local unit_groups_size = WD.get('unit_groups_size')
+    if unit_groups_size == 0 then
         return
     end
 
     local random_group = WD.get('random_group')
+    if not (random_group and random_group.valid) then
+        return
+    end
     local unit_groups = WD.get('unit_groups')
-    local group = unit_groups[random_group]
+    local group = unit_groups[random_group.group_number]
     if not group then
         return
     end
@@ -157,7 +164,8 @@ function Public.build_worm()
      then
         return
     end
-    unit.surface.create_entity({name = worm, position = position, force = unit.force})
+    local u = unit.surface.create_entity({name = worm, position = position, force = unit.force})
+    BiterHealthBooster.add_boss_unit(u, 4)
     unit.surface.create_entity({name = 'blood-explosion-huge', position = position})
     unit.surface.create_entity({name = 'blood-explosion-huge', position = unit.position})
     remove_unit(unit)

From 2e277143f6cf1a3294291cc0de4648af81dc2a2a Mon Sep 17 00:00:00 2001
From: Gerkiz <fs@nvfs.se>
Date: Thu, 11 Nov 2021 01:56:14 +0100
Subject: [PATCH 2/5] rpg - use local values

---
 modules/rpg/gui.lua | 54 +++++++++++++++++++++++----------------------
 1 file changed, 28 insertions(+), 26 deletions(-)

diff --git a/modules/rpg/gui.lua b/modules/rpg/gui.lua
index f3a0a270..e3098b17 100644
--- a/modules/rpg/gui.lua
+++ b/modules/rpg/gui.lua
@@ -26,6 +26,8 @@ local spell2_button_name = Public.spell2_button_name
 local spell3_button_name = Public.spell3_button_name
 
 local sub = string.sub
+local round = math.round
+local floor = math.floor
 
 function Public.draw_gui_char_button(player)
     if player.gui.top[draw_main_frame_name] then
@@ -236,7 +238,7 @@ local function draw_main_frame(player, location)
     end
 
     add_gui_description(scroll_table, ({'rpg_gui.experience_name'}), 100)
-    local exp_gui = add_gui_stat(scroll_table, math.floor(rpg_t.xp), 125, ({'rpg_gui.gain_info_tooltip'}))
+    local exp_gui = add_gui_stat(scroll_table, floor(rpg_t.xp), 125, ({'rpg_gui.gain_info_tooltip'}))
     data.exp_gui = exp_gui
 
     add_gui_description(scroll_table, ' ', 75)
@@ -281,11 +283,11 @@ local function draw_main_frame(player, location)
     add_gui_description(left_bottom_table, ' ', 40)
 
     add_gui_description(left_bottom_table, ({'rpg_gui.life_name'}), w1, ({'rpg_gui.life_tooltip'}))
-    local health_gui = add_gui_stat(left_bottom_table, math.floor(player.character.health), w2, ({'rpg_gui.life_increase'}))
+    local health_gui = add_gui_stat(left_bottom_table, floor(player.character.health), w2, ({'rpg_gui.life_increase'}))
     data.health = health_gui
     add_gui_stat(
         left_bottom_table,
-        math.floor(player.character.prototype.max_health + player.character_health_bonus + player.force.character_health_bonus),
+        floor(player.character.prototype.max_health + player.character_health_bonus + player.force.character_health_bonus),
         w2,
         ({'rpg_gui.life_maximum'})
     )
@@ -299,8 +301,8 @@ local function draw_main_frame(player, location)
     local i = player.character.get_inventory(defines.inventory.character_armor)
     if not i.is_empty() then
         if i[1].grid then
-            shield = math.floor(i[1].grid.shield)
-            shield_max = math.floor(i[1].grid.max_shield)
+            shield = floor(i[1].grid.shield)
+            shield_max = floor(i[1].grid.max_shield)
             shield_desc_tip = ({'rpg_gui.shield_tooltip'})
             shield_tip = ({'rpg_gui.shield_current'})
             shield_max_tip = ({'rpg_gui.shield_max'})
@@ -341,17 +343,17 @@ local function draw_main_frame(player, location)
 
     add_gui_description(right_bottom_table, ' ', w0)
     add_gui_description(right_bottom_table, ({'rpg_gui.mining_name'}), w1)
-    local mining_speed_value = math.round((player.force.manual_mining_speed_modifier + player.character_mining_speed_modifier + 1) * 100) .. '%'
+    local mining_speed_value = round((player.force.manual_mining_speed_modifier + player.character_mining_speed_modifier + 1) * 100) .. '%'
     add_gui_stat(right_bottom_table, mining_speed_value, w2)
 
     add_gui_description(right_bottom_table, ' ', w0)
     add_gui_description(right_bottom_table, ({'rpg_gui.slot_name'}), w1)
-    local slot_bonus_value = '+ ' .. math.round(player.force.character_inventory_slots_bonus + player.character_inventory_slots_bonus)
+    local slot_bonus_value = '+ ' .. round(player.force.character_inventory_slots_bonus + player.character_inventory_slots_bonus)
     add_gui_stat(right_bottom_table, slot_bonus_value, w2)
 
     add_gui_description(right_bottom_table, ' ', w0)
     add_gui_description(right_bottom_table, ({'rpg_gui.melee_name'}), w1)
-    local melee_damage_value = math.round(100 * (1 + Public.get_melee_modifier(player))) .. '%'
+    local melee_damage_value = round(100 * (1 + Public.get_melee_modifier(player))) .. '%'
     local melee_damage_tooltip
     if rpg_extra.enable_one_punch then
         melee_damage_tooltip = ({
@@ -391,17 +393,17 @@ local function draw_main_frame(player, location)
 
     add_gui_description(right_bottom_table, ' ', w0)
     add_gui_description(right_bottom_table, ({'rpg_gui.crafting_speed'}), w1)
-    local crafting_speed_value = math.round((player.force.manual_crafting_speed_modifier + player.character_crafting_speed_modifier + 1) * 100) .. '%'
+    local crafting_speed_value = round((player.force.manual_crafting_speed_modifier + player.character_crafting_speed_modifier + 1) * 100) .. '%'
     add_gui_stat(right_bottom_table, crafting_speed_value, w2)
 
     add_gui_description(right_bottom_table, ' ', w0)
     add_gui_description(right_bottom_table, ({'rpg_gui.running_speed'}), w1)
-    local running_speed_value = math.round((player.force.character_running_speed_modifier + player.character_running_speed_modifier + 1) * 100) .. '%'
+    local running_speed_value = round((player.force.character_running_speed_modifier + player.character_running_speed_modifier + 1) * 100) .. '%'
     add_gui_stat(right_bottom_table, running_speed_value, w2)
 
     add_gui_description(right_bottom_table, ' ', w0)
     add_gui_description(right_bottom_table, ({'rpg_gui.health_bonus_name'}), w1)
-    local health_bonus_value = '+ ' .. math.round((player.force.character_health_bonus + player.character_health_bonus))
+    local health_bonus_value = '+ ' .. round((player.force.character_health_bonus + player.character_health_bonus))
     local health_tooltip = ({'rpg_gui.health_tooltip', Public.get_heal_modifier(player)})
     add_gui_stat(right_bottom_table, health_bonus_value, w2, health_tooltip)
 
@@ -409,10 +411,10 @@ local function draw_main_frame(player, location)
 
     if rpg_extra.enable_mana then
         add_gui_description(right_bottom_table, ({'rpg_gui.mana_bonus'}), w1)
-        local mana_bonus_value = '+ ' .. (math.floor(Public.get_mana_modifier(player) * 10) / 10)
+        local mana_bonus_value = '+ ' .. (floor(Public.get_mana_modifier(player) * 10) / 10)
         local mana_bonus_tooltip = ({
             'rpg_gui.mana_regen_bonus',
-            (math.floor(Public.get_mana_modifier(player) * 10) / 10)
+            (floor(Public.get_mana_modifier(player) * 10) / 10)
         })
         add_gui_stat(right_bottom_table, mana_bonus_value, w2, mana_bonus_tooltip)
     end
@@ -475,28 +477,28 @@ function Public.update_player_stats(player)
     local rpg_extra = Public.get('rpg_extra')
     local rpg_t = Public.get_value_from_player(player.index)
     local strength = rpg_t.strength - 10
-    P.update_single_modifier(player, 'character_inventory_slots_bonus', 'rpg', math.round(strength * 0.2, 3))
-    P.update_single_modifier(player, 'character_mining_speed_modifier', 'rpg', math.round(strength * 0.007, 3))
-    P.update_single_modifier(player, 'character_maximum_following_robot_count_bonus', 'rpg', math.round(strength / 2 * 0.03, 3))
+    P.update_single_modifier(player, 'character_inventory_slots_bonus', 'rpg', round(strength * 0.2, 3))
+    P.update_single_modifier(player, 'character_mining_speed_modifier', 'rpg', round(strength * 0.007, 3))
+    P.update_single_modifier(player, 'character_maximum_following_robot_count_bonus', 'rpg', round(strength / 2 * 0.03, 3))
 
     local magic = rpg_t.magicka - 10
     local v = magic * 0.22
-    P.update_single_modifier(player, 'character_build_distance_bonus', 'rpg', math.min(60, math.round(v * 0.12, 3)))
-    P.update_single_modifier(player, 'character_item_drop_distance_bonus', 'rpg', math.min(60, math.round(v * 0.05, 3)))
-    P.update_single_modifier(player, 'character_reach_distance_bonus', 'rpg', math.min(60, math.round(v * 0.12, 3)))
-    P.update_single_modifier(player, 'character_loot_pickup_distance_bonus', 'rpg', math.min(20, math.round(v * 0.12, 3)))
-    P.update_single_modifier(player, 'character_item_pickup_distance_bonus', 'rpg', math.min(20, math.round(v * 0.12, 3)))
-    P.update_single_modifier(player, 'character_resource_reach_distance_bonus', 'rpg', math.min(20, math.round(v * 0.05, 3)))
+    P.update_single_modifier(player, 'character_build_distance_bonus', 'rpg', math.min(60, round(v * 0.12, 3)))
+    P.update_single_modifier(player, 'character_item_drop_distance_bonus', 'rpg', math.min(60, round(v * 0.05, 3)))
+    P.update_single_modifier(player, 'character_reach_distance_bonus', 'rpg', math.min(60, round(v * 0.12, 3)))
+    P.update_single_modifier(player, 'character_loot_pickup_distance_bonus', 'rpg', math.min(20, round(v * 0.12, 3)))
+    P.update_single_modifier(player, 'character_item_pickup_distance_bonus', 'rpg', math.min(20, round(v * 0.12, 3)))
+    P.update_single_modifier(player, 'character_resource_reach_distance_bonus', 'rpg', math.min(20, round(v * 0.05, 3)))
     if rpg_t.mana_max >= rpg_extra.mana_limit then
         rpg_t.mana_max = rpg_extra.mana_limit
     else
-        rpg_t.mana_max = math.round((magic) * 2, 3)
+        rpg_t.mana_max = round((magic) * 2, 3)
     end
 
     local dexterity = rpg_t.dexterity - 10
-    P.update_single_modifier(player, 'character_running_speed_modifier', 'rpg', math.round(dexterity * 0.0015, 3))
-    P.update_single_modifier(player, 'character_crafting_speed_modifier', 'rpg', math.round(dexterity * 0.015, 3))
-    P.update_single_modifier(player, 'character_health_bonus', 'rpg', math.round((rpg_t.vitality - 10) * 6, 3))
+    P.update_single_modifier(player, 'character_running_speed_modifier', 'rpg', round(dexterity * 0.0010, 3)) -- reduced since too high speed kills UPS.
+    P.update_single_modifier(player, 'character_crafting_speed_modifier', 'rpg', round(dexterity * 0.015, 3))
+    P.update_single_modifier(player, 'character_health_bonus', 'rpg', round((rpg_t.vitality - 10) * 6, 3))
     P.update_player_modifiers(player)
 end
 

From ac026d36c85c43ea34ed9d179edb1ca1fbc73052 Mon Sep 17 00:00:00 2001
From: Gerkiz <fs@nvfs.se>
Date: Thu, 11 Nov 2021 01:56:30 +0100
Subject: [PATCH 3/5] event - minor changes

Added support for filters.
---
 utils/event.lua      |  5 +++--
 utils/event_core.lua | 14 +++++++++++---
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/utils/event.lua b/utils/event.lua
index a957090b..7a20999c 100644
--- a/utils/event.lua
+++ b/utils/event.lua
@@ -161,12 +161,13 @@ end
 -- See documentation at top of file for details on using events.
 -- @param event_name<number>
 -- @param handler<function>
-function Event.add(event_name, handler)
+-- @optional param filters<table>
+function Event.add(event_name, handler, filters)
     if _LIFECYCLE == 8 then
         error('Calling Event.add after on_init() or on_load() has run is a desync risk.', 2)
     end
 
-    core_add(event_name, handler)
+    core_add(event_name, handler, filters)
 end
 
 --- Register a handler for the script.on_init event.
diff --git a/utils/event_core.lua b/utils/event_core.lua
index 328a9148..50655fe3 100644
--- a/utils/event_core.lua
+++ b/utils/event_core.lua
@@ -91,15 +91,23 @@ local function on_nth_tick_event(event)
 end
 
 --- Do not use this function, use Event.add instead as it has safety checks.
-function Public.add(event_name, handler)
+function Public.add(event_name, handler, filters)
     local handlers = event_handlers[event_name]
     if not handlers then
         event_handlers[event_name] = {handler}
-        script_on_event(event_name, on_event)
+        if filters then
+            script_on_event(event_name, on_event, filters)
+        else
+            script_on_event(event_name, on_event)
+        end
     else
         table.insert(handlers, handler)
         if #handlers == 1 then
-            script_on_event(event_name, on_event)
+            if filters then
+                script_on_event(event_name, on_event, filters)
+            else
+                script_on_event(event_name, on_event)
+            end
         end
     end
 end

From a983d0bf78412a7e71fa624f145fa0616f82fb1a Mon Sep 17 00:00:00 2001
From: Gerkiz <fs@nvfs.se>
Date: Thu, 11 Nov 2021 01:56:52 +0100
Subject: [PATCH 4/5] biter health booster - restored stun effect

Biters now have a chance to recover from stun-effect and get slowed instead.
---
 modules/biter_health_booster_v2.lua | 68 +++++++++++++++++++++--------
 1 file changed, 50 insertions(+), 18 deletions(-)

diff --git a/modules/biter_health_booster_v2.lua b/modules/biter_health_booster_v2.lua
index b109e2b1..a264f65b 100644
--- a/modules/biter_health_booster_v2.lua
+++ b/modules/biter_health_booster_v2.lua
@@ -30,7 +30,8 @@ local this = {
     acid_lines_delay = {},
     acid_nova = false,
     boss_spawns_projectiles = false,
-    enable_boss_loot = false
+    enable_boss_loot = false,
+    randomize_discharge_stun = false
 }
 
 local radius = 6
@@ -40,6 +41,8 @@ local acid_splashes = {
     ['behemoth-biter'] = 'acid-stream-worm-big'
 }
 local acid_lines = {
+    ['small-spitter'] = 'acid-stream-spitter-small',
+    ['medium-spitter'] = 'acid-stream-spitter-medium',
     ['big-spitter'] = 'acid-stream-spitter-big',
     ['behemoth-spitter'] = 'acid-stream-spitter-big'
 }
@@ -71,9 +74,19 @@ Global.register(
     end
 )
 
+local filters = {
+    {filter = 'name', name = 'unit'},
+    {filter = 'name', name = 'turret'},
+    {filter = 'name', name = 'ammo-turret'},
+    {filter = 'name', name = 'electric-turret'},
+    {filter = 'name', name = 'unit-spawner'}
+}
+
 local entity_types = {
     ['unit'] = true,
     ['turret'] = true,
+    ['ammo-turret'] = true,
+    ['electric-turret'] = true,
     ['unit-spawner'] = true
 }
 
@@ -112,7 +125,7 @@ local function loaded_biters(event)
         {
             name = projectiles[random(1, 10)],
             position = entity.position,
-            force = 'neutral',
+            force = 'enemy',
             source = entity.position,
             target = position,
             max_range = 16,
@@ -137,7 +150,8 @@ local function acid_nova(event)
         )
     end
 end
-local function acid_line(surface, name, source, target)
+
+local function create_entity_radius(surface, name, source, target)
     local distance = sqrt((source.x - target.x) ^ 2 + (source.y - target.y) ^ 2)
     local modifier = {(target.x - source.x) / distance, (target.y - source.y) / distance}
 
@@ -228,7 +242,7 @@ local function set_boss_healthbar(health, max_health, healthbar_id)
     rendering.set_color(healthbar_id, {floor(255 - 255 * m), floor(200 * m), 0})
 end
 
-local function try_acid(cause, target)
+local function extra_projectiles(cause, target)
     if not cause or not cause.valid then
         return
     end
@@ -242,7 +256,7 @@ local function try_acid(cause, target)
                     this.acid_lines_delay[cause_unit_number] = 0
                 end
                 if this.acid_lines_delay[cause_unit_number] < game.tick then
-                    acid_line(cause.surface, acid_lines[cause.name], cause.position, target.position)
+                    create_entity_radius(cause.surface, acid_lines[cause.name], cause.position, target.position)
                     this.acid_lines_delay[cause_unit_number] = game.tick + 180
                 end
             end
@@ -255,28 +269,37 @@ local function on_entity_damaged(event)
     if not (biter and biter.valid) then
         return
     end
+    local surface = biter.surface
     local cause = event.cause
 
     local biter_health_boost_units = this.biter_health_boost_units
 
     local unit_number = biter.unit_number
 
+    local damage = event.final_damage_amount
+
     --Create new health pool
     local health_pool = biter_health_boost_units[unit_number]
-    try_acid(cause, biter)
+    extra_projectiles(cause, biter)
 
     if not entity_types[biter.type] then
         return
     end
 
-    local damage_type = event.damage_type
-    if damage_type and damage_type.name == 'electric' then
-        local stickers = event.entity.stickers
-        if stickers and #stickers > 0 then
-            for i = 1, #stickers, 1 do
-                if stickers[i].sticked_to == event.entity and stickers[i].name == 'stun-sticker' then
-                    stickers[i].destroy()
-                    break
+    if this.randomize_discharge_stun then
+        local damage_type = event.damage_type
+        if damage_type and damage_type.name == 'electric' then
+            local stickers = biter.stickers
+            if stickers and #stickers > 0 then
+                for i = 1, #stickers, 1 do
+                    if random(1, 4) == 1 then -- there's a % that biters can recover from stun and get slowed instead.
+                        if stickers[i].sticked_to == biter and stickers[i].name == 'stun-sticker' then
+                            stickers[i].destroy()
+                            local slow = surface.create_entity {name = 'slowdown-sticker', position = biter.position, target = biter}
+                            slow.time_to_live = 200
+                            break
+                        end
+                    end
                 end
             end
         end
@@ -305,7 +328,7 @@ local function on_entity_damaged(event)
     end
 
     --Reduce health pool
-    health_pool[1] = health_pool[1] - event.final_damage_amount
+    health_pool[1] = health_pool[1] - damage
 
     --Set entity health relative to health pool
     biter.health = health_pool[1] * health_pool[2]
@@ -352,7 +375,7 @@ local function on_entity_died(event)
                 end
             end
             if this.boss_spawns_projectiles then
-                if random(1, 96) == 1 then
+                if random(1, 32) == 1 then
                     loaded_biters(event)
                 end
             end
@@ -513,13 +536,22 @@ function Public.enable_make_normal_unit_mini_bosses(boolean)
     return this.make_normal_unit_mini_bosses
 end
 
+--- Enables that enemies can recover from stun randomly.
+---@param boolean
+function Public.enable_randomize_discharge_stun(boolean)
+    this.randomize_discharge_stun = boolean or false
+
+    return this.randomize_discharge_stun
+end
+
 Event.on_init(
     function()
         Public.reset_table()
     end
 )
-Event.add(defines.events.on_entity_damaged, on_entity_damaged)
+
+Event.add(defines.events.on_entity_damaged, on_entity_damaged, filters)
 Event.on_nth_tick(7200, check_clear_table)
-Event.add(defines.events.on_entity_died, on_entity_died)
+Event.add(defines.events.on_entity_died, on_entity_died, filters)
 
 return Public

From 3392526d702af9345064591c5b0ca090f61d8aa6 Mon Sep 17 00:00:00 2001
From: Gerkiz <fs@nvfs.se>
Date: Thu, 11 Nov 2021 01:57:58 +0100
Subject: [PATCH 5/5] mtn v3 - minor changes

Changed some functions to utilize local vars.
Modified how we spawn biters. Instead of clumping them together, they're spawned near eachother.
---
 maps/mountain_fortress_v3/commands.lua      |  56 +++++++++
 maps/mountain_fortress_v3/functions.lua     |  11 +-
 maps/mountain_fortress_v3/generate.lua      | 131 +++++++++++---------
 maps/mountain_fortress_v3/icw/functions.lua |  21 +++-
 maps/mountain_fortress_v3/main.lua          |   1 +
 maps/mountain_fortress_v3/table.lua         |   1 +
 maps/mountain_fortress_v3/terrain.lua       |  57 +++++----
 7 files changed, 192 insertions(+), 86 deletions(-)

diff --git a/maps/mountain_fortress_v3/commands.lua b/maps/mountain_fortress_v3/commands.lua
index 3e524dd6..83e61b0f 100644
--- a/maps/mountain_fortress_v3/commands.lua
+++ b/maps/mountain_fortress_v3/commands.lua
@@ -2,7 +2,9 @@ local Color = require 'utils.color_presets'
 local Task = require 'utils.task'
 local Server = require 'utils.server'
 local WPT = require 'maps.mountain_fortress_v3.table'
+local Collapse = require 'modules.collapse'
 local WD = require 'modules.wave_defense.table'
+local WDM = require 'modules.wave_defense.main'
 
 local mapkeeper = '[color=blue]Mapkeeper:[/color]'
 
@@ -175,3 +177,57 @@ commands.add_command(
         end
     end
 )
+
+if _DEBUG then
+    commands.add_command(
+        'start_collapse',
+        'Enabled only on SP',
+        function()
+            local p
+            local player = game.player
+
+            if game.is_multiplayer() then
+                return
+            end
+
+            if player and player.valid then
+                p = player.print
+                if not player.admin then
+                    p("[ERROR] You're not admin!", Color.fail)
+                    return
+                end
+                if not Collapse.start_now() then
+                    Collapse.start_now(true)
+                    p('Collapse started!')
+                else
+                    Collapse.start_now(false)
+                    p('Collapse stopped!')
+                end
+            end
+        end
+    )
+
+    commands.add_command(
+        'spawn_wave',
+        'Enabled only on SP',
+        function()
+            local p
+            local player = game.player
+
+            if game.is_multiplayer() then
+                return
+            end
+
+            if player and player.valid then
+                p = player.print
+                if not player.admin then
+                    p("[ERROR] You're not admin!", Color.fail)
+                    return
+                end
+                WD.enable_debug()
+                WDM.spawn_unit_group(true)
+                p('Spawning wave!')
+            end
+        end
+    )
+end
diff --git a/maps/mountain_fortress_v3/functions.lua b/maps/mountain_fortress_v3/functions.lua
index 28e8ed7e..8494b9fd 100644
--- a/maps/mountain_fortress_v3/functions.lua
+++ b/maps/mountain_fortress_v3/functions.lua
@@ -1189,6 +1189,7 @@ function Public.set_spawn_position()
 
     ::retry::
 
+    local y_value_position = WPT.get('y_value_position')
     local locomotive_positions = WPT.get('locomotive_pos')
     local total_pos = #locomotive_positions.tbl
 
@@ -1206,7 +1207,7 @@ function Public.set_spawn_position()
             collapse_position = surface.find_non_colliding_position('solar-panel', collapse_pos, 32, 2)
         end
         if not collapse_position then
-            collapse_position = surface.find_non_colliding_position('small-biter', collapse_pos, 32, 2)
+            collapse_position = surface.find_non_colliding_position('steel-chest', collapse_pos, 32, 2)
         end
         local sizeof = locomotive_positions.tbl[total_pos - total_pos + 1]
         if not sizeof then
@@ -1221,7 +1222,7 @@ function Public.set_spawn_position()
             goto retry
         end
 
-        local locomotive_position = surface.find_non_colliding_position('small-biter', sizeof, 128, 1)
+        local locomotive_position = surface.find_non_colliding_position('steel-chest', sizeof, 128, 1)
         local distance_from = floor(math2d.position.distance(locomotive_position, locomotive.position))
         local l_y = l.y
         local t_y = locomotive_position.y
@@ -1248,20 +1249,22 @@ function Public.set_spawn_position()
                         return
                     end
                     debug_str('distance_from was higher - spawning at locomotive_position')
-                    WD.set_spawn_position({x = locomotive_position.x, y = collapse_pos.y - 20})
+                    WD.set_spawn_position({x = locomotive_position.x, y = collapse_pos.y - y_value_position})
                 else
                     debug_str('distance_from was lower - spawning at locomotive_position')
-                    WD.set_spawn_position({x = locomotive_position.x, y = collapse_pos.y - 20})
+                    WD.set_spawn_position({x = locomotive_position.x, y = collapse_pos.y - y_value_position})
                 end
             else
                 if collapse_position then
                     debug_str('total_pos was higher - spawning at collapse_position')
+                    collapse_position = {x = collapse_position.x, y = collapse_position.y - y_value_position}
                     WD.set_spawn_position(collapse_position)
                 end
             end
         else
             if collapse_position then
                 debug_str('total_pos was lower - spawning at collapse_position')
+                collapse_position = {x = collapse_position.x, y = collapse_position.y - y_value_position}
                 WD.set_spawn_position(collapse_position)
             end
         end
diff --git a/maps/mountain_fortress_v3/generate.lua b/maps/mountain_fortress_v3/generate.lua
index dcf4f154..16b6ec20 100644
--- a/maps/mountain_fortress_v3/generate.lua
+++ b/maps/mountain_fortress_v3/generate.lua
@@ -5,6 +5,8 @@ local Task = require 'utils.task'
 local Token = require 'utils.token'
 local Event = require 'utils.event'
 local Terrain = require 'maps.mountain_fortress_v3.terrain'
+local WD = require 'modules.wave_defense.table'
+local BiterHealthBooster = require 'modules.biter_health_booster_v2'
 
 local Public = {}
 
@@ -269,25 +271,27 @@ local function do_place_buildings(data)
                 } == 0
              then
                 entity = surface.create_entity(e)
-                if entity and e.direction then
-                    entity.direction = e.direction
-                end
-                if entity and e.force then
-                    entity.force = e.force
-                end
-                if entity and e.callback then
-                    local c = e.callback.callback
-                    if not c then
-                        return
+                if entity then
+                    if e.direction then
+                        entity.direction = e.direction
                     end
-                    local d = {callback_data = e.callback.data}
-                    if not d then
+                    if e.force then
+                        entity.force = e.force
+                    end
+                    if e.callback then
+                        local c = e.callback.callback
+                        if not c then
+                            return
+                        end
+                        local d = {callback_data = e.callback.data}
+                        if not d then
+                            callback = Token.get(c)
+                            callback(entity)
+                            return
+                        end
                         callback = Token.get(c)
-                        callback(entity)
-                        return
+                        callback(entity, d)
                     end
-                    callback = Token.get(c)
-                    callback(entity, d)
                 end
             end
         end
@@ -340,56 +344,67 @@ local function do_place_entities(data)
         if e.collision then
             if surface.can_place_entity(e) then
                 entity = surface.create_entity(e)
-                wintery(entity)
-                if entity and e.direction then
-                    entity.direction = e.direction
-                end
-                if entity and e.force then
-                    entity.force = e.force
-                end
-                if entity and e.amount then
-                    entity.amount = e.amount
-                end
-                if entity and e.callback then
-                    local c = e.callback.callback
-                    if not c then
-                        return
+                if entity then
+                    if e.note and e.note == 'wall' then
+                        local wall_defenders_health_modifier = WD.get('modified_boss_unit_health')
+                        BiterHealthBooster.add_unit(entity, wall_defenders_health_modifier)
                     end
-                    local d = {callback_data = e.callback.data}
-                    if not d then
-                        callback = Token.get(c)
-                        callback(entity)
-                        return
+                    wintery(entity)
+                    if e.direction then
+                        entity.direction = e.direction
+                    end
+                    if e.force then
+                        entity.force = e.force
+                    end
+                    if e.amount then
+                        entity.amount = e.amount
+                    end
+                    if e.callback then
+                        local c = e.callback.callback
+                        if not c then
+                            return
+                        end
+                        local d = {callback_data = e.callback.data}
+                        if not d then
+                            callback = Token.get(c)
+                            callback(entity)
+                        else
+                            callback = Token.get(c)
+                            callback(entity, d)
+                        end
                     end
-                    callback = Token.get(c)
-                    callback(entity, d)
                 end
             end
         else
             entity = surface.create_entity(e)
-            wintery(entity)
-            if entity and e.direction then
-                entity.direction = e.direction
-            end
-            if entity and e.force then
-                entity.force = e.force
-            end
-            if entity and e.amount then
-                entity.amount = e.amount
-            end
-            if entity and e.callback then
-                local c = e.callback.callback
-                if not c then
-                    return
+            if entity then
+                if e.note and e.note == 'wall' then
+                    local wall_defenders_health_modifier = WD.get('modified_boss_unit_health')
+                    BiterHealthBooster.add_unit(entity, wall_defenders_health_modifier)
                 end
-                local d = {callback_data = e.callback.data}
-                if not d then
-                    callback = Token.get(c)
-                    callback(entity)
-                    return
+                wintery(entity)
+                if e.direction then
+                    entity.direction = e.direction
+                end
+                if e.force then
+                    entity.force = e.force
+                end
+                if e.amount then
+                    entity.amount = e.amount
+                end
+                if e.callback then
+                    local c = e.callback.callback
+                    if c then
+                        local d = {callback_data = e.callback.data}
+                        if not d then
+                            callback = Token.get(c)
+                            callback(entity)
+                        else
+                            callback = Token.get(c)
+                            callback(entity, d)
+                        end
+                    end
                 end
-                callback = Token.get(c)
-                callback(entity, d)
             end
         end
     end
diff --git a/maps/mountain_fortress_v3/icw/functions.lua b/maps/mountain_fortress_v3/icw/functions.lua
index 556705c0..a029d02f 100644
--- a/maps/mountain_fortress_v3/icw/functions.lua
+++ b/maps/mountain_fortress_v3/icw/functions.lua
@@ -268,7 +268,7 @@ function Public.hazardous_debris()
         end
     end
 
-    for _ = 1, 2 * speed, 1 do
+    for _ = 1, 6 * speed, 1 do
         local position = fallout_debris[random(1, size_of_debris)]
         local p = {x = position[1], y = position[2]}
         local get_tile = surface.get_tile(p)
@@ -277,7 +277,24 @@ function Public.hazardous_debris()
         end
     end
 
-    for _ = 1, 1 * speed, 1 do
+    for _ = 1, 4 * speed, 1 do
+        local position = fallout_debris[random(1, size_of_debris)]
+        local p = {x = position[1], y = position[2]}
+        local get_tile = surface.get_tile(p)
+        if get_tile.valid and get_tile.name == 'out-of-map' then
+            create(
+                {
+                    name = 'atomic-bomb-wave-spawns-nuke-shockwave-explosion',
+                    position = position,
+                    force = 'neutral',
+                    target = {position[1], position[2] + fallout_width * 2},
+                    speed = speed
+                }
+            )
+        end
+    end
+
+    for _ = 1, 6 * speed, 1 do
         local position = fallout_debris[random(1, size_of_debris)]
         local p = {x = position[1], y = position[2]}
         local get_tile = surface.get_tile(p)
diff --git a/maps/mountain_fortress_v3/main.lua b/maps/mountain_fortress_v3/main.lua
index e175b788..7db48ed3 100644
--- a/maps/mountain_fortress_v3/main.lua
+++ b/maps/mountain_fortress_v3/main.lua
@@ -203,6 +203,7 @@ function Public.reset_map()
     BiterHealthBooster.check_on_entity_died(true)
     BiterHealthBooster.boss_spawns_projectiles(true)
     BiterHealthBooster.enable_boss_loot(false)
+    BiterHealthBooster.enable_randomize_discharge_stun(true)
 
     Balance.init_enemy_weapon_damage()
 
diff --git a/maps/mountain_fortress_v3/table.lua b/maps/mountain_fortress_v3/table.lua
index e0fb397f..0091b146 100644
--- a/maps/mountain_fortress_v3/table.lua
+++ b/maps/mountain_fortress_v3/table.lua
@@ -192,6 +192,7 @@ function Public.reset_table()
     this.offline_players = {}
     this.collapse_amount = false
     this.collapse_speed = false
+    this.y_value_position = 20
     this.spawn_near_collapse = {
         active = true,
         total_pos = 35,
diff --git a/maps/mountain_fortress_v3/terrain.lua b/maps/mountain_fortress_v3/terrain.lua
index 0bb22779..7c295433 100644
--- a/maps/mountain_fortress_v3/terrain.lua
+++ b/maps/mountain_fortress_v3/terrain.lua
@@ -227,7 +227,8 @@ local function spawn_turret(entities, p, probability)
         force = 'enemy',
         callback = turret_list[probability].callback,
         direction = 4,
-        collision = true
+        collision = true,
+        note = 'wall'
     }
 end
 
@@ -369,7 +370,8 @@ local function wall(p, data)
                 entities[#entities + 1] = {
                     name = Biters.wave_defense_roll_worm_name(),
                     position = p,
-                    force = 'enemy'
+                    force = 'enemy',
+                    note = 'wall'
                 }
             end
         end
@@ -2002,12 +2004,12 @@ local function process_level_1_position(x, y, data, void_or_lab)
     end
 
     --Chasms
-    if noise_cave_ponds < 0.111 and noise_cave_ponds > -0.112 then
-        if small_caves > 0.53 then
+    if noise_cave_ponds < 0.110 and noise_cave_ponds > -0.112 then
+        if small_caves > 0.5 then
             tiles[#tiles + 1] = {name = void_or_lab, position = p}
             return
         end
-        if small_caves < -0.53 then
+        if small_caves < -0.5 then
             tiles[#tiles + 1] = {name = void_or_lab, position = p}
             return
         end
@@ -2144,10 +2146,10 @@ local function process_level_0_position(x, y, data, void_or_lab)
     local markets = data.markets
     local treasure = data.treasure
 
-    local small_caves = get_perlin('dungeons', p, seed)
-    local noise_cave_ponds = get_perlin('cave_ponds', p, seed)
-    local smol_areas = get_perlin('smol_areas', p, seed)
-    local no_rocks_2 = get_perlin('no_rocks_2', p, seed)
+    local small_caves = get_perlin('dungeons', p, seed + 34883)
+    local noise_cave_ponds = get_perlin('cave_ponds', p, seed + 28939)
+    local smol_areas = get_perlin('smol_areas', p, seed + 3992)
+    local no_rocks_2 = get_perlin('no_rocks_2', p, seed + 1922)
     local cave_rivers = get_perlin('cave_rivers', p, seed)
     local no_rocks = get_perlin('no_rocks', p, seed)
 
@@ -2168,20 +2170,20 @@ local function process_level_0_position(x, y, data, void_or_lab)
     end
 
     --Chasms
-    if noise_cave_ponds < 0.111 and noise_cave_ponds > -0.112 then
-        if small_caves > 0.53 then
+    if noise_cave_ponds < 0.105 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.53 then
+        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
+    if noise_cave_ponds > 0.64 then
+        if noise_cave_ponds > 0.74 then
             tiles[#tiles + 1] = {name = 'grass-' .. floor(noise_cave_ponds * 32) % 3 + 1, position = p}
             if random(1, 4) == 1 then
                 markets[#markets + 1] = p
@@ -2199,7 +2201,7 @@ local function process_level_0_position(x, y, data, void_or_lab)
     end
 
     --Rivers
-    if cave_rivers < 0.044 and cave_rivers > -0.062 then
+    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
@@ -2209,7 +2211,7 @@ local function process_level_0_position(x, y, data, void_or_lab)
         end
     end
 
-    if noise_cave_ponds > 0.632 then
+    if noise_cave_ponds > 0.622 then
         if noise_cave_ponds > 0.542 then
             if cave_rivers > -0.302 then
                 tiles[#tiles + 1] = {name = 'refined-hazard-concrete-right', position = p}
@@ -2222,7 +2224,7 @@ local function process_level_0_position(x, y, data, void_or_lab)
     end
 
     --Worm oil Zones
-    if no_rocks < 0.031 and no_rocks > -0.141 then
+    if no_rocks < 0.035 and no_rocks > -0.145 then
         if small_caves > 0.081 then
             tiles[#tiles + 1] = {name = 'grass-' .. floor(noise_cave_ponds * 32) % 3 + 1, position = p}
             if random(1, 250) == 1 then
@@ -2254,7 +2256,7 @@ local function process_level_0_position(x, y, data, void_or_lab)
             return
         end
         tiles[#tiles + 1] = {name = 'dirt-' .. floor(no_rocks_2 * 8) % 2 + 5, position = p}
-        if random(1, 32) == 1 then
+        if random(1, 18) == 1 then
             entities[#entities + 1] = {name = 'tree-0' .. random(1, 9), position = p}
         end
 
@@ -2333,11 +2335,22 @@ local function border_chunk(p, data)
         entities[#entities + 1] = {name = trees[random(1, #trees)], position = pos}
     end
 
-    if random(1, 10) == 1 then
-        tiles[#tiles + 1] = {name = 'red-desert-' .. random(1, 3), position = pos}
-    else
-        tiles[#tiles + 1] = {name = 'dirt-' .. math.random(1, 6), position = pos}
+    local seed = data.seed
+    local small_caves = get_perlin('dungeons', p, seed + 34883)
+    local noise_cave_ponds = get_perlin('cave_ponds', p, seed + 28939)
+    local cave_rivers = get_perlin('cave_rivers', p, seed)
+
+    if small_caves > 0.64 then
+        tiles[#tiles + 1] = {name = 'nuclear-ground', position = pos}
     end
+    if noise_cave_ponds > 0.33 then
+        tiles[#tiles + 1] = {name = 'red-desert-' .. random(1, 3), position = pos}
+    end
+    if cave_rivers > 0.54 then
+        tiles[#tiles + 1] = {name = 'grass-' .. random(1, 4), position = pos}
+    end
+
+    -- tiles[#tiles + 1] = {name = 'dirt-' .. random(1, 6), position = pos}
 
     local scrap_mineable_entities, scrap_mineable_entities_index = get_scrap_mineable_entities()