local Event = require 'utils.event' local Global = require 'utils.global' local Task = require 'utils.task' local Token = require 'utils.token' local register_on_object_destroyed = script.register_on_object_destroyed local Public = {} local turrets_map = {} local primitives = { index = nil } local Artillery = { enabled = true, turret_name = 'artillery-turret', character_projectile = 'artillery-projectile', structure_projectile = 'rocket', target_force = 'player', last_fire = 0, fire_interval = 60, -- ticks cooldown = 480, -- ticks min_range = 32, -- tiles max_range = 224, -- tiles max_targeting_attempts = 10, range_modifier = nil, manual_range_modifier = nil, creation_distance = 1.6, target_offset = { -0.15625, -0.07812 }, target_entities = { 'artillery-turret', 'artillery-wagon', 'car', 'cargo-wagon', 'character', 'flamethrower-turret', 'fluid-wagon', 'furnace', 'gun-turret', 'lab', 'laser-turret', 'locomotive', 'radar', 'silo', 'spidertron', 'tank', }, } Global.register( { turrets_map = turrets_map, primitives = primitives, artillery = Artillery, }, function(tbl) turrets_map = tbl.turrets_map primitives = tbl.primitives Artillery = tbl.artillery end) local function distance(posA, posB) return math.sqrt((posA.x - posB.x)^2 + (posA.y - posB.y)^2) end function Public.get_artillery_settings() return Artillery end function Public.set_artillery_settings(key, value) Artillery[key] = value end function Public.register(entity, refill_name) if not (entity and entity.valid) then return end if not refill_name then return end local is_item = prototypes.item[refill_name] and true or false local is_fluid = prototypes.fluid[refill_name] and true or false local is_artillery = entity.prototype.type == Artillery.turret_name if not (is_item or is_fluid or is_artillery) then return end register_on_object_destroyed(entity) local unit_id = entity.unit_number local data = { entity = entity, refill = refill_name, is_fluid = is_fluid, is_artillery = is_artillery, } if data.is_artillery then data.next_fire = game.tick end if data.is_fluid then data.fluid_stack = { name = data.refill, amount = data.entity.fluidbox.get_capacity(1) or 100 } else data.item_stack = { name = data.refill, count = prototypes.item[data.refill].stack_size } end turrets_map[unit_id] = data end function Public.remove(entity) if not entity.unit_number then return end turrets_map[entity.unit_number] = nil end function Public.reset() for k, _ in pairs(turrets_map) do turrets_map[k] = nil end end local artillery_projectile_token = Token.register(function(data) local surface = data.surface local target = data.target local source = data.source if not (surface and surface.valid) then return end if not (target and target.valid) then return end if not (source and source.valid) then return end surface.create_entity{ name = data.name, position = data.position, target = target, source = source, force = 'enemy', speed = 1.5, } end) local function simulate_automatic_artillery(data) if data.next_fire > game.tick then return end local entity = data.entity local surface = entity.surface local source_position = entity.position local range_modifier = Artillery.range_modifier or entity.force.artillery_range_modifier or 0 local manual_range_modifier = Artillery.manual_range_modifier or entity.prototype.manual_range_modifier or 0 local params = { position = source_position, radius = Artillery.max_range * (1 + range_modifier) * (1 + manual_range_modifier), name = Artillery.target_entities, force = Artillery.target_force, limit = Artillery.max_targeting_attempts, } local targets = surface.find_entities_filtered(params) if #targets == 0 then return end for i = 1, #targets do local target = targets[i] if distance(source_position, target.position) > Artillery.min_range then Task.set_timeout_in_ticks(Artillery.fire_interval, artillery_projectile_token, { surface = surface, name = target.name == 'character' and Artillery.character_projectile or Artillery.structure_projectile, position = target.position, target = target, source = entity, force = 'enemy', speed = 1.5, }) data.next_fire = game.tick + Artillery.cooldown Artillery.last_fire = game.tick break end end end local function on_object_destroyed(event) if not event.useful_id then return end turrets_map[event.useful_id] = nil end Event.add(defines.events.on_object_destroyed, on_object_destroyed) local function on_tick() if primitives.index ~= nil and turrets_map[primitives.index] == nil then primitives.index = nil return end local idx, data = next(turrets_map, primitives.index) if not (data and data.entity and data.entity.valid) then primitives.index = nil return end if Artillery.enabled and data.is_artillery and ((Artillery.last_fire + Artillery.fire_interval) < game.tick) then simulate_automatic_artillery(data) end if data.is_fluid then data.entity.insert_fluid(data.fluid_stack) else data.entity.insert(data.item_stack) end primitives.index = idx end Event.add(defines.events.on_tick, on_tick) return Public