166 lines
7.0 KiB
Lua
166 lines
7.0 KiB
Lua
local radar = {}
|
|
|
|
local GRID_SIZE = 16
|
|
local MAX_RADIUS = 3200
|
|
|
|
local function powered(entity)
|
|
return entity.energy > 0
|
|
end
|
|
|
|
-- Half-size of the square active-vision footprint, matching what the entity reveals on the map.
|
|
local function vision_half_size(entity)
|
|
if entity.type == "radar" then
|
|
return (entity.prototype.max_distance_of_nearby_sector_revealed or 3) * 32
|
|
end
|
|
local cell = entity.logistic_cell
|
|
return cell and cell.construction_radius or 0
|
|
end
|
|
|
|
local function enemy_vision(entity, force)
|
|
return entity.force ~= force and entity.force.name ~= "neutral"
|
|
end
|
|
|
|
local function sector_jammed(surface, position, force)
|
|
for _, source in pairs(surface.find_entities_filtered{type = {"radar", "roboport"}}) do
|
|
if source.active and powered(source) and enemy_vision(source, force) then
|
|
local half = vision_half_size(source)
|
|
if math.abs(position.x - source.position.x) <= half and math.abs(position.y - source.position.y) <= half then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
radar.sector_jammed = sector_jammed
|
|
|
|
local function scan_orbit(entity, planet_name)
|
|
local surface = game.planets[planet_name] and game.planets[planet_name].surface
|
|
if not surface then return end
|
|
|
|
storage.grid_pos = storage.grid_pos or {}
|
|
local scan_key = entity.force.name .. "_" .. planet_name
|
|
if not storage.grid_pos[scan_key] then
|
|
storage.grid_pos[scan_key] = {x = 0, y = 0, d = 0, step_limit = 1, steps_taken = 0, iteration = 0}
|
|
end
|
|
|
|
local state = storage.grid_pos[scan_key]
|
|
local position = {x = state.x, y = state.y}
|
|
local blocked = sector_jammed(surface, position, entity.force)
|
|
|
|
local scan_range = 1
|
|
if not blocked then
|
|
surface.request_to_generate_chunks(position, scan_range)
|
|
entity.force.chart(surface, {
|
|
{position.x - (GRID_SIZE * scan_range), position.y - (GRID_SIZE * scan_range)},
|
|
{position.x + (GRID_SIZE * scan_range), position.y + (GRID_SIZE * scan_range)}
|
|
})
|
|
end
|
|
|
|
if state.d == 0 then state.x = state.x + GRID_SIZE * scan_range * 2
|
|
elseif state.d == 1 then state.y = state.y + GRID_SIZE * scan_range * 2
|
|
elseif state.d == 2 then state.x = state.x - GRID_SIZE * scan_range * 2
|
|
else state.y = state.y - GRID_SIZE * scan_range * 2 end
|
|
|
|
state.steps_taken = state.steps_taken + 1
|
|
if state.steps_taken >= state.step_limit then
|
|
state.steps_taken = 0
|
|
state.d = (state.d + 1) % 4
|
|
state.iteration = state.iteration + 1
|
|
if state.iteration % 2 == 0 then state.step_limit = state.step_limit + 1 end
|
|
end
|
|
if math.abs(state.x) > MAX_RADIUS or math.abs(state.y) > MAX_RADIUS then
|
|
storage.grid_pos[scan_key] = {x = 0, y = 0, d = 0, step_limit = 1, steps_taken = 0, iteration = 0}
|
|
end
|
|
end
|
|
|
|
-- Stored charge as a fraction of the buffer; drives how fast a radar accumulates scans.
|
|
local function scan_power(entity)
|
|
local buffer = entity.electric_buffer_size
|
|
if not buffer or buffer == 0 then return 1 end
|
|
return math.min(1, entity.energy / buffer)
|
|
end
|
|
|
|
function radar.on_orbital_scan_tick()
|
|
storage.radar_scan_progress = storage.radar_scan_progress or {}
|
|
for _, surface in pairs(game.surfaces) do
|
|
local platform = surface.platform
|
|
local location = platform and platform.space_location
|
|
if location and game.planets[location.name] then
|
|
for _, entity in pairs(surface.find_entities_filtered{type = "radar"}) do
|
|
if entity.valid and entity.active then
|
|
local progress = (storage.radar_scan_progress[entity.unit_number] or 0) + scan_power(entity)
|
|
if progress >= 1 then
|
|
progress = progress - 1
|
|
scan_orbit(entity, location.name)
|
|
end
|
|
storage.radar_scan_progress[entity.unit_number] = progress
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Powered overlapping enemy vision squares jam each other. The engine cannot
|
|
-- darken only the overlap, so an overlapped radar is disabled entirely until
|
|
-- the other side dies, loses power, or moves out of range. Roboports count as
|
|
-- vision sources but are never disabled themselves.
|
|
function radar.on_jam_tick()
|
|
storage.jammed_entities = storage.jammed_entities or {}
|
|
for unit_number, entity in pairs(storage.jammed_entities) do
|
|
if entity.valid then entity.active = true end
|
|
storage.jammed_entities[unit_number] = nil
|
|
end
|
|
for _, surface in pairs(game.surfaces) do
|
|
if not surface.platform then
|
|
local sources = {}
|
|
for _, entity in pairs(surface.find_entities_filtered{type = {"radar", "roboport"}}) do
|
|
if entity.valid and entity.active and powered(entity) then sources[#sources + 1] = entity end
|
|
end
|
|
for i = 1, #sources do
|
|
for j = i + 1, #sources do
|
|
local a, b = sources[i], sources[j]
|
|
if enemy_vision(a, b.force) then
|
|
local reach = vision_half_size(a) + vision_half_size(b)
|
|
if math.abs(a.position.x - b.position.x) <= reach and math.abs(a.position.y - b.position.y) <= reach then
|
|
for _, entity in pairs({a, b}) do
|
|
if entity.type == "radar" then
|
|
entity.active = false
|
|
storage.jammed_entities[entity.unit_number] = entity
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function radar.on_built_entity(event)
|
|
local entity = event.entity
|
|
if not (entity and entity.valid) or entity.type ~= "radar" or entity.surface.platform then return end
|
|
|
|
-- A fresh vision source jams any enemy drop-zone lock inside its footprint.
|
|
local jammer_force, jammer_pos = entity.force, entity.position
|
|
local jammer_half = vision_half_size(entity)
|
|
for _, enemy_force in pairs(game.forces) do
|
|
if enemy_force.name ~= jammer_force.name and enemy_force.name ~= "neutral" then
|
|
storage.player_targets = storage.player_targets or {}
|
|
local key = enemy_force.name .. "_" .. entity.surface.name
|
|
local target = storage.player_targets[key]
|
|
if target then
|
|
local dx, dy = math.abs(target.position.x - jammer_pos.x), math.abs(target.position.y - jammer_pos.y)
|
|
if dx <= jammer_half and dy <= jammer_half then
|
|
storage.player_targets[key] = nil
|
|
local message = "[color=red][JAMMER] Your drop zone at [" .. math.floor(target.position.x)
|
|
.. ", " .. math.floor(target.position.y) .. "] on " .. entity.surface.name
|
|
.. " has been JAMMED by " .. jammer_force.name .. "![/color]"
|
|
for _, player in pairs(enemy_force.players) do if player.valid then player.print(message) end end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return radar
|