From 4cd397f815dad36631157a54660b4f5438262ec1 Mon Sep 17 00:00:00 2001 From: Oarcinae Date: Tue, 19 Nov 2024 20:59:25 -0500 Subject: [PATCH 1/2] Add basic support for Vulcanus secondary spawns. Have a workaround for demolishers that tracks them and destroys them if they are too close to the spawn. Can definitely be abused, but good enough for now. Fixed issue with joiners being teleported unexpectedly when rerolling a secondary spawn. Removed some old commented out code in utils. Tweak default danger radius to be smaller based on feedback. Fix fuild resources and sharing entities not moving when scaling spawn size. Added locale for welcome home ground text. --- control.lua | 10 ++ lib/config.lua | 9 +- lib/oarc_tests.lua | 9 +- lib/oarc_utils.lua | 244 -------------------------------- lib/planet_configs/nauvis.lua | 2 +- lib/planet_configs/vulcanus.lua | 70 +++++++++ lib/scaled_enemies.lua | 107 +++++++++++--- lib/separate_spawns.lua | 49 ++++--- lib/spawn_area_generation.lua | 53 ++----- locale/en/locale.cfg | 4 + 10 files changed, 218 insertions(+), 339 deletions(-) create mode 100644 lib/planet_configs/vulcanus.lua diff --git a/control.lua b/control.lua index 18fefd8..8553f76 100644 --- a/control.lua +++ b/control.lua @@ -237,6 +237,10 @@ script.on_event(defines.events.on_tick, function(event) RegrowthOnTick() end RegrowthForceRemovalOnTick() -- Allows for abandoned base cleanup without regrowth enabled. + + if storage.ocfg.gameplay.modified_enemy_spawning then + RemoveDemolishersInWarningZone(event) + end end) ---------------------------------------- @@ -343,6 +347,12 @@ script.on_event(defines.events.on_biter_base_built, function(event) end end) +script.on_event(defines.events.on_segment_entity_created, function(event) + if storage.ocfg.gameplay.modified_enemy_spawning then + TrackDemolishers(event) + end +end) + ---------------------------------------- -- On unit group finished gathering -- This is where I remove biter waves on offline players diff --git a/lib/config.lua b/lib/config.lua index 7b38f2c..0964210 100644 --- a/lib/config.lua +++ b/lib/config.lua @@ -17,6 +17,7 @@ require("lib/planet_configs/nauvis") require("lib/planet_configs/fulgora") +require("lib/planet_configs/vulcanus") ---@alias SpawnShapeChoice "circle" | "octagon" | "square" SPAWN_SHAPE_CHOICE_CIRCLE = "circle" @@ -363,10 +364,10 @@ OCFG = { starting_items = NAUVIS_STARTER_ITEMS, spawn_config = NAUVIS_SPAWN_CONFIG }, - -- ["vulcanus"] = { - -- starting_items = NAUVIS_STARTER_ITEMS, - -- spawn_config = NAUVIS_SPAWN_CONFIG - -- }, + ["vulcanus"] = { + starting_items = VULCANUS_STARTER_ITEMS, + spawn_config = VULCANUS_SPAWN_CONFIG + }, ["fulgora"] = { starting_items = FULGORA_STARTER_ITEMS, spawn_config = FULGORA_SPAWN_CONFIG diff --git a/lib/oarc_tests.lua b/lib/oarc_tests.lua index 91c40c7..8735e49 100644 --- a/lib/oarc_tests.lua +++ b/lib/oarc_tests.lua @@ -303,10 +303,13 @@ function RerollSpawn(player) -- Send them to the holding pen SafeTeleport(player, game.surfaces[HOLDING_PEN_SURFACE_NAME], {x=0,y=0}) - -- Queue joiners too! + -- Queue joiners too (if they are on this surface?) for _,joiner in pairs(old_spawn_point.joiners) do - QueuePlayerForSpawn(joiner, delayed_spawn) - SafeTeleport(game.players[joiner], game.surfaces[HOLDING_PEN_SURFACE_NAME], {x=0,y=0}) + local joiner_player = game.players[joiner] + if (joiner_player ~= nil) and joiner_player.surface.name == surface_name then + QueuePlayerForSpawn(joiner, delayed_spawn) + SafeTeleport(game.players[joiner], game.surfaces[HOLDING_PEN_SURFACE_NAME], {x=0,y=0}) + end end -- Announce diff --git a/lib/oarc_utils.lua b/lib/oarc_utils.lua index 313f57e..bc4d4f3 100644 --- a/lib/oarc_utils.lua +++ b/lib/oarc_utils.lua @@ -1181,204 +1181,6 @@ end -- end -- end --- -------------------------------------------------------------------------------- --- -- Gravestone soft mod. With my own modifications/improvements. --- -------------------------------------------------------------------------------- --- -- Return steel chest entity (or nil) --- function DropEmptySteelChest(player) --- local pos = player.surface.find_non_colliding_position("steel-chest", player.position, 15, 1) --- if not pos then --- return nil --- end --- local grave = player.surface.create_entity{name="steel-chest", position=pos, force="neutral"} --- return grave --- end - --- function DropGravestoneChests(player) - --- local grave --- local count = 0 - --- -- Make sure we save stuff we're holding in our hands. --- player.clean_cursor() - --- -- Loop through a players different inventories --- -- Put it all into a chest. --- -- If the chest is full, create a new chest. --- for i, id in ipairs{ --- defines.inventory.character_armor, --- defines.inventory.character_main, --- defines.inventory.character_guns, --- defines.inventory.character_ammo, --- defines.inventory.character_vehicle, --- defines.inventory.character_trash} do - --- local inv = player.get_inventory(id) - --- -- No idea how inv can be nil sometimes...? --- if (inv ~= nil) then --- if ((#inv > 0) and not inv.is_empty()) then --- for j = 1, #inv do --- if inv[j].valid_for_read then - --- -- Create a chest when counter is reset --- if (count == 0) then --- grave = DropEmptySteelChest(player) --- if (grave == nil) then --- -- player.print("Not able to place a chest nearby! Some items lost!") --- return --- end --- grave_inv = grave.get_inventory(defines.inventory.chest) --- end --- count = count + 1 - --- -- Copy the item stack into a chest slot. --- grave_inv[count].set_stack(inv[j]) - --- -- Reset counter when chest is full --- if (count == #grave_inv) then --- count = 0 --- end --- end --- end --- end - --- -- Clear the player inventory so we don't have duplicate items lying around. --- inv.clear() --- end --- end - --- if (grave ~= nil) then --- player.print("Successfully dropped your items into a chest! Go get them quick!") --- end --- end - --- -- Dump player items into a chest after the body expires. --- function DropGravestoneChestFromCorpse(corpse) --- if ((corpse == nil) or (corpse.character_corpse_player_index == nil)) then return end - --- local grave, grave_inv --- local count = 0 - --- local inv = corpse.get_inventory(defines.inventory.character_corpse) - --- -- No idea how inv can be nil sometimes...? --- if (inv ~= nil) then --- if ((#inv > 0) and not inv.is_empty()) then --- for j = 1, #inv do --- if inv[j].valid_for_read then - --- -- Create a chest when counter is reset --- if (count == 0) then --- grave = DropEmptySteelChest(corpse) --- if (grave == nil) then --- -- player.print("Not able to place a chest nearby! Some items lost!") --- return --- end --- grave_inv = grave.get_inventory(defines.inventory.chest) --- end --- count = count + 1 - --- -- Copy the item stack into a chest slot. --- grave_inv[count].set_stack(inv[j]) - --- -- Reset counter when chest is full --- if (count == #grave_inv) then --- count = 0 --- end --- end --- end --- end - --- -- Clear the player inventory so we don't have duplicate items lying around. --- -- inv.clear() --- end - --- if (grave ~= nil) and (game.players[corpse.character_corpse_player_index] ~= nil)then --- game.players[corpse.character_corpse_player_index].print("Your corpse got eaten by biters! They kindly dropped your items into a chest! Go get them quick!") --- end - --- end - --- -------------------------------------------------------------------------------- --- -- Item/Inventory stuff (used in autofill) --- -------------------------------------------------------------------------------- - --- -- Transfer Items Between Inventory --- -- Returns the number of items that were successfully transferred. --- -- Returns -1 if item not available. --- -- Returns -2 if can't place item into destInv (ERROR) --- function TransferItems(srcInv, destEntity, itemStack) --- -- Check if item is in srcInv --- if (srcInv.get_item_count(itemStack.name) == 0) then --- return -1 --- end - --- -- Check if can insert into destInv --- if (not destEntity.can_insert(itemStack)) then --- return -2 --- end - --- -- Insert items --- local itemsRemoved = srcInv.remove(itemStack) --- itemStack.count = itemsRemoved --- return destEntity.insert(itemStack) --- end - --- -- Attempts to transfer at least some of one type of item from an array of items. --- -- Use this to try transferring several items in order --- -- It returns once it successfully inserts at least some of one type. --- function TransferItemMultipleTypes(srcInv, destEntity, itemNameArray, itemCount) --- local ret = 0 --- for _,itemName in pairs(itemNameArray) do --- ret = TransferItems(srcInv, destEntity, {name=itemName, count=itemCount}) --- if (ret > 0) then --- return ret -- Return the value succesfully transferred --- end --- end --- return ret -- Return the last error code --- end - --- -- Autofills a turret with ammo --- function AutofillTurret(player, turret) --- local mainInv = player.get_main_inventory() --- if (mainInv == nil) then return end - --- -- Attempt to transfer some ammo --- local ret = TransferItemMultipleTypes(mainInv, turret, {"uranium-rounds-magazine", "piercing-rounds-magazine", "firearm-magazine"}, AUTOFILL_TURRET_AMMO_QUANTITY) - --- -- Check the result and print the right text to inform the user what happened. --- if (ret > 0) then --- -- Inserted ammo successfully --- -- FlyingText("Inserted ammo x" .. ret, turret.position, my_color_red, player.surface) --- elseif (ret == -1) then --- FlyingText("Out of ammo!", turret.position, my_color_red, player.surface) --- elseif (ret == -2) then --- FlyingText("Autofill ERROR! - Report this bug!", turret.position, my_color_red, player.surface) --- end --- end - --- -- Autofills a vehicle with fuel, bullets and shells where applicable --- function AutoFillVehicle(player, vehicle) --- local mainInv = player.get_main_inventory() --- if (mainInv == nil) then return end - --- -- Attempt to transfer some fuel --- if ((vehicle.name == "car") or (vehicle.name == "tank") or (vehicle.name == "locomotive")) then --- TransferItemMultipleTypes(mainInv, vehicle, {"nuclear-fuel", "rocket-fuel", "solid-fuel", "coal", "wood"}, 50) --- end - --- -- Attempt to transfer some ammo --- if ((vehicle.name == "car") or (vehicle.name == "tank")) then --- TransferItemMultipleTypes(mainInv, vehicle, {"uranium-rounds-magazine", "piercing-rounds-magazine", "firearm-magazine"}, 100) --- end - --- -- Attempt to transfer some tank shells --- if (vehicle.name == "tank") then --- TransferItemMultipleTypes(mainInv, vehicle, {"explosive-uranium-cannon-shell", "uranium-cannon-shell", "explosive-cannon-shell", "cannon-shell"}, 100) --- end --- end - -- -------------------------------------------------------------------------------- -- -- Resource patch and starting area generation -- -------------------------------------------------------------------------------- @@ -1752,35 +1554,6 @@ function PlaceFulgoranLightningAttractors(surface, position, count) end end --- -------------------------------------------------------------------------------- --- -- Holding pen for new players joining the map --- -------------------------------------------------------------------------------- --- function CreateWall(surface, pos) --- local wall = surface.create_entity({name="stone-wall", position=pos, force=MAIN_TEAM}) --- if wall then --- wall.destructible = false --- wall.minable = false --- end --- end - --- function CreateHoldingPen(surface, chunkArea) --- local radiusTiles = storage.ocfg.spawn_config.general.spawn_radius_tiles-10 --- if (((chunkArea.left_top.x >= -(radiusTiles+2*CHUNK_SIZE)) and (chunkArea.left_top.x <= (radiusTiles+2*CHUNK_SIZE))) and --- ((chunkArea.left_top.y >= -(radiusTiles+2*CHUNK_SIZE)) and (chunkArea.left_top.y <= (radiusTiles+2*CHUNK_SIZE)))) then - --- -- Remove stuff --- RemoveAliensInArea(surface, chunkArea) --- RemoveInArea(surface, chunkArea, "tree") --- RemoveInArea(surface, chunkArea, "resource") --- RemoveInArea(surface, chunkArea, "cliff") - --- CreateCropCircle(surface, {x=0,y=0}, chunkArea, radiusTiles, "landfill") --- CreateMoat(surface, {x=0,y=0}, chunkArea, radiusTiles, "water", false) --- CreateMoat(surface, {x=0,y=0}, chunkArea, radiusTiles+10, "out-of-map", false) --- CreateMoat(surface, {x=0,y=0}, chunkArea, 2, "out-of-map", false) --- end --- end - -- -------------------------------------------------------------------------------- -- -- EVENT SPECIFIC FUNCTIONS -- -------------------------------------------------------------------------------- @@ -1802,20 +1575,3 @@ end -- -- If you care to, you can remove all fish with the Undecorator option here: -- -- RemoveFish(surface, chunkArea) -- end - --- -- Autofill softmod --- function Autofill(event) --- local player = game.players[event.player_index] --- local eventEntity = event.created_entity - --- -- Make sure player isn't dead? --- if (player.character == nil) then return end - --- if (eventEntity.name == "gun-turret") then --- AutofillTurret(player, eventEntity) --- end - --- if ((eventEntity.name == "car") or (eventEntity.name == "tank") or (eventEntity.name == "locomotive")) then --- AutoFillVehicle(player, eventEntity) --- end --- end diff --git a/lib/planet_configs/nauvis.lua b/lib/planet_configs/nauvis.lua index eb7f055..12effc4 100644 --- a/lib/planet_configs/nauvis.lua +++ b/lib/planet_configs/nauvis.lua @@ -84,7 +84,7 @@ NAUVIS_SPAWN_CONFIG = -- Danger area has slightly reduced aliens -- This is the radius in chunks of danger area. - danger_radius = 32, + danger_radius = 24, -- 1 : X (spawners alive : spawners destroyed) in this area danger_reduction = 5, diff --git a/lib/planet_configs/vulcanus.lua b/lib/planet_configs/vulcanus.lua new file mode 100644 index 0000000..4f5356f --- /dev/null +++ b/lib/planet_configs/vulcanus.lua @@ -0,0 +1,70 @@ +-- This config is used as the default config for the planet vulcanus. + +---@type OarcConfigStartingItems +VULCANUS_STARTER_ITEMS = table.deepcopy(NO_STARTER_ITEMS) + +---@type OarcConfigSpawn +VULCANUS_SPAWN_CONFIG = table.deepcopy(NAUVIS_SPAWN_CONFIG) +VULCANUS_SPAWN_CONFIG.fill_tile = "volcanic-ash-flats" +VULCANUS_SPAWN_CONFIG.liquid_tile = "lava" +VULCANUS_SPAWN_CONFIG.tree_entity = "ashland-lichen-tree" +VULCANUS_SPAWN_CONFIG.random_entities = { + {name = "vulcanus-chimney", count = 10}, + {name = "vulcanus-chimney-truncated", count = 10}, + {name = "huge-volcanic-rock", count = 10}, + {name = "big-volcanic-rock", count = 10}, + {name = "ashland-lichen-tree-flaming", count = 5}, + {name = "ashland-lichen-tree", count = 5}, +} + +-- These don't matter in the current implementation. +-- VULCANUS_SPAWN_CONFIG.safe_area.warn_reduction = 0 +-- VULCANUS_SPAWN_CONFIG.safe_area.danger_reduction = 0 + +-- Feels like more space might be helpful on Vulcanus? +VULCANUS_SPAWN_CONFIG.radius_modifier = 1.2 + +VULCANUS_SPAWN_CONFIG.solid_resources = +{ + ["coal"] = { + amount = 2000, + size = 25, + + -- These are only used if not using automatic placing. + x_offset = -29, + y_offset = 16 + }, + ["calcite"] = { + amount = 1000, + size = 25, + + -- These are only used if not using automatic placing. + x_offset = -28, + y_offset = -3 + }, + ["tungsten-ore"] = { + amount = 250, + size = 25, + + -- These are only used if not using automatic placing. + x_offset = -28, + y_offset = -3 + } +} + +VULCANUS_SPAWN_CONFIG.fluid_resources = { + ["sulfuric-acid-geyser"] = + { + num_patches = 4, + amount = 9000000, + spacing = 6, -- Spacing between each patch, only used for automatic placing. + + -- These are only used if not using automatic placing. + -- Starting position offset (relative to bottom/south of spawn area) + x_offset_start = -3, + y_offset_start = -10, + -- Additional position offsets for each new oil patch (relative to previous oil patch) + x_offset_next = 6, + y_offset_next = 0 + } +} \ No newline at end of file diff --git a/lib/scaled_enemies.lua b/lib/scaled_enemies.lua index 24593cb..c1db06a 100644 --- a/lib/scaled_enemies.lua +++ b/lib/scaled_enemies.lua @@ -8,7 +8,8 @@ ENEMY_FORCES_NAMES = { "enemy" } ENEMY_FORCES_NAMES_INCL_NEUTRAL = { "enemy", "neutral" } -ENEMY_BUILT_TYPES = { "biter-spawner", "spitter-spawner", "small-worm-turret", "medium-worm-turret", "big-worm-turret", "behemoth-worm-turret" } +ENEMY_BUILT_TYPES = { "biter-spawner", "spitter-spawner", "small-worm-turret", "medium-worm-turret", "big-worm-turret", + "behemoth-worm-turret" } ---Downgrades worms based on distance from origin and near/far spawn distances. @@ -16,18 +17,17 @@ ENEMY_BUILT_TYPES = { "biter-spawner", "spitter-spawner", "small-worm-turret", " ---@param event EventData.on_chunk_generated ---@return nil function DowngradeWormsDistanceBasedOnChunkGenerate(event) - ---@type OarcConfigGameplaySettings local gameplay = storage.ocfg.gameplay if (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.near_spawn_distance * CHUNK_SIZE)) then DowngradeWormsInArea(event.surface, event.area, 50, 100, 100) -- 50% small, 50% medium elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE)) then - DowngradeWormsInArea(event.surface, event.area, 25, 50, 95) -- 25% small, 25% medium, 45% big, 5% behemoth + DowngradeWormsInArea(event.surface, event.area, 25, 50, 95) -- 25% small, 25% medium, 45% big, 5% behemoth elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE * 1.5)) then - DowngradeWormsInArea(event.surface, event.area, 0, 40, 85) -- 40% medium, 45% big, 15% behemoth + DowngradeWormsInArea(event.surface, event.area, 0, 40, 85) -- 40% medium, 45% big, 15% behemoth else - DowngradeWormsInArea(event.surface, event.area, 0, 20, 50) -- 20% medium, 30% big, 50% behemoth + DowngradeWormsInArea(event.surface, event.area, 0, 20, 50) -- 20% medium, 30% big, 50% behemoth end end @@ -35,7 +35,6 @@ end ---@param event EventData.on_chunk_generated ---@return nil function DowngradeAndReduceEnemiesOnChunkGenerate(event) - local surface = event.surface local chunk_area = event.area @@ -48,25 +47,33 @@ function DowngradeAndReduceEnemiesOnChunkGenerate(event) y = chunk_area.left_top.y + (CHUNK_SIZE / 2) } + -- TODO: Change this lookup to be done once during surface init. + local nauvis_enemies = surface.map_gen_settings.autoplace_controls["enemy-base"] ~= nil + local gleba_enemies = surface.map_gen_settings.autoplace_controls["gleba_enemy_base"] ~= nil + -- local vulcanus_enemies = surface.map_gen_settings.territory_settings ~= nil + -- Make chunks near a spawn safe by removing enemies if (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.safe_radius * CHUNK_SIZE) then - RemoveEnemiesInArea(surface, chunk_area) + if nauvis_enemies or gleba_enemies then + RemoveEnemiesInArea(surface, chunk_area) + end -- Create a warning area with heavily reduced enemies elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.warn_radius * CHUNK_SIZE) then - - -- TODO: Refactor this to reduce calls to find_entities_filtered! - ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.warn_reduction) - RemoveWormsInArea(surface, chunk_area, false, true, true, true) -- remove all non-small worms. + if nauvis_enemies then + -- TODO: Refactor this to reduce calls to find_entities_filtered! + ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.warn_reduction) + RemoveWormsInArea(surface, chunk_area, false, true, true, true) -- remove all non-small worms. + end -- Create a third area with moderately reduced enemies elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.danger_radius * CHUNK_SIZE) then - - -- TODO: Refactor this to reduce calls to find_entities_filtered! - ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.danger_reduction) - RemoveWormsInArea(surface, chunk_area, false, false, true, true) -- remove all huge/behemoth worms. + if nauvis_enemies then + -- TODO: Refactor this to reduce calls to find_entities_filtered! + ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.danger_reduction) + RemoveWormsInArea(surface, chunk_area, false, false, true, true) -- remove all huge/behemoth worms. + end end - end ---Convenient way to remove aliens, just provide an area @@ -115,21 +122,21 @@ function DowngradeWormsInArea(surface, area, small_percent, medium_percent, big_ entity.destroy() surface.create_entity { name = "small-worm-turret", position = worm_pos, force = force } - -- ELSE If number is less than medium percent, change to medium + -- ELSE If number is less than medium percent, change to medium elseif (rand_percent <= medium_percent) then if (not (worm_name == "medium-worm-turret")) then entity.destroy() surface.create_entity { name = "medium-worm-turret", position = worm_pos, force = force } end - -- ELSE If number is less than big percent, change to big + -- ELSE If number is less than big percent, change to big elseif (rand_percent <= big_percent) then if (not (worm_name == "big-worm-turret")) then entity.destroy() surface.create_entity { name = "big-worm-turret", position = worm_pos, force = force } end - -- ELSE ignore it. + -- ELSE ignore it. end end end @@ -198,7 +205,6 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event) elseif (util.distance(enemy_pos, closest_spawn.position) < storage.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius * CHUNK_SIZE) then if ((enemy_name == "biter-spawner") or (enemy_name == "spitter-spawner")) then -- Do nothing. - elseif ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter") or (enemy_name == "medium-biter")) then event.entity.destroy() surface.create_entity { name = "small-biter", position = enemy_pos, force = game.forces.enemy } @@ -237,7 +243,6 @@ end ---@param event EventData.on_entity_damaged ---@return nil function ApplySpawnerDamageScaling(event) - -- Check if force is a player force if (event.force == nil) then log("Entity damaged with no force") @@ -253,7 +258,8 @@ function ApplySpawnerDamageScaling(event) -- Get distance to spawn_position local distance = util.distance(spawn.position, entity.position) - local max_danger_distance = storage.ocfg.surfaces_config[surface_name].spawn_config.safe_area.danger_radius * CHUNK_SIZE + local max_danger_distance = storage.ocfg.surfaces_config[surface_name].spawn_config.safe_area.danger_radius * + CHUNK_SIZE -- If distance is greater than the danger radius, ignore. if (distance > max_danger_distance) then return end @@ -272,4 +278,59 @@ function ApplySpawnerDamageScaling(event) -- If evo is 1, and distance to spawn is 0, then damage_modifier is 10 -- If evo is 1, and distance to spawn is 1, then damage_modifier is 1 -- If evo is 0, then damage_modifier is 1 -end \ No newline at end of file +end + +---@param event EventData.on_segment_entity_created +---@return nil +function TrackDemolishers(event) + local entity = event.entity --[[@as LuaEntity]] + if (entity.type ~= "segmented-unit") or (not entity.valid) then return end + if (entity.name == "big-demolisher") or (entity.name == "medium-demolisher") or (entity.name == "small-demolisher") then + if storage.demolisher_tracker == nil then + storage.demolisher_tracker = {} + storage.demolisher_tracker.demolishers = {} + end + + if storage.demolisher_tracker.demolishers[entity.unit_number] == nil then + storage.demolisher_tracker.demolishers[entity.unit_number] = entity + end + else + log("Unexpected segmented-unit entity type spawned: " .. entity.name) + end +end + +---This function checks where demolishers are every tick and if it is inside the warning zone it gets removed. +---TODO: This is a TEMPORARY WORK AROUND until there is a better demolisher and territory API. +-- Shouldn't be too bad since we only check a single one per tick. +---@param event EventData.on_tick +---@return nil +function RemoveDemolishersInWarningZone(event) + if storage.demolisher_tracker == nil then return end + + --TODO: Figure out lua type annotation?! + local index, next_demolisher = next(storage.demolisher_tracker.demolishers, storage.demolisher_tracker.index) + + if next_demolisher == nil then + storage.demolisher_tracker.index = nil + return + end + + if next_demolisher.valid then + local closest_spawn = GetClosestUniqueSpawn(next_demolisher.surface.name, next_demolisher.position) + if (closest_spawn == nil) then return end + + local distance = util.distance(next_demolisher.position, closest_spawn.position) + local safe_radius_tiles = (storage.ocfg.surfaces_config[next_demolisher.surface.name].spawn_config.safe_area.safe_radius) * CHUNK_SIZE -- TODO: Should probably cache this on first init. + + if (distance < safe_radius_tiles) then + next_demolisher.destroy() + storage.demolisher_tracker.demolishers[index] = nil + storage.demolisher_tracker.index = nil + else + storage.demolisher_tracker.index = index + end + else + storage.demolisher_tracker.demolishers[index] = nil + storage.demolisher_tracker.index = nil + end +end diff --git a/lib/separate_spawns.lua b/lib/separate_spawns.lua index e46cab1..09673c1 100644 --- a/lib/separate_spawns.lua +++ b/lib/separate_spawns.lua @@ -401,12 +401,14 @@ function GenerateStartingResources(surface, position) local size_mod = storage.ocfg.resource_placement.size_multiplier local amount_mod = storage.ocfg.resource_placement.amount_multiplier + local spawn_general = storage.ocfg.spawn_general + -- Generate all resource tile patches -- Generate resources in random order around the spawn point. if storage.ocfg.resource_placement.enabled then - if (storage.ocfg.spawn_general.shape == SPAWN_SHAPE_CHOICE_CIRCLE) or (storage.ocfg.spawn_general.shape == SPAWN_SHAPE_CHOICE_OCTAGON) then + if (spawn_general.shape == SPAWN_SHAPE_CHOICE_CIRCLE) or (spawn_general.shape == SPAWN_SHAPE_CHOICE_OCTAGON) then PlaceResourcesInSemiCircle(surface, position, size_mod, amount_mod) - elseif (storage.ocfg.spawn_general.shape == SPAWN_SHAPE_CHOICE_SQUARE) then + elseif (spawn_general.shape == SPAWN_SHAPE_CHOICE_SQUARE) then PlaceResourcesInSquare(surface, position, size_mod, amount_mod) end @@ -418,12 +420,15 @@ function GenerateStartingResources(surface, position) end end + local spawn_config = storage.ocfg.surfaces_config[surface.name].spawn_config + local radius = spawn_general.spawn_radius_tiles * spawn_config.radius_modifier + -- Generate special fluid resource patches (oil) -- Autoplace using spacing and vertical offset. -- Reference position is the bottom of the spawn area. if storage.ocfg.resource_placement.enabled then local y_offset = storage.ocfg.resource_placement.distance_to_edge - local fluid_ref_pos = { x = position.x, y = position.y + storage.ocfg.spawn_general.spawn_radius_tiles - y_offset } + local fluid_ref_pos = { x = position.x, y = position.y + radius - y_offset } for r_name, r_data in pairs(storage.ocfg.surfaces_config[surface.name].spawn_config.fluid_resources --[[@as table]]) do @@ -445,7 +450,7 @@ function GenerateStartingResources(surface, position) -- This places using specified offsets if auto placement is disabled. else - local fluid_ref_pos = { x = position.x, y = position.y + storage.ocfg.spawn_general.spawn_radius_tiles } + local fluid_ref_pos = { x = position.x, y = position.y + radius } for r_name, r_data in pairs(storage.ocfg.surfaces_config[surface.name].spawn_config.fluid_resources --[[@as table]]) do local oil_patch_x = fluid_ref_pos.x + r_data.x_offset_start local oil_patch_y = fluid_ref_pos.y + r_data.y_offset_start @@ -581,9 +586,11 @@ function GenerateFinalSpawnPieces(delayed_spawn) -- Create the spawn resources here GenerateStartingResources(surface, delayed_spawn.position) + local radius = storage.ocfg.spawn_general.spawn_radius_tiles * spawn_config.radius_modifier + -- Reference position is RIGHT (WEST) of the spawn area. local sharing_ref_pos = { - x = delayed_spawn.position.x + storage.ocfg.spawn_general.spawn_radius_tiles, + x = delayed_spawn.position.x + radius, y = delayed_spawn.position.y } @@ -624,11 +631,6 @@ function GenerateFinalSpawnPieces(delayed_spawn) -- Render some welcoming text... DisplayWelcomeGroundTextAtSpawn(delayed_spawn.surface_name, delayed_spawn.position) - -- -- Chart the area. - -- local player = game.players[delayed_spawn.host_name] - -- ChartArea(player.force, delayed_spawn.position, math.ceil(storage.ocfg.spawn_general.spawn_radius_tiles / CHUNK_SIZE), - -- surface) - -- Trigger the event that the spawn was created. script.raise_event("oarc-mod-on-spawn-created", {spawn_data = storage.unique_spawns[delayed_spawn.surface_name][delayed_spawn.host_name]}) end @@ -674,7 +676,7 @@ function DisplayWelcomeGroundTextAtSpawn(surface, position) -- Render some welcoming text... local tcolor = { 0.9, 0.7, 0.3, 0.8 } local ttl = 2000 - local render_object_1 = rendering.draw_text { text = "Welcome", + local render_object_1 = rendering.draw_text { text = {"oarc-spawn-ground-text-welcome"}, surface = surface, target = { x = position.x - 21, y = position.y - 15 }, color = tcolor, @@ -686,7 +688,7 @@ function DisplayWelcomeGroundTextAtSpawn(surface, position) -- alignment=center, scale_with_zoom = false, only_in_alt_mode = false } - local render_object_2 = rendering.draw_text { text = "Home", + local render_object_2 = rendering.draw_text { text = {"oarc-spawn-ground-text-home"}, surface = surface, target = { x = position.x - 14, y = position.y - 5 }, color = tcolor, @@ -1424,6 +1426,12 @@ end ---@return nil function QueuePlayerForSpawn(player_name, delayed_spawn) + -- Send them to the holding pen if they are not already there. + local player = game.players[player_name] + if (player.surface.name ~= HOLDING_PEN_SURFACE_NAME) then + SafeTeleport(player, game.surfaces[HOLDING_PEN_SURFACE_NAME], {x=0,y=0}) + end + SetPlayerRespawn(player_name, delayed_spawn.surface_name, delayed_spawn.position, true) game.players[player_name].print({ "oarc-generating-spawn-please-wait" }) @@ -1435,7 +1443,7 @@ function QueuePlayerForSpawn(player_name, delayed_spawn) DisplayPleaseWaitForSpawnDialog(game.players[player_name], seconds_remaining, game.surfaces[delayed_spawn.surface_name], delayed_spawn.position) table.insert(delayed_spawn.waiting_players, player_name) - log("QueuePlayerForSpawn - " .. player_name .. " - " .. delayed_spawn.host_name) + log("QueuePlayerForSpawn - Player:" .. player_name .. " - Host:" .. delayed_spawn.host_name) end ---Sets the custom spawn point for a player. They can have one per surface. @@ -1515,10 +1523,11 @@ end ---@return OarcDelayedSpawn function QueueNewSpawnGeneration(unique_spawn) - -- Add a 1 chunk buffer to be safe - local total_spawn_width = storage.ocfg.spawn_general.spawn_radius_tiles + - storage.ocfg.spawn_general.moat_width_tiles - local spawn_chunk_radius = math.ceil(total_spawn_width / CHUNK_SIZE) + 1 + local spawn_config = storage.ocfg.surfaces_config[unique_spawn.surface_name].spawn_config + local radius = storage.ocfg.spawn_general.spawn_radius_tiles * spawn_config.radius_modifier + + local total_spawn_width = radius + storage.ocfg.spawn_general.moat_width_tiles + local spawn_chunk_radius = math.ceil(total_spawn_width / CHUNK_SIZE) + 1 -- Add a 1 chunk buffer to be safe -- This is just a rough estimate of worst case chunk generation time. -- If we hit this timeout, usually it means something has gone wrong. @@ -1619,11 +1628,11 @@ function SecondarySpawn(player, surface_name) end end - -- Send them to the holding pen - SafeTeleport(player, game.surfaces[HOLDING_PEN_SURFACE_NAME], {x=0,y=0}) - -- Announce SendBroadcastMsg({"", { "oarc-player-new-secondary", player_name, surface_name }, " ", GetGPStext(surface_name, spawn_position)}) + + -- Tell the player about the reroll command: + player.print({ "oarc-reroll-spawn-command" }) end -- Check a table to see if there are any players waiting to spawn diff --git a/lib/spawn_area_generation.lua b/lib/spawn_area_generation.lua index 980257e..7e70e1a 100644 --- a/lib/spawn_area_generation.lua +++ b/lib/spawn_area_generation.lua @@ -20,12 +20,12 @@ function CreateCropCircle(surface, unique_spawn, chunk_area) fill_tile = spawn_config.fill_tile end - local moat = unique_spawn.moat - local bridge = storage.ocfg.gameplay.enable_moat_bridging - local liquid_tile = spawn_config.liquid_tile local fish_enabled = (liquid_tile == "water") + local moat = unique_spawn.moat + local bridge = storage.ocfg.gameplay.enable_moat_bridging or liquid_tile == "lava" + local moat_width = storage.ocfg.spawn_general.moat_width_tiles local tree_width = storage.ocfg.spawn_general.tree_width_tiles -------------------------------------------- @@ -105,12 +105,12 @@ function CreateCropOctagon(surface, unique_spawn, chunk_area) fill_tile = spawn_config.fill_tile end - local moat = unique_spawn.moat - local bridge = storage.ocfg.gameplay.enable_moat_bridging - local liquid_tile = spawn_config.liquid_tile local fish_enabled = (liquid_tile == "water") + local moat = unique_spawn.moat + local bridge = storage.ocfg.gameplay.enable_moat_bridging or liquid_tile == "lava" + local moat_width = storage.ocfg.spawn_general.moat_width_tiles local tree_width = storage.ocfg.spawn_general.tree_width_tiles -------------------------------------------- @@ -191,12 +191,12 @@ function CreateCropSquare(surface, unique_spawn, chunk_area) fill_tile = spawn_config.fill_tile end - local moat = unique_spawn.moat - local bridge = storage.ocfg.gameplay.enable_moat_bridging - local liquid_tile = spawn_config.liquid_tile local fish_enabled = (liquid_tile == "water") + local moat = unique_spawn.moat + local bridge = storage.ocfg.gameplay.enable_moat_bridging or liquid_tile == "lava" + local moat_width = storage.ocfg.spawn_general.moat_width_tiles local tree_width = storage.ocfg.spawn_general.tree_width_tiles -------------------------------------------- @@ -255,41 +255,6 @@ function CreateCropSquare(surface, unique_spawn, chunk_area) end end ----Add a circle of water ----@param surface LuaSurface ----@param centerPos MapPosition ----@param chunkArea BoundingBox ----@param tileRadius number ----@param moatTile string ----@param bridge boolean ----@param shape SpawnShapeChoice ----@return nil -function CreateMoat(surface, centerPos, chunkArea, tileRadius, moatTile, bridge, shape) - local tileRadSqr = tileRadius ^ 2 - - local tiles = {} - for i = chunkArea.left_top.x, chunkArea.right_bottom.x, 1 do - for j = chunkArea.left_top.y, chunkArea.right_bottom.y, 1 do - if (bridge and ((j == centerPos.y - 1) or (j == centerPos.y) or (j == centerPos.y + 1))) then - -- This will leave the tiles "as is" on the left and right of the spawn which has the effect of creating - -- land connections if the spawn is on or near land. - else - -- This ( X^2 + Y^2 ) is used to calculate if something - -- is inside a circle area. - local distVar = math.floor((centerPos.x - i) ^ 2 + (centerPos.y - j) ^ 2) - - -- Create a circle of water - if ((distVar < tileRadSqr + (1500 * storage.ocfg.spawn_general.moat_width_tiles)) and - (distVar > tileRadSqr)) then - table.insert(tiles, { name = moatTile, position = { i, j } }) - end - end - end - end - - surface.set_tiles(tiles) -end - -- Create a horizontal line of tiles (typically used for water) ---@param surface LuaSurface ---@param leftPos TilePosition diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index 5990ee6..da3bb11 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -401,6 +401,10 @@ oarc-command-dude-wheres-my-cargo-pod=This will attempt to teleport any cargo-po oarc-command-reroll-spawn=This will reroll your spawn (on whichever surface you are currently on). This is useful if you are stuck in a bad spot or want to try a different location. [color=red]Spam this at your own peril![/color] oarc-no-reroll-buddy-spawn=I haven't implemethed spawn rerolls for buddy spawns yet! You can try leaving and rejoining the game to get a new buddy spawn. oarc-spawn-rerolled=__1__ has rerolled their spawn point! +oarc-reroll-spawn-command=If your spawn has issues, you can reroll it with /oarc-reroll-spawn! Please don't abuse this. + +oarc-spawn-ground-text-welcome=Welcome +oarc-spawn-ground-text-home=Home [entity-name] oarc-linked-chest=OARC Linked Chest From de55c774f96bcf5c0e1ea04410a8eb2071824bd2 Mon Sep 17 00:00:00 2001 From: Oarcinae Date: Tue, 19 Nov 2024 21:34:15 -0500 Subject: [PATCH 2/2] Add vulcanus config migration. Disable moat on Vulcanus to avoid spawn locking. It also serves no real defensive purpose. Revert radius mod to 1. --- lib/planet_configs/vulcanus.lua | 2 +- lib/separate_spawns.lua | 2 +- lib/spawn_area_generation.lua | 14 ++++++++------ migrations/oarc-mod-v2.1.13.lua | 13 +++++++++++++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/planet_configs/vulcanus.lua b/lib/planet_configs/vulcanus.lua index 4f5356f..444e767 100644 --- a/lib/planet_configs/vulcanus.lua +++ b/lib/planet_configs/vulcanus.lua @@ -22,7 +22,7 @@ VULCANUS_SPAWN_CONFIG.random_entities = { -- VULCANUS_SPAWN_CONFIG.safe_area.danger_reduction = 0 -- Feels like more space might be helpful on Vulcanus? -VULCANUS_SPAWN_CONFIG.radius_modifier = 1.2 +VULCANUS_SPAWN_CONFIG.radius_modifier = 1 VULCANUS_SPAWN_CONFIG.solid_resources = { diff --git a/lib/separate_spawns.lua b/lib/separate_spawns.lua index 09673c1..7e34d7f 100644 --- a/lib/separate_spawns.lua +++ b/lib/separate_spawns.lua @@ -579,7 +579,7 @@ function GenerateFinalSpawnPieces(delayed_spawn) game.surfaces[delayed_spawn.surface_name]) -- Generate water strip only if we don't have a moat. - if (not delayed_spawn.moat) then + if (not delayed_spawn.moat or spawn_config.liquid_tile == "lava") then GenerateStartingLiquedStrip(delayed_spawn, spawn_config, surface) end diff --git a/lib/spawn_area_generation.lua b/lib/spawn_area_generation.lua index 7e70e1a..50d720c 100644 --- a/lib/spawn_area_generation.lua +++ b/lib/spawn_area_generation.lua @@ -8,6 +8,8 @@ ---@param chunk_area BoundingBox ---@return nil function CreateCropCircle(surface, unique_spawn, chunk_area) + + --- Repeated code... TODO: Extract into a function -------------------------------------------- local spawn_general = storage.ocfg.spawn_general local spawn_config = storage.ocfg.surfaces_config[surface.name].spawn_config @@ -23,8 +25,8 @@ function CreateCropCircle(surface, unique_spawn, chunk_area) local liquid_tile = spawn_config.liquid_tile local fish_enabled = (liquid_tile == "water") - local moat = unique_spawn.moat - local bridge = storage.ocfg.gameplay.enable_moat_bridging or liquid_tile == "lava" + local moat = unique_spawn.moat and liquid_tile ~= "lava" + local bridge = storage.ocfg.gameplay.enable_moat_bridging local moat_width = storage.ocfg.spawn_general.moat_width_tiles local tree_width = storage.ocfg.spawn_general.tree_width_tiles @@ -108,8 +110,8 @@ function CreateCropOctagon(surface, unique_spawn, chunk_area) local liquid_tile = spawn_config.liquid_tile local fish_enabled = (liquid_tile == "water") - local moat = unique_spawn.moat - local bridge = storage.ocfg.gameplay.enable_moat_bridging or liquid_tile == "lava" + local moat = unique_spawn.moat and liquid_tile ~= "lava" + local bridge = storage.ocfg.gameplay.enable_moat_bridging local moat_width = storage.ocfg.spawn_general.moat_width_tiles local tree_width = storage.ocfg.spawn_general.tree_width_tiles @@ -194,8 +196,8 @@ function CreateCropSquare(surface, unique_spawn, chunk_area) local liquid_tile = spawn_config.liquid_tile local fish_enabled = (liquid_tile == "water") - local moat = unique_spawn.moat - local bridge = storage.ocfg.gameplay.enable_moat_bridging or liquid_tile == "lava" + local moat = unique_spawn.moat and liquid_tile ~= "lava" + local bridge = storage.ocfg.gameplay.enable_moat_bridging local moat_width = storage.ocfg.spawn_general.moat_width_tiles local tree_width = storage.ocfg.spawn_general.tree_width_tiles diff --git a/migrations/oarc-mod-v2.1.13.lua b/migrations/oarc-mod-v2.1.13.lua index eccdf3e..e9469d5 100644 --- a/migrations/oarc-mod-v2.1.13.lua +++ b/migrations/oarc-mod-v2.1.13.lua @@ -15,3 +15,16 @@ for _,surface in pairs(game.surfaces) do log("Applying migration for V2.1.13: Marked center of "..surface.name.." safe from regrowth.") end end + +-- Make sure vulcanus config is set up if it is missing. +if script.active_mods["space-age"] ~= nil then + if (storage.ocfg.surfaces_config["vulcanus"] == nil) or + (storage.ocfg.surfaces_config["vulcanus"].spawn_config.liquid_tile ~= "lava") then + storage.ocfg.surfaces_config["vulcanus"] = + { + spawn_config = VULCANUS_SPAWN_CONFIG, + starting_items = VULCANUS_STARTER_ITEMS + } + log("Updating vulcanus config with new spawn_config and starting_items.") + end +end \ No newline at end of file