From a52e9f710c27aa4f7ea527f538d18fd630c29382 Mon Sep 17 00:00:00 2001 From: Lynn Date: Wed, 21 Nov 2018 15:26:04 +0100 Subject: [PATCH] Improved biter spawning algorithm (#408) * Improved biter spawning algorithm * HAIL HYDRA, biter hell --- map_gen/Diggy/Config.lua | 53 ++++-- map_gen/Diggy/Feature/AlienSpawner.lua | 186 ++++++++++++++++++++-- map_gen/Diggy/Feature/ArtefactHunting.lua | 20 +-- map_gen/Diggy/Feature/DiggyHole.lua | 10 +- map_gen/Diggy/Template.lua | 33 ---- 5 files changed, 225 insertions(+), 77 deletions(-) diff --git a/map_gen/Diggy/Config.lua b/map_gen/Diggy/Config.lua index 6e9e8801..49b49387 100644 --- a/map_gen/Diggy/Config.lua +++ b/map_gen/Diggy/Config.lua @@ -36,7 +36,7 @@ local Config = { character_inventory_slots_bonus = 1000, character_running_speed_modifier = 2, starting_items = { - {name = 'modular-armor', count = 1}, + {name = 'power-armor-mk2', count = 1}, {name = 'submachine-gun', count = 1}, {name = 'uranium-rounds-magazine', count = 200}, }, @@ -113,22 +113,28 @@ local Config = { minimal_treasure_chest_distance = 25, -- chances to receive a coin when mining - mining_artefact_chance = 0.10, - mining_artefact_amount = {min = 1, max = 4}, + mining_coin_chance = 0.10, + mining_coin_amount = {min = 1, max = 4}, -- lets you set the coin modifiers for aliens -- the modifier value increases the upper random limit that biters can drop alien_coin_modifiers = { - ['small-biter'] = 1, - ['small-spitter'] = 1, - ['medium-biter'] = 2, - ['medium-spitter'] = 2, - ['big-biter'] = 4, - ['big-spitter'] = 4, - ['behemoth-biter'] = 6, - ['behemoth-spitter'] = 6, + ['small-biter'] = 2, + ['small-spitter'] = 2, + ['small-worm'] = 2, + ['medium-biter'] = 3, + ['medium-spitter'] = 3, + ['medium-worm'] = 3, + ['big-biter'] = 5, + ['big-spitter'] = 5, + ['big-worm'] = 5, + ['behemoth-biter'] = 7, + ['behemoth-spitter'] = 7, }, + -- chance of aliens dropping coins between 0 and 1, where 1 is 100% + alien_coin_drop_chance = 0.30, + -- shows the chest locations, only use when debugging display_chest_locations = false, @@ -285,7 +291,30 @@ local Config = { alien_minimum_distance = 40, -- chance of spawning aliens when mining - alien_probability = 0.07, + alien_probability = 0.05, + + -- spawns the following units when they die. To disable change it to: + --hail_hydra = nil, + -- any non-rounded number will turn into a chance to spawn an additional alien + -- example: 2.5 would spawn 2 for sure and 50% chance to spawn one additionally + hail_hydra = { + -- spitters + ['small-spitter'] = {['small-worm-turret'] = 0.4}, + ['medium-spitter'] = {['medium-worm-turret'] = 0.4}, + ['big-spitter'] = {['big-worm-turret'] = 0.4}, + ['behemoth-spitter'] = {['big-worm-turret'] = 0.6}, + + -- biters + ['medium-biter'] = {['small-biter'] = 1.7}, + ['big-biter'] = {['medium-biter'] = 1.7}, + ['behemoth-biter'] = {['big-biter'] = 1.7}, + ['behemoth-biter'] = {['big-biter'] = 1.7}, + + -- worms + ['small-worm-turret'] = {['small-biter'] = 2.5}, + ['medium-worm-turret'] = {['small-biter'] = 2.5, ['medium-biter'] = 0.5}, + ['big-worm-turret'] = {['small-biter'] = 3.5, ['medium-biter'] = 1, ['big-biter'] = 0.5}, + }, }, -- controls the market and buffs diff --git a/map_gen/Diggy/Feature/AlienSpawner.lua b/map_gen/Diggy/Feature/AlienSpawner.lua index 7c49d54a..50a04e3b 100644 --- a/map_gen/Diggy/Feature/AlienSpawner.lua +++ b/map_gen/Diggy/Feature/AlienSpawner.lua @@ -4,31 +4,136 @@ -- dependencies local Event = require 'utils.event' +local Global = require 'utils.global' +local Token = require 'utils.global_token' +local Task = require 'utils.task' local AlienEvolutionProgress = require 'map_gen.Diggy.AlienEvolutionProgress' local Debug = require 'map_gen.Diggy.Debug' local Template = require 'map_gen.Diggy.Template' -local insert = table.insert local random = math.random +local floor = math.floor +local ceil = math.ceil +local insert = table.insert +local raise_event = script.raise_event -- this local AlienSpawner = {} -local function spawn_alien(surface, x, y) - local enemy_force = game.forces.enemy - local enemy_force_evolution = enemy_force.evolution_factor +local alien_size_chart = {} + +Global.register_init({ + alien_size_chart = alien_size_chart, +}, function(tbl) + for name, prototype in pairs(game.entity_prototypes) do + if prototype.type == 'unit' and prototype.subgroup.name == 'enemies' then + tbl.alien_size_chart[name] = { + name = name, + collision_box = prototype.collision_box + } + end + end +end, function(tbl) + alien_size_chart = tbl.alien_size_chart +end) + +---Triggers mining at the collision_box of the alien, to free it +local do_alien_mining = Token.register(function(params) + local surface = params.surface + local create_entity = surface.create_entity + local find_non_colliding_position = surface.find_non_colliding_position + local find_entities_filtered = surface.find_entities_filtered + + for _, area in ipairs(params.positions_to_mine) do + local rocks = find_entities_filtered({area = area, name = {'sand-rock-big', 'rock-huge'}}) + + if (0 == #rocks) then + break + end + + for _, rock in pairs(rocks) do + raise_event(defines.events.on_entity_died, {entity = rock}) + rock.destroy() + end + end + + for _, prototype in ipairs(params.locations_to_spawn) do + -- amount is not used for aliens prototypes, it just carries along in the params + local amount = prototype.amount + while amount > 0 do + prototype.position = find_non_colliding_position(prototype.name, prototype.position, 2, 0.4) or prototype.position + create_entity(prototype) + amount = amount - 1 + end + end +end) + +---Spawns aliens given the parameters. +---@param aliens table index is the name, value is the amount of biters to spawn +---@param force LuaForce of the biters +---@param surface LuaSurface to spawn on +---@param x number +---@param y number +local function spawn_aliens(aliens, force, surface, x, y) local position = {x = x, y = y} - local biters = AlienEvolutionProgress.getBitersByEvolution(random(1, 2), enemy_force_evolution) - local spitters = AlienEvolutionProgress.getSpittersByEvolution(random(1, 2), enemy_force_evolution) + local count_tiles_filtered = surface.count_tiles_filtered + local areas_to_mine = {} + local locations_to_spawn = {} - local units = {} - for name, amount in pairs(biters) do - insert(units, {name = name, position = position, force = enemy_force, amount = amount}) - end - for name, amount in pairs(spitters) do - insert(units, {name = name, position = position, force = enemy_force, amount = amount}) + for name, amount in pairs(aliens) do + local size_data = alien_size_chart[name] + if not size_data then + Debug.print_position(position, 'Unable to find prototype data for ' .. name) + break + end + + local locations_to_scan = { + {x = 0, y = -1.5}, -- up + {x = 1.5, y = 0}, -- right + {x = 0, y = 1.5}, -- bottom + {x = -1.5, y = 0}, -- left + } + + local collision_box = size_data.collision_box + local left_top_x = collision_box.left_top.x * 1.6 + local left_top_y = collision_box.left_top.y * 1.6 + local right_bottom_x = collision_box.right_bottom.x * 1.6 + local right_bottom_y = collision_box.right_bottom.y * 1.6 + + for _, direction in ipairs(locations_to_scan) do + local x_center = direction.x + x + local y_center = direction.y + y + + -- *_center indicates the offset center relative to the location where it should spawn + -- the area is composed of the bounding_box of the alien with a bigger size so it has space to move + local offset_area = { + left_top = { + x = floor(x_center + left_top_x), + y = floor(y_center + left_top_y), + }, + right_bottom = { + x = ceil(x_center + right_bottom_x), + y = ceil(y_center + right_bottom_y), + }, + } + + -- can't spawn properly if void is present + if count_tiles_filtered({area = offset_area, name = 'out-of-map'}) == 0 then + insert(areas_to_mine, offset_area) + insert(locations_to_spawn, {name = name, position = {x = x_center, y = y_center}, force = force, amount = amount}) + break + end + end end - Template.units(surface, units, 1.5, 'small-biter') + -- can't do mining in the same tick as it would invalidate the rock just mined and there + -- is no way to distinguish them as multiple can occupy the same position + if #locations_to_spawn > 0 then + Task.set_timeout_in_ticks(1, do_alien_mining, { + surface = surface, + positions_to_mine = areas_to_mine, + locations_to_spawn = locations_to_spawn, + }) + end end --[[-- @@ -36,19 +141,68 @@ end ]] function AlienSpawner.register(config) local alien_minimum_distance_square = config.alien_minimum_distance ^ 2 + local alien_probability = config.alien_probability + local hail_hydra = config.hail_hydra + + if hail_hydra then + Event.add(defines.events.on_entity_died, function (event) + local entity = event.entity + local name = entity.name + + local force + local position + local surface + local create_entity + local find_non_colliding_position + + for alien, hydras in pairs(hail_hydra) do + if name == alien then + for hydra_spawn, amount in pairs(hydras) do + local extra_chance = amount % 1 + if extra_chance > 0 then + if random() <= extra_chance then + amount = ceil(amount) + else + amount = floor(amount) + end + end + + while amount > 0 do + force = force or entity.force + position = position or entity.position + surface = surface or entity.surface + create_entity = create_entity or surface.create_entity + find_non_colliding_position = find_non_colliding_position or surface.find_non_colliding_position + position = find_non_colliding_position(hydra_spawn, position, 2, 0.4) or position + create_entity({name = hydra_spawn, force = force, position = position}) + amount = amount - 1 + end + end + break + end + end + end) + end Event.add(Template.events.on_void_removed, function (event) - game.forces.enemy.evolution_factor = game.forces.enemy.evolution_factor + 0.0000012 + local force = game.forces.enemy + local evolution_factor = force.evolution_factor + force.evolution_factor = evolution_factor + 0.0000012 local position = event.position local x = position.x local y = position.y - if (x * x + y * y < alien_minimum_distance_square or config.alien_probability < random()) then + if (x * x + y * y < alien_minimum_distance_square or alien_probability < random()) then return end - spawn_alien(event.surface, x, y) + local aliens = AlienEvolutionProgress.getBitersByEvolution(random(1, 2), evolution_factor) + for name, amount in pairs(AlienEvolutionProgress.getSpittersByEvolution(random(1, 2), evolution_factor)) do + aliens[name] = amount + end + + spawn_aliens(aliens, force, event.surface, x, y) end) end diff --git a/map_gen/Diggy/Feature/ArtefactHunting.lua b/map_gen/Diggy/Feature/ArtefactHunting.lua index e133fd9f..886c5d5a 100644 --- a/map_gen/Diggy/Feature/ArtefactHunting.lua +++ b/map_gen/Diggy/Feature/ArtefactHunting.lua @@ -165,25 +165,21 @@ function ArtefactHunting.register(config) ScoreTable.reset('Collected coins') + local alien_coin_drop_chance = config.alien_coin_drop_chance + Event.add(defines.events.on_entity_died, function (event) local entity = event.entity local force = entity.force - if force.name ~= 'enemy' then - return - end - - local cause = event.cause - - if not cause or cause.type ~= 'player' or not cause.valid then + if force.name ~= 'enemy' or random() > alien_coin_drop_chance then return end local modifier = modifiers[entity.name] or 1 - local evolution_multiplier = force.evolution_factor * 11 + local evolution_multiplier = force.evolution_factor local count = random( - ceil(2 * evolution_multiplier * 0.1), - ceil(5 * (evolution_multiplier * evolution_multiplier + modifier) * 0.1) + ceil(2 * evolution_multiplier * modifier), + ceil(5 * evolution_multiplier * modifier) ) entity.surface.create_entity({ @@ -207,11 +203,11 @@ function ArtefactHunting.register(config) return end - if random() > config.mining_artefact_chance then + if random() > config.mining_coin_chance then return end - local count = random(config.mining_artefact_amount.min, config.mining_artefact_amount.max) + local count = random(config.mining_coin_amount.min, config.mining_coin_amount.max) local player_index = event.player_index Game.get_player_by_index(player_index).insert({name = 'coin', count = count}) diff --git a/map_gen/Diggy/Feature/DiggyHole.lua b/map_gen/Diggy/Feature/DiggyHole.lua index 049caac4..47558eb8 100644 --- a/map_gen/Diggy/Feature/DiggyHole.lua +++ b/map_gen/Diggy/Feature/DiggyHole.lua @@ -236,14 +236,16 @@ function DiggyHole.register(config) local entity = event.entity local name = entity.name + if entity.health ~= 0 then + return + end + if name ~= 'sand-rock-big' and name ~= 'rock-huge' then return end - if entity.health == 0 then - raise_event(defines.events.on_entity_died, {entity = entity, cause = event.cause, force = event.force}) - entity.destroy() - end + raise_event(defines.events.on_entity_died, {entity = entity, cause = event.cause, force = event.force}) + entity.destroy() end) local enable_digging_warning = config.enable_digging_warning diff --git a/map_gen/Diggy/Template.lua b/map_gen/Diggy/Template.lua index 0f2bb488..56d25c9d 100644 --- a/map_gen/Diggy/Template.lua +++ b/map_gen/Diggy/Template.lua @@ -140,39 +140,6 @@ function Template.insert(surface, tiles, entities) end end ---[[-- - Designed to spawn aliens, uses find_non_colliding_position. - - @see LuaSurface.entity - - @param surface LuaSurface to put the tiles and entities on - @param units table of entities as required by create_entity - @param non_colliding_distance int amount of tiles to scan around original position in case it's already taken - @param generic_unit_name_for_spawn_size String allows setting a custom unit name for spawn size, will overwrite the actual -]] -function Template.units(surface, units, non_colliding_distance, generic_unit_name_for_spawn_size) - non_colliding_distance = non_colliding_distance or 1 - generic_unit_name_for_spawn_size = generic_unit_name_for_spawn_size or 'player' - - local create_entity = surface.create_entity - local position - - for _, entity in pairs(units) do - position = position or surface.find_non_colliding_position( - generic_unit_name_for_spawn_size, - entity.position, non_colliding_distance, - 0.5 - ) - - if (nil ~= position) then - entity.position = position - create_entity(entity) - elseif (nil == create_entity(entity)) then - Debug.print_position(entity.position, "Failed to spawn '" .. entity.name .. "'") - end - end -end - --[[-- Designed to spawn resources.