1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2024-12-16 10:19:27 +02:00
RedMew/map_gen/maps/crash_site/events.lua

538 lines
16 KiB
Lua
Raw Normal View History

2018-07-20 23:01:34 +02:00
local Event = require 'utils.event'
2019-01-04 22:02:55 +02:00
local Task = require 'utils.task'
2018-11-26 03:07:03 +02:00
local Token = require 'utils.token'
2018-07-20 23:01:34 +02:00
local Global = require 'utils.global'
2019-01-11 17:31:32 +02:00
local math = require 'utils.math'
2019-06-21 22:29:35 +02:00
local table = require 'utils.table'
2019-01-10 16:34:51 +02:00
local random = math.random
local set_timeout_in_ticks = Task.set_timeout_in_ticks
2019-01-11 17:31:32 +02:00
local ceil = math.ceil
2019-06-21 22:29:35 +02:00
local draw_arc = rendering.draw_arc
local fast_remove = table.fast_remove
local tau = 2 * math.pi
local start_angle = -tau / 4
local update_rate = 4 -- ticks between updates
local time_to_live = update_rate + 1
local pole_respawn_time = 60 * 60
2018-07-20 23:01:34 +02:00
local no_coin_entity = {}
2020-04-16 13:18:08 +02:00
Global.register(
{no_coin_entity = no_coin_entity},
function(tbl)
no_coin_entity = tbl.no_coin_entity
end
)
2018-07-20 23:01:34 +02:00
local entity_drop_amount = {
--[[['small-biter'] = {low = -62, high = 1},
['small-spitter'] = {low = -62, high = 1},
['medium-biter'] = {low = -14, high = 1},
['medium-spitter'] = {low = -14, high = 1},
['big-biter'] = {low = -2, high = 1},
['big-spitter'] = {low = -2, high = 1},
['behemoth-biter'] = {low = 1, high = 1},
['behemoth-spitter'] = {low = 1, high = 1}, ]]
2019-01-11 17:31:32 +02:00
['biter-spawner'] = {low = 8, high = 24},
['spitter-spawner'] = {low = 8, high = 24},
['small-worm-turret'] = {low = 3, high = 10},
['medium-worm-turret'] = {low = 8, high = 24},
2019-03-07 17:55:49 +02:00
['big-worm-turret'] = {low = 15, high = 30},
['behemoth-worm-turret'] = {low = 25, high = 45}
2018-07-20 23:01:34 +02:00
}
2020-04-16 13:18:08 +02:00
local spill_items =
Token.register(
function(data)
local stack = {name = 'coin', count = data.count}
data.surface.spill_item_stack(data.position, stack, true)
end
)
2018-07-20 23:01:34 +02:00
local entity_spawn_map = {
2019-11-01 18:04:43 +02:00
['medium-biter'] = {name = 'small-worm-turret', count = 1, chance = 0.2},
['big-biter'] = {name = 'medium-worm-turret', count = 1, chance = 0.2},
['behemoth-biter'] = {name = 'big-worm-turret', count = 1, chance = 0.2},
2019-01-11 17:31:32 +02:00
['medium-spitter'] = {name = 'small-worm-turret', count = 1, chance = 0.2},
['big-spitter'] = {name = 'medium-worm-turret', count = 1, chance = 0.2},
['behemoth-spitter'] = {name = 'big-worm-turret', count = 1, chance = 0.2},
2019-01-10 16:34:51 +02:00
['biter-spawner'] = {type = 'biter', count = 5, chance = 1},
['spitter-spawner'] = {type = 'spitter', count = 5, chance = 1},
2019-11-01 18:04:43 +02:00
['behemoth-worm-turret'] = {
type = 'compound',
spawns = {
{name = 'behemoth-spitter', count = 2},
{name = 'behemoth-biter', count = 2}
},
chance = 1
},
2019-01-23 21:15:28 +02:00
['stone-furnace'] = {type = 'cause', count = 2, chance = 1},
['steel-furnace'] = {type = 'cause', count = 2, chance = 1},
['electric-furnace'] = {type = 'cause', count = 4, chance = 1},
['assembling-machine-1'] = {type = 'cause', count = 4, chance = 1},
['assembling-machine-2'] = {type = 'cause', count = 4, chance = 1},
['assembling-machine-3'] = {type = 'cause', count = 4, chance = 1},
['chemical-plant'] = {type = 'cause', count = 4, chance = 1},
['centrifuge'] = {type = 'cause', count = 6, chance = 1},
['pumpjack'] = {type = 'cause', count = 6, chance = 1},
['storage-tank'] = {type = 'cause', count = 4, chance = 1},
2019-01-23 21:15:28 +02:00
['oil-refinery'] = {type = 'cause', count = 8, chance = 1},
['offshore-pump'] = {type = 'cause', count = 2, chance = 1},
['boiler'] = {type = 'cause', count = 2, chance = 1},
['heat-exchanger'] = {type = 'cause', count = 4, chance = 1},
['steam-engine'] = {type = 'cause', count = 6, chance = 1},
['steam-turbine'] = {type = 'cause', count = 10, chance = 1},
['nuclear-reactor'] = {type = 'cause', count = 20, chance = 1},
['rocket-silo'] = {type = 'cause', count = 40, chance = 1},
['train-stop'] = {type = 'cause', count = 2, chance = 1},
['burner-mining-drill'] = {type = 'cause', count = 2, chance = 1},
['electric-mining-drill'] = {type = 'cause', count = 4, chance = 1},
['lab'] = {type = 'cause', count = 6, chance = 1},
['solar-panel'] = {type = 'cause', count = 4, chance = 1},
['accumulator'] = {type = 'cause', count = 2, chance = 1},
['beacon'] = {type = 'cause', count = 6, chance = 1},
['radar'] = {type = 'cause', count = 4, chance = 1}
2018-07-20 23:01:34 +02:00
}
2019-01-10 16:34:51 +02:00
local unit_levels = {
biter = {'small-biter', 'medium-biter', 'big-biter', 'behemoth-biter'},
2019-01-10 16:34:51 +02:00
spitter = {
2020-04-16 13:18:08 +02:00
'small-spitter',
'medium-spitter',
'big-spitter',
'behemoth-spitter'
2019-01-10 16:34:51 +02:00
}
2018-07-20 23:01:34 +02:00
}
2019-01-11 17:31:32 +02:00
local worms = {
['small-worm-turret'] = true,
['medium-worm-turret'] = true,
2019-03-07 17:55:49 +02:00
['big-worm-turret'] = true,
['behemoth-worm-turret'] = true
2019-01-11 17:31:32 +02:00
}
local allowed_cause_source = {
['small-biter'] = true,
['medium-biter'] = true,
['big-biter'] = true,
['behemoth-biter'] = true,
['small-spitter'] = true,
['medium-spitter'] = true,
['big-spitter'] = true,
['behemoth-spitter'] = true
}
2018-08-02 14:51:42 +02:00
local turret_evolution_factor = {
2019-01-10 16:34:51 +02:00
['gun-turret'] = 0.001,
['laser-turret'] = 0.002,
['flamethrower-turret'] = 0.0015,
['artillery-turret'] = 0.004
2018-08-02 14:51:42 +02:00
}
2020-04-16 13:18:08 +02:00
local spawn_worm =
Token.register(
function(data)
local surface = data.surface
local name = data.name
local position = data.position
2018-07-20 23:01:34 +02:00
2020-04-16 13:18:08 +02:00
local p = surface.find_non_colliding_position(name, position, 8, 1)
2018-07-20 23:01:34 +02:00
2020-04-16 13:18:08 +02:00
if p then
local entity = surface.create_entity({name = data.name, position = data.position})
no_coin_entity[entity.unit_number] = true
end
2018-07-20 23:01:34 +02:00
end
2020-04-16 13:18:08 +02:00
)
2018-07-20 23:01:34 +02:00
local function get_level()
2018-07-28 14:04:14 +02:00
local ef = game.forces.enemy.evolution_factor
2019-01-11 17:31:32 +02:00
if ef == 0 then
return 1
else
return ceil(ef * 4)
end
2018-07-20 23:01:34 +02:00
end
2020-04-16 13:18:08 +02:00
local spawn_units =
Token.register(
function(data)
local surface = data.surface
local name = data.name
local position = data.position
for _ = 1, data.count do
local p = surface.find_non_colliding_position(name, position, 8, 1)
if p then
surface.create_entity {name = name, position = p}
end
end
2018-07-20 23:01:34 +02:00
end
2020-04-16 13:18:08 +02:00
)
2018-07-20 23:01:34 +02:00
2020-04-16 13:18:08 +02:00
local spawn_player =
Token.register(
function(player)
if player and player.valid then
player.ticks_to_respawn = 3600
end
end
)
2019-01-10 16:34:51 +02:00
2019-06-21 22:29:35 +02:00
local function has_valid_turret(turrets)
for i = #turrets, 1, -1 do
local turret = turrets[i]
if turret.valid then
return true
else
fast_remove(turrets, i)
end
end
return false
end
local pole_callback
2020-04-16 13:18:08 +02:00
pole_callback =
Token.register(
function(data)
if not has_valid_turret(data.turrets) then
return
end
2019-06-21 22:29:35 +02:00
2020-04-16 13:18:08 +02:00
local tick = data.tick
local now = game.tick
if now >= tick then
data.surface.create_entity(
{
name = data.name,
force = 'enemy',
position = data.position
}
)
return
end
2019-06-21 22:29:35 +02:00
2020-04-16 13:18:08 +02:00
local fraction = ((now - tick) / pole_respawn_time) + 1
draw_arc(
{
color = {1 - fraction, fraction, 0},
max_radius = 0.5,
min_radius = 0.4,
start_angle = start_angle,
angle = fraction * tau,
target = data.position,
surface = data.surface,
time_to_live = time_to_live
}
)
set_timeout_in_ticks(update_rate, pole_callback, data)
end
)
2019-06-21 22:29:35 +02:00
local filter = {area = nil, name = 'laser-turret', force = 'enemy'}
local function do_pole(entity)
2020-04-16 13:18:08 +02:00
if entity.type ~= 'electric-pole' then
return
end
2019-06-21 22:29:35 +02:00
local supply_area_distance = entity.prototype.supply_area_distance
2020-04-16 13:18:08 +02:00
if not supply_area_distance then
return
end
2019-06-21 22:29:35 +02:00
local surface = entity.surface
local position = entity.position
local x, y = position.x, position.y
local d = supply_area_distance / 2
filter.area = {{x - d, y - d}, {x + d, y + d}}
local turrets = surface.find_entities_filtered(filter)
2020-04-16 13:18:08 +02:00
if #turrets == 0 then
return
end
2019-06-21 22:29:35 +02:00
2020-04-16 13:18:08 +02:00
set_timeout_in_ticks(
update_rate,
pole_callback,
{
name = entity.name,
position = position,
surface = surface,
tick = game.tick + pole_respawn_time,
turrets = turrets
}
)
end
local function do_evolution(entity_name, entity_force)
local factor = turret_evolution_factor[entity_name]
if factor then
local old = entity_force.evolution_factor
local new = old + (1 - old) * factor
entity_force.evolution_factor = math.min(new, 1)
end
2019-06-21 22:29:35 +02:00
end
local bot_spawn_whitelist = {
['gun-turret'] = true,
['laser-turret'] = true,
2020-04-26 00:17:12 +02:00
['flamethrower-turret'] = true,
['artillery-turret'] = true
}
local bot_cause_whitelist = {
['character'] = true,
['artillery-turret'] = true,
2020-08-18 03:34:24 +02:00
['artillery-wagon'] = true,
['spidertron'] = true
}
2018-07-20 23:01:34 +02:00
-- https://en.wikipedia.org/wiki/Euclidean_distance#Two_dimensions
2021-02-01 19:06:43 +02:00
-- math.sqrt is computationally expensive so we compare to the distance squared instead
local function euclidean_distance_squared(p, q)
local distance = (p.x-q.x)^2 + (p.y-q.y)^2
return distance
end
2021-01-29 15:05:21 +02:00
local destroyer_callback
destroyer_callback =
Token.register(
function(data)
local entity = data.entity
local destroyer = data.destroyer
if not destroyer or not destroyer.valid or not entity or not entity.valid then
return
end
2021-02-01 19:06:43 +02:00
local distance = 10
if euclidean_distance_squared(destroyer.position, entity.position) < distance^2 then
entity.surface.create_entity{name = "laser", position=destroyer.position, target=entity, speed=1}
end
set_timeout_in_ticks(30, destroyer_callback, data)
2021-01-29 15:05:21 +02:00
end
)
2020-04-16 13:18:08 +02:00
local function do_bot_spawn(entity_name, entity, event)
-- Return if the entity killed is not on the white list
2020-12-08 10:18:31 +02:00
if not bot_spawn_whitelist[entity_name] then
return
end
-- Return if the evolution is too low
local entity_force = entity.force
local ef = entity_force.evolution_factor
if ef <= 0.2 then
return
end
2020-11-29 23:52:41 +02:00
local cause = event.cause
local create_entity = entity.surface.create_entity
local spawn_entity = {
position = entity.position,
target = cause,
force = entity_force
}
-- Cbeck if there is a cause for the entity's death
-- If there is no cause then the player probably picked up an artillery turret before the projectile hit the entity.
-- This causes no bots to spawn because there is no cause. Punish the player for the behaviour by sending some bots to spawn instead of their location
if not cause then
return
end
-- Now we have checked for no cause, check for if the cause was on the cause whitelist (players, artillery, spidertrons)
if not bot_cause_whitelist[cause.name] then
2020-04-16 13:18:08 +02:00
return
end
2019-06-21 22:29:35 +02:00
2020-04-16 13:18:08 +02:00
local repeat_cycle = 1 -- The number of times a squad of robots are spawned default must be 1
if ef > .95 then
repeat_cycle = 2
end
2019-01-10 16:34:51 +02:00
2020-04-26 01:46:04 +02:00
if cause.name ~= 'character' then
if (entity_name == 'artillery-turret') then
2020-04-26 01:46:04 +02:00
repeat_cycle = 15
else
repeat_cycle = 4
end
2020-04-16 13:18:08 +02:00
for i = 1, repeat_cycle do
2021-01-29 15:05:21 +02:00
if (cause.name == 'artillery-turret') then
spawn_entity.target = cause.position -- Overwrite target. Artillery turrets/wagons don't move so send them to entity position. Stops players from picking up the arty and the bots stopping dead.
2020-11-29 23:52:41 +02:00
spawn_entity.speed = 0.2
2020-12-08 10:18:31 +02:00
-- This is particularly risky for players to do because defender-capsule quantities are not limited by the player force's follower robot count.
spawn_entity.name = 'defender-capsule' -- use 'defender-capsule' (projectile) not 'defender' (entity) since a projectile can target a position but a capsule entity must have another entity as target
create_entity(spawn_entity)
create_entity(spawn_entity)
spawn_entity.name = 'destroyer-capsule'
create_entity(spawn_entity)
2020-11-29 23:52:41 +02:00
create_entity(spawn_entity)
2021-01-29 15:05:21 +02:00
elseif (cause.name == 'artillery-wagon') then
spawn_entity.name = 'defender'
create_entity(spawn_entity)
create_entity(spawn_entity)
spawn_entity.name = 'destroyer'
local destroyer = create_entity(spawn_entity)
set_timeout_in_ticks(random(30,60), destroyer_callback, {destroyer = destroyer, entity = cause})
2021-01-29 15:07:30 +02:00
destroyer = create_entity(spawn_entity)
set_timeout_in_ticks(random(30,60), destroyer_callback, {destroyer = destroyer, entity = cause})
else
spawn_entity.name = 'defender'
create_entity(spawn_entity)
create_entity(spawn_entity)
spawn_entity.name = 'destroyer'
create_entity(spawn_entity)
2020-11-29 23:52:41 +02:00
create_entity(spawn_entity)
end
2019-11-01 18:04:43 +02:00
end
2020-04-26 01:46:04 +02:00
elseif entity_name == 'gun-turret' then
2020-04-16 13:18:08 +02:00
for i = 1, repeat_cycle do
spawn_entity.name = 'defender'
create_entity(spawn_entity)
2020-04-26 01:46:04 +02:00
create_entity(spawn_entity)
2020-04-16 13:18:08 +02:00
spawn_entity.name = 'destroyer'
create_entity(spawn_entity)
end
2020-04-26 01:46:04 +02:00
elseif entity_name == 'laser-turret' then
2020-04-16 13:18:08 +02:00
for i = 1, repeat_cycle do
2020-04-26 00:17:12 +02:00
spawn_entity.name = 'defender'
create_entity(spawn_entity)
spawn_entity.name = 'destroyer'
create_entity(spawn_entity)
create_entity(spawn_entity)
end
2020-04-26 01:46:04 +02:00
else
for i = 1, repeat_cycle do
spawn_entity.name = 'distractor-capsule'
spawn_entity.speed = 0
create_entity(spawn_entity)
end
end
2020-04-16 13:18:08 +02:00
end
2019-11-01 18:04:43 +02:00
2020-04-16 13:18:08 +02:00
local function do_coin_drop(entity_name, entity)
local position = entity.position
local bounds = entity_drop_amount[entity_name]
2020-04-16 13:18:08 +02:00
if not bounds then
return
2019-01-10 16:34:51 +02:00
end
2018-08-02 14:51:42 +02:00
2020-04-16 13:18:08 +02:00
local unit_number = entity.unit_number
if no_coin_entity[unit_number] then
no_coin_entity[unit_number] = nil
return
end
local count = random(bounds.low, bounds.high)
if count > 0 then
set_timeout_in_ticks(
1,
spill_items,
{
count = count,
surface = entity.surface,
position = position
}
)
end
end
local function do_spawn_entity(entity_name, entity, event)
local spawn = entity_spawn_map[entity_name]
2020-04-16 13:18:08 +02:00
if not spawn then
return
end
local chance = spawn.chance
2020-04-16 13:18:08 +02:00
if chance ~= 1 and random() > chance then
return
end
local name = spawn.name
if name == nil then
local type = spawn.type
if type == 'cause' then
local cause = event.cause
2020-04-16 13:18:08 +02:00
if not cause then
return
end
name = cause.name
2020-04-16 13:18:08 +02:00
if not allowed_cause_source[cause.name] then
return
end
elseif type == 'compound' then
local spawns = spawn.spawns
spawn = spawns[random(#spawns)]
name = spawn.name
else
name = unit_levels[type][get_level()]
end
2018-07-20 23:01:34 +02:00
end
2020-04-16 13:18:08 +02:00
local position = entity.position
if worms[name] then
2020-04-16 13:18:08 +02:00
set_timeout_in_ticks(
5,
spawn_worm,
{
surface = entity.surface,
name = name,
position = position
}
)
else
2020-04-16 13:18:08 +02:00
set_timeout_in_ticks(
5,
spawn_units,
{
surface = entity.surface,
name = name,
position = position,
count = spawn.count
}
)
end
2020-04-16 13:18:08 +02:00
end
Event.add(
defines.events.on_entity_died,
function(event)
local entity = event.entity
if not entity or not entity.valid then
return
end
local entity_force = entity.force
local entity_name = entity.name
2020-04-16 13:18:08 +02:00
if entity_force.name == 'enemy' then
do_pole(entity)
do_evolution(entity_name, entity_force)
do_coin_drop(entity_name, entity)
do_bot_spawn(entity_name, entity, event)
end
do_spawn_entity(entity_name, entity, event)
2020-04-16 13:18:08 +02:00
end
)
2020-04-16 13:18:08 +02:00
Event.add(
defines.events.on_player_died,
function(event)
local player = game.get_player(event.player_index)
set_timeout_in_ticks(1, spawn_player, player)
end
2021-01-29 15:05:21 +02:00
)