diff --git a/better-air-filtering/control.lua b/better-air-filtering/control.lua index 6ae8cc1..9cafd39 100644 --- a/better-air-filtering/control.lua +++ b/better-air-filtering/control.lua @@ -1,47 +1,20 @@ +-- ################# +-- # Constants # +-- ################# + +local INTERVAL = 30 -function updateAirFilters(surface, filters) - for _, filter in pairs(filters) do - local chunk_pollution = surface.get_pollution(filter.position) - if chunk_pollution > 0 then - local suctionRate = getSuctionRate(filter) - game.print(suctionRate) - if suctionRate > 0 then - local to_insert = math.min(chunk_pollution, suctionRate) - local inserted_amount = filter.insert_fluid({name="pollution", amount=to_insert}) - surface.pollute(filter.position, -inserted_amount) - game.print("Inserted: " .. inserted_amount) - end - end - end -end +-- ################# +-- # Utilities # +-- ################# -function groupByChunk(entities) - local chunks = {} - for _, e in pairs(entities) do - local chunk = positionToChunk(e.position) - local chunkListX = chunks[chunk.x] or {} - local chunkList = chunkListX[chunk.y] or {} - table.insert(chunkList, e) - chunkListX[chunk.y] = chunkList - chunks[chunk.x] = chunkListX - end - local pretty_chunks = {} - for chunkX, t in pairs(chunks) do - for chunkY, l in pairs(t) do - print(t) - table.insert(pretty_chunks, {chunk={x = chunkX, y = chunkY}, entities=l}) - end - end - return pretty_chunks +function starts_with(str, start) + return str:sub(1, #start) == start end function positionToChunk(position) - return {x=math.floor(position.x / 32), y=math.floor(position.y / 32)} -end - -function chunkToPosition(chunk) - return {x=chunk.x *32, y=chunk.y*32} + return { x = math.floor(position.x / 32), y = math.floor(position.y / 32) } end function movePollution(surface, chunkFrom, chunkTo, amount) @@ -54,9 +27,9 @@ end function getBasePurificationRate(entity) -- Depends mostly on recipe. Should be multiplied by crafting speed to achieve actual max purification rate if entity.name == "air-filter-machine-1" then - return 4 -- 4 max pollution cleaning per second among mk1 recipes + return 4 -- 4 max pollution cleaning per second among mk1 recipes elseif entity.name == "air-filter-machine-2" or entity.name == "air-filter-machine-3" then - return 6 -- 6 max pollution cleaning for mk2 and mk3 recipes (liquid) + return 6 -- 6 max pollution cleaning for mk2 and mk3 recipes (liquid) else return 0 end @@ -70,6 +43,10 @@ function getSuctionRate(entity) end end +function getAbsorptionRate(entity) + return math.min(getSpaceForPollution(entity), getSuctionRate(entity)) +end + function energyCraftingModifier(entity) -- Approximation to speed modifier for machine running out of power if entity.electric_buffer_size then @@ -92,24 +69,104 @@ function getSpaceForPollution(entity) return capacity - pollution end +function inRadius(filter, radius) + if filter.name == "air-filter-machine-1" then + return radius <= 0 + elseif filter.name == "air-filter-machine-2" then + return radius <= 2 + elseif filter.name == "air-filter-machine-3" then + return radius <= 3 + else + return false + end +end + +-- ##################### +-- # Update script # +-- ##################### + +function insertPollution(event) +-- game.print("Filtered chunks:") +-- for _, c in pairs(global.air_filtered_chunks) do +-- game.print(c.x .. ", " .. c.y) +-- end +-- +-- game.print("Chunkmap:") +-- for surface, chunkListX in pairs(global.air_filtered_chunks_map) do +-- game.print("Surface " .. surface) +-- for x, chunkListY in pairs(chunkListX) do +-- game.print("x: " .. x) +-- for y, chunk in pairs(chunkListY) do +-- game.print("y: " .. y) +-- end +-- end +-- end + + game.print("insertPollution") + for _, c in pairs(global.air_filtered_chunks) do + absorbChunk(c) + end +end + +function absorbChunk(chunk) + if chunk:get_pollution() == 0 then + return + end + + local totalAbsorption = 0.0 + for _, filter in pairs(chunk.filters) do + local absorptionRate = getAbsorptionRate(filter) * INTERVAL / 60 + totalAbsorption = totalAbsorption + absorptionRate + end + + if totalAbsorption == 0 then + return + end + + local toAbsorb = math.min(chunk:get_pollution() , totalAbsorption) + -- game.print("To absorb: " .. toAbsorb) + + local totalInsertedAmount = 0.0 + for _, filter in pairs(chunk.filters) do + local toInsert = ((getAbsorptionRate(filter) * INTERVAL / 60) / totalAbsorption) * toAbsorb + if toInsert > 0 then + local insertedAmount = filter.insert_fluid({ name = "pollution", amount = toInsert }) + totalInsertedAmount = totalInsertedAmount + insertedAmount + end + end + -- game.print("Total inserted: " .. totalInsertedAmount) + assert(math.abs(toAbsorb - totalInsertedAmount) < 0.01, "Error with inserting pollution in air filter machine. Different amounts absorbed/inserted: " .. toAbsorb .. " absorbed and " .. totalInsertedAmount .. " inserted.") + chunk:pollute(-totalInsertedAmount) +end + + +function generateRadiusUpdateFunctions(radius) + return {} +end + + function generateFunctions() local functions = {} - local function test(event) - for _, surface in pairs(game.surfaces) do - local filters = surface.find_entities_filtered { - name = {"air-filter-machine-1", "air-filter-machine-2", "air-filter-machine-3"} - } - updateAirFilters(surface, filters, 0) +-- for i = 1, 110 do +-- table.insert(functions, function(event) +-- end) +-- end +-- +-- table.insert(functions, init) + table.insert(functions, insertPollution) + + for radius = 1, 4 do + for _, f in pairs(generateRadiusUpdateFunctions(radius)) do + table.insert(functions, f) end end - table.insert(functions, test) - return functions end + function spreadOverTicks(functions, interval) local tickMap = {} local funcs = {} @@ -129,7 +186,7 @@ function spreadOverTicks(functions, interval) local function onTick(event) local step = (event.tick % interval) + 1 - game.print("Step: " .. step) + -- game.print("Step: " .. step) local funcs = tickMap[step] if funcs ~= nil then for _, f in pairs(funcs) do @@ -142,9 +199,173 @@ function spreadOverTicks(functions, interval) end +function FilteredChunk(surface, x, y) + local self = { + surface = surface, + x = x, + y = y, + filters = {}, + } + + local function equal(self, other) + return self.surface.name == other.surface.name and self.x == other.x and self.y == other.y + end + + local function addToMap() + game.print("Adding chunk to map") + local chunkListX = global.air_filtered_chunks_map[surface.name] or {} + local chunkListY = chunkListX[x] or {} + assert(chunkListY[y] == nil, "Chunklist entry should not exist yet.") + chunkListY[y] = self + chunkListX[x] = chunkListY + global.air_filtered_chunks_map[surface.name] = chunkListX + table.insert(global.air_filtered_chunks, self) + end + + local function removeFromMap() + game.print("Removing chunk from map") + table.remove(global.air_filtered_chunks_map[surface.name][x], y) + for i, c in pairs(global.air_filtered_chunks) do + if equal(self, c) then + table.remove(global.air_filtered_chunks, i) + break + end + end + end + + local function get_pollution(self) + return self.surface.get_pollution(self:toPosition()) + end + + local function pollute(self, amount) + self.surface.pollute(self:toPosition(), amount) + end + + local function toPosition(self) + return { x = self.x * 32, y = self.y * 32 } + end + + local function addFilter(self, filter) + table.insert(self.filters, filter) + if #self.filters == 1 then + addToMap() + end + end + + local function removeFilter(self, filter) + for i, f in pairs(self.filters) do + if f.unit_number == filter.unit_number then + table.remove(self.filters, i) + break + end + end + if #self.filters == 0 then + removeFromMap() + end + end + + self.get_pollution = get_pollution + self.pollute = pollute + self.toPosition = toPosition + self.addFilter = addFilter + self.removeFilter = removeFilter + return self +end + +function getFilteredChunk(surface, x, y) + local chunkListX = global.air_filtered_chunks_map[surface.name] + if chunkListX ~= nil then + local chunkListY = chunkListX[x] + if chunkListY ~= nil then + local chunk = chunkListY[y] + if chunk ~= nil then + return chunk + end + end + end + return FilteredChunk(surface, x, y) +end + + +--function groupByChunk(entities) +-- local chunks = {} +-- for _, e in pairs(entities) do +-- local chunk = positionToChunk(e.position) +-- local chunkListX = chunks[chunk.x] or {} +-- local chunkList = chunkListX[chunk.y] or {} +-- table.insert(chunkList, e) +-- chunkListX[chunk.y] = chunkList +-- chunks[chunk.x] = chunkListX +-- end +-- local pretty_chunks = {} +-- for chunkX, t in pairs(chunks) do +-- for chunkY, l in pairs(t) do +-- print(t) +-- table.insert(pretty_chunks, { chunk = { x = chunkX, y = chunkY }, entities = l }) +-- end +-- end +-- return pretty_chunks +--end + + + +-- ################# +-- # callbacks # +-- ################# + +function isAirFilterMachine(entity) + return starts_with(entity.name, "air-filter-machine-") +end + +function OnEntityCreated(event) + game.print("entity created") + game.print(event.created_entity.name) + if isAirFilterMachine(event.created_entity) then + game.print("air filter created") + local chunkPos = positionToChunk(event.created_entity.position) + local chunk = getFilteredChunk(event.created_entity.surface, chunkPos.x, chunkPos.y) + chunk:addFilter(event.created_entity) + game.print("Air filter added to chunk") + end +end + +function OnEntityRemoved(event) + if isAirFilterMachine(event.entity) then + local chunkPos = positionToChunk(event.entity.position) + local chunk = getFilteredChunk(event.entity.surface, chunkPos.x, chunkPos.y) + chunk:removeFilter(event.entity) + end +end + +script.on_event({ defines.events.on_built_entity, defines.events.on_robot_built_entity }, OnEntityCreated) +script.on_event({ defines.events.on_pre_player_mined_item, defines.events.on_robot_pre_mined, defines.events.on_entity_died }, OnEntityRemoved) + + +function init() + game.print("Init") + -- gather all filters on every surface + global.air_filtered_chunks_map = {} + global.air_filtered_chunks = {} + for _, surface in pairs(game.surfaces) do + local filters = surface.find_entities_filtered { + name = { "air-filter-machine-1", "air-filter-machine-2", "air-filter-machine-3" } + } + for _, filter in pairs(filters) do + game.print(#filters) + local chunkPos = positionToChunk(filter.position) + local chunk = getFilteredChunk(surface, chunkPos.x, chunkPos.y) + game.print("Chunk: " .. chunk.x .. ", " .. chunk.y) + chunk:addFilter(filter) + end + end +end + +script.on_init(init) +script.on_configuration_changed(init) + script.on_load(function() local functions = generateFunctions() - local onTick = spreadOverTicks(functions, 30) + local onTick = spreadOverTicks(functions, INTERVAL) script.on_event(defines.events.on_tick, onTick) end)