diff --git a/scenarios/doomsday/control.lua b/scenarios/doomsday/control.lua new file mode 100644 index 00000000..5bbfcde5 --- /dev/null +++ b/scenarios/doomsday/control.lua @@ -0,0 +1,149 @@ +--[[ +HUGE SHOUTOUT TO HORNWITSER FOR WRITING THIS SEXY AS MODLOADER <3 +and for not getting mad when i coppyed it :) +]] + +local modules = { + require("pdnc"), + require("doomsday"), + require("gui"), +} + +script.on_init(function() + for i,mod in ipairs(modules) do + if mod.on_init then + mod.on_init() + end + end +end) + +script.on_load(function() + for i,mod in ipairs(modules) do + if mod.on_load then + mod.on_load() + end + end +end) + +script.on_configuration_changed(function(data) + for i,mod in ipairs(modules) do + if mod.on_configuration_changed then + mod.on_configuration_changed(data) + end + end +end) + +local function register_tick_handlers() + local ticks = {} + for i,mod in ipairs(modules) do + print(mod.on_nth_ticks) + if mod.on_nth_ticks then + mod_ticks = mod.on_nth_ticks + for t,f in pairs(mod_ticks) do + if not ticks[t] then + ticks[t] = {} + end + + table.insert(ticks[t], f) + end + end + end + + for t,fs in pairs(ticks) do + script.on_nth_tick(t, function(event_data) + for i,f in ipairs(fs) do + f(event_data) + end + end) + end +end +register_tick_handlers() + +local function register_events() + local events = {} + for i,mod in ipairs(modules) do + if mod.get_events then + mod_events = mod.get_events() + for e,f in pairs(mod_events) do + if not events[e] then + events[e] = {} + end + + table.insert(events[e], f) + end + end + end + + for e,fs in pairs(events) do + script.on_event(e, function(event_data) + for i,f in ipairs(fs) do + f(event_data) + end + end) + end +end +register_events() + +for i,mod in ipairs(modules) do + if mod.add_remote_interfaces then + mod.add_remote_interfaces() + end +end + +for i,mod in ipairs(modules) do + if mod.add_commands then + mod.add_commands() + end +end + + +--[[ + +-- This is a example of what to put at the end of your code, ie doomsday.lua +-- replace all the EXAMPLE 's with any name you want, they have to be all the same +-- replace tick with number if ticks and function with the function name to call +-- everywhere there is a blank space is where you put stuff in + + + +local EXAMPLE_init = {} + +local script_events = { + --place the here what you would normaly use Event.register for + -- Event.register(defines.events.on_player_created, testfunction) + -- is the same as + -- [defines.events.on_player_created] = testfunction, + -- where testfunction is | local function testfunction() { } + --[Event] = function, + --put stuff here + +} + +EXAMPLE_init.on_nth_ticks = { + --place the here what you would normaly use + --[tick] = function, + --put stuff here + +} + +EXAMPLE_init.on_init = function() -- this runs when Event.core_events.init + log("EXAMPLE init") + --put stuff here + + global.EXAMPLE_data = global.EXAMPLE_data or script_data -- NO TOUCHY + +end + +EXAMPLE_init.on_load = function() -- this runs when Event.core_events.load + log("EXAMPLE load") + --put stuff here + + script_data = global.EXAMPLE_data or script_data -- NO TOUCHY +end + +EXAMPLE_init.get_events = function() + return script_events +end + +return EXAMPLE_init +]] \ No newline at end of file diff --git a/scenarios/doomsday/doomsday.lua b/scenarios/doomsday/doomsday.lua new file mode 100644 index 00000000..0cea7c69 --- /dev/null +++ b/scenarios/doomsday/doomsday.lua @@ -0,0 +1,251 @@ +-- doomsday module. Requires the programmable day-night cycle (pdnc) module to work. +-- made by Zr4g0n +-- this module currently has issues with the event module and 'on nth tick'. +require("pdnc") --is this the best way to do this? +global.doomsday_enabled = true +global.doomsday = global.doomsday or {} -- used to check if this exists. +global.doomsday_start = 8.75 -- in ingame days. Use n.75 to make sure doomsday is at midnight. +global.doomsday_pollution = 20 -- amount to be applied per tick +global.doomsday_surface = 1 +global.doomsday_enable_players_online_compensator = false +global.doomsday_current_fuzzy_playercount = 1.5 -- start assuming 1.5 players! :V +global.doomsday_use_early_death = false +global.doomsday_early_death_has_happened = false +global.doomsday_use_different_spawn = false +global.doomsday_different_spawn = {x = -2000, y = -500} +global.doomsday_has_happened = false + +function doomsday_status() + stats = { + "Time Left: " .. doomsday_time_left(), + "global.doomsday_start: " .. global.doomsday_start, + "global.doomsday_pollution: " .. global.doomsday_pollution, + "global.doomsday_surface: " .. global.doomsday_surface, + "global.doomsday_enabled: " .. pdnc_bool_to_string(global.doomsday_enabled), + "global.doomsday_enable_players_online_compensator: " .. pdnc_bool_to_string(global.doomsday_enable_players_online_compensator), + "global.doomsday_current_fuzzy_playercount: " .. global.doomsday_current_fuzzy_playercount, + "global.doomsday_use_early_death: " .. pdnc_bool_to_string(global.doomsday_use_early_death), + "global.doomsday_early_death_has_happened: " .. pdnc_bool_to_string(global.doomsday_early_death_has_happened), + "global.doomsday_use_different_spawn: " .. pdnc_bool_to_string(global.doomsday_use_different_spawn), + "global.doomsday_different_spawn: x = " .. global.doomsday_different_spawn.x .. ", y = " .. global.doomsday_different_spawn.y, + "doomsday_has_happened: " .. pdnc_bool_to_string(global.doomsday_has_happened), + } + return stats +end + +function doomsday_console_status() + --logs to console, used for debuging + log("Time Left: " .. doomsday_time_left()) + log("global.doomsday_start: " .. global.doomsday_start) + log("global.doomsday_pollution: " .. global.doomsday_pollution) + log("global.doomsday_surface: " .. global.doomsday_surface) + log("global.doomsday_enabled: " .. pdnc_bool_to_string(global.doomsday_enabled)) + log("global.doomsday_enable_players_online_compensator: " .. pdnc_bool_to_string(global.doomsday_enable_players_online_compensator)) + log("global.doomsday_current_fuzzy_playercount: " .. global.doomsday_current_fuzzy_playercount) + log("global.doomsday_use_early_death: " .. pdnc_bool_to_string(global.doomsday_use_early_death)) + log("global.doomsday_early_death_has_happened: " .. pdnc_bool_to_string(global.doomsday_early_death_has_happened)) + log("global.doomsday_use_different_spawn: " .. pdnc_bool_to_string(global.doomsday_use_different_spawn)) + log("global.doomsday_different_spawn: x = " .. global.doomsday_different_spawn.x .. ", y = " .. global.doomsday_different_spawn.y) + log("doomsday_has_happened: " .. pdnc_bool_to_string(global.doomsday_has_happened)) + return stats +end + +function doomsday_toggle() + global.doomsday_enabled = not global.doomsday_enabled + doomsday_status() +end + +function doomsday_setup() + if global.doomsday_use_different_spawn then + doomsday_activate_different_spawn() + end +end + +function doomsday_early_death() + local radius = 512 + local pollution = 2500 + doomsday_pollute(radius*0.66,pollution,12) + doomsday_pollute(radius*1.00,pollution,24) + doomsday_pollute(radius*1.50,pollution,36) + global.doomsday_early_death_has_happened = true +end + +function doomsday_activate_different_spawn() + game.forces["player"].set_spawn_position(global.doomsday_different_spawn, game.surfaces[global.doomsday_surface]) +end + +function doomsday_core() + local current_time = game.tick / game.surfaces[global.doomsday_surface].ticks_per_day + local x = current_time * 6.2831853 --2pi + local returnvalue = 0 + if(global.doomsday_enable_players_online_compensator)then + doomsday_players_online_compensator() + end + + if global.doomsday_use_early_death + and (current_time > 1) + and not global.doomsday_early_death_has_happened then + doomsday_early_death() + end + + local x = current_time * 6.2831853 --2pi + local returnvalue = 0 + if (current_time < global.doomsday_start) then + returnvalue = math.pow(pdnc_c_boxy(x), (1 + current_time / 4)) + -- days become darker over time towards n^6.125 + elseif (current_time < global.doomsday_start + 1) then + returnvalue = doomsday_pollution_zero_hour(current_time) + if not global.doomsday_has_happened then + global.doomsday_has_happened = true + log("Doomsday activated at tick: " .. game.tick) + end + else + global.pdnc_enable_brightness_limit = true + returnvalue = math.pow(pdnc_c_boxy(x), 6.125)--*0.5 + end + return pdnc_scaler(returnvalue) +end + +function doomsday_pollution_zero_hour(current_time) + local radius = 128 --make global + local pollution = global.doomsday_pollution -- total pollution applied per tick + local nodes = 5 -- the number of nodes to spread + doomsday_pollute(radius*0.66,pollution,nodes*0.66) + doomsday_pollute(radius*1.00,pollution,nodes*1.00) + doomsday_pollute(radius*1.50,pollution,nodes*1.50) + return math.pow(((global.doomsday_start + 1) - current_time), 7) +end + + +function doomsday_normal_curve(x) + return (1+ ((math.sin(x) + (0.111 * math.sin(3 * x))) * 1.124859392575928))/2 + -- magic numbers to make it scale to (-1, 1) +end + +function doomsday_pollute(radius,pollution,nodes) -- spawn a ring of pollution based on radius, amount of pollution and number of points in the ring. + local p = global.pdnc_stepsize * pollution -- needed to make it 'step size independant' + p = p / nodes + local position = {x = 0.0, y = 0.0} + if global.doomsday_use_different_spawn then + position = global.doomsday_different_spawn + end + --game.surfaces[global.doomsday_surface].pollute(position, p) --circle + center point + local step = (math.pi * 2) / (nodes - 1) + for i=0, (nodes - 1) do + position = {x = math.sin(step*i)*radius, y = math.cos(step*i)*radius} + game.surfaces[global.doomsday_surface].pollute(position, p) + end +end + +function doomsday_time_left() + if (global.doomsday_start > 0)then + local ticks_until_doomsday = game.surfaces[global.doomsday_surface].ticks_per_day * global.doomsday_start + local ticks = ticks_until_doomsday - game.tick + if (ticks >= 0) then + local seconds = math.floor(ticks/ 60) + local minutes = math.floor(seconds / 60) + local hours = math.floor(minutes / 60) + local days = math.floor(hours / 24) + return(string.format("-" .. "%d:%02d:%02d:%02d", hours, minutes % 60, seconds % 60, ticks % 60)) + else + ticks = ticks * -1 + local seconds = math.floor(ticks / 60) + local minutes = math.floor(seconds / 60) + local hours = math.floor(minutes / 60) + local days = math.floor(hours / 24) + return(string.format("%d:%02d:%02d:%02d", hours, minutes % 60, seconds % 60, ticks % 60) .. " ago...") + end + else + return("Nothing to see here, move along! No doomsday here, nope!") + end +end + +function doomsday_time_left_with_ticks() + local tick = (((game.surfaces[global.doomsday_surface].ticks_per_day * global.doomsday_start) - game.tick)%60) + return doomsday_time_left() .. ":" .. string.format("%02d", tick) +end + +function doomsday_players_online_compensator() + local target = #game.connected_players + local current = global.doomsday_current_fuzzy_playercount + local step_size = 0.02 -- (at 21 ticks per iteration, that's 17.5sec per player change. + + if(current < 0)then + game.print("Error; 'current' number of players' is less than 0!") + end + if(target < 0)then + game.print("Error; 'number of players' is less than 0!") + end + + if(current == target) then + -- do nothing, this should be most of the time + elseif ( current < target) then + current = current + step_size + elseif ( current > target) then + current = current - step_size + else + -- do nothing? + end + local modifier = 20/(0.1 * current * current + 6) -- Nice, smooth curve that should hopefully give boost equal to the playercount! + game.forces.player.character_running_speed_modifier = modifier + global.doomsday_current_fuzzy_playercount = current +end + +--[[ +function reduce_brightness(n) + global.pdnc_max_brightness = 1 - ((global.pdnc_current_time / global.pdnc_doomsday_start)*n) + if(global.pdnc_max_brightness < n) then + global.pdnc_max_brightness = n + end +end +]] + + + +-- This is a example of what to put at the end of your code, ie doomsday.lua +-- replaze EXAMPLE with any name you want +-- replace tick with number if ticks and function with the function name to call +-- everywhere there is a blank space is where you put stuff in + +local doomsday_init = {} + +local script_events = { + --place the here what you would normaly use Event.register for + -- Event.register(defines.events.on_player_created, testfunction) + -- is the same as + -- [defines.events.on_player_created] = testfunction, + -- where testfunction is | local functuin testfunction() { } + --[Event] = function, + --put stuff here + +} + +doomsday_init.on_nth_ticks = { + --place the here what you would normaly use + --[tick] = function, + --put stuff here + --[60] = doomsday_console_status, -- prints status to console ever second + +} + +doomsday_init.on_init = function() -- this runs when Event.core_events.init + log("doomsday init") + --put stuff here + doomsday_setup() + global.doomsday_data = global.doomsday_data or script_data -- NO TOUCHY +end + +doomsday_init.on_load = function() -- this runs when Event.core_events.load + log("doomsday load") + --put stuff here + script_data = global.doomsday_data or script_data -- NO TOUCHY +end + +doomsday_init.get_events = function() + return script_events +end + +return doomsday_init + +--Event.register(-global.pdnc_stepsize, doomsday_pdnc_program) --intentionally using the PDNC stepsize so the functions sync this can be skipped, since it's called from pdnc! +--Event.register(Event.core_events.init, doomsday_setup) diff --git a/scenarios/doomsday/gui.lua b/scenarios/doomsday/gui.lua new file mode 100644 index 00000000..07da7e2d --- /dev/null +++ b/scenarios/doomsday/gui.lua @@ -0,0 +1,266 @@ +local mod_gui = require 'mod-gui' + +-- ADDS DOOMSDAY STATS BUTTON +local function gui_update_DOOM(player) + local gui = mod_gui.get_frame_flow(player) + local frame = gui.doomsday_stats + if not frame then + return + end + + frame.clear() + for i,stat in ipairs(doomsday_status()) do + frame.add{ + type = "label", + caption = stat + } + end +end + +local function toggle_frame_DOOM(player) + local gui = mod_gui.get_frame_flow(player) + local frame = gui.doomsday_stats + + if frame then + frame.destroy() + return + end + + frame = gui.add{ + type = "frame", + name = "doomsday_stats", + direction = "vertical", + caption = "Doomsday stats", + style = mod_gui.frame_style, + } + + frame.style.horizontally_stretchable = false + frame.style.vertically_stretchable = false + gui_update_DOOM(player) +end + +local function get_sprite_button_DOOM(player) + local button_flow = mod_gui.get_button_flow(player) + button_DOOM = button_flow.doomsday_stats_button + + if button_DOOM then + button_DOOM.destroy() + end + + if player.admin then + button_DOOM = button_flow.add{ + type = "sprite-button", + name = "doomsday_stats_button", + sprite = "item/raw-fish", + style = mod_gui.button_style, + tooltip = "Show debug stats for doomsday", + } + end +end + +--ADDS PDNC STATS BUTTON +local function gui_update_PDNC(player) + local gui = mod_gui.get_frame_flow(player) + local frame_PDNC = gui.PDNC_stats + if not frame_PDNC then + return + end + + frame_PDNC.clear() + for i,stat in ipairs(pdnc_extended_status()) do + frame_PDNC.add{ + type = "label", + caption = stat + } + end +end + +local function toggle_frame_PDNC(player) + local gui = mod_gui.get_frame_flow(player) + local frame_PDNC = gui.PDNC_stats + + if frame_PDNC then + frame_PDNC.destroy() + return + end + + frame_PDNC = gui.add{ + type = "frame", + name = "PDNC_stats", + direction = "vertical", + caption = "PDNC stats", + style = mod_gui.frame_style, + } + + frame_PDNC.style.horizontally_stretchable = false + frame_PDNC.style.vertically_stretchable = false + gui_update_PDNC(player) +end + +local function get_sprite_button_PDNC(player) + local button_flow = mod_gui.get_button_flow(player) + local button_PDNC = button_flow.PDNC_stats_button + + if button_PDNC then + button_PDNC.destroy() + end + + if player.admin then + button_PDNC = button_flow.add{ + type = "sprite-button", + name = "PDNC_stats_button", + sprite = "item/raw-fish", + style = mod_gui.button_style, + tooltip = "Show debug stats for PDNC", + } + end +end + +-- ADDS DOOMSDAY TIME LEFT COUNTER +local function gui_update_counter(player) + local gui = player.gui.screen --mod_gui.get_frame_flow(player) + local frame_counter = gui.doomsday_counter + if not frame_counter then + return + end + + frame_counter.clear() + frame_counter.add{ + type = "label", + caption = doomsday_time_left() + } + frame_counter.location = {player.display_resolution.width/2 - 59, 10} + frame_counter.visible = true +end + +local function toggle_frame_counter(player) + --local player = player + local gui = player.gui.screen --mod_gui.get_frame_flow(player) + local frame_counter = gui.doomsday_counter + -- if frame_counter then + -- frame_counter.destroy() + -- return + -- end + + frame_counter = gui.add{ + type = "frame", + name = "doomsday_counter", + direction = "horizontal", + --caption = "Doomsday counter", + style = mod_gui.frame_style, + } + + --frame_counter.style.horizontally_stretchable = false + frame_counter.style.vertically_stretchable = false + frame_counter.visible = false + gui_update_counter(player) +end + +local function update_buttons(event) + local player = game.players[event.player_index] + if not (player and player.valid) then + return + end + + get_sprite_button_DOOM(player) + get_sprite_button_PDNC(player) + + -- Make sure the frames are not visible to players + if not player.admin then + local gui = mod_gui.get_frame_flow(player) + local frame_PDNC = gui.PDNC_stats + + if frame_PDNC then + frame_PDNC.destroy() + end + + local frame_DOOM = gui.doomsday_stats + + if frame_DOOM then + frame_DOOM.destroy() + end + end +end + +-- ON GUI CLICK AND OTHERS +local function on_gui_click(event) + local gui = event.element + local player = game.players[event.player_index] + if not (player and player.valid and gui and gui.valid) then + return + end + + if gui.name == "doomsday_stats_button" then + toggle_frame_DOOM(player) + end + if gui.name == "PDNC_stats_button" then + toggle_frame_PDNC(player) + end +end + +local function on_player_created(event) + local player = game.players[event.player_index] + if not (player and player.valid) then + return + end + + update_buttons(event) + toggle_frame_counter(player) +end + +local function gui_update_all() + for _, player in pairs(game.players) do + if player and player.valid then + gui_update_DOOM(player) + gui_update_PDNC(player) + gui_update_counter(player) + end + end +end + +local function on_gui_tick(event) + gui_update_all() +end + +local doomsdaygui_init = {} +local script_events = { + --place the here what you would normaly use Event.register for + -- Event.register(defines.events.on_player_created, testfunction) + -- is the same as + -- [defines.events.on_player_created] = testfunction, + -- where testfunction is | local functuin testfunction() { } + --[Event] = function, + --put stuff here + [defines.events.on_gui_click] = on_gui_click, + [defines.events.on_player_created] = on_player_created, + [defines.events.on_player_promoted] = update_buttons, + [defines.events.on_player_demoted] = update_buttons, +} + +doomsdaygui_init.on_nth_ticks = { + --place the here what you would normaly use + --[tick] = function, + --put stuff here + [1] = on_gui_tick, +} + +doomsdaygui_init.on_init = function() -- this runs when Event.core_events.init + log("doomsdaygui init") + --put stuff here + + global.doomsdaygui_data = global.doomsdaygui_data or script_data -- NO TOUCHY + +end + +doomsdaygui_init.on_load = function() -- this runs when Event.core_events.load + log("doomsdaygui load") + --put stuff here + + script_data = global.doomsdaygui_data or script_data -- NO TOUCHY +end + +doomsdaygui_init.get_events = function() + return script_events +end + +return doomsdaygui_init \ No newline at end of file diff --git a/scenarios/doomsday/pdnc.lua b/scenarios/doomsday/pdnc.lua new file mode 100644 index 00000000..d37b19fa --- /dev/null +++ b/scenarios/doomsday/pdnc.lua @@ -0,0 +1,309 @@ +-- Third itteration of programmable day-night cycle for Factorio +-- Developed by Zr4g0n with help +-- Features needed: +-- Set 'maximum brightness' from rocket launches cluster wide +-- Set 'time until doomsday' to any time, in minutes. +-- Set 'evolution target' to any number [0.0, 1.0] +-- Changing day-night cycle based on days +-- Handle 'next time' elegantly + +-- lamps enable darkness: [0.595 - 0.425] scaled to [0.0 - 0.85] range from [0.0 - 1.0] range +global.pdnc = global.pdnc or {} +global.pdnc_enabled = true +global.pdnc_stepsize = 53 -- also used for script.on_nth_tick. Cannot be changed at runtime! +global.pdnc_surface = 1 +global.pdnc_current_time = 0 +global.pdnc_current_point = {x = 0, y = 1.0} +global.pdnc_last_point = {x = -1, y = 0.0} +global.pdnc_max_brightness = 0.5 -- for clusterio +global.pdnc_debug = false +global.pdnc_enable_brightness_limit = false +global.pdnc_enable_rocket_darkness = false +global.pdnc_rockets_launched = 0 +global.pdnc_rockets_launched_step_size = 0.025 +global.pdnc_rockets_launched_smooth = 0 +global.pdnc_min_per_day = 15.0 + +function pdnc_setup() + --game.surfaces[global.pdnc_surface].ticks_per_day = pdnc_min_to_ticks(10.0) + doomsday_setup() +end + +-- Returns true if the player is an admin +function check_admin(ctx) + local player = game.players[ctx.player_index] + if not player.admin then + player.print("Only admins can use /"..ctx.name) + return false + end + return true +end + +function pdnc_toggle_debug(ctx) + if not check_admin(ctx) then return end + global.pdnc_debug = not global.pdnc_debug + pdnc_print_status(ctx) +end + +function pdnc_toggle(ctx) + if not check_admin(ctx) then return end + global.pdnc_enabled = not global.pdnc_enabled + pdnc_print_status(ctx) +end + +function pdnc_print_status(ctx) + local player = game.players[ctx.player_index] + if(global.pdnc_enabled)then + player.print("PDNC is enabled") + else + player.print("PDNC is disabled") + end + + if(global.pdnc_debug)then + player.print("PDNC debug is enabled") + pdnc_extended_status() + else + player.print("PDNC debug is disabled") + end +end + +function pdnc_extended_status() + stats = { + "global.pdnc_stepsize: " .. global.pdnc_stepsize, + "global.pdnc_surface: " .. global.pdnc_surface, + "global.pdnc_current_time: " .. global.pdnc_current_time, + "global.pdnc_max_brightness: " .. global.pdnc_max_brightness, + "global.pdnc_enable_brightness_limit: " .. pdnc_bool_to_string(global.pdnc_enable_brightness_limit), + "global.pdnc_enable_rocket_darkness: " .. pdnc_bool_to_string(global.pdnc_enable_rocket_darkness), + "global.pdnc_rockets_launched: " .. global.pdnc_rockets_launched, + "global.pdnc_rockets_launched_step_size: " .. global.pdnc_rockets_launched_step_size, + "global.pdnc_rockets_launched_smooth: " .. global.pdnc_rockets_launched_smooth, + "ticks per day: " .. game.surfaces[global.pdnc_surface].ticks_per_day, + "current tick: " .. game.tick, + } + return stats +end + +function pdnc_bool_to_string(b) + if(b)then + return "true" + else + return "false" + end +end + +function pdnc_core() + if(global.pdnc_enabled)then + doomsday_core() + local current_surface = game.surfaces[global.pdnc_surface] + pdnc_freeze_check(current_surface) + current_surface.ticks_per_day = pdnc_min_to_ticks(global.pdnc_min_per_day) -- move this somewhere else; doesn't need to run every nth tick! + global.pdnc_current_time = game.tick / current_surface.ticks_per_day + + -- get current Y coordinate. X = 0 always. + local current_point = {x = global.pdnc_current_time, y = pdnc_program(global.pdnc_current_time)} + -- get next Y coordinate, X = 'step size' + local next_point = {x = (global.pdnc_current_time + (global.pdnc_stepsize/current_surface.ticks_per_day)), y = pdnc_program(global.pdnc_current_time + (global.pdnc_stepsize/current_surface.ticks_per_day))} + + local top_point = pdnc_intersection_top(current_point, next_point) + local bot_point = pdnc_intersection_bot(current_point, next_point) + + -- the order is dusk - evening - morning - dawn. They *must* be in that order and they cannot be equal + if(top_point < bot_point) then -- dusk -> evening + pdnc_cleanup_last_tick(current_surface) + current_surface.evening = bot_point - current_point.x + current_surface.dusk = top_point - current_point.x + elseif(top_point > bot_point) then -- morning -> dawn + pdnc_cleanup_last_tick(current_surface) + current_surface.morning = bot_point - current_point.x + current_surface.dawn = top_point - current_point.x + elseif(top_point == bot_point) then + pdnc_debug_message("PDNC: Top and bot point equal") + -- no cleanup is done here + -- if the points are equal, use last value until not equal + -- this should never be reached unless the pdnc_program() is broken. + else + pdnc_debug_message("Top and bot not different nor equal. probably a NaN error") + pdnc_debug_message("bot_point: " .. bot_point) + pdnc_debug_message("top_point: " .. top_point) + -- this should never be reached. + end + else + end +end + +function pdnc_cleanup_last_tick(current_surface) + current_surface.daytime = 0 + -- must be in this spesific order to + -- preserve the order at all times + -- dusk < evening < morning < dawn. + -- DO NOT CHANGE THE ORDER! + current_surface.dusk = -999999999999 + current_surface.dawn = 999999999999 + current_surface.evening = -999999999998 + current_surface.morning = 999999999998 +end + +function pdnc_disable_and_reset(ctx) + if not check_admin(ctx) then return end + local current_surface = game.surfaces[global.pdnc_surface] + pdnc_cleanup_last_tick(current_surface) + -- DO NOT CHANGE THIS ORDER! + current_surface.evening = 0.45 + current_surface.morning = 0.55 + current_surface.dusk = 0.25 + current_surface.dawn = 0.75 + current_surface.ticks_per_day = 25000 + global.pdnc_enabled = false + pdnc_debug_message("PDNC Programable Day-Night Cycle disabled, normal day-night cycle enabled") +end + +function pdnc_freeze_check(current_surface) + if(current_surface.freeze_daytime)then + current_surface.freeze_daytime = false + game.print("Can't use freeze_daytime while programmable day-night cycle is active; time has been unfrozen") + end +end + +function pdnc_program(x) + return pdnc_scaler(pdnc_c_boxy(x*6.28318530717958647692)) -- that's Tau, aka 2xPi +end + +function pdnc_c_boxy(x) + return pdnc_normalize((math.sin(x) + (0.111 * math.sin(3 * x))) * 1.124859392575928) + -- magic numbers to make it scale to (-1, 1) +end + +function pdnc_normalize(n) + return (n + 1)/2 +end + +function pdnc_scaler(r) -- a bit messy, but simplifies a lot elsewhere + if(pdnc_check_valid(r, "pdnc_scaler"))then + + local a = 1 + local b = 1 + + if(global.pdnc_enable_brightness_limit) then + a = global.pdnc_max_brightness + end + + if(global.pdnc_enable_rocket_darkness) then + b = 1 - pdnc_rocket_launch_darkness() + end + + return r * 0.85 * a * b -- 0 -> 0.85 is the 'brightness range' of factorio + end +end + +function pdnc_intersection (s1, e1, s2, e2) + local d = (s1.x - e1.x) * (s2.y - e2.y) - (s1.y - e1.y) * (s2.x - e2.x) + local a = s1.x * e1.y - s1.y * e1.x + local b = s2.x * e2.y - s2.y * e2.x + local x = (a * (s2.x - e2.x) - (s1.x - e1.x) * b) / d + --local y = (a * (s2.y - e2.y) - (s1.y - e1.y) * b) / d + return x--, y +end + +function pdnc_intersection_top (s2, e2) + local s1, e1 = {x = -999999999, y = 0.85}, {x = 999999999, y = 0.85} + return pdnc_intersection (s1, e1, s2, e2) +end + +function pdnc_intersection_bot (s2, e2) + local s1, e1 = {x = -999999999, y = 0.0}, {x = 999999999, y = 0.0} + return pdnc_intersection (s1, e1, s2, e2) +end + +function pdnc_set_max_brightness(n) + if(pdnc_check_valid(n, "pdnc_set_max_brightness")) then + global.pdnc_max_brightness = n + pdnc_debug_message("global.pdnc_max_brightness set to " .. global.pdnc_max_brightness) + end +end + +function pdnc_min_to_ticks(m) + return 60*60*m +end + +function pdnc_rocket_launch_counter() + global.pdnc_rockets_launched = 1 + global.pdnc_rockets_launched +end + +function pdnc_rocket_launch_darkness() + if (global.pdnc_rockets_launched_smooth < global.pdnc_rockets_launched)then + global.pdnc_rockets_launched_smooth = global.pdnc_rockets_launched_step_size + global.pdnc_rockets_launched_smooth + end + return (1 - (50/(global.pdnc_rockets_launched_smooth+50))) +end + +function pdnc_check_valid(n, s) + if (n == nil) then + pdnc_debug_message(s .. " set to nil! Set to 1.0 instead") + return false + elseif (n < 0) then + pdnc_debug_message(s .. " cannot be " .. n .. " limited to 0.0 instead") + return false + elseif (n > 1) then + pdnc_debug_message(s .. " cannot be " .. n .. " limited to 1.0 instead") + return false + elseif (n ~= n) then + pdnc_debug_message(s .. " cannot be " .. n .. " since it's not a valid number!") + else return true + end +end + +function pdnc_debug_message(s) + if(global.pdnc_debug) then + game.print(s) + end +end + + +local PDNC_init = {} + +local script_events = { + --place the here what you would normaly use Event.register for + -- Event.register(defines.events.on_player_created, testfunction) + -- is the same as + -- [defines.events.on_player_created] = testfunction, + -- where testfunction is | local functuin testfunction() { } + --[Event] = function, + --put stuff here + +} + +PDNC_init.on_nth_ticks = { + --place the here what you would normaly use + --[tick] = function, + --put stuff here + [global.pdnc_stepsize] = pdnc_core, +} + +PDNC_init.add_commands = function() + commands.add_command("pdnc", "gives PDNC's status", pdnc_print_status) + commands.add_command("pdnc_toggle", "toggles pdnc", pdnc_toggle) + commands.add_command("pdnc_toggle_debug", "toggles pdnc debug mode", pdnc_toggle_debug) + commands.add_command("pdnc_disable_and_reset", "Disabled pdnc and resets the dnc to the vanilla default", pdnc_disable_and_reset) +end + +PDNC_init.on_init = function() -- this runs when Event.core_events.init + log("PDNC init") + --put stuff here + global.PDNC_data = global.PDNC_data or script_data -- NO TOUCHY + +end + +PDNC_init.on_load = function() -- this runs when Event.core_events.load + log("PDNC load") + + --put stuff here + + script_data = global.PDNC_data or script_data -- NO TOUCHY +end + +PDNC_init.get_events = function() + return script_events +end + +return PDNC_init