You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
11 KiB
271 lines
11 KiB
Event = require('scripts/event')
|
|
|
|
min_attrition_rate = 0.0011
|
|
tickskip = 10
|
|
|
|
function is_system_force(force_name)
|
|
return force_name == "enemy"
|
|
or force_name == "neutral"
|
|
or force_name == "capture"
|
|
or force_name == "conquest"
|
|
or force_name == "ignore"
|
|
or force_name == "friendly"
|
|
end
|
|
|
|
function get_attrition_rate_for_surface(surface_index)
|
|
-- use cache
|
|
if global.suface_attrition_rates[surface_index] then
|
|
return global.suface_attrition_rates[surface_index]
|
|
end
|
|
-- or load
|
|
local rate = nil
|
|
local default_rate = settings.global["robot-attrition-factor"].value
|
|
for interface, functions in pairs(remote.interfaces) do
|
|
if functions["robot_attrition_for_surface"] then
|
|
local returned_rate = remote.call(interface, "robot_attrition_for_surface", {default_rate = default_rate, surface_index = surface_index})
|
|
if rate == nil or returned_rate > rate then
|
|
rate = returned_rate
|
|
end
|
|
end
|
|
end
|
|
if rate == nil then
|
|
rate = default_rate
|
|
end
|
|
global.suface_attrition_rates[surface_index] = rate
|
|
--game.print("Robot attrition rate for surface " .. surface_index .. " ("..game.surfaces[surface_index].name..") is " .. rate)
|
|
|
|
return rate
|
|
end
|
|
|
|
function get_crash_item(bot)
|
|
if global.crash_items[bot.name] then
|
|
if global.crash_items[bot.name] ~= "none" then
|
|
return global.crash_items[bot.name]
|
|
else
|
|
return nil
|
|
end
|
|
else
|
|
if bot.prototype.mineable_properties.products and bot.prototype.mineable_properties.products[1] then
|
|
local name = bot.prototype.mineable_properties.products[1].name.."-crashed"
|
|
if game.item_prototypes[name] then
|
|
global.crash_items[bot.name] = name
|
|
return name
|
|
end
|
|
end
|
|
end
|
|
global.crash_items[bot.name] = "none"
|
|
end
|
|
|
|
function get_bot_speed(name)
|
|
if not global.bot_speed then global.bot_speed = {} end
|
|
if not global.bot_speed[name] then
|
|
global.bot_speed[name] = game.entity_prototypes[name].speed
|
|
end
|
|
return global.bot_speed[name]
|
|
end
|
|
|
|
function get_bot_slow_speed_multiplier(name)
|
|
if not global.bot_slow_speed_multiplier then global.bot_slow_speed_multiplier = {} end
|
|
if not global.bot_slow_speed_multiplier[name] then
|
|
global.bot_slow_speed_multiplier[name] = game.entity_prototypes[name].speed_multiplier_when_out_of_energy
|
|
end
|
|
return global.bot_slow_speed_multiplier[name]
|
|
end
|
|
|
|
function bot_crash(bot, n_bots)
|
|
|
|
local inventory = bot.get_inventory(defines.inventory.robot_cargo)
|
|
local contents = inventory.get_contents()
|
|
if global.robot_repair_setting == "Repair75" then
|
|
local item = get_crash_item(bot)
|
|
if item then
|
|
bot.surface.spill_item_stack(bot.position, {name=item, count=1}, false, bot.force, false) --Spill, mark for decon, disallow belts
|
|
end
|
|
end
|
|
for name, count in pairs(contents) do
|
|
bot.surface.spill_item_stack(bot.position, {name=name, count=count}, false, bot.force, false) --Spill, mark for decon, disallow belts
|
|
end
|
|
bot.get_inventory(defines.inventory.robot_cargo).clear()
|
|
bot.force.kill_count_statistics.on_flow(bot.name, -1) --Track bot's death.
|
|
if global.forcedata and global.forcedata[bot.force.name] and global.forcedata[bot.force.name]["robot-attrition-explosion-safety"]
|
|
and n_bots <= 500 * global.forcedata[bot.force.name]["robot-attrition-explosion-safety"] then
|
|
--game.print("Skip explosion, n_bots "..n_bots.."<= ".. 500 * global.forcedata[bot.force.name]["robot-attrition-explosion-safety"])
|
|
else
|
|
bot.surface.create_entity{name = "robot-explosion", position=bot.position}
|
|
end
|
|
if bot.valid then
|
|
bot.destroy()
|
|
end
|
|
global.bots_crashed = (global.bots_crashed or 0) + 1 -- used as an achievement metric
|
|
|
|
end
|
|
|
|
function process_bot(bot, n_bots)
|
|
local force_speed_multiplier = 1 + bot.force.worker_robots_speed_modifier
|
|
local speed = get_bot_speed(bot.name) * force_speed_multiplier
|
|
local held_item_count = 0
|
|
if bot.energy > 0 then
|
|
local inventory = bot.get_inventory(defines.inventory.robot_cargo)
|
|
held_item_count = inventory.get_item_count()
|
|
else
|
|
speed = 0.5 * speed * get_bot_slow_speed_multiplier(bot.name)
|
|
end
|
|
local speed_items = speed * (held_item_count + 0.5) -- carrying itself counts as 0.5 items
|
|
local crash_score = speed_items
|
|
bot_crash(bot, n_bots)
|
|
return crash_score
|
|
end
|
|
|
|
function on_tick(event)
|
|
--[[
|
|
slowest funtions is by far: network.logistic_robots[i]
|
|
so only do that once per explosion.
|
|
which means that if a robot is selected it must die.
|
|
but risk factors should still be speed * items carried
|
|
so add these factors to the probability of the next selection round
|
|
factors apply multiplier to next selection phase
|
|
]]--
|
|
if not global.force_surfaces then return end
|
|
if game.tick % tickskip ~= 0 then return end
|
|
|
|
--game.forces[force].logistic_networks[network].logistic_robots :: array of LuaEntity
|
|
--for _, force in pairs(game.forces) do
|
|
for force_name, force_surfaces in pairs(global.force_surfaces) do
|
|
local force = game.forces[force_name]
|
|
if not force then
|
|
global.force_surfaces[force_name] = nil
|
|
else
|
|
local i = randint
|
|
--for surface_name, networks in pairs(force.logistic_networks) do
|
|
local force_logistic_networks = force.logistic_networks -- array of surface_name, networks
|
|
for surface_name, _ in pairs(force_surfaces) do
|
|
local surface = game.surfaces[surface_name]
|
|
if not surface then
|
|
force_surfaces[surface_name] = nil
|
|
else
|
|
local networks = force_logistic_networks[surface_name]
|
|
if networks then
|
|
local surface_attrition_rate = get_attrition_rate_for_surface(game.surfaces[surface_name].index)
|
|
if surface_attrition_rate > min_attrition_rate then
|
|
for _, network in pairs(networks) do
|
|
local n_bots = network.all_logistic_robots - network.available_logistic_robots
|
|
if n_bots > 50 then -- ignore small networks
|
|
if not global.forces[force.name] then global.forces[force.name] = {} end
|
|
if not global.forces[force.name][surface_name] then global.forces[force.name][surface_name] = { crash = 0, crash_rate = 0.1 } end
|
|
local crash_rate = global.forces[force.name][surface_name].crash_rate * tickskip * surface_attrition_rate / 1000000
|
|
local crash = global.forces[force.name][surface_name].crash + crash_rate * n_bots
|
|
if crash >= 1 then
|
|
local logistic_robots = network.logistic_robots
|
|
local to_crash = math.min(math.ceil(#logistic_robots/2), 1 + math.random(math.floor(crash))) -- don't crash all
|
|
local i = math.random(#logistic_robots) -- choose a starting bot
|
|
local crashed = 0
|
|
while crashed < to_crash do
|
|
-- then step through bots
|
|
i = (i % #logistic_robots) + 1
|
|
if logistic_robots[i] and logistic_robots[i].valid then
|
|
global.forces[force.name][surface_name].crash_rate = global.forces[force.name][surface_name].crash_rate * 0.9 + 0.1 * process_bot(logistic_robots[i], n_bots)
|
|
end -- if invalid bots were found just skip them anyway
|
|
crashed = crashed + 1
|
|
end
|
|
crash = crash - crashed
|
|
end
|
|
global.forces[force.name][surface_name].crash = crash
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function on_init(event)
|
|
global.forces = {}
|
|
global.bot_speed = {}
|
|
global.bot_slow_speed_multiplier = {}
|
|
global.suface_attrition_rates = {}
|
|
|
|
global.robot_repair_setting = settings.startup["robot-attrition-repair"].value
|
|
global.crash_items = {}
|
|
end
|
|
|
|
function on_configuration_changed(event)
|
|
global.forces = {}
|
|
global.bot_speed = {}
|
|
global.bot_slow_speed_multiplier = {}
|
|
global.suface_attrition_rates = {} -- clear
|
|
if not global.force_surfaces then
|
|
for _, force in pairs(game.forces) do
|
|
if not is_system_force(force.name) then
|
|
for surface_name, networks in pairs(force.logistic_networks) do
|
|
for _, network in pairs(networks) do
|
|
if network.all_logistic_robots > 0 then
|
|
add_surface(force, game.surfaces[surface_name])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
global.robot_repair_setting = settings.startup["robot-attrition-repair"].value
|
|
global.crash_items = {}
|
|
|
|
end
|
|
function on_runtime_mod_setting_changed(event)
|
|
if event.setting == "robot-attrition-factor" then
|
|
global.suface_attrition_rates = {} -- clear
|
|
end
|
|
end
|
|
|
|
|
|
-- Surface Gathering
|
|
function add_surface(force, surface)
|
|
if not is_system_force(force.name) then
|
|
if #force.players > 0 then
|
|
global.force_surfaces = global.force_surfaces or {}
|
|
global.force_surfaces[force.name] = global.force_surfaces[force.name] or {}
|
|
if not global.force_surfaces[force.name][surface.name] then
|
|
global.force_surfaces[force.name][surface.name] = game.tick
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function on_built_roboport(event)
|
|
if not(event.created_entity and event.created_entity.valid) then return end
|
|
add_surface(event.created_entity.force, event.created_entity.surface)
|
|
end
|
|
function on_cloned_roboport(event)
|
|
if not(event.destination and event.destination.valid) then return end
|
|
add_surface(event.destination.force, event.destination.surface)
|
|
end
|
|
function on_script_built_roboport(event)
|
|
if not(event.entity and event.entity.valid) then return end
|
|
add_surface(event.entity.force, event.entity.surface)
|
|
end
|
|
script.on_event(defines.events.on_built_entity, on_built_roboport, {{filter = "type", type = "roboport"}})
|
|
script.on_event(defines.events.on_robot_built_entity, on_built_roboport, {{filter = "type", type = "roboport"}})
|
|
script.on_event(defines.events.on_entity_cloned, on_cloned_roboport, {{filter = "type", type = "roboport"}})
|
|
script.on_event(defines.events.script_raised_built, on_script_built_roboport, {{filter = "type", type = "roboport"}})
|
|
script.on_event(defines.events.script_raised_revive, on_script_built_roboport, {{filter = "type", type = "roboport"}})
|
|
|
|
-- Swarm safety
|
|
function on_research_finished(event)
|
|
local force = event.research.force
|
|
if event.research.name == "robot-attrition-explosion-safety" then
|
|
global.forcedata = global.forcedata or {}
|
|
global.forcedata[force.name] = global.forcedata[force.name] or {}
|
|
global.forcedata[force.name]["robot-attrition-explosion-safety"] = event.research.level - 1
|
|
end
|
|
end
|
|
|
|
Event.addListener(defines.events.on_research_finished, on_research_finished)
|
|
|
|
-- standard events
|
|
Event.addListener(defines.events.on_tick, on_tick)
|
|
Event.addListener(defines.events.on_runtime_mod_setting_changed, on_runtime_mod_setting_changed)
|
|
|
|
Event.addListener("on_init", on_init, true)
|
|
Event.addListener("on_configuration_changed", on_configuration_changed, true)
|
|
|