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
Jayefuu ecff8f86eb Fixed part of artillery pickup exploit
- Current behaviour is that when destroyers/defenders are created their target position is set to the entity that killed them.
- If a player picks up the artillery after the capsules spawn then they lose their cause.target and stop, not reaching the cause of the turret death.
- I've changed this behaviour so that if the target.cause is artillery then it spawns a defender/destroyer projectile NOT an entity. The projectile target can therefore be a position not an entity. So the capsules won't stop if the artillery is picked up.
- If the cause is not artillery turret or wagon then a destroyer/defender capsule entity is spawned instead of a projectile because a projectile cannot track the player.
- If the player picks up the artillery turret before the projectile that kills a turret arrives then the event cause.name will be missing and no bots spawn at all. I have changed the behaviour so that "if not cause" then we assume they're doing something fucky. We won't have the entity cause name or position, so let's just spawn double the normal amount of destroyers and just send them at 0, 0 since we know nothing else to do with them.
2020-11-29 21:26:01 +00:00

527 lines
16 KiB
Lua

local Event = require 'utils.event'
local Task = require 'utils.task'
local Token = require 'utils.token'
local Global = require 'utils.global'
local math = require 'utils.math'
local table = require 'utils.table'
local random = math.random
local set_timeout_in_ticks = Task.set_timeout_in_ticks
local ceil = math.ceil
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
local no_coin_entity = {}
Global.register(
{no_coin_entity = no_coin_entity},
function(tbl)
no_coin_entity = tbl.no_coin_entity
end
)
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}, ]]
['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},
['big-worm-turret'] = {low = 15, high = 30},
['behemoth-worm-turret'] = {low = 25, high = 45}
}
local spill_items =
Token.register(
function(data)
local stack = {name = 'coin', count = data.count}
data.surface.spill_item_stack(data.position, stack, true)
end
)
local entity_spawn_map = {
['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},
['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},
['biter-spawner'] = {type = 'biter', count = 5, chance = 1},
['spitter-spawner'] = {type = 'spitter', count = 5, chance = 1},
['behemoth-worm-turret'] = {
type = 'compound',
spawns = {
{name = 'behemoth-spitter', count = 2},
{name = 'behemoth-biter', count = 2}
},
chance = 1
},
['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},
['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}
}
local unit_levels = {
biter = {'small-biter', 'medium-biter', 'big-biter', 'behemoth-biter'},
spitter = {
'small-spitter',
'medium-spitter',
'big-spitter',
'behemoth-spitter'
}
}
local worms = {
['small-worm-turret'] = true,
['medium-worm-turret'] = true,
['big-worm-turret'] = true,
['behemoth-worm-turret'] = true
}
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
}
local turret_evolution_factor = {
['gun-turret'] = 0.001,
['laser-turret'] = 0.002,
['flamethrower-turret'] = 0.0015,
['artillery-turret'] = 0.004
}
local spawn_worm =
Token.register(
function(data)
local surface = data.surface
local name = data.name
local position = data.position
local p = surface.find_non_colliding_position(name, position, 8, 1)
if p then
local entity = surface.create_entity({name = data.name, position = data.position})
no_coin_entity[entity.unit_number] = true
end
end
)
local function get_level()
local ef = game.forces.enemy.evolution_factor
if ef == 0 then
return 1
else
return ceil(ef * 4)
end
end
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
end
)
local spawn_player =
Token.register(
function(player)
if player and player.valid then
player.ticks_to_respawn = 3600
end
end
)
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
pole_callback =
Token.register(
function(data)
if not has_valid_turret(data.turrets) then
return
end
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
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
)
local filter = {area = nil, name = 'laser-turret', force = 'enemy'}
local function do_pole(entity)
if entity.type ~= 'electric-pole' then
return
end
local supply_area_distance = entity.prototype.supply_area_distance
if not supply_area_distance then
return
end
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)
if #turrets == 0 then
return
end
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
end
local bot_spawn_whitelist = {
['gun-turret'] = true,
['laser-turret'] = true,
['flamethrower-turret'] = true,
['artillery-turret'] = true
}
local bot_cause_whitelist = {
['character'] = true,
['artillery-turret'] = true,
['artillery-wagon'] = true,
['spidertron'] = true
}
local function do_bot_spawn(entity_name, entity, event)
local cause = event.cause
local entity_force = entity.force
local ef = entity_force.evolution_factor
local create_entity = entity.surface.create_entity
if ef <= 0.2 then
return
end
local spawn_entity = {
position = entity.position,
target = cause,
force = entity_force
}
if not cause then
-- If we reach here then the player might have picked up the artillery turret before the projectile hit the entity and killed it.
-- We therefore won't know the location of the artillery turret/wagon that killed the turret, so let's punish them and send even more bots, straight at spawn?
for i = 1, 60 do
spawn_entity.name = 'destroyer-capsule'
spawn_entity.speed = 0.2
spawn_entity.target = {0,0}
create_entity(spawn_entity)
end
return
end
if not bot_spawn_whitelist[entity_name] then
return
end
if not bot_cause_whitelist[cause.name] then
return
end
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
if cause.name ~= 'character' then
if (entity_name == 'artillery-turret') or (entity_name == 'artillery-wagon') then
repeat_cycle = 15
else
repeat_cycle = 4
end
for i = 1, repeat_cycle do
if (cause.name == 'artillery-turret') or (cause.name == 'artillery-wagon') 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.
spawn_entity.speed = 0.2
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)
create_entity(spawn_entity)
else
-- projectiles don't have AI so won't track/follow a player
-- if the cause wasn't artillery turret/wagon then spawn a capsule entity not projectile
spawn_entity.name = 'defender' -- 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'
create_entity(spawn_entity)
create_entity(spawn_entity)
end
end
elseif entity_name == 'gun-turret' then
for i = 1, repeat_cycle do
spawn_entity.name = 'defender'
create_entity(spawn_entity)
create_entity(spawn_entity)
spawn_entity.name = 'destroyer'
create_entity(spawn_entity)
end
elseif entity_name == 'laser-turret' then
for i = 1, repeat_cycle do
spawn_entity.name = 'defender'
create_entity(spawn_entity)
spawn_entity.name = 'destroyer'
create_entity(spawn_entity)
create_entity(spawn_entity)
end
else
for i = 1, repeat_cycle do
spawn_entity.name = 'distractor-capsule'
spawn_entity.speed = 0
create_entity(spawn_entity)
end
end
end
local function do_coin_drop(entity_name, entity)
local position = entity.position
local bounds = entity_drop_amount[entity_name]
if not bounds then
return
end
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]
if not spawn then
return
end
local chance = spawn.chance
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
if not cause then
return
end
name = cause.name
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
end
local position = entity.position
if worms[name] then
set_timeout_in_ticks(
5,
spawn_worm,
{
surface = entity.surface,
name = name,
position = position
}
)
else
set_timeout_in_ticks(
5,
spawn_units,
{
surface = entity.surface,
name = name,
position = position,
count = spawn.count
}
)
end
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
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)
end
)
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
)
Event.add(
defines.events.on_combat_robot_expired,
function(event)
local entity = event.robot
local position = entity.position
local owner = event.owner
if owner == nil or not owner.valid then
return
end
if entity.force.name == 'enemy' and owner.name == "artillery-wagon" then
-- only create a grenade entity if an artillery wagon (the event owner) killed the target that spawned the combabt robot
entity.surface.create_entity{name = "cluster-grenade", position=position, target=position, speed=1}
end
end
)