diff --git a/maps/mountain_fortress_v3/main.lua b/maps/mountain_fortress_v3/main.lua index f8126003..b18001c7 100644 --- a/maps/mountain_fortress_v3/main.lua +++ b/maps/mountain_fortress_v3/main.lua @@ -44,6 +44,7 @@ local BiterHealthBooster = require 'modules.biter_health_booster_v2' local JailData = require 'utils.datastore.jail_data' local RPG_Progression = require 'utils.datastore.rpg_data' local OfflinePlayers = require 'modules.clear_vacant_players' +local Beam = require 'modules.render_beam' -- Use these settings for live local send_ping_to_channel = Discord.channel_names.mtn_channel @@ -179,6 +180,8 @@ function Public.reset_map() Explosives.set_surface_whitelist({[surface.name] = true}) Explosives.check_growth_below_void(true) + Beam.reset_valid_targets() + game.forces.player.set_spawn_position({x = -27, y = 25}, surface) game.forces.player.manual_mining_speed_modifier = 0 game.forces.player.set_ammo_damage_modifier('artillery-shell', -0.95) diff --git a/maps/mountain_fortress_v3/stateful/main.lua b/maps/mountain_fortress_v3/stateful/main.lua index 9eab6373..fc73cbac 100644 --- a/maps/mountain_fortress_v3/stateful/main.lua +++ b/maps/mountain_fortress_v3/stateful/main.lua @@ -1,6 +1,7 @@ local Public = require 'maps.mountain_fortress_v3.stateful.table' local Event = require 'utils.event' local WD = require 'modules.wave_defense.table' +local Beam = require 'modules.render_beam' Public.stateful_gui = require 'maps.mountain_fortress_v3.stateful.gui' Public.stateful_terrain = require 'maps.mountain_fortress_v3.stateful.terrain' @@ -45,15 +46,23 @@ Event.on_nth_tick( end if collection.time_until_attack and collection.time_until_attack <= 0 and collection.survive_for > 0 then + local surface = game.get_surface('boss_room') + if not surface or not surface.valid then + return + end + local spawn_positions = Public.stateful_spawn_points local sizeof = Public.sizeof_stateful_spawn_points + local rounds_survived = Public.get_stateful('rounds_survived') local area = spawn_positions[random(1, sizeof)] shuffle(area) + WD.build_worm_custom() + WD.set_spawn_position(area[1]) - Event.raise(WD.events.on_spawn_unit_group, {fs = true, bypass = true, random_bosses = true, scale = Public.stateful.scale(20, 100)}) + Event.raise(WD.events.on_spawn_unit_group, {fs = true, bypass = true, random_bosses = true, scale = Public.stateful.scale(20 * (rounds_survived + 1), 100)}) return end @@ -65,6 +74,31 @@ Event.on_nth_tick( end ) +Event.on_nth_tick( + 14400, + -- 200, + function() + local final_battle = Public.get_stateful('final_battle') + if not final_battle then + return + end + + local collection = Public.get_stateful('collection') + if not collection then + return + end + + local surface = game.get_surface('boss_room') + if not surface or not surface.valid then + return + end + + if collection.time_until_attack and collection.time_until_attack <= 0 and collection.survive_for > 0 then + Beam.new_beam(surface, game.tick + 600) + end + end +) + Event.add(defines.events.on_pre_player_died, Public.on_pre_player_died) return Public diff --git a/maps/mountain_fortress_v3/stateful/table.lua b/maps/mountain_fortress_v3/stateful/table.lua index 17ef02a8..32665152 100644 --- a/maps/mountain_fortress_v3/stateful/table.lua +++ b/maps/mountain_fortress_v3/stateful/table.lua @@ -15,6 +15,7 @@ local Alert = require 'utils.alert' local IC = require 'maps.mountain_fortress_v3.ic.table' local RPG = require 'modules.rpg.table' local BiterHealthBooster = require 'modules.biter_health_booster_v2' +local Beam = require 'modules.render_beam' local this = { enabled = false, @@ -788,6 +789,8 @@ function Public.allocate() if stateful_locomotive and not stateful_locomotive_migrated then Task.set_timeout_in_ticks(100, move_all_players_token, {}) + Beam.new_valid_targets({'wall', 'turret', 'furnace', 'gate'}) + Public.soft_reset.add_schedule_to_delete_surface() Public.set_stateful('stateful_locomotive_migrated', true) local locomotive = Public.get('locomotive') @@ -809,7 +812,7 @@ function Public.allocate() Server.to_discord_embed('Final boss wave is occuring soon!') - WD.set('final_boss', true) + WD.set('final_battle', true) Core.iter_connected_players( function(player) diff --git a/modules/render_beam.lua b/modules/render_beam.lua index a002a674..1b9493c1 100644 --- a/modules/render_beam.lua +++ b/modules/render_beam.lua @@ -14,6 +14,17 @@ local this = { 'artillery-wagon', 'artillery-turret', 'spidertron' + }, + backup_valid_targets = { + 'character', + 'tank', + 'car', + 'locomotive', + 'cargo-wagon', + 'fluid-wagon', + 'artillery-wagon', + 'artillery-turret', + 'spidertron' } } @@ -55,7 +66,7 @@ function Public:new_target() return end local position - local entities = surface.find_entities_filtered {type = this.valid_targets} + local entities = surface.find_entities_filtered {type = this.valid_targets, force = 'player'} if entities and #entities > 0 then position = entities[random(#entities)].position end @@ -146,7 +157,7 @@ function Public:notify_new_beam() return end - game.print('[Orbital] A new orbital strike has been spotted at: [gps=' .. self.position.x .. ',' .. self.position.y .. ',' .. surface.name .. ']') + game.print('[color=yellow][Orbital][/color] A new orbital strike has been spotted at: [gps=' .. self.position.x .. ',' .. self.position.y .. ',' .. surface.name .. ']') end end @@ -233,7 +244,7 @@ function Public:damage_entities_nearby() if entity.valid then if entity.health then if entity.force.name ~= 'enemy' then - entity.damage(damage, 'enemy') + entity.damage(damage, 'enemy', 'explosion') end end end @@ -245,7 +256,8 @@ end ---@return boolean|integer function Public:validate() if not self.render_id then - return self:new_render() + self:new_render() + return false end if rendering.is_valid(self.render_id) then return true @@ -297,7 +309,7 @@ end --- Creates a new render. ---@param sprite string ----@param surface userdata +---@param surface LuaSurface ---@param ttl integer|nil ---@param scalar table|nil ---@param delayed number|nil @@ -329,13 +341,30 @@ function Public.new(sprite, surface, ttl, scalar, delayed) end --- Creates a new defined beam ----@param surface userdata -function Public.new_beam(surface) - Public.new(Gui.beam, surface) +---@param surface LuaSurface +---@param ttl number|nil +function Public.new_beam(surface, ttl) + Public.new(Gui.beam, surface, ttl) +end + +--- Defines new targets as valid targets +---@param targets table +function Public.new_valid_targets(targets) + if targets and type(targets) == 'table' then + this.backup_valid_targets = this.valid_targets + this.valid_targets = targets + else + error('New valid targets needs to be of type table', 2) + end +end + +--- Defaults the valid targets +function Public.reset_valid_targets() + this.valid_targets = this.backup_valid_targets end --- Creates a new defined beam with a delayed action ----@param surface userdata +---@param surface LuaSurface ---@param time number function Public.new_beam_delayed(surface, time) Public.new(Gui.beam, surface, nil, nil, time) diff --git a/modules/wave_defense/enemy_states.lua b/modules/wave_defense/enemy_states.lua index 5d1b2344..ad146e03 100644 --- a/modules/wave_defense/enemy_states.lua +++ b/modules/wave_defense/enemy_states.lua @@ -22,7 +22,8 @@ local this = { frenzy_burst_length = 160, update_rate = 60, enabled = true, - track_bosses_only = true + track_bosses_only = true, + wave_number = 0 }, target_settings = {} } diff --git a/modules/wave_defense/gui.lua b/modules/wave_defense/gui.lua index 50204cdd..46b726f2 100644 --- a/modules/wave_defense/gui.lua +++ b/modules/wave_defense/gui.lua @@ -66,8 +66,8 @@ local function get_threat_gain() end function Public.update_gui(player) - local final_boss = Public.get('final_boss') - if final_boss then + local final_battle = Public.get('final_battle') + if final_battle then if player.gui.top.wave_defense and player.gui.top.wave_defense.valid then player.gui.top.wave_defense.destroy() end diff --git a/modules/wave_defense/main.lua b/modules/wave_defense/main.lua index 2a1ded7a..404b3d50 100644 --- a/modules/wave_defense/main.lua +++ b/modules/wave_defense/main.lua @@ -476,8 +476,8 @@ local function get_active_unit_groups_count() return count end -local function spawn_biter(surface, position, forceSpawn, is_boss_biter, unit_settings) - if not forceSpawn then +local function spawn_biter(surface, position, force_spawn, is_boss_biter, unit_settings) + if not force_spawn then if not is_boss_biter then if not can_units_spawn() then return @@ -556,7 +556,15 @@ local function spawn_biter(surface, position, forceSpawn, is_boss_biter, unit_se local generated_units = Public.get('generated_units') - generated_units.active_biters[biter.unit_number] = {entity = biter, spawn_tick = game.tick} + if is_boss_biter then + if not generated_units.boss_units then + generated_units.boss_units = {} + end + + generated_units.boss_units[#generated_units.boss_units + 1] = biter + else + generated_units.active_biters[biter.unit_number] = {entity = biter, spawn_tick = game.tick} + end local active_biter_count = Public.get('active_biter_count') Public.set('active_biter_count', active_biter_count + 1) local active_biter_threat = Public.get('active_biter_threat') @@ -564,6 +572,71 @@ local function spawn_biter(surface, position, forceSpawn, is_boss_biter, unit_se return biter end +local function spawn_worm(surface, position, is_boss_worm) + local boosted_health = BiterHealthBooster.get('biter_health_boost') + + local name = Public.wave_defense_roll_worm_name() + + local old_position = position + + local enable_random_spawn_positions = Public.get('enable_random_spawn_positions') + + if enable_random_spawn_positions then + if random(1, 3) == 1 then + position = {x = (-1 * (position.x + random(1, 10))), y = (position.y + random(1, 10))} + else + position = {x = (position.x + random(1, 10)), y = (position.y + random(1, 10))} + end + end + + position = surface.find_non_colliding_position('steel-chest', position, 3, 1) + if not position then + position = old_position + end + + local force = 'enemy' + local es_settings = Public.get_es('settings') + + if es_settings.enabled then + force = 'aggressors' + end + + local worm = surface.create_entity({name = name, position = position, force = force}) + local increase_health_per_wave = Public.get('increase_health_per_wave') + local boost_units_when_wave_is_above = Public.get('boost_units_when_wave_is_above') + local boost_bosses_when_wave_is_above = Public.get('boost_bosses_when_wave_is_above') + local wave_number = Public.get('wave_number') + + if (increase_health_per_wave and (wave_number >= boost_units_when_wave_is_above)) and not is_boss_worm then + local modified_unit_health = Public.get('modified_unit_health') + local unit_settings = Public.get('unit_settings') + local final_health = round(modified_unit_health.current_value * unit_settings.worm_unit_settings[worm.name], 3) + if final_health < 1 then + final_health = 1 + end + Public.debug_print_health('final_health - unit: ' .. worm.name .. ' with h-m: ' .. final_health) + BiterHealthBooster.add_unit(worm, final_health) + end + + if is_boss_worm then + if (wave_number >= boost_bosses_when_wave_is_above) then + local increase_boss_health_per_wave = Public.get('increase_boss_health_per_wave') + if increase_boss_health_per_wave then + local modified_boss_unit_health = Public.get('modified_boss_unit_health') + BiterHealthBooster.add_boss_unit(worm, modified_boss_unit_health.current_value, 0.55) + else + local sum = boosted_health * 5 + BiterHealthBooster.add_boss_unit(worm, sum, 0.55) + end + else + local sum = boosted_health * 5 + BiterHealthBooster.add_boss_unit(worm, sum, 0.55) + end + end + + return worm +end + local function increase_biter_damage(force) local increase_damage_per_wave = Public.get('increase_damage_per_wave') if not increase_damage_per_wave then @@ -1140,7 +1213,6 @@ local function spawn_unit_group(fs, only_bosses) Public.set('boss_wave', false) end else - event_data.boss_wave = true local count = fs.scale or 30 event_data.spawn_count = count for _ = 1, count, 1 do @@ -1272,10 +1344,10 @@ Event.on_nth_tick( if game_lost then return end - local final_boss = Public.get('final_boss') + local final_battle = Public.get('final_battle') local paused = Public.get('paused') - if paused and not final_boss then + if paused and not final_battle then local players = game.connected_players for _, player in pairs(players) do Public.update_gui(player) @@ -1307,7 +1379,7 @@ Event.on_nth_tick( tick_tasks_t2[t2]() end - if final_boss then + if final_battle then return end @@ -1335,8 +1407,8 @@ Event.add(Public.events.on_spawn_unit_group, spawn_unit_group) Event.on_nth_tick( 50, function() - local final_boss = Public.get('final_boss') - if final_boss then + local final_battle = Public.get('final_battle') + if final_battle then return end @@ -1364,5 +1436,6 @@ Event.on_nth_tick( Public.set_next_wave = set_next_wave Public.normalize_spawn_position = normalize_spawn_position Public.check_if_near_target = check_if_near_target +Public.spawn_worm = spawn_worm return Public diff --git a/modules/wave_defense/pause_waves.lua b/modules/wave_defense/pause_waves.lua index 175150f0..6ef3b0d4 100644 --- a/modules/wave_defense/pause_waves.lua +++ b/modules/wave_defense/pause_waves.lua @@ -240,8 +240,8 @@ Event.on_nth_tick( return end - local final_boss = Public.get('final_boss') - if final_boss then + local final_battle = Public.get('final_battle') + if final_battle then return end diff --git a/modules/wave_defense/table.lua b/modules/wave_defense/table.lua index 00cbe90d..93d37c4d 100644 --- a/modules/wave_defense/table.lua +++ b/modules/wave_defense/table.lua @@ -71,7 +71,7 @@ function Public.reset_wave_defense() this.get_random_close_spawner_attempts = 5 this.group_size = 2 this.last_wave = game.tick - this.final_boss = false + this.final_battle = false this.max_active_biters = 1280 this.max_active_unit_groups = 32 this.max_biter_age = 3600 * 60 diff --git a/modules/wave_defense/threat_events.lua b/modules/wave_defense/threat_events.lua index 1d45ee8f..d41a84d8 100644 --- a/modules/wave_defense/threat_events.lua +++ b/modules/wave_defense/threat_events.lua @@ -216,8 +216,8 @@ else end function Public.build_nest() - local final_boss = Public.get('final_boss') - if final_boss then + local final_battle = Public.get('final_battle') + if final_battle then return end @@ -237,14 +237,14 @@ function Public.build_nest() end function Public.build_worm() - local final_boss = Public.get('final_boss') + local final_battle = Public.get('final_battle') local threat = Public.get('threat') - if threat < 512 and not final_boss then + if threat < 512 and not final_battle then return end local worm_building_chance = Public.get('worm_building_chance') --[[@as integer]] - if random(1, worm_building_chance) ~= 1 then + if not final_battle and random(1, worm_building_chance) ~= 1 then return end @@ -321,6 +321,68 @@ function Public.build_worm() Public.set('threat', threat - Public.threat_values[worm]) end +function Public.build_worm_custom() + local unit_groups_size = Public.get('unit_groups_size') + if unit_groups_size == 0 then + return + end + + local random_group = Public.get('random_group') + if not (random_group and random_group.valid) then + return + end + local generated_units = Public.get('generated_units') + local group = generated_units.boss_units + if not group then + return + end + + if not next(group) then + return + end + + local unit + generated_units.boss_unit_index, unit = next(group, generated_units.boss_unit_index) + + if not unit or not unit.valid then + table.remove(group, generated_units.boss_unit_index) + return + end + + local wave_number = Public.get('wave_number') + local position = unit.surface.find_non_colliding_position('assembling-machine-1', unit.position, 8, 1) + Public.wave_defense_set_worm_raffle(wave_number) + local worm = Public.wave_defense_roll_worm_name() + if not position then + return + end + + local worm_building_density = Public.get('worm_building_density') + local r = worm_building_density + if + unit.surface.count_entities_filtered( + { + type = 'turret', + force = unit.force, + area = {{position.x - r, position.y - r}, {position.x + r, position.y + r}} + } + ) > 0 + then + return + end + local u = unit.surface.create_entity({name = worm, position = position, force = unit.force}) + local modified_boss_unit_health = Public.get('modified_boss_unit_health') + + BiterHealthBooster.add_boss_unit(u, modified_boss_unit_health.current_value, 0.5) + + table.remove(group, generated_units.boss_unit_index) + + unit.surface.create_entity({name = 'blood-explosion-huge', position = position}) + unit.surface.create_entity({name = 'blood-explosion-huge', position = unit.position}) + remove_unit(unit) + unit.destroy() +end + local function shred_simple_entities(entity) local threat = Public.get('threat') if threat < 5000 then