437 lines
20 KiB
Lua

-------------------------------------------------------------------------
-- TO DO
-------------------------------------------------------------------------
-- INCREASE PROFORMANCE
-- FIX BUGS
-------------------------------------------------------------------------
-- variables
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-- MAIN FUNCTIONS
-------------------------------------------------------------------------
-- wil set is_setup to false
local function set_is_setup()
global.is_setup = false
end
-- returns a table with all the players that have the "show-pollution-visuals" option on
local function get_players_to_render_for()
local result = {}
for i, player in pairs(game.players) do
local show_pollution_visuals = player.mod_settings["show-pollution-visuals"].value
if show_pollution_visuals then
table.insert(result, player)
end
end
return result
end
-- sets up all the variables and populates them with initial values
local function setup()
-- clearing all visuals juist incase there are some
rendering.clear("pollution-visuals")
-- defining chunk tables
global.polluted_chunks = {}
global.polluted_chunks_i = nil
global.all_chunks = {}
global.all_chunks_i = nil
-- defining fading sprite tables
global.sprites_fading_in = {}
global.sprites_fading_in_i = nil
global.sprites_fading_out = {}
global.sprites_fading_out_i = nil
--bool for if we need to check all_chunks or just polluted_chunks
global.is_checking = false
--bool for if we need to start fading the sprites
global.is_fading = false
-- bool for checking if the game is setup or not
global.is_setup = true
-- defining the overworld.
for _, surface in pairs(game.surfaces) do
--looping through all the chunks on surface
for chunk in surface.get_chunks() do
local x = (chunk.x)
local y = (chunk.y)
-- inserting chunk in all_chunks
global.all_chunks[surface.index..":"..x..":"..y] = {position = {x=x,y=y}, surface=surface.index}
-- inserting chunk in polluted_chunks
local chunk_pollution_level = surface.get_pollution(chunk.area.left_top)
if chunk_pollution_level > 0 then
-- if a neighboring chunk has 0 pollution add it to the polluted_chunk table anyway.
local N = surface.get_pollution({x*32, (y-1)*32})
local E = surface.get_pollution({(x+1)*32, y*32})
local S = surface.get_pollution({x*32, (y+1)*32})
local W = surface.get_pollution({(x-1)*32, y*32})
local NE = surface.get_pollution({(x+1)*32, (y-1)*32})
local SE = surface.get_pollution({(x+1)*32, (y+1)*32})
local SW = surface.get_pollution({(x-1)*32, (y+1)*32})
local NW = surface.get_pollution({(x-1)*32, (y-1)*32})
global.polluted_chunks[surface.index..":"..x..":"..y] = {position = {x=x,y=y}, r_id={}, surface=surface.index}
if N == 0 then
global.polluted_chunks[surface.index..":"..x..":"..(y-1)] = {position = {x=x, y=y-1}, r_id={}, surface=surface.index}
end
if E == 0 then
global.polluted_chunks[surface.index..":"..(x+1)..":"..y] = {position = {x=x+1, y=y}, r_id={}, surface=surface.index}
end
if S == 0 then
global.polluted_chunks[surface.index..":"..x..":"..(y+1)] = {position = {x=x, y=y+1}, r_id={}, surface=surface.index}
end
if W == 0 then
global.polluted_chunks[surface.index..":"..(x-1)..":"..y] = {position = {x=x-1, y=y}, r_id={}, surface=surface.index}
end
if NE == 0 then
global.polluted_chunks[surface.index..":"..(x+1)..":"..(y-1)] = {position = {x=x+1,y=y-1}, r_id={}, surface=surface.index}
end
if SE == 0 then
global.polluted_chunks[surface.index..":"..(x+1)..":"..(y+1)] = {position = {x=x+1, y=y+1}, r_id={}, surface=surface.index}
end
if SW == 0 then
global.polluted_chunks[surface.index..":"..(x-1)..":"..(y+1)] = {position = {x=x-1, y=y+1}, r_id={}, surface=surface.index}
end
if NW == 0 then
global.polluted_chunks[surface.index..":"..(x-1)..":"..(y-1)] = {position = {x=x-1, y=y-1}, r_id={}, surface=surface.index}
end
end
end
end
end
-- will check if chunks are polluted and put them in polluted_chunks if they are
local function check_all_chunks()
for i = 1, settings.global["chunks-per-tick-pollution-visuals"].value do
local index ,chunk_data = next(global.all_chunks, global.all_chunks_i)
if index == nil then global.all_chunks_i = nil global.is_checking = false break
else global.all_chunks_i = index end
local surface_i = chunk_data.surface
local surface = game.surfaces[surface_i]
local x = chunk_data.position.x
local y = chunk_data.position.y
local r_id = chunk_data.r_id
local chunk_pollution_level = surface.get_pollution({x*32,y*32})
--inserting chunk into polluted_chunks
if chunk_pollution_level > 0 then
local N = surface.get_pollution({x*32, (y-1)*32})
local E = surface.get_pollution({(x+1)*32, y*32})
local S = surface.get_pollution({x*32, (y+1)*32})
local W = surface.get_pollution({(x-1)*32, y*32})
local NE = surface.get_pollution({(x+1)*32, (y-1)*32})
local SE = surface.get_pollution({(x+1)*32, (y+1)*32})
local SW = surface.get_pollution({(x-1)*32, (y+1)*32})
local NW = surface.get_pollution({(x-1)*32, (y-1)*32})
--adding chunk or neighboring chunks to the polluted_chunks table
if global.polluted_chunks[surface_i..":"..x..":"..y] == nil then
global.polluted_chunks[surface_i..":"..x..":"..y] = {position = {x=x,y=y}, r_id={}, surface=surface_i}
end
if N == 0 and global.polluted_chunks[surface_i..":"..x..":"..(y-1)] == nil and global.all_chunks[surface_i..":"..x..":"..(y-1)] ~= nil then
global.polluted_chunks[surface_i..":"..x..":"..(y-1)] = {position = {x=x, y=y-1}, r_id={}, surface=surface_i}
end
if E == 0 and global.polluted_chunks[surface_i..":"..(x+1)..":"..y] == nil and global.all_chunks[surface_i..":"..(x+1)..":"..y] ~= nil then
global.polluted_chunks[surface_i..":"..(x+1)..":"..y] = {position = {x=x+1, y=y}, r_id={}, surface=surface_i}
end
if S == 0 and global.polluted_chunks[surface_i..":"..x..":"..(y+1)] == nil and global.all_chunks[surface_i..":"..x..":"..(y+1)] ~= nil then
global.polluted_chunks[surface_i..":"..x..":"..(y+1)] = {position = {x=x, y=y+1}, r_id={}, surface=surface_i}
end
if W == 0 and global.polluted_chunks[surface_i..":"..(x-1)..":"..y] == nil and global.all_chunks[surface_i..":"..(x-1)..":"..y] ~= nil then
global.polluted_chunks[surface_i..":"..(x-1)..":"..y] = {position = {x=x-1, y=y}, r_id={}, surface=surface_i}
end
if NE == 0 and global.polluted_chunks[surface_i..":"..(x+1)..":"..(y-1)] == nil and global.all_chunks[surface_i..":"..(x+1)..":"..(y-1)] ~= nil then
global.polluted_chunks[surface_i..":"..(x+1)..":"..(y-1)] = {position = {x=x+1,y=y-1}, r_id={}, surface=surface_i}
end
if SE == 0 and global.polluted_chunks[surface_i..":"..(x+1)..":"..(y+1)] == nil and global.all_chunks[surface_i..":"..(x+1)..":"..(y+1)] ~= nil then
global.polluted_chunks[surface_i..":"..(x+1)..":"..(y+1)] = {position = {x=x+1, y=y+1}, r_id={}, surface=surface_i}
end
if SW == 0 and global.polluted_chunks[surface_i..":"..(x-1)..":"..(y+1)] == nil and global.all_chunks[surface_i..":"..(x-1)..":"..(y+1)] ~= nil then
global.polluted_chunks[surface_i..":"..(x-1)..":"..(y+1)] = {position = {x=x-1, y=y+1}, r_id={}, surface=surface_i}
end
if NW == 0 and global.polluted_chunks[surface_i..":"..(x-1)..":"..(y-1)] == nil and global.all_chunks[surface_i..":"..(x-1)..":"..(y-1)] ~= nil then
global.polluted_chunks[surface_i..":"..(x-1)..":"..(y-1)] = {position = {x=x-1, y=y-1}, r_id={}, surface=surface_i}
end
end
end
end
-- draws a sprite for a specified chunk
local function check_polluted_chunk(chunk_data)
local surface = game.surfaces[chunk_data.surface]
local x = chunk_data.position.x
local y = chunk_data.position.y
local chunk_pollution_level = surface.get_pollution({x*32,y*32})
local sprite_target = {(x*32)+16, (y*32)+16}
local remove_chunk = false
local players = get_players_to_render_for()
local max_layers = settings.global["layers-pollution-visuals"].value
local offset = settings.global["x-offset-pollution-visuals"].value
local a = settings.global["y-intercept-pollution-visuals"].value
local b = settings.global["base-pollution-visuals"].value
local r_id = chunk_data.r_id
for i = 1, max_layers do
local level = a*b^i + offset
local current_sprite = ""
local next_sprite = ""
-- setting the current sprite
if r_id[i] ~= nil then current_sprite = rendering.get_sprite(r_id[i]) end
-- setting the next sprite
if chunk_pollution_level > level then
next_sprite = "smog_middle"
else
-- variables of neighboring chunks
local N = surface.get_pollution({x*32, (y-1)*32})
local E = surface.get_pollution({(x+1)*32, y*32})
local S = surface.get_pollution({x*32, (y+1)*32})
local W = surface.get_pollution({(x-1)*32, y*32})
local NE = surface.get_pollution({(x+1)*32, (y-1)*32})
local SE = surface.get_pollution({(x+1)*32, (y+1)*32})
local SW = surface.get_pollution({(x-1)*32, (y+1)*32})
local NW = surface.get_pollution({(x-1)*32, (y-1)*32})
--selecting the right sprite depending on neighboring chunks pollution values
if (N > level and S > level) or (W > level and E > level) then next_sprite="smog_middle"
elseif S > level and W <= level and E <= level then next_sprite="smog_top"
elseif N > level and W <= level and E <= level then next_sprite="smog_bottom"
elseif (W > level or (NW > level and SW > level)) and (N <= level and S <= level and NE <= level and SE <= level) then next_sprite="smog_left"
elseif (E > level or (NE > level and SE > level)) and (N <= level and S <= level and NW <= level and SW <= level) then next_sprite="smog_right"
elseif SW > level and N <= level and NE <= level and E <= level and SE <= level and S <= level and W <= level and NW <= level then next_sprite="smog_corner_left_top"
elseif SE > level and N <= level and NE <= level and E <= level and SW <= level and S <= level and W <= level and NW <= level then next_sprite="smog_corner_right_top"
elseif NW > level and N <= level and NE <= level and E <= level and SW <= level and S <= level and W <= level and SE <= level then next_sprite="smog_corner_left_bottom"
elseif NE > level and E <= level and SE <= level and S <= level and SW <= level and W <= level and NW <= level and N <= level then next_sprite="smog_corner_right_bottom"
elseif S > level and E > level and W <= level and N <= level and NW <= level then next_sprite="smog_corner_inv_left_top"
elseif S > level and W > level and E <= level and N <= level and NE <= level then next_sprite="smog_corner_inv_right_top"
elseif N > level and E > level and W <= level and S <= level and SW <= level then next_sprite="smog_corner_inv_left_bottom"
elseif N > level and W > level and E <= level and S <= level and SE <= level then next_sprite="smog_corner_inv_right_bottom"
elseif N == 0 and E == 0 and S == 0 and W == 0 and NE == 0 and NW == 0 and SE == 0 and SW == 0 and chunk_pollution_level == 0 and r_id == {} then
remove_chunk = true
end
end
-- marking sprite for fade in/out
if r_id[i] ~= nil then
if next_sprite == "" then
--mark for fade out
global.sprites_fading_out[r_id[i]] = {tint = {r=1, g=1, b=1, a=1}, position={x=x,y=y}}
r_id[i] = nil
elseif next_sprite ~= current_sprite then
--mark for fade out
global.sprites_fading_out[r_id[i]] = {tint = {r=1, g=1, b=1, a=1}, position={x=x,y=y}}
--mark for fade in
r_id[i] = rendering.draw_sprite{sprite=next_sprite, surface=surface,target=sprite_target, y_scale=2, x_scale=2, tint={r=0, g=0, b=0, a=0}, players=players}
if #players == 0 then
rendering.set_visible(r_id[i], false)
end
global.sprites_fading_in[r_id[i]] = {tint = {r=0, g=0, b=0, a=0}, position={x=x,y=y}}
end
elseif next_sprite ~= "" then
--mark for fade in
r_id[i] = rendering.draw_sprite{sprite=next_sprite, surface=surface,target=sprite_target, y_scale=2, x_scale=2, tint={r=0, g=0,b=0, a=0}, players=players}
if #players == 0 then rendering.set_visible(r_id[i], false) end
global.sprites_fading_in[r_id[i]] = {tint = {r=0, g=0, b=0, a=0}, position={x=x,y=y}}
end
--update global tables with new values
global.polluted_chunks[chunk_data.surface..":"..x..":"..y].r_id[i] = r_id[i]
end
-- delete chunk from polluted_chunks
if remove_chunk then
global.polluted_chunks[chunk_data.surface..":"..x..":"..y] = nil
end
end
-- will check every polluted chunk and draw visuals
local function check_polluted_chunks()
for i = 1, settings.global["chunks-per-tick-pollution-visuals"].value do
local index ,chunk_data = next(global.polluted_chunks, global.polluted_chunks_i)
if index == nil then global.polluted_chunks_i = nil global.is_fading = true break
else global.polluted_chunks_i = index end
check_polluted_chunk(chunk_data)
end
end
-- fade in/out sprites
local function fade()
local is_fading_in = true
local is_fading_out = true
for i = 1, 1000 do
-- fading in
if is_fading_in and next(global.sprites_fading_in, global.sprites_fading_in_i) ~= nil then
local r_id, value = next(global.sprites_fading_in, global.sprites_fading_in_i)
local r = value.tint.r + 0.01
local g = value.tint.g + 0.01
local b = value.tint.b + 0.01
local a = value.tint.a + 0.01
local x = value.position.x
local y = value.position.y
if a >= 1 then
rendering.set_color(r_id, {r=1, g=1,b=1, a=1})
global.sprites_fading_in[r_id] = nil
else
rendering.set_color(r_id, {r=b, g=g,b=b, a=a})
global.sprites_fading_in[r_id].tint = {r=b, g=g,b=b, a=a}
end
global.sprites_fading_in_i = r_id
else
is_fading_in = false
end
--fading out
if is_fading_out and next(global.sprites_fading_out, global.sprites_fading_out_i) ~= nil then
local r_id, value = next(global.sprites_fading_out, global.sprites_fading_out_i)
local r = value.tint.r - 0.01
local g = value.tint.g - 0.01
local b = value.tint.b - 0.01
local a = value.tint.a - 0.01
local x = value.position.x
local y = value.position.y
if a <= 0 then
rendering.set_color(r_id, {r=0, g=0,b=0, a=0})
global.sprites_fading_out[r_id] = nil
rendering.destroy(r_id)
else
rendering.set_color(r_id, {r=b, g=g,b=b, a=a})
global.sprites_fading_out[r_id].tint = {r=b, g=g,b=b, a=a}
end
global.sprites_fading_out_i = r_id
else
is_fading_out = false
end
if is_fading_in == false and is_fading_out == false then
break
end
end
if next(global.sprites_fading_in, global.sprites_fading_in_i) == nil then global.sprites_fading_in_i = nil end
if next(global.sprites_fading_out, global.sprites_fading_out_i) == nil then global.sprites_fading_out_i = nil end
if next(global.sprites_fading_in, nil) == nil and next(global.sprites_fading_out, nil) == nil then
global.is_fading = false
global.is_checking = true
end
end
-- adds chunk to all_chunks
local function add_chunk(chunk)
local x = chunk.position.x
local y = chunk.position.y
local surface = chunk.surface
-- is needed for RSO
if global.is_setup == false then
setup()
end
global.all_chunks[surface.index..":"..x..":"..y] = {position = {x=x,y=y}, surface=surface.index}
end
--deletes chunk from all_chunks and polluted_chunks
local function remove_chunks(data)
local positions = data.positions
local surface_i = data.surface_index
for _ , position in pairs(positions) do
local x = position.x
local y = position.y
local r_id = {}
if global.polluted_chunks[surface_i..":"..x..":"..y] ~= nil then
r_id = global.polluted_chunks[surface_i..":"..x..":"..y].r_id
end
for i ,v in pairs(r_id) do
global.sprites_fading_in[r_id[i]] = nil
global.sprites_fading_out[r_id[i]] = nil
rendering.destroy(r_id[i])
end
global.all_chunks[surface_i..":"..x..":"..y] = nil
global.polluted_chunks[surface_i..":"..x..":"..y] = nil
end
end
-- wil call if a setting has been changed
local function setting_changed(data)
if data.setting == "show-pollution-visuals" then
local players = get_players_to_render_for()
for i, chunk_data in pairs(global.polluted_chunks) do
for x, r_id in pairs(chunk_data.r_id) do
if #players == 0 then
rendering.set_visible(r_id, false)
else
rendering.set_visible(r_id, true)
rendering.set_players(r_id, players)
end
end
end
else
set_is_setup()
end
end
-- function that triggers every tick
local function on_tick()
if global.is_setup == false then
--log("setup")
setup()
elseif not(global.is_checking) and not(global.is_fading) then
--log("check polluted chunks")
check_polluted_chunks()
elseif global.is_checking then
--log("check all chunks")
check_all_chunks()
elseif global.is_fading then
--log("fade sprites")
fade()
end
end
local function remove_surface(data)
local remove_data = {}
remove_data.positions = {}
remove_data.surface_index = data.surface_index
for i ,chunk_data in pairs(global.all_chunks) do
if chunk_data.surface == data.surface_index then
remove_data.positions[#remove_data.positions+1] = chunk_data.position
end
end
if remove_data.positions ~= {} then
remove_chunks(remove_data)
end
end
-------------------------------------------------------------------------
-- EVENTS
-------------------------------------------------------------------------
script.on_event(defines.events.on_chunk_deleted, remove_chunks)
script.on_event(defines.events.on_chunk_generated, add_chunk)
script.on_event(defines.events.on_tick, on_tick)
script.on_event(defines.events.on_runtime_mod_setting_changed, setting_changed)
script.on_event(defines.events.on_surface_deleted, remove_surface)
script.on_configuration_changed(set_is_setup)
script.on_init(set_is_setup)