mirror of
https://github.com/Refactorio/RedMew.git
synced 2024-12-16 10:19:27 +02:00
ecff8f86eb
- 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.
527 lines
16 KiB
Lua
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
|
|
)
|