Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Code mostly reused from folk
- -- Original Mod: https://mods.factorio.com/mods/folk/folk-radar
- -------------------------------------------------------------------------------
- -- CONFIG
- --
- -- On on_configuration_changed, if we detect any entities of type "radar"
- -- that are NOT included in this table, it means we need to track radars
- -- being .destroy()'d and such in ontick, which is expensive.
- -- So, to be clear: we only track radar destruction if we find any
- -- radar-type entities that are NOT in this table.
- -- We also check if the radar has a max_distance_of_sector_revealed property larger than zero.
- -- And if it does not, we do not track that either.
- -- Regardless, we only scan for instantly-replaced entities one time per on_configuration_changed.
- -- Otherwise we depend on on_built_entity/on_robot_built_entity.
- local CONFIG_TRACK_IGNORE = {
- radar = true,
- ["smart-display-radar"] = true, -- Smart Display
- ["vehicle-deployer-belt"] = true, -- AAI
- ["train-tracker"] = true, -- Vehicle Radar
- ["vehicular-tracker"] = true, -- Vehicle Radar
- }
- -- ZZZ needs to be the same as in data-final-fixes.lua
- local CONFIG_REFERENCE_STRING = "zzz radar-signals [[name]]"
- -------------------------------------------------------------------------------
- -- UTILITY
- --
- -- true/false
- local function isValidRadar(radar)
- local reference = game.item_prototypes[CONFIG_REFERENCE_STRING:gsub("%[name%]", tostring(radar.name))]
- if type(reference) == "table" then return true end
- return false
- end
- -- Can return nil
- local function findRadar(cc)
- local radars = cc.surface.find_entities_filtered({
- area = { { cc.position.x - 1, cc.position.y - 1 }, { cc.position.x + 1, cc.position.y + 1 } },
- type = "radar",
- force = cc.force,
- })
- if type(radars) == "table" then
- for _, radar in next, radars do
- if isValidRadar(radar) then
- return radar
- end
- end
- end
- end
- -------------------------------------------------------------------------------
- -- SCANNING FOR UNITS
- --
- local scan
- do
- -- ZZZ
- -- This map probably can grow quite large during a play session, but
- -- I dont really care.
- local radarCoverageMap = {}
- -- We presume that radars dont move!
- local function cacheCoverage(radar)
- local name = radar.name or "radar"
- local reference = game.item_prototypes[CONFIG_REFERENCE_STRING:gsub("%[name%]", tostring(name))]
- local chunks = reference and reference.order and tonumber(reference.order) or 14
- local tiles = ((chunks / 4) * 32) + 3 -- One chunk is 32 tiles
- --print("Radar coverage for " .. tostring(name) .. " is " .. tostring(chunks) .. "/" .. tostring(tiles))
- local x, y = radar.position.x, radar.position.y
- radarCoverageMap[radar.unit_number] = {
- area = { { x - tiles, y - tiles }, { x + tiles, y + tiles } },
- type = "unit",
- }
- --print(serpent.block(radarCoverageMap[radar.unit_number]))
- end
- local params = {
- parameters = {
- {
- index = 1,
- count = 0,
- signal = {
- type = "virtual",
- name = "signal-radar-signals-units"
- }
- },
- { index = 2,
- count = 0,
- signal = {
- type = "virtual",
- name = "signal-radar-signals-worms"
- }
- },
- { index = 3,
- count = 0,
- signal = {
- type = "virtual",
- name = "signal-radar-signals-spawners"
- }
- },
- }
- }
- scan = function(radar, control)
- -- Cached
- if not radarCoverageMap[radar.unit_number] then cacheCoverage(radar) end
- local scanUnits = -1
- local scanWorms = -1
- local scanSpawners = -1
- for i=1, control.signals_count do
- local sig = control.get_signal( i )
- if sig and sig.signal and sig.signal.name then
- if sig.signal.name == "signal-radar-signals-units" then
- scanUnits = i
- elseif sig.signal.name == "signal-radar-signals-worms" then
- scanWorms = i
- elseif sig.signal.name == "signal-radar-signals-spawners" then
- scanSpawners = i
- end
- end
- end
- if (scanUnits >= 0 or scanWorms >= 0 or scanSpawners >= 0) then
- local units, worms, bases = 0, 0, 0
- if scanUnits >= 0 then
- local entities = radar.surface.find_entities_filtered({
- area = radarCoverageMap[radar.unit_number].area,
- type = {"unit", "player", "car", "locomotive"},
- })
- if entities and #entities > 0 then
- for _, ent in next, entities do
- if ent and ent.valid and ent.force then
- if ent.force == radar.force then
- units = units + 0
- else
- units = units + 1
- end
- end
- end
- end
- control.set_signal( scanUnits, {
- count = units,
- signal = {
- type = "virtual",
- name = "signal-radar-signals-units"
- }
- })
- end
- if scanWorms >= 0 then
- local enemyWorms = radar.surface.find_entities_filtered({
- area = radarCoverageMap[radar.unit_number].area,
- type = {"turret", "artillery-turret", "ammo-turret", "electric-turret", "fluid-turret", "artillery-wagon"},
- })
- if enemyWorms and #enemyWorms > 0 then
- for _, ent in next, enemyWorms do
- if ent and ent.valid and ent.force then
- if ent.force == radar.force then
- worms = worms + 0
- else
- worms = worms + 1
- end
- end
- end
- end
- control.set_signal( scanWorms, {
- count = worms,
- signal = {
- type = "virtual",
- name = "signal-radar-signals-worms"
- }
- })
- end
- if scanSpawners >= 0 then
- local enemyBases = radar.surface.find_entities_filtered({
- area = radarCoverageMap[radar.unit_number].area,
- type = "unit-spawner",
- })
- if enemyBases and #enemyBases > 0 then
- for _, ent in next, enemyBases do
- if ent and ent.valid and ent.force then
- if ent.force == radar.force then
- bases = bases + 0
- else
- bases = bases + 1
- end
- end
- end
- end
- control.set_signal( scanSpawners, {
- count = bases,
- signal = {
- type = "virtual",
- name = "signal-radar-signals-spawners"
- }
- })
- end
- end
- end
- end
- local function Execute()
- -- Instead using table.remove while moving reporters items to reportersDone here used a runner
- global.reportersOffset = global.reportersOffset + 1
- if global.reportersOffset > #global.reporters then
- -- game.print("Index out of range")
- return
- end
- local m = global.reporters[global.reportersOffset]
- if m and m.cc and m.cc.valid then
- if global.needToTrackRadars and (not m.radar or not m.radar.valid) and not m.ignore then
- local newRadar = findRadar(m.cc)
- if newRadar then
- m.radar = newRadar
- else
- -- We didnt find any radar. Assume that there just isnt one there, and dont scan again.
- -- Until on_configuration_changed.
- m.ignore = true
- end
- end
- if m.radar and m.radar.valid and m.radar.energy > 0 then
- scan(m.radar, m.cb)
- end
- -- This combinator is still executable, save from next gigatick
- global.reportersDone[#global.reportersDone + 1] = m;
- end
- end
- -------------------------------------------------------------------------------
- -- TICK HANDLER
- -- Fired every 240 ticks (4 seconds?)
- --
- local tick
- do
- -- Every 4 seconds, so lets be careful!
- tick = function(event)
- if not global.reporters then return end
- if not global.reportersDone then global.reportersDone = {} end
- -- I dont know how works with nil integers, maybe this codepart is silly
- if type(global.reportersOffset) ~= "number" then global.reportersOffset = 0; end
- local totalTicks = 240; -- Maybe do a map option?
- local leaseTicks = totalTicks - 1 - event.tick % totalTicks;
- if leaseTicks == 0 then
- if #global.reporters > global.reportersOffset then
- -- Strange! But sometimes in first zero-tick this happens. Calc least repos.
- -- game.print("Unexecuted " .. (#global.reporters - global.reportersOffset + 1) .. " items")
- while #global.reporters > global.reportersOffset do
- Execute()
- end
- end
- -- swapping tablesses
- global.reporters = global.reportersDone
- -- Until not possible to use
- -- lua_createtable(global.reportersDone, #global.reporters, 0)
- -- Which set hashtable stack to whole array we have little sagging in first parts of gigatick
- global.reportersDone = {} -- Daddy GC will care unlinked table itself
- global.reportersOffset = 0;
- -- game.print("Done worked with " .. #global.reporters .. " radar combinators")
- else
- local currentReportersCount = 0;
- if #global.reporters >= totalTicks then
- currentReportersCount = math.floor((#global.reporters - global.reportersOffset) / leaseTicks)
- else
- local unFloor = totalTicks / #global.reporters
- if(unFloor >= 2) then
- -- uniform distribution
- local floor = math.floor(unFloor)
- if (leaseTicks % floor) == 0 then
- currentReportersCount = 1
- else
- currentReportersCount = 0
- -- game.print("LT " .. leaseTicks .. ": Nit, floor is " .. floor)
- end
- else
- --uniform dedistribution
- local floor = math.floor(totalTicks / (totalTicks - #global.reporters))
- if (leaseTicks % floor) == 0 then
- currentReportersCount = 0
- -- game.print("LT " .. leaseTicks .. ": Nit, defloor is " .. floor)
- else
- currentReportersCount = 1
- end
- end
- end
- -- if currentReportersCount > 0 then
- -- game.print("LT " .. leaseTicks .. ": " .. currentReportersCount .. " items")
- -- end
- for _ = 1, currentReportersCount do
- Execute()
- end
- end
- end
- end
- -------------------------------------------------------------------------------
- -- ON BUILT HANDLER
- --
- do
- local function onBuilt(event)
- local e = event.created_entity
- if event and e then
- if e.name == "radar-signals" then
- if not global.reporters then global.reporters = {} end
- if not global.reportersDone then global.reportersDone = {} end
- if #global.reporters == 0 then
- script.on_event(defines.events.on_tick, tick)
- end
- -- Prevents clicking it and using it like a normal constant combinator
- e.operable = true
- --e.get_control_behavior().parameters = nil
- -- find out if we are next to a radar
- local newRadar = findRadar(e)
- -- Nothing to do for now, but lets wait for a radar
- if newRadar then
- global.reporters[#global.reporters + 1] = { cc = e, cb = e.get_control_behavior(), radar = newRadar }
- else
- global.reporters[#global.reporters + 1] = { cc = e, cb = e.get_control_behavior() }
- end
- elseif e.type == "radar" then
- -- find out if there is a reporter next to this radar
- local cc = e.surface.find_entities_filtered({
- area = { { e.position.x - 2.3, e.position.y - 2.3 }, { e.position.x + 2.3, e.position.y + 2.3 } },
- name = "radar-signals",
- force = e.force,
- limit = 1,
- })
- if type(cc) ~= "table" or #cc == 0 then return end
- for _, m in next, global.reporters do
- if m and m.cc and m.cc.valid and m.cc.unit_number == cc[1].unit_number then
- m.radar = e
- m.ignore = nil
- --m.cb.parameters = nil
- break
- end
- end
- end
- end
- end
- script.on_event(defines.events.on_built_entity, onBuilt)
- script.on_event(defines.events.on_robot_built_entity, onBuilt)
- end
- -------------------------------------------------------------------------------
- -- CONFIGURATION HANDLING
- -- Mostly checks if there are 3rd party mods that add custom radars.
- -- If there is, we presume that they instantly destroy() and create_entity()
- -- new radar types, which does NOT trigger any events.
- --
- -- So if we find any custom radar types in the data, we set a flag that enables
- -- each CC-box to look around itself on the next tick (4 seconds) for any
- -- randomly-appearing radars.
- --
- -- If it cant find any on that tick, it wont look again until the next
- -- on_configuration_changed, but will obviously react to one that is built next
- -- to it.
- --
- do
- local function checkForWeirdRadars()
- local found = false
- for name, ent in pairs(game.entity_prototypes) do
- if ent.type == "radar" and not CONFIG_TRACK_IGNORE[name] then
- -- Check if this radar type has a max_distance_of_sector_revealed bigger than zero
- if isValidRadar(ent) then
- global.needToTrackRadars = true
- found = true
- break
- end
- end
- end
- if not found then global.needToTrackRadars = false end
- -- Mark CCs for re-scanning for radar entities that are instantly replaced
- if global.reporters and #global.reporters > 0 then
- for _, m in next, global.reporters do
- m.ignore = nil
- end
- end
- if global.reportersDone and #global.reportersDone > 0 then
- for _, m in next, global.reportersDone do
- m.ignore = nil
- end
- end
- end
- script.on_configuration_changed(checkForWeirdRadars)
- script.on_load(function()
- if #global.reporters > 0 then
- script.on_event(defines.events.on_tick, tick)
- end
- end)
- script.on_init(function()
- global.reporters = global.reporters or {}
- global.reportersDone = global.reportersDone or {}
- global.reportersOffset = global.reportersOffset or 0 -- Because table.remove is very expensively operation we use offset
- global.needToTrackRadars = global.needToTrackRadars or false
- checkForWeirdRadars()
- end)
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement