Compare commits

...

19 Commits

Author SHA1 Message Date
Joey De Pauw c359a92d45 changelog 3.3 2021-03-09 16:46:10 +01:00
JoeyDP 986997ece2
Merge pull request #2 from AngledLuffa/fewer_tables
Fewer tables
2021-03-09 16:42:29 +01:00
Joey De Pauw 226be2bbef changelog 3.2 2021-03-09 16:30:35 +01:00
John Bauer 35e1c523af Optimize a division 2021-03-08 13:41:18 -08:00
John Bauer 798ed388cf Slightly optimize the tables in the absorb and suction functions 2021-03-08 11:56:17 -08:00
John Bauer 422e24bf32 Create fewer tables when spreading pollution 2021-03-08 10:51:09 -08:00
John Bauer 680e2c09e0 Bump version number 2021-03-07 08:16:06 -08:00
John Bauer 181b9a71cd Rearrange a couple functions for readability 2021-02-27 09:12:54 -08:00
John Bauer a3394b3759 Add some comments 2021-02-27 08:02:50 -08:00
Joey De Pauw 76dbc156c7 Merge branch 'master' of github.com:JoeyDP/Factorio-Better-Air-Filtering 2021-02-05 18:57:54 +01:00
Joey De Pauw e7f576a1b3 apply fix by boskid 2021-02-05 18:57:31 +01:00
JoeyDP 1d42185a7d
Merge pull request #1 from EldrinnElantey/master
Localization into Russian
2021-02-05 18:45:23 +01:00
Eldrinn Elantey 6e9e8087dc Update air-filtering.cfg 2021-02-01 23:35:36 +04:00
Eldrinn Elantey f487bdbc1d Localization into Russian 2021-02-01 23:24:17 +04:00
Joey De Pauw d92ed1e91e Factorio 1.1 2020-12-05 14:38:48 +01:00
Joey De Pauw 29614e6d26 v2.3 2020-01-04 13:36:59 +01:00
Joey De Pauw ff0d0c0d8d v2.2 2020-01-03 16:17:35 +01:00
Joey De Pauw 3de4a98b64 v2.1 2019-12-14 18:03:46 +01:00
Joey De Pauw 7f08bc633b update readme 2019-12-06 20:22:23 +01:00
16 changed files with 200 additions and 116 deletions

View File

@ -19,7 +19,7 @@ The upgraded version has a stronger filtering effect. In addition, by using a mo
The third and final upgrade of the air filtering machine features a larger radius of three chunks along with more air filtering per second.
### Filter Types
There are currently two types of filters: __expendable filters__ and __recyclable filters__. The first ones are easier to craft, but filter out less pollution and are destroyed upon use. Recyclable filters are more expensive but can be refreshed with a bit of coal to be used again.
There are currently two types of filters: __expendable filters__ and __recyclable filters__. The first ones are easier to craft, but filter out less pollution and are destroyed upon use. Recyclable filters are more expensive but can be refreshed with a bit of coal to be used again. However, beware that there is a slight chance the filter breaks in the recycling process.
> Known issue: used air filters cannot be extracted from the first tier of air filter machines by inserters. Factorio does not feature inserters that can extract from burnt_result_inventory.
@ -27,7 +27,7 @@ There are currently two types of filters: __expendable filters__ and __recyclabl
## Technical Details
Some things to keep in mind when using this mod:
- Pollution updates are very UPS efficient. The suction of pollution is implemented per chunk, rather than per machine and updates are spread evenly over an interval of 20 ticks.
- Pollution updates are very UPS efficient. The suction of pollution is implemented per chunk, rather than per machine and updates are spread evenly over an interval of 20 ticks (default). The interval can be changed in the settings.
- The [evolution](https://wiki.factorio.com/Enemies#Evolution) factor of enemies is based on the total amount of produced pollution. Cleaning it back up does not reverse this effect. This means that biters will still get stronger, no matter how proficient you are at cleaning pollution. You can prevent attacks caused by pollution reaching the biter nests however.
- Pollution from other chunks is sucked toward air filters at an exponentially decreasing rate depending on the distance. The formula is `suction = base_suction * (1/4)^distance`. This means that filters only pull in approximately as much pollution as they can filter.
- When an air filter machine or any other entity containing pollution is destroyed or mined, the pollution inside is dispersed back into the atmosphere.

View File

@ -1,4 +1,40 @@
---------------------------------------------------------------------------------------------------
Version: 0.3.3
Date: 2021.03.09
Performance:
- Merge pull request by AngledLuffa to optimize code for pollution suction (https://github.com/JoeyDP/Factorio-Better-Air-Filtering/pull/2).
---------------------------------------------------------------------------------------------------
Version: 0.3.2
Date: 2021.02.05
Fixes:
- Apply fix by boskid: https://forums.factorio.com/95616
---------------------------------------------------------------------------------------------------
Version: 0.3.1
Date: 2020.12.05
Updates:
- Updated to Factorio 1.1.
---------------------------------------------------------------------------------------------------
Version: 0.3.0
Date: 2020.01.24
Updates:
- Updated to Factorio 0.18.
---------------------------------------------------------------------------------------------------
Version: 0.2.3
Date: 2020.01.04
Fixes:
- Register on_tick event on a different callback.
---------------------------------------------------------------------------------------------------
Version: 0.2.2
Date: 2020.01.03
Fixes:
- Air filter machine mk1 changed to type furnace.
- Air filter machine mk1 removed from fast replace group.
---------------------------------------------------------------------------------------------------
Version: 0.2.1
Date: 2019.12.14
Fixes:
- Desync when upgrading from 0.1.3 should be fixed.
---------------------------------------------------------------------------------------------------
Version: 0.2.0
Date: 2019.12.06
Performance:

View File

@ -2,7 +2,7 @@
-- # Constants #
-- #################
local INTERVAL = settings.global["baf-update-interval"].value
local INTERVAL
@ -58,18 +58,6 @@ function energyCraftingModifier(entity)
end
end
function getSuctionRate(entity)
if not entity.is_crafting() and getSpaceForPollution(entity) == 0 then
return 0
else
return getBasePurificationRate(entity) * entity.crafting_speed * energyCraftingModifier(entity)
end
end
function getAbsorptionRate(entity)
return math.min(getSpaceForPollution(entity), getSuctionRate(entity))
end
function pollutionInPollutedWater(amount)
return amount * 6 / 10
end
@ -87,6 +75,26 @@ function getSpaceForPollution(entity)
return capacity - pollution
end
function getSuctionRate(entity)
if not entity.is_crafting() and getSpaceForPollution(entity) == 0 then
return 0
else
return getBasePurificationRate(entity) * entity.crafting_speed * energyCraftingModifier(entity)
end
end
function getAbsorptionRate(entity)
return math.min(getSpaceForPollution(entity), getSuctionRate(entity))
end
function getTotalAbsorptionRate(filters)
local totalAbsorptionRate = 0.0
for _, filter in pairs(filters) do
totalAbsorptionRate = totalAbsorptionRate + getAbsorptionRate(filter)
end
return totalAbsorptionRate
end
function inRadius(filter, radius)
if filter.name == "air-filter-machine-1" then
return radius <= 0
@ -99,7 +107,6 @@ function inRadius(filter, radius)
end
end
-- #####################
-- # Update script #
-- #####################
@ -113,27 +120,31 @@ function absorbPollution(event)
end
function absorbChunk(chunk)
if chunk:get_pollution() == 0 then
local chunk_pollution = chunk:get_pollution()
if chunk_pollution == 0 then
return
end
local totalAbsorptionRate = chunk:getTotalAbsorptionRate()
local filters = chunk:getFilters()
local totalAbsorptionRate = getTotalAbsorptionRate(filters)
--game.print("totalAbsorptionRate: " .. totalAbsorptionRate)
--game.print("filter count: " .. #chunk.filters)
--game.print("filter count: " .. #filters)
if totalAbsorptionRate == 0 then
return
end
local toAbsorb = math.min(chunk:get_pollution(), totalAbsorptionRate)
local toAbsorb = math.min(chunk_pollution, totalAbsorptionRate)
-- game.print("To absorb: " .. toAbsorb)
local totalInsertedAmount = 0.0
for _, filter in pairs(chunk:getFilters()) do
local fluid = { name = "pollution", amount = 0.0 }
for _, filter in pairs(filters) do
local toInsert = (getAbsorptionRate(filter) / totalAbsorptionRate) * toAbsorb
if toInsert > 0 then
local insertedAmount = filter.insert_fluid({ name = "pollution", amount = toInsert })
fluid.amount = toInsert
local insertedAmount = filter.insert_fluid(fluid)
game.pollution_statistics.on_flow(filter.name, -insertedAmount)
totalInsertedAmount = totalInsertedAmount + insertedAmount
end
@ -149,10 +160,10 @@ function stepsToOrigin(x, y)
-- Provide coordinates of possible 1-steps toward (0, 0)
local steps = {}
if x ~= 0 then
table.insert(steps, { x = x - sign(x), y = y })
table.insert(steps, { x - sign(x), y })
end
if y ~= 0 then
table.insert(steps, { x = x, y = y - sign(y) })
table.insert(steps, { x, y - sign(y) })
end
return steps
end
@ -167,24 +178,26 @@ function suctionUpdateChunk(chunkTo, dx, dy)
-- game.print("From " .. dx .. ", " .. dy)
-- game.print("suction: " .. totalSuction)
local chunkFrom = getFilteredChunk(chunkTo.surface, chunkTo.x + dx, chunkTo.y + dy)
local test = chunkFrom:getTotalSuctionRate(0)
local pollution = chunkFrom:get_pollution()
local surface = chunkTo.surface
-- get_pollution can handle indexed x, y as well as named x, y
local position = { (chunkTo.x + dx) * 32, (chunkTo.y + dy) * 32 }
local pollution = surface.get_pollution(position)
if pollution > 0.1 then
local toPollute = math.min(pollution, totalSuction)
local chunksVia = {}
for _, step in pairs(stepsToOrigin(dx, dy)) do
local chunk = getFilteredChunk(chunkTo.surface, chunkTo.x + step.x, chunkTo.y + step.y)
table.insert(chunksVia, chunk)
end
-- first, unpollute the chunkFrom
surface.pollute(position, -toPollute)
--game.print("Moving " .. toPollute .. " pollution")
--game.print("From: " .. position[1] .. ", " .. position[2] .. " (" .. toPollute .. ")")
-- game.print("Moving " .. toPollute .. " pollution")
-- game.print("From: " .. chunkFrom.x .. ", " .. chunkFrom.y)
for _, chunkVia in pairs(chunksVia) do
-- game.print("To: " .. chunkVia.x .. ", " .. chunkVia.y)
chunkVia:pollute(toPollute / #chunksVia)
local steps = stepsToOrigin(dx, dy)
toPollute = toPollute / #steps
for _, step in pairs(steps) do
position[1] = (chunkTo.x + step[1]) * 32
position[2] = (chunkTo.y + step[2]) * 32
surface.pollute(position, toPollute)
--game.print("To: " .. position[1] .. ", " .. position[2] .. " (" .. toPollute .. ")")
end
chunkFrom:pollute(-toPollute)
end
end
@ -375,15 +388,6 @@ function FilteredChunk:removeFromMap()
--end
end
function FilteredChunk:getTotalAbsorptionRate()
local totalAbsorptionRate = 0.0
for _, filter in pairs(self:getFilters()) do
local absorptionRate = getAbsorptionRate(filter)
totalAbsorptionRate = totalAbsorptionRate + absorptionRate
end
return totalAbsorptionRate
end
function FilteredChunk:getTotalSuctionRate(distance)
local totalSuctionRate = 0.0
for _, filter in pairs(self:getFilters()) do
@ -414,6 +418,9 @@ function FilteredChunk:addFilter(filter)
end
end
-- Remove a filter machine from its chunk
-- If this is removing the last filter, the record
-- for this chunk is dropped.
function FilteredChunk:removeFilter(filter)
for i, f in pairs(self.filters) do
if f.unit_number == filter.unit_number then
@ -426,6 +433,9 @@ function FilteredChunk:removeFilter(filter)
end
end
-- either return an existing FilteredChunk for the given coordinates,
-- or create a new one at the corresponding coordinates if there are
-- no existing filters on this chunk
function getFilteredChunk(surface, x, y)
local chunkListX = global.air_filtered_chunks_map[surface.name]
if chunkListX ~= nil then
@ -450,6 +460,8 @@ function isAirFilterMachine(entity)
return starts_with(entity.name, "air-filter-machine-")
end
-- when a new machine is created, add it to the list of filters
-- on whichever chunk it is on
function onEntityCreated(event)
if isAirFilterMachine(event.created_entity) then
local chunkPos = positionToChunk(event.created_entity.position)
@ -530,6 +542,21 @@ function preEntityRemoved(event)
end
end
-- Set up callbacks
script.on_event({ defines.events.on_built_entity, defines.events.on_robot_built_entity }, onEntityCreated)
-- on_entity_died should trigger both functions -> called manually
script.on_event({ defines.events.on_player_mined_entity, defines.events.on_robot_mined_entity, defines.events.on_entity_died }, onEntityRemoved)
script.on_event({ defines.events.on_pre_player_mined_item, defines.events.on_pre_robot_mined_item, defines.events.on_entity_died }, preEntityRemoved)
local functions = generateFunctions()
function refreshMetatables()
for _, chunkListX in pairs(global.air_filtered_chunks_map) do
for x, chunkListY in pairs(chunkListX) do
@ -541,8 +568,18 @@ function refreshMetatables()
end
end
function load()
refreshMetatables()
if INTERVAL ~= settings.global["baf-update-interval"].value then
setup()
end
end
script.on_load(load)
function init()
-- gather all filters on every surface
air_filtered_chunks = {}
global.air_filtered_chunks_map = {}
for _, surface in pairs(game.surfaces) do
local filters = surface.find_entities_filtered {
@ -556,35 +593,23 @@ function init()
end
end
function load()
refreshMetatables()
end
-- Set up callbacks
script.on_event({ defines.events.on_built_entity, defines.events.on_robot_built_entity }, onEntityCreated)
-- on_entity_died should trigger both functions -> called manually
script.on_event({ defines.events.on_player_mined_entity, defines.events.on_robot_mined_entity, defines.events.on_entity_died }, onEntityRemoved)
script.on_event({ defines.events.on_pre_player_mined_item, defines.events.on_pre_robot_mined_item, defines.events.on_entity_died }, preEntityRemoved)
functions = generateFunctions()
local onTick = spreadOverTicks(functions, INTERVAL)
script.on_event(defines.events.on_tick, onTick)
script.on_load(load)
script.on_init(init)
script.on_configuration_changed(init)
function onSettingsChanged(event)
INTERVAL = settings.global["baf-update-interval"].value
local onTick = spreadOverTicks(functions, INTERVAL)
script.on_event(defines.events.on_tick, onTick)
if event.setting == "baf-update-interval" then
setup()
end
end
script.on_event(defines.events.on_runtime_mod_setting_changed, onSettingsChanged)
function setup()
INTERVAL = settings.global["baf-update-interval"].value
onTick = spreadOverTicks(functions, INTERVAL)
script.on_event(defines.events.on_tick, onTick)
end
setup()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,13 +1,13 @@
{
"name": "better-air-filtering",
"version": "0.2.0",
"version": "0.3.3",
"title": "Better Air Filtering",
"author": "Joey De Pauw",
"contact": "joeydepauw@gmail.com",
"homepage": "",
"factorio_version": "0.17",
"factorio_version": "1.1",
"dependencies": [
"base >= 0.17",
"base >= 1.1",
"!air-filtering",
"!NauvisDay",
"!CW-carbon-capture"

View File

@ -0,0 +1,56 @@
[entity-name]
air-filter-machine-1=Машина для фильтрации воздуха 1
air-filter-machine-2=Машина для фильтрации воздуха 2
air-filter-machine-3=Машина для фильтрации воздуха 3
[entity-description]
air-filter-machine-1=Пассивная установка для фильтрации воздуха, действует в радиусе 1 чанка. Для работы требуются воздушные фильтры.
air-filter-machine-2=Активная установка для фильтрации воздуха, которая использует электричество, чтобы притягивать к себе загрязнения из области радиусом в 2 чанка.
air-filter-machine-3=Мощная установка для фильтрации воздуха, способный очищать от загрязнений область радиусом 3 чанка.
[item-name]
expendable-air-filter=Одноразовый воздушный фильтр
air-filter=Воздушный фильтр
used-air-filter=Использованный воздушный фильтр
liquid-filter=Жидкостный фильтр
used-liquid-filter=Использованный жидкостный фильтр
[fuel-category-name]
pollution-filter=Воздушный фильтр
[fluid-name]
pollution=Загрязнение
polluted-water=Загрязненная вода
[recipe-name]
filter-air=Фильтрация воздуха
filter-air2=Фильтрация воздуха
filter-air-expendable=Фильтрация воздуха
liquid-pollution=Конденсировать загрязнения
air-filter-recycling=Переработка использованных воздушных фильтров
[technology-name]
air-filtering=Фильтрация воздуха
air-filter-recycling=Переработка использованных воздушных фильтров
reusable-air-filters=Многоразовые воздушные фильтры
[technology-description]
air-filtering-1=Позволяет создавать пассивные установки для фильтрации воздуха которые улучшают качества воздуха за счет уменьшения загрязнения.
air-filtering-2=Позволяет создавать активные установки для фильтрации воздуха, который использует электричество, чтобы притягивать к себе загрязнения из области радиусом в 2 чанка.
air-filtering-3=Позволяет создавать мощные установки для фильтрации воздуха, способный очищать от загрязнений область радиусом в 3 чанка.
reusable-air-filters=Более прочные воздушные фильтры, которые можно переработать с помощью технологий будущего.
air-filter-recycling=Отработанные воздушные фильтры можно повторно использовать после переработки, заправив их небольшим количеством угля. Помните, что существует небольшая вероятность того, что фильтр сломается в процессе переработки.
[mod-setting-name]
baf-update-interval=Интервал обновления
[mod-setting-description]
baf-update-interval=Обновления выполняются эффективно и распределяются по интервалу ОБНОВЛЕНИЯ в тиках. По умолчанию 20 дает три обновления в секунду. Например, 30 приведет к 2 обновлениям в секунду.

View File

@ -1,12 +1,12 @@
data:extend({
{
type = "assembling-machine",
type = "furnace",
name = "air-filter-machine-1",
icon = "__better-air-filtering__/graphics/icons/air-filter-machine-1.png",
icon_size = 32,
flags = { "placeable-neutral", "placeable-player", "player-creation" },
minable = { hardness = 0.2, mining_time = 0.5, result = "air-filter-machine-1" },
fast_replaceable_group = "air-filter-machine",
--fast_replaceable_group = "air-filter-machine",
max_health = 150,
corpse = "medium-remnants",
alert_icon_shift = util.by_pixel(-3, -12),
@ -65,8 +65,9 @@ data:extend({
burnt_inventory_size = 1,
},
energy_usage = "50kW",
fixed_recipe = "filter-air",
ingredient_count = 1,
result_inventory_size = 0,
source_inventory_size = 0,
--ingredient_count = 1,
return_ingredients_on_change = true,
module_specification = {
module_slots = 0
@ -128,23 +129,6 @@ data:extend({
base_level = -1,
pipe_connections = {}
},
--{
-- production_type = "input",
-- pipe_picture = assembler2pipepictures(),
-- pipe_covers = pipecoverspictures(),
-- base_area = 10,
-- base_level = -1,
-- pipe_connections = {{ type="input", position = {0, -2} }}
--},
--{
-- production_type = "output",
-- pipe_picture = assembler2pipepictures(),
-- pipe_covers = pipecoverspictures(),
-- base_area = 10,
-- base_level = 1,
-- pipe_connections = { { type = "output", position = { 0, 2 } } },
-- secondary_draw_orders = { north = -1 }
--},
off_when_no_fluid_recipe = true
},
crafting_categories = { "air-filtering-advanced" },
@ -214,23 +198,6 @@ data:extend({
base_level = -1,
pipe_connections = {}
},
--{
-- production_type = "input",
-- pipe_picture = assembler3pipepictures(),
-- pipe_covers = pipecoverspictures(),
-- base_area = 2,
-- base_level = -1,
-- pipe_connections = { { type = "input", position = { 0, -2 } } }
--},
--{
-- production_type = "output",
-- pipe_picture = assembler3pipepictures(),
-- pipe_covers = pipecoverspictures(),
-- base_area = 2,
-- base_level = 1,
-- pipe_connections = { { type = "output", position = { 0, 2 } } },
-- secondary_draw_orders = { north = -1 }
--},
off_when_no_fluid_recipe = true
},
crafting_categories = { "air-filtering-advanced" },

View File

@ -7,7 +7,7 @@ data:extend({
gas_temperature = 0,
base_color = { r = 0.95, g = 0.9, b = 0.9 },
flow_color = { r = 0.95, g = 0.9, b = 0.9 },
icon = "__base__/graphics/icons/fluid/pollution.png",
icon = "__better-air-filtering__/graphics/icons/fluid/pollution.png",
icon_size = 32,
order = "a[fluid]-z[water]",
auto_barrel = false
@ -19,7 +19,7 @@ data:extend({
max_temperature = 100,
base_color = { r = 0.27, g = 0.30, b = 0.34 },
flow_color = { r = 0.27, g = 0.30, b = 0.34 },
icon = "__better-air-filtering__/graphics/icons/fluid/pollution.png",
icon = "__better-air-filtering__/graphics/icons/fluid/polluted-water.png",
icon_size = 32,
order = "a[fluid]-z[pollution]",
auto_barrel = false

View File

@ -114,7 +114,7 @@ data:extend({
hide_from_stats = true,
icons = {
{
icon = "__base__/graphics/icons/fluid/pollution.png"
icon = "__better-air-filtering__/graphics/icons/fluid/pollution.png"
},
{
icon = "__better-air-filtering__/graphics/icons/recipe/filter-air.png",
@ -140,7 +140,7 @@ data:extend({
hide_from_stats = true,
icons = {
{
icon = "__base__/graphics/icons/fluid/pollution.png"
icon = "__better-air-filtering__/graphics/icons/fluid/pollution.png"
},
{
icon = "__better-air-filtering__/graphics/icons/recipe/filter-air.png",
@ -168,7 +168,7 @@ data:extend({
hide_from_stats = true,
icons = {
{
icon = "__base__/graphics/icons/fluid/pollution.png"
icon = "__better-air-filtering__/graphics/icons/fluid/pollution.png"
},
{
icon = "__better-air-filtering__/graphics/icons/recipe/filter-air.png",

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB