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:
parent
3eadc68aa9
commit
a52e9f710c
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user