1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2025-03-03 14:53:01 +02:00

Improved biter spawning algorithm (#408)

* Improved biter spawning algorithm

* HAIL HYDRA, biter hell
This commit is contained in:
Lynn 2018-11-21 15:26:04 +01:00 committed by Valansch
parent 3eadc68aa9
commit a52e9f710c
5 changed files with 225 additions and 77 deletions

View File

@ -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

View File

@ -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
Template.units(surface, units, 1.5, 'small-biter')
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
-- 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

View File

@ -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})

View File

@ -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
end)
local enable_digging_warning = config.enable_digging_warning

View File

@ -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.