diff --git a/.luacheckrc b/.luacheckrc index 5413ec9b0..271abfeac 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -1150,6 +1150,7 @@ stds.factorio_defines = { 'on_unit_added_to_group', 'on_unit_group_created', 'on_unit_removed_from_group', + 'on_unit_group_finished_gathering', 'script_raised_built', 'script_raised_destroy', 'script_raised_revive' diff --git a/locale/en/redmew_maps.cfg b/locale/en/redmew_maps.cfg index b47cbcd4b..ab22c93f5 100644 --- a/locale/en/redmew_maps.cfg +++ b/locale/en/redmew_maps.cfg @@ -174,7 +174,8 @@ anti_grief_jail_reason=You have spilled too many items on the ground, contact an [danger_ores] biters_disabled=Launching the first [item=satellite] has killed all the biters. Launch __1__ [item=satellite] to win the map. -win=Congratulations! The map has been won. Restart the map with /restart +win=[color=green]Congratulations! The map has been won. Restart the map with /restart[/color] +lose=[color=red]Alas! Unfortunately, the map has been lost. Restart the map with /restart[/color] satellite_launch=Launch another __1__ [item=satellite] to win the map. biters_disabled_k2=Launching the first [item=satellite] has killed all the biters. Build and activate the Intergalactic Transceiver to win the map. biters_disabled_ei=Launching the first [item=satellite] has killed all the biters. Launch more Exploration Satellites to the asteroids to unlock the final age. diff --git a/map_gen/maps/danger_ores/modules/enemy.lua b/map_gen/maps/danger_ores/modules/enemy.lua index 96a4a8f37..31e2c1d2a 100644 --- a/map_gen/maps/danger_ores/modules/enemy.lua +++ b/map_gen/maps/danger_ores/modules/enemy.lua @@ -18,6 +18,7 @@ return function(config) local max_chance = config.enemy_max_chance or 1 / 6 local scale_factor = config.enemy_scale_factor or 32 local seed = config.enemy_seed or seed_provider() + local radius = config.enemy_radius or 64 local sf = 1 / scale_factor local m = 1 / 768 @@ -29,7 +30,7 @@ return function(config) local d = sqrt(world.x * world.x + world.y * world.y) - if d < 64 then + if d < radius then return nil end diff --git a/map_gen/maps/danger_ores/modules/evolution_control.lua b/map_gen/maps/danger_ores/modules/evolution_control.lua new file mode 100644 index 000000000..e9f282f5b --- /dev/null +++ b/map_gen/maps/danger_ores/modules/evolution_control.lua @@ -0,0 +1,22 @@ +local Event = require 'utils.event' + +local relations = { + ['logistic-science-pack'] = 10, + ['military-science-pack'] = 20, + ['chemical-science-pack'] = 40, + ['production-science-pack'] = 50, + ['utility-science-pack'] = 90, +} + +Event.on_nth_tick(103, function() + local max = 0 + local tech = game.forces.player.technologies + for name, evo in pairs(relations) do + if tech[name] and tech[name].researched then + max = math.max(max, evo) + end + end + if game.forces.enemy.evolution_factor > 10 and max > 2 then + game.forces.enemy.evolution_factor = math.min(game.forces.enemy.evolution_factor, max - 1) + end +end) diff --git a/map_gen/maps/danger_ores/modules/map_poll.lua b/map_gen/maps/danger_ores/modules/map_poll.lua index fffb95dbd..4cc5d70e3 100644 --- a/map_gen/maps/danger_ores/modules/map_poll.lua +++ b/map_gen/maps/danger_ores/modules/map_poll.lua @@ -24,6 +24,7 @@ local ei_mod_pack = 'ei_mod_pack' -- local py_short_mod_pack = 'py_short_mod_pack' local ir3_mod_pack = 'ir3_mod_pack' local scrap_mod_pack = 'scrap_mod_pack' +local silo_defense_mod_pack = 'silo_defense_mod_pack' local mod_packs = { normal_mod_pack = 'danger_ore29', @@ -35,6 +36,7 @@ local mod_packs = { py_short_mod_pack = 'danger_ore_py_short', ir3_mod_pack = 'danger_ore_ir3', scrap_mod_pack = 'danger_ore_scrap', + silo_defense_mod_pack = 'danger_ore_silo_defense', } local maps = { @@ -189,6 +191,11 @@ local maps = { name = 'danger-ore-scrap', mod_pack = scrap_mod_pack, display_name = 'Scrapworld (no ores, all scraps)' + }, + { + name = 'danger-ore-silo-defense', + mod_pack = silo_defense_mod_pack, + display_name = 'Silo Defense (wave defense style)' } } diff --git a/map_gen/maps/danger_ores/modules/memory_storage_control.lua b/map_gen/maps/danger_ores/modules/memory_storage_control.lua new file mode 100644 index 000000000..7411f3f09 --- /dev/null +++ b/map_gen/maps/danger_ores/modules/memory_storage_control.lua @@ -0,0 +1,44 @@ +local Event = require 'utils.event' +local RS = require 'map_gen.shared.redmew_surface' + +if not script.active_mods['deep-storage-unit'] then + return +end + +-- Disable MU tech & drop 4 of them at spawn +Event.on_init(function() + game.forces.player.technologies['memory-unit'].enabled = false + + local chest = RS.get_surface().create_entity{ + name = 'iron-chest', + position = {0, 0}, + force = 'player', + create_build_effect_smoke = true, + move_stuck_players = true + } + + chest.insert({ count = 4, name = 'memory-unit'}) +end) + +-- If a player disconnects with any Memory Storage in its inventory, spill all of it immediately +Event.add(defines.events.on_pre_player_left_game, function(event) + local player = game.get_player(event.player_index) + if not (player and player.valid) then + return + end + + for _, inv_type in pairs({defines.inventory.character_main, defines.inventory.character_trash}) do + local inv = player.get_inventory(inv_type) + local count = inv.get_item_count + + if count('memory-unit') + count('memory-unit-with-tags') > 0 then + local spill_stack = player.surface and player.surface.spill_item_stack + local position = player.position + + for i=1, #inv do + spill_stack(position, inv[i], true, nil, false) + inv[i].clear() + end + end + end +end) diff --git a/map_gen/maps/danger_ores/modules/restart_command.lua b/map_gen/maps/danger_ores/modules/restart_command.lua index 08a26f953..60a2c1d99 100644 --- a/map_gen/maps/danger_ores/modules/restart_command.lua +++ b/map_gen/maps/danger_ores/modules/restart_command.lua @@ -151,10 +151,15 @@ return function(config) end ore_totals_message = ore_totals_message:sub(1, -3)..')' -- remove the last ", " and add a bracket ore_totals_message = "Total ore mined: "..format_number(total_ore, true).. "\\n"..ore_totals_message + local map_won = true + if ShareGlobals.data.map_won_objective ~= nil then + map_won = ShareGlobals.data.map_won_objective + end - local statistics_message = statistics.scenario..' completed!\\n\\n'.. + local statistics_message = statistics.scenario..' finished!\\n\\n'.. 'Statistics:\\n'.. 'Map time: '..time_string..'\\n'.. + 'Status: ' ..(map_won and 'Victorious!' or 'Defeated!')..'\\n'.. 'Total entities built: '..statistics.entities_built..'\\n'.. 'Total ore mined:'..ore_totals_message..'\\n'.. 'Total ore resources exhausted: '..statistics.resources_exhausted..'\\n'.. diff --git a/map_gen/maps/danger_ores/presets/danger_ore_silo_defense.lua b/map_gen/maps/danger_ores/presets/danger_ore_silo_defense.lua new file mode 100644 index 000000000..a72a87b7d --- /dev/null +++ b/map_gen/maps/danger_ores/presets/danger_ore_silo_defense.lua @@ -0,0 +1,314 @@ +local b = require 'map_gen.shared.builders' +local Config = require 'config' +local Event = require 'utils.event' +local MGSP = require 'resources.map_gen_settings' +local RS = require 'map_gen.shared.redmew_surface' +local Task = require 'utils.task' +local Token = require 'utils.token' +local Global = require 'utils.global' +local Server = require 'features.server' +local ShareGlobals = require 'map_gen.maps.danger_ores.modules.shared_globals' + +local ScenarioInfo = require 'features.gui.info' +ScenarioInfo.set_map_name('Danger Ore Silo Defense') +ScenarioInfo.set_map_description([[ + Clear the ore to expand the base, + focus mining efforts on specific sectors to ensure + proper material ratios, expand the map with pollution! +]]) +ScenarioInfo.add_map_extra_info([[ + This map is split in three sectors [item=iron-ore] [item=copper-ore] [item=coal]. + Each sector has a main resource and the other resources at a lower ratio. + + You may not build the factory on ore patches. Exceptions: + [item=burner-mining-drill] [item=electric-mining-drill] [item=pumpjack] [item=small-electric-pole] [item=medium-electric-pole] [item=big-electric-pole] [item=substation] [item=car] [item=tank] [item=spidertron] [item=locomotive] [item=cargo-wagon] [item=fluid-wagon] [item=artillery-wagon] + [item=transport-belt] [item=fast-transport-belt] [item=express-transport-belt] [item=underground-belt] [item=fast-underground-belt] [item=express-underground-belt] [item=rail] [item=rail-signal] [item=rail-chain-signal] [item=train-stop] + + The map size is restricted to the pollution generated. A significant amount of + pollution must affect a section of the map before it is revealed. Pollution + does not affect biter evolution. +]]) + +ScenarioInfo.set_new_info([[ + 2024-03-25 + - increased turret base power + - decrease science cost 20x -> 5x + - added evolution control module + + 2024-03-17: + - initial relese DO/Silo Defense +]]) + +ScenarioInfo.add_extra_rule({ 'info.rules_text_danger_ore' }) + +local this = { + groups = {} +} + +local map = require 'map_gen.maps.danger_ores.modules.map' +local main_ores_config = require 'map_gen.maps.danger_ores.config.deadlock_beltboxes_ores_landfill' +local trees = require 'map_gen.maps.danger_ores.modules.trees' +local enemy = require 'map_gen.maps.danger_ores.modules.enemy' + +local banned_entities = require 'map_gen.maps.danger_ores.modules.banned_entities' +local allowed_entities = require 'map_gen.maps.danger_ores.config.deadlock_beltboxes_allowed_entities' +banned_entities(allowed_entities) + +RS.set_map_gen_settings({ + MGSP.grass_only, + MGSP.enable_water, + { terrain_segmentation = 'normal', water = 'normal' }, + { starting_area = 2.00 }, + MGSP.ore_oil_none, + MGSP.enemy_none, + MGSP.cliff_none, + MGSP.tree_none, +}) + +Config.market.enabled = false +Config.player_rewards.enabled = false +Config.redmew_qol.loaders = false +Config.dump_offline_inventories = { + enabled = true, + offline_timout_mins = 30 -- time after which a player logs off that their inventory is provided to the team +} +Config.paint.enabled = false +Config.permissions.presets.no_blueprints = true +Config.player_create.starting_items = { + { count = 5, name = 'stone-furnace' }, + { count = 5, name = 'burner-mining-drill' }, + { count = 100, name = 'wood' }, + { count = 1, name = 'pistol' }, + { count = 20, name = 'firearm-magazine' }, +} + +---@param entity LuaEntity +local fill_turret_callback = Token.register(function(entity) + if not (entity and entity.valid) then + return + end + + entity.insert({name = 'firearm-magazine', count = 35}) +end) + +---@param data +---@field name string, prototype name +---@field position MapPosition +---@field callback? TokenID +local place_entity_callback = Token.register(function(data) + local surface = RS.get_surface() + local position = surface.find_non_colliding_position(data.name, data.position, 5, 1) + if not position then + return + end + + local entity = surface.create_entity{ + name = data.name, + position = position, + force = 'player', + create_build_effect_smoke = true, + move_stuck_players = true + } + + if entity.name == 'rocket-silo' then + entity.minable = false + this.silo = entity + this.silo_position = entity.position + this.silo_id = script.register_on_entity_destroyed(entity) + end + + if data.callback then + Task.queue_task(data.callback, entity) + end +end) + +local set_game_lost = Token.register(function() + ShareGlobals.data.map_finished = true + ShareGlobals.data.map_won_objective = false + local message = 'Alas! Unfortunately, the map has been lost. Restart the map with /restart' + game.print({'danger_ores.lose'}) + Server.to_discord_bold(message) + + game.set_game_state({ + game_finished = true, + player_won = false, + can_continue = true, + victorious_force = game.forces.enemy, + }) +end) + +Global.register_init( + { this = this }, + function(tbl) + tbl.this = this + + game.draw_resource_selection = false + game.difficulty_settings.technology_price_multiplier = game.difficulty_settings.technology_price_multiplier * 5 + game.map_settings.enemy_evolution.time_factor = 0.000002 -- default 0.000004 + game.map_settings.enemy_evolution.destroy_factor = 0.000010 -- default 0.002 + game.map_settings.enemy_evolution.pollution_factor = 0.0000004 -- Pollution has no affect on evolution default 0.0000009 + + game.forces.player.friendly_fire = false + game.forces.player.set_ammo_damage_modifier('bullet', 0.16) + game.forces.player.set_ammo_damage_modifier('shotgun-shell', 1.00) + game.forces.player.set_turret_attack_modifier('gun-turret', 0.80) + game.forces.player.set_turret_attack_modifier('laser-turret', 1.20) + + local techs = game.forces.player.technologies + techs['mining-productivity-1'].enabled = false + techs['mining-productivity-2'].enabled = false + techs['mining-productivity-3'].enabled = false + techs['mining-productivity-4'].enabled = false + techs['logistics'].researched = true + techs['automation'].researched = true + techs['gun-turret'].researched = true + techs['stone-wall'].researched = true + + local path_finder = game.map_settings.path_finder + path_finder.fwd2bwd_ratio = 2 + path_finder.goal_pressure_ratio = 3 + path_finder.short_cache_size = 30 + path_finder.long_cache_size = 50 + path_finder.short_cache_min_cacheable_distance = 8 + path_finder.long_cache_min_cacheable_distance = 60 + path_finder.max_clients_to_accept_any_new_request = 4 + path_finder.max_clients_to_accept_short_new_request = 150 + path_finder.start_to_goal_cost_multiplier_to_terminate_path_find = 10000 + + Task.queue_task(place_entity_callback, { name = 'rocket-silo', position = {17, -17}}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {18, -24}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {24, -23}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {25, -16}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {24, -10}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {17, -09}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {11, -10}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {10, -17}, callback = fill_turret_callback}) + Task.queue_task(place_entity_callback, { name = 'gun-turret', position = {11, -23}, callback = fill_turret_callback}) + end, + function(tbl) + this = tbl.this + end +) + +Event.add(defines.events.on_unit_group_finished_gathering, function(event) + local group = event.group + if not (group and group.valid and group.surface.index == RS.get_surface().index) then + return + end + + group.set_command{ + type = defines.command.go_to_location, + destination_entity = this.silo, + distraction = defines.distraction.by_enemy, + radius = 8, + } + group.start_moving() + this.groups[group.group_number] = group +end) + +Event.add(defines.events.on_ai_command_completed, function(event) + if event.was_distracted then + return + end + + local id = event.unit_number + local result = event.result + local group = this.groups[id] + if not (group and group.valid) then + this.groups[id] = nil + return + end + + if result == defines.behavior_result.success then + group.set_command{ + type = defines.command.attack, + target = this.silo, + distraction = defines.distraction.by_damage, + } + elseif result == defines.behavior_result.fail or result == defines.behavior_result.deleted then + this.groups[id] = nil + end +end) + +Event.add(defines.events.on_entity_destroyed, function(event) + local registration_number = event.registration_number + if registration_number ~= this.silo_id then + return + end + + local spawn = RS.get_surface().create_entity + local target = spawn{ + name = 'rocket-silo', + position = this.silo_position, + force = 'player', + create_build_effect_smoke = false, + move_stuck_players = true, + } + local _ = spawn{ + name = 'atomic-rocket', + position = this.silo_position, + target = target, + speed = 0.5, + } + + Task.set_timeout(8, set_game_lost) +end) + +--- Map expansion limited by biters +-- local terraforming = require 'map_gen.maps.danger_ores.modules.terraforming' +-- terraforming({ start_size = 8 * 32, min_pollution = 600, max_pollution = 24000, pollution_increment = 9 }) + +local rocket_launched = require 'map_gen.maps.danger_ores.modules.rocket_launched' +rocket_launched({ + recent_chunks_max = 10, + ticks_between_waves = 60 * 30, + enemy_factor = 5, + max_enemies_per_wave_per_chunk = 60, + extra_rockets = 20 +}) + +local restart_command = require 'map_gen.maps.danger_ores.modules.restart_command' +restart_command({ scenario_name = 'danger-ore-silo-defense' }) + +local container_dump = require 'map_gen.maps.danger_ores.modules.container_dump' +container_dump({ entity_name = 'coal' }) + +--- Already all landfill tiles +-- local concrete_on_landfill = require 'map_gen.maps.danger_ores.modules.concrete_on_landfill' +-- concrete_on_landfill({tile = 'blue-refined-concrete'}) + +local remove_non_ore_stacked_recipes = require 'map_gen.maps.danger_ores.modules.remove_non_ore_stacked_recipes' +remove_non_ore_stacked_recipes() + +-- require 'map_gen.maps.danger_ores.modules.biter_drops' +require 'map_gen.maps.danger_ores.modules.map_poll' +require 'map_gen.maps.danger_ores.modules.memory_storage_control' +require 'map_gen.maps.danger_ores.modules.evolution_control' + +local config = { + spawn_shape = b.translate(b.rectangle(48), 5, -5), + start_ore_shape = b.translate(b.rectangle(56), 5, -5), + no_resource_patch_shape = b.translate(b.rectangle(92), 5, -5), + spawn_tile = 'landfill', + main_ores = main_ores_config, + main_ores_shuffle_order = true, + main_ores_rotate = 30, + water_scale = 1 / 96, + water_threshold = 0.4, + deepwater_threshold = 0.45, + trees = trees, + trees_scale = 1 / 32, + trees_threshold = 0.4, + trees_chance = 0.875, + enemy = enemy, + enemy_factor = 10 / (768 * 32), + enemy_max_chance = 1 / 6, + enemy_scale_factor = 32, + enemy_radius = 80, + fish_spawn_rate = 0.05, + dense_patches_scale = 1 / 48, + dense_patches_threshold = 0.55, + dense_patches_multiplier = 25, +} + +return map(config) diff --git a/scenario_templates/danger-ore-silo-defense/map_selection.lua b/scenario_templates/danger-ore-silo-defense/map_selection.lua new file mode 100644 index 000000000..cce82b384 --- /dev/null +++ b/scenario_templates/danger-ore-silo-defense/map_selection.lua @@ -0,0 +1 @@ +return require 'map_gen.maps.danger_ores.presets.danger_ore_silo_defense' \ No newline at end of file