commit 2d340cbb0522ea8cd3b71ba5143c4273056a28e5 Author: numzero Date: Sun Jul 14 21:56:00 2024 +0300 Import RealisticReactors 3.1.5 diff --git a/DOCUMENTATION/RealisticReactors_Output_2.17_IngosMode.ods b/DOCUMENTATION/RealisticReactors_Output_2.17_IngosMode.ods new file mode 100644 index 0000000..248f450 Binary files /dev/null and b/DOCUMENTATION/RealisticReactors_Output_2.17_IngosMode.ods differ diff --git a/DOCUMENTATION/fuel_cells_ownly.ods b/DOCUMENTATION/fuel_cells_ownly.ods new file mode 100644 index 0000000..5c1bef2 Binary files /dev/null and b/DOCUMENTATION/fuel_cells_ownly.ods differ diff --git a/DOCUMENTATION/ownly_mox.png b/DOCUMENTATION/ownly_mox.png new file mode 100644 index 0000000..81035aa Binary files /dev/null and b/DOCUMENTATION/ownly_mox.png differ diff --git a/DOCUMENTATION/ownly_plutonium.png b/DOCUMENTATION/ownly_plutonium.png new file mode 100644 index 0000000..33b674e Binary files /dev/null and b/DOCUMENTATION/ownly_plutonium.png differ diff --git a/DOCUMENTATION/ownly_thorium.png b/DOCUMENTATION/ownly_thorium.png new file mode 100644 index 0000000..2d9b79c Binary files /dev/null and b/DOCUMENTATION/ownly_thorium.png differ diff --git a/DOCUMENTATION/ownly_uranium.png b/DOCUMENTATION/ownly_uranium.png new file mode 100644 index 0000000..012ccbb Binary files /dev/null and b/DOCUMENTATION/ownly_uranium.png differ diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..cb97cc6 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,116 @@ +--------------------------------------------------------------------------------------------------- +Version: 3.1.5 +Date: 2024.04.15 + Bugfixes: + - fixed error during mod migration phase +--------------------------------------------------------------------------------------------------- +Version: 3.1.4 +Date: 2022.05.22 + Changes: + - cleaned up heat network merge algorithm code + - improved debug functions + - updated readme.md + Bugfixes: + - fixed merging heat networks when placing reactors at chunk borders + - fixed changelog Locale wording + Locale: + - updated russian translation, big thanks to Gesugao-san +--------------------------------------------------------------------------------------------------- +Version: 3.1.3 +Date: 2022.04.24 + Bugfixes: + - fixed sarcophagus recipe remained disabled after research + - fixed exception if cell was empty in get_entity_neighbour_cells at scripts/heat/math.lua:332 + Locale: + - added korean translation, big thanks to x2605 +--------------------------------------------------------------------------------------------------- +Version: 3.1.2 +Date: 2021.08.20 + Bugfixes: + - fix settings localizations + - fix error due to missing setting name + - fix sarcophagus entity picture statement + - remove unused code + Locale: + - update english tips and tricks + - update settings descriptions + License: + - update license.txt +--------------------------------------------------------------------------------------------------- +Version: 3.1.1 +Date: 2021.08.17 + Bugfixes: + - fix specific heat of heat buffer of reactor +--------------------------------------------------------------------------------------------------- +Version: 3.1.0 +Date: 2021.08.14 + Info: + - revived meltdown explosion map setting + - atomic bombs from True-Nukes need to be enabled if alternate meltdown explosion setting is used + Features: + - meltdown explosion compatibility with True-Nukes and PlutoniumEnergy + - cooling tower lights + Changes: + - rm tint from breeder + Bugfixes: + - fix crash on surface deleted + - fix removing non-empty reactors with robots + - fix reactors running forever when burnt result is full + - fix missing mod interface script_raised_revive event + - fix heat buffer connections; hack limit of 200 using big-data-string mod + - fix mod setting namespace + - fix lighting & masking + - cleanup textures +--------------------------------------------------------------------------------------------------- +Version: 3.0.0 +Date: 2021.01.29 + Info: + - code rewrite, cleanup + - saves with the 2.x version will be converted + - saves with the 3.x version aren't backward compatible! Test carefully. + - unlimited heat-pipe network size + - no periodic heat-pipe checks anymore + - meltdown explosion fixed to regular + - fallout mode fixed to clouds and radioactivity + - meltdown mode fixed to ruin with sarcophagus + - manual neighbor check removed due to heat-network optimizations + - periodic neighbor check removed due to heat-network optimizations + Bugfixes: + - blueprinting fixes + - bot and pipette placement fixes + - consistent event handling + - sarcophagus fits over adjacent reactor ruins + - tons of code and logic fixes + - code reorder, modularization + - heat-network rewrite, new logic with clustered A* pathfinder + - underground heat-pipe support removed, now handled through event system + - hack to get heat buffer connections from prototype + - deterministic runtime behavior + - remove rr-interface calls, except for debugging + - add readme.md + - fix tofu in changelog.txt + Locale: + - update english translation + - add german translation + - update russian translation + Graphics: + - cleanup unused files + - remove nuclear explosion +--------------------------------------------------------------------------------------------------- +Version: 2.20.0 +Date: 2020.12.26 + Changes: + - included a few (hopefully) helpful entries in the tips and tricks +--------------------------------------------------------------------------------------------------- +Version: 2.19.1 +Date: 2020.12.25 + Bugfixes: + - fixed error in GUI + Changes: + - reactor cannot be started / stopped from GUI anymore +--------------------------------------------------------------------------------------------------- +Version: 2.19.0 +Date: 2020.12.23 + Info: + - compatibility with Factorio version 1.1 + - BACKUP YOUR SAVEGAME BEFORE OVERWRITING IT ! diff --git a/control.lua b/control.lua new file mode 100644 index 0000000..44facdc --- /dev/null +++ b/control.lua @@ -0,0 +1,76 @@ +local rpath = "scripts." +require(rpath .. "const") -- globals +local mod = require(rpath .. "init") +local gui = require(rpath .. "gui.init") +local events = require(rpath .. "events.init") +local interface = require(rpath .. "interface") +local network = require(rpath .. "heat.network") +local fx = require(rpath .. "fx") + +-- INITIALIZING AND UPDATING FUNCTIONS + +-- mod initialization +script.on_init(mod.init) +script.on_load(mod.load) +script.on_configuration_changed(mod.migration) + +-- hook ticks +script.on_event(events.defined.tick, function (event) + mod.tick(event.tick) +end) + +-- hook gui +script.on_event(events.defined.gui.opened, function (event) + gui.opened(event.player_index) +end) +script.on_event(events.defined.gui.clicked, function (event) + gui.clicked(event.player_index, event.element, event.tick) +end) + +-- hook heat network +script.on_event(events.defined.removed.surface, function (event) + network.remove_from_surface(event.surface_index) +end) +script.on_event(events.defined.removed.chunk, function (event) + local z = event.surface_index + for _,p in pairs(event.positions) do + network.remove_from_chunk(p.x,p.y,z) + end +end) + +-- hook fx +script.on_event(events.defined.trigger.effect, function (event) + fx.effect(event.effect_id, event.target_entity, event.force, event.tick) +end) + +-- hook construct events +script.on_event(events.defined.pipette, function (event) + events.construct.pipette(event.player_index, event.item) +end) +script.on_event(events.defined.added.entity, function (event) + events.construct.entity(event.created_entity or event.entity, event.tick) +end) + +-- hook destruct events +script.on_event(events.defined.removed.ghost, function (event) + if event.ghost.type == "item-request-proxy" then return end + events.destruct.ghost(event.ghost) +end) +script.on_event(events.defined.removed.entity, function (event) + local has_died = event.name == defines.events.on_entity_died + events.destruct.entity(event.entity, event.tick, has_died, event.name) +end) + + +-- add events filter +for action,event_filters in pairs(events.filters) do + for key,filters in pairs(event_filters) do + for _,event in pairs(events.defined[action][key]) do + script.set_event_filter(event, filters) + end + end +end + + +remote.add_interface(REMOTE_INTERFACE_NAME, interface) + diff --git a/data-final-fixes.lua b/data-final-fixes.lua new file mode 100644 index 0000000..2c02c1a --- /dev/null +++ b/data-final-fixes.lua @@ -0,0 +1,4 @@ + +require("prototypes.connections_hack") +require("prototypes.category_fix") +require("prototypes.apm_fix") diff --git a/data-updates.lua b/data-updates.lua new file mode 100644 index 0000000..3ab2cbb --- /dev/null +++ b/data-updates.lua @@ -0,0 +1 @@ +require("prototypes.patch_other_mods") diff --git a/data.lua b/data.lua new file mode 100644 index 0000000..54267b0 --- /dev/null +++ b/data.lua @@ -0,0 +1,15 @@ + +require("prototypes.entities") +require("prototypes.items") +require("prototypes.recipes") +require("prototypes.signals") +require("prototypes.technology") +require("prototypes.sprites_font") +require("prototypes.lamps") +require("prototypes.fx") +require("prototypes.tips_and_tricks") + + + +data.raw["heat-pipe"]["heat-pipe"].heat_buffer.specific_heat = "1MJ" +data.raw["boiler"]["heat-exchanger"].energy_source.specific_heat = "1MJ" diff --git a/graphics/black192x100.png b/graphics/black192x100.png new file mode 100644 index 0000000..4d12a34 Binary files /dev/null and b/graphics/black192x100.png differ diff --git a/graphics/black192x101.png b/graphics/black192x101.png new file mode 100644 index 0000000..56a237c Binary files /dev/null and b/graphics/black192x101.png differ diff --git a/graphics/cloud-45-frames.png b/graphics/cloud-45-frames.png new file mode 100644 index 0000000..f4128e4 Binary files /dev/null and b/graphics/cloud-45-frames.png differ diff --git a/graphics/entity/breeder-heated.png b/graphics/entity/breeder-heated.png new file mode 100644 index 0000000..ce41a98 Binary files /dev/null and b/graphics/entity/breeder-heated.png differ diff --git a/graphics/entity/breeder-lights.png b/graphics/entity/breeder-lights.png new file mode 100644 index 0000000..7b4bc91 Binary files /dev/null and b/graphics/entity/breeder-lights.png differ diff --git a/graphics/entity/breeder-ruin.png b/graphics/entity/breeder-ruin.png new file mode 100644 index 0000000..bb421f9 Binary files /dev/null and b/graphics/entity/breeder-ruin.png differ diff --git a/graphics/entity/breeder.png b/graphics/entity/breeder.png new file mode 100644 index 0000000..002a55e Binary files /dev/null and b/graphics/entity/breeder.png differ diff --git a/graphics/entity/cooling-tower-glow.png b/graphics/entity/cooling-tower-glow.png new file mode 100644 index 0000000..7785d4d Binary files /dev/null and b/graphics/entity/cooling-tower-glow.png differ diff --git a/graphics/entity/cooling-tower-light.png b/graphics/entity/cooling-tower-light.png new file mode 100644 index 0000000..59c761b Binary files /dev/null and b/graphics/entity/cooling-tower-light.png differ diff --git a/graphics/entity/cooling-tower-shadow.png b/graphics/entity/cooling-tower-shadow.png new file mode 100644 index 0000000..21a21f5 Binary files /dev/null and b/graphics/entity/cooling-tower-shadow.png differ diff --git a/graphics/entity/cooling-tower.png b/graphics/entity/cooling-tower.png new file mode 100644 index 0000000..3d019f7 Binary files /dev/null and b/graphics/entity/cooling-tower.png differ diff --git a/graphics/entity/reactor-heated.png b/graphics/entity/reactor-heated.png new file mode 100644 index 0000000..373355b Binary files /dev/null and b/graphics/entity/reactor-heated.png differ diff --git a/graphics/entity/reactor-lights.png b/graphics/entity/reactor-lights.png new file mode 100644 index 0000000..3d3896e Binary files /dev/null and b/graphics/entity/reactor-lights.png differ diff --git a/graphics/entity/reactor-pipes-heated.png b/graphics/entity/reactor-pipes-heated.png new file mode 100644 index 0000000..a2c7fd1 Binary files /dev/null and b/graphics/entity/reactor-pipes-heated.png differ diff --git a/graphics/entity/reactor-pipes.png b/graphics/entity/reactor-pipes.png new file mode 100644 index 0000000..9dad1bb Binary files /dev/null and b/graphics/entity/reactor-pipes.png differ diff --git a/graphics/entity/reactor-ruin.png b/graphics/entity/reactor-ruin.png new file mode 100644 index 0000000..5537c35 Binary files /dev/null and b/graphics/entity/reactor-ruin.png differ diff --git a/graphics/entity/reactor-shadow.png b/graphics/entity/reactor-shadow.png new file mode 100644 index 0000000..df5a188 Binary files /dev/null and b/graphics/entity/reactor-shadow.png differ diff --git a/graphics/entity/reactor.png b/graphics/entity/reactor.png new file mode 100644 index 0000000..a1097ec Binary files /dev/null and b/graphics/entity/reactor.png differ diff --git a/graphics/entity/reactor4.xcf b/graphics/entity/reactor4.xcf new file mode 100644 index 0000000..cc87811 Binary files /dev/null and b/graphics/entity/reactor4.xcf differ diff --git a/graphics/entity/sarcophagus2-shadow.png b/graphics/entity/sarcophagus2-shadow.png new file mode 100644 index 0000000..5e5414a Binary files /dev/null and b/graphics/entity/sarcophagus2-shadow.png differ diff --git a/graphics/entity/sarcophagus2.png b/graphics/entity/sarcophagus2.png new file mode 100644 index 0000000..658b8ad Binary files /dev/null and b/graphics/entity/sarcophagus2.png differ diff --git a/graphics/fallout/cloud-45-frames.png b/graphics/fallout/cloud-45-frames.png new file mode 100644 index 0000000..f4128e4 Binary files /dev/null and b/graphics/fallout/cloud-45-frames.png differ diff --git a/graphics/fallout/fallout-green.png b/graphics/fallout/fallout-green.png new file mode 100644 index 0000000..fdba321 Binary files /dev/null and b/graphics/fallout/fallout-green.png differ diff --git a/graphics/fallout/fallout_spritesheet.png b/graphics/fallout/fallout_spritesheet.png new file mode 100644 index 0000000..1fd7ab3 Binary files /dev/null and b/graphics/fallout/fallout_spritesheet.png differ diff --git a/graphics/fallout/fallout_spritesheet_half.png b/graphics/fallout/fallout_spritesheet_half.png new file mode 100644 index 0000000..3cefe46 Binary files /dev/null and b/graphics/fallout/fallout_spritesheet_half.png differ diff --git a/graphics/icons/bonuscell_sprite.png b/graphics/icons/bonuscell_sprite.png new file mode 100644 index 0000000..d30820a Binary files /dev/null and b/graphics/icons/bonuscell_sprite.png differ diff --git a/graphics/icons/breeder-interface.png b/graphics/icons/breeder-interface.png new file mode 100644 index 0000000..864b38c Binary files /dev/null and b/graphics/icons/breeder-interface.png differ diff --git a/graphics/icons/breeder-reactor.png b/graphics/icons/breeder-reactor.png new file mode 100644 index 0000000..8d5e6ce Binary files /dev/null and b/graphics/icons/breeder-reactor.png differ diff --git a/graphics/icons/clowns_mox_recipe.png b/graphics/icons/clowns_mox_recipe.png new file mode 100644 index 0000000..39a9d03 Binary files /dev/null and b/graphics/icons/clowns_mox_recipe.png differ diff --git a/graphics/icons/cooling-tower.png b/graphics/icons/cooling-tower.png new file mode 100644 index 0000000..15e2fe2 Binary files /dev/null and b/graphics/icons/cooling-tower.png differ diff --git a/graphics/icons/mox_fuel_cell.png b/graphics/icons/mox_fuel_cell.png new file mode 100644 index 0000000..3634668 Binary files /dev/null and b/graphics/icons/mox_fuel_cell.png differ diff --git a/graphics/icons/neighbours.png b/graphics/icons/neighbours.png new file mode 100644 index 0000000..bf3975d Binary files /dev/null and b/graphics/icons/neighbours.png differ diff --git a/graphics/icons/nuclear-reactor-interface.png b/graphics/icons/nuclear-reactor-interface.png new file mode 100644 index 0000000..3ffc8c5 Binary files /dev/null and b/graphics/icons/nuclear-reactor-interface.png differ diff --git a/graphics/icons/nuclear-reactor.png b/graphics/icons/nuclear-reactor.png new file mode 100644 index 0000000..73f9cdb Binary files /dev/null and b/graphics/icons/nuclear-reactor.png differ diff --git a/graphics/icons/sarcophagus2.png b/graphics/icons/sarcophagus2.png new file mode 100644 index 0000000..d7a3762 Binary files /dev/null and b/graphics/icons/sarcophagus2.png differ diff --git a/graphics/icons/signal_blue.png b/graphics/icons/signal_blue.png new file mode 100644 index 0000000..60a43e4 Binary files /dev/null and b/graphics/icons/signal_blue.png differ diff --git a/graphics/icons/signal_cell_bonus.png b/graphics/icons/signal_cell_bonus.png new file mode 100644 index 0000000..d2700eb Binary files /dev/null and b/graphics/icons/signal_cell_bonus.png differ diff --git a/graphics/icons/signal_control_scram.png b/graphics/icons/signal_control_scram.png new file mode 100644 index 0000000..4ad5091 Binary files /dev/null and b/graphics/icons/signal_control_scram.png differ diff --git a/graphics/icons/signal_control_start.png b/graphics/icons/signal_control_start.png new file mode 100644 index 0000000..4e98e96 Binary files /dev/null and b/graphics/icons/signal_control_start.png differ diff --git a/graphics/icons/signal_coolant_amount.png b/graphics/icons/signal_coolant_amount.png new file mode 100644 index 0000000..b3f12c5 Binary files /dev/null and b/graphics/icons/signal_coolant_amount.png differ diff --git a/graphics/icons/signal_core_temp.png b/graphics/icons/signal_core_temp.png new file mode 100644 index 0000000..310e005 Binary files /dev/null and b/graphics/icons/signal_core_temp.png differ diff --git a/graphics/icons/signal_green_background.png b/graphics/icons/signal_green_background.png new file mode 100644 index 0000000..f4d8563 Binary files /dev/null and b/graphics/icons/signal_green_background.png differ diff --git a/graphics/icons/signal_grey_background.png b/graphics/icons/signal_grey_background.png new file mode 100644 index 0000000..4409dff Binary files /dev/null and b/graphics/icons/signal_grey_background.png differ diff --git a/graphics/icons/signal_orange_background.png b/graphics/icons/signal_orange_background.png new file mode 100644 index 0000000..55df91b Binary files /dev/null and b/graphics/icons/signal_orange_background.png differ diff --git a/graphics/icons/signal_overheat.png b/graphics/icons/signal_overheat.png new file mode 100644 index 0000000..4506d17 Binary files /dev/null and b/graphics/icons/signal_overheat.png differ diff --git a/graphics/icons/signal_reactor_efficiency.png b/graphics/icons/signal_reactor_efficiency.png new file mode 100644 index 0000000..f6553f4 Binary files /dev/null and b/graphics/icons/signal_reactor_efficiency.png differ diff --git a/graphics/icons/signal_reactor_electric_power.png b/graphics/icons/signal_reactor_electric_power.png new file mode 100644 index 0000000..90fa5ab Binary files /dev/null and b/graphics/icons/signal_reactor_electric_power.png differ diff --git a/graphics/icons/signal_reactor_power_output.png b/graphics/icons/signal_reactor_power_output.png new file mode 100644 index 0000000..3e179da Binary files /dev/null and b/graphics/icons/signal_reactor_power_output.png differ diff --git a/graphics/icons/signal_red_background.png b/graphics/icons/signal_red_background.png new file mode 100644 index 0000000..6cc4594 Binary files /dev/null and b/graphics/icons/signal_red_background.png differ diff --git a/graphics/icons/signal_state.png b/graphics/icons/signal_state.png new file mode 100644 index 0000000..09fb70d Binary files /dev/null and b/graphics/icons/signal_state.png differ diff --git a/graphics/icons/signal_yellow_background.png b/graphics/icons/signal_yellow_background.png new file mode 100644 index 0000000..5cf4f4c Binary files /dev/null and b/graphics/icons/signal_yellow_background.png differ diff --git a/graphics/icons/used-up-fuel-cell.png b/graphics/icons/used-up-fuel-cell.png new file mode 100644 index 0000000..1e8faa6 Binary files /dev/null and b/graphics/icons/used-up-fuel-cell.png differ diff --git a/graphics/lamps/black.png b/graphics/lamps/black.png new file mode 100644 index 0000000..0b25a73 Binary files /dev/null and b/graphics/lamps/black.png differ diff --git a/graphics/lamps/green.png b/graphics/lamps/green.png new file mode 100644 index 0000000..e69449d Binary files /dev/null and b/graphics/lamps/green.png differ diff --git a/graphics/lamps/red.png b/graphics/lamps/red.png new file mode 100644 index 0000000..6df80ae Binary files /dev/null and b/graphics/lamps/red.png differ diff --git a/graphics/lamps/yellow.png b/graphics/lamps/yellow.png new file mode 100644 index 0000000..606f657 Binary files /dev/null and b/graphics/lamps/yellow.png differ diff --git a/graphics/smoke.png b/graphics/smoke.png new file mode 100644 index 0000000..250460f Binary files /dev/null and b/graphics/smoke.png differ diff --git a/graphics/sprites/a.png b/graphics/sprites/a.png new file mode 100644 index 0000000..fc1d75c Binary files /dev/null and b/graphics/sprites/a.png differ diff --git a/graphics/sprites/b.png b/graphics/sprites/b.png new file mode 100644 index 0000000..052bdca Binary files /dev/null and b/graphics/sprites/b.png differ diff --git a/graphics/sprites/ba.png b/graphics/sprites/ba.png new file mode 100644 index 0000000..da1eb6d Binary files /dev/null and b/graphics/sprites/ba.png differ diff --git a/graphics/sprites/black.png b/graphics/sprites/black.png new file mode 100644 index 0000000..39beefc Binary files /dev/null and b/graphics/sprites/black.png differ diff --git a/graphics/sprites/button_graph.png b/graphics/sprites/button_graph.png new file mode 100644 index 0000000..0c3ed03 Binary files /dev/null and b/graphics/sprites/button_graph.png differ diff --git a/graphics/sprites/button_graph_off.png b/graphics/sprites/button_graph_off.png new file mode 100644 index 0000000..879dc7e Binary files /dev/null and b/graphics/sprites/button_graph_off.png differ diff --git a/graphics/sprites/button_progress.png b/graphics/sprites/button_progress.png new file mode 100644 index 0000000..2fe4f8d Binary files /dev/null and b/graphics/sprites/button_progress.png differ diff --git a/graphics/sprites/button_progress_off.png b/graphics/sprites/button_progress_off.png new file mode 100644 index 0000000..b2d2986 Binary files /dev/null and b/graphics/sprites/button_progress_off.png differ diff --git a/graphics/sprites/button_signals.png b/graphics/sprites/button_signals.png new file mode 100644 index 0000000..e1ac78e Binary files /dev/null and b/graphics/sprites/button_signals.png differ diff --git a/graphics/sprites/button_signals.xcf b/graphics/sprites/button_signals.xcf new file mode 100644 index 0000000..64d4793 Binary files /dev/null and b/graphics/sprites/button_signals.xcf differ diff --git a/graphics/sprites/button_signals_off.png b/graphics/sprites/button_signals_off.png new file mode 100644 index 0000000..f7a0c37 Binary files /dev/null and b/graphics/sprites/button_signals_off.png differ diff --git a/graphics/sprites/button_x.png b/graphics/sprites/button_x.png new file mode 100644 index 0000000..9637772 Binary files /dev/null and b/graphics/sprites/button_x.png differ diff --git a/graphics/sprites/r.png b/graphics/sprites/r.png new file mode 100644 index 0000000..f17adaf Binary files /dev/null and b/graphics/sprites/r.png differ diff --git a/graphics/sprites/ra.png b/graphics/sprites/ra.png new file mode 100644 index 0000000..9a57369 Binary files /dev/null and b/graphics/sprites/ra.png differ diff --git a/graphics/sprites/rb.png b/graphics/sprites/rb.png new file mode 100644 index 0000000..d183557 Binary files /dev/null and b/graphics/sprites/rb.png differ diff --git a/graphics/sprites/rba.png b/graphics/sprites/rba.png new file mode 100644 index 0000000..674b3a5 Binary files /dev/null and b/graphics/sprites/rba.png differ diff --git a/graphics/sprites/ry.png b/graphics/sprites/ry.png new file mode 100644 index 0000000..7de0b3f Binary files /dev/null and b/graphics/sprites/ry.png differ diff --git a/graphics/sprites/rya.png b/graphics/sprites/rya.png new file mode 100644 index 0000000..097e1cc Binary files /dev/null and b/graphics/sprites/rya.png differ diff --git a/graphics/sprites/ryb.png b/graphics/sprites/ryb.png new file mode 100644 index 0000000..775dc24 Binary files /dev/null and b/graphics/sprites/ryb.png differ diff --git a/graphics/sprites/ryba.png b/graphics/sprites/ryba.png new file mode 100644 index 0000000..d21d6be Binary files /dev/null and b/graphics/sprites/ryba.png differ diff --git a/graphics/sprites/y.png b/graphics/sprites/y.png new file mode 100644 index 0000000..9a390ef Binary files /dev/null and b/graphics/sprites/y.png differ diff --git a/graphics/sprites/ya.png b/graphics/sprites/ya.png new file mode 100644 index 0000000..17dda29 Binary files /dev/null and b/graphics/sprites/ya.png differ diff --git a/graphics/sprites/yb.png b/graphics/sprites/yb.png new file mode 100644 index 0000000..4527056 Binary files /dev/null and b/graphics/sprites/yb.png differ diff --git a/graphics/sprites/yba.png b/graphics/sprites/yba.png new file mode 100644 index 0000000..bebde95 Binary files /dev/null and b/graphics/sprites/yba.png differ diff --git a/graphics/technology/breeder-reactors.png b/graphics/technology/breeder-reactors.png new file mode 100644 index 0000000..8e456b8 Binary files /dev/null and b/graphics/technology/breeder-reactors.png differ diff --git a/graphics/technology/sarcophagus.png b/graphics/technology/sarcophagus.png new file mode 100644 index 0000000..cd630ed Binary files /dev/null and b/graphics/technology/sarcophagus.png differ diff --git a/graphics/tips_and_tricks/meltdown.png b/graphics/tips_and_tricks/meltdown.png new file mode 100644 index 0000000..3bd3b2a Binary files /dev/null and b/graphics/tips_and_tricks/meltdown.png differ diff --git a/graphics/tips_and_tricks/output_breeder.png b/graphics/tips_and_tricks/output_breeder.png new file mode 100644 index 0000000..79e7695 Binary files /dev/null and b/graphics/tips_and_tricks/output_breeder.png differ diff --git a/graphics/tips_and_tricks/output_reactor.png b/graphics/tips_and_tricks/output_reactor.png new file mode 100644 index 0000000..0ba6cd7 Binary files /dev/null and b/graphics/tips_and_tricks/output_reactor.png differ diff --git a/graphics/tips_and_tricks/reactor_interface.png b/graphics/tips_and_tricks/reactor_interface.png new file mode 100644 index 0000000..94754d9 Binary files /dev/null and b/graphics/tips_and_tricks/reactor_interface.png differ diff --git a/graphics/tips_and_tricks/signal_efficiency.png b/graphics/tips_and_tricks/signal_efficiency.png new file mode 100644 index 0000000..f8c892c Binary files /dev/null and b/graphics/tips_and_tricks/signal_efficiency.png differ diff --git a/graphics/tips_and_tricks/signal_power.png b/graphics/tips_and_tricks/signal_power.png new file mode 100644 index 0000000..784e4a9 Binary files /dev/null and b/graphics/tips_and_tricks/signal_power.png differ diff --git a/graphics/tips_and_tricks/signal_start.png b/graphics/tips_and_tricks/signal_start.png new file mode 100644 index 0000000..8c92fc0 Binary files /dev/null and b/graphics/tips_and_tricks/signal_start.png differ diff --git a/graphics/tips_and_tricks/tab-b-1.png b/graphics/tips_and_tricks/tab-b-1.png new file mode 100644 index 0000000..f1832c1 Binary files /dev/null and b/graphics/tips_and_tricks/tab-b-1.png differ diff --git a/graphics/tips_and_tricks/tab-b-2.png b/graphics/tips_and_tricks/tab-b-2.png new file mode 100644 index 0000000..b608004 Binary files /dev/null and b/graphics/tips_and_tricks/tab-b-2.png differ diff --git a/graphics/tips_and_tricks/tab-b-3.png b/graphics/tips_and_tricks/tab-b-3.png new file mode 100644 index 0000000..9473643 Binary files /dev/null and b/graphics/tips_and_tricks/tab-b-3.png differ diff --git a/graphics/tips_and_tricks/tab-b-4.png b/graphics/tips_and_tricks/tab-b-4.png new file mode 100644 index 0000000..c42ae6a Binary files /dev/null and b/graphics/tips_and_tricks/tab-b-4.png differ diff --git a/graphics/tips_and_tricks/tab-b-5.png b/graphics/tips_and_tricks/tab-b-5.png new file mode 100644 index 0000000..a10bfb7 Binary files /dev/null and b/graphics/tips_and_tricks/tab-b-5.png differ diff --git a/graphics/tips_and_tricks/tab-r-1.png b/graphics/tips_and_tricks/tab-r-1.png new file mode 100644 index 0000000..fbdcded Binary files /dev/null and b/graphics/tips_and_tricks/tab-r-1.png differ diff --git a/graphics/tips_and_tricks/tab-r-2.png b/graphics/tips_and_tricks/tab-r-2.png new file mode 100644 index 0000000..438948a Binary files /dev/null and b/graphics/tips_and_tricks/tab-r-2.png differ diff --git a/graphics/tips_and_tricks/tab-r-3.png b/graphics/tips_and_tricks/tab-r-3.png new file mode 100644 index 0000000..cdae91b Binary files /dev/null and b/graphics/tips_and_tricks/tab-r-3.png differ diff --git a/graphics/tips_and_tricks/tab-r-4.png b/graphics/tips_and_tricks/tab-r-4.png new file mode 100644 index 0000000..882d2e3 Binary files /dev/null and b/graphics/tips_and_tricks/tab-r-4.png differ diff --git a/graphics/tips_and_tricks/tab-r-5.png b/graphics/tips_and_tricks/tab-r-5.png new file mode 100644 index 0000000..bc64e50 Binary files /dev/null and b/graphics/tips_and_tricks/tab-r-5.png differ diff --git a/graphics/tips_and_tricks/title_pic.png b/graphics/tips_and_tricks/title_pic.png new file mode 100644 index 0000000..bea01d5 Binary files /dev/null and b/graphics/tips_and_tricks/title_pic.png differ diff --git a/graphics/transparent32.png b/graphics/transparent32.png new file mode 100644 index 0000000..f3822ba Binary files /dev/null and b/graphics/transparent32.png differ diff --git a/info.json b/info.json new file mode 100644 index 0000000..0c135ca --- /dev/null +++ b/info.json @@ -0,0 +1,15 @@ +{ + "name": "RealisticReactors", + "version": "3.1.5", + "title": "RealisticReactors", + "author": "dodo.the.last, max2344, IngoKnieto, OwnlyMe", + "description": "Add more realistic nuclear reactors with a breeder reactor and a cooling tower. The reactors have to be controlled via integrated circuit interface signals. The reactors have a dynamic heat output depending on their temperature. They need proper cooling, otherwise a nuclear meltdown will occur.", + "homepage": "https://forums.factorio.com/viewtopic.php?f=93&t=56621", + "factorio_version": "1.1", + "dependencies": [ + "? PlutoniumEnergy >= 1.2.15", + "? True-Nukes >= 0.1.15", + "big-data-string", + "base" + ] +} diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..0053086 --- /dev/null +++ b/license.txt @@ -0,0 +1,16 @@ +RealisticReactors mod: +https://mods.factorio.com/mod/RealisticReactors +by dodo.the.last, max2344, IngoKnieto, OwnlyMe under MIT +https://opensource.org/licenses/MIT + + +Geiger counter sounds based on: +http://soundbible.com/1113-Radio-Active.html +by Mike Koenig under CC BY 3.0 +https://creativecommons.org/licenses/by/3.0/ + + +Reactor sarcophagus graphic based on: +https://www.artstation.com/artwork/nbPvO +by Anthony Garcellano + diff --git a/locale/de/locale.cfg b/locale/de/locale.cfg new file mode 100644 index 0000000..19588e5 --- /dev/null +++ b/locale/de/locale.cfg @@ -0,0 +1,156 @@ +[mod-name] +RealisticReactors=Realistische Kernreaktoren + +[mod-description] +RealisticReactors=Füge Realistische Reaktoren mit dem Kernreaktor, Brutreaktor und Kühlturm hinzu. Diese Reaktoren werden über das Schaltungsnetz gesteuert. Sie verfügen über dynamische Wärmeproduktion in Abhängigkeit von der Kerntemperatur. Die Reaktoren brauchen entsprechende Kühlung, sonst tritt eine nukleare Kernschmelze mit weitreichenden Folgen ein. + +[entity-name] +realistic-reactor-interface=Schnittstelle des Kernreaktors mit dem Schaltungsnetz +realistic-breeder-interface=Schnittstelle des Brutreaktors mit dem Schaltungsnetz +realistic-reactor-eccs=Notkühlsystem (Emergency Core Cooling System, ECCS) +rr-cooling-tower=Kühlturm +realistic-reactor-normal=Kernreaktor +realistic-reactor-power-normal=Kernreaktor +realistic-reactor-breeder=Brutreaktor +realistic-reactor-power-breeder=Brutreaktor +reactor-ruin=Ruine des Kernreaktors +breeder-ruin=Ruine des Brutreaktors +reactor-sarcophagus=Sarkophag +realistic-reactor=Kernreaktor + +[entity-description] +realistic-reactor-interface=Sendet die Statussignale des Kerneaktors ins Schaltungsnetz und empfängt Befehle +realistic-breeder-interface=Sendet die Statussignale des Brutreaktors ins Schaltungsnetz und empfängt Befehle +realistic-reactor-eccs=Enthält Wasser für Notkühlung des Reaktorkerns. Pumpe kaltes Wasser in und heisses Wasser aus dem Reaktorkern! Der Kühlvorgang benötigt elektrische Energie +rr-cooling-tower=Kühlt Dampf oder heißes Wasser auf 15°C herunter +realistic-reactor-normal=Produziert Wärme. Hat hohe Leistungsabgabe +realistic-reactor-breeder=Produziert Wärme. Hat mittlere Leistungsabgabe und produziert zusätzliche verbrauchte Brennelemente. Leistung, Effizienz und Produktion von verbrannten Elementen hängen vom Arbeitspunkt des Reaktorkerns ab +reactor-ruin=Emittiert Radioaktivität. Bau einen Sarkophag drauf! +breeder-ruin=Emittiert Radioaktivität. Bau einen Sarkophag drauf! +reactor-sarcophagus=Soll auf die Reaktorruine platziert werden +realistic-reactor=Hat hohe Leistungsabgabe. Thermische Leistung und Effizienz ändern sich, in Abhängigkeit von Kerntemperatur + +[virtual-signal-name] +signal-reactor-core-temp=Kerntemperatur des Kernreaktors +signal-uranium-fuel-cells=Brennelemente +signal-used-uranium-fuel-cells=Verbrauchte Brennelemente +signal-state-stopped=[Reaktor Zustand] Gestoppt +signal-state-starting=[Reaktor Zustand] Startet +signal-state-running=[Reaktor Zustand] In Betrieb +signal-state-scramed=[Reaktor Zustand] Im SCRAM +signal-control-start=[Reaktor Kontrolle] Start +signal-control-scram=[Reaktor Kontrolle] SCRAM +signal-reactor-power-output=Leistung des Reaktorkerns +signal-reactor-efficiency=Effizienz des Reaktorkerns +signal-reactor-cell-bonus=Bonusproduktion des Brutreaktors +signal-coolant-amount=Menge des Kühlmittels im ECCS +signal-reactor-electric-power=Akkuladung +signal-neighbour-bonus=Nachbarschaftsbonus des Reaktors + +[virtual-signal-description] +signal-reactor-core-temp=Temperatur des Kerns des Reaktors. Die Temperatur darf 1000°C nicht überschreiten! +signal-uranium-fuel-cells=Menge der Uranbrennstäbe im Reaktor +signal-used-uranium-fuel-cells=Menge der verbrauchten Uranbrennstäbe im Reaktor +signal-state-stopped=Wenn der Reaktor angehalten ist, dann ist dieses Signal 1, ansonsten 0 +signal-state-starting=Wenn der Reaktor startet, wird hier runter gezählt bis die Startphase vorbei ist. Danach ist der Wert 0 +signal-state-running=Wenn der Reaktor läuft dann ist das Signal 1, ansonsten 0 +signal-state-scramed=Wenn der Reaktor im SCRAM ist, wird der Wert bis Ende der Phase runtergezählt. Ansonsten ist das Signal 0 +signal-control-start=Wenn dieses Startsignal an den Reaktor gesendet wird, geht der Reaktor in die Startphase über +signal-control-scram=Wenn dieses SCRAM Signal an den Reaktor gesendet wird, geht der Reaktor in die SCRAM Phase über +signal-reactor-power-output=Aktuelle Leistungsabgabe des Reaktorkerns (MWth) +signal-reactor-efficiency=Aktuelle Effizienz des Reaktorkerns in %. Die Effizienz von 200 bedeutet die doppelte Energieausbeute aus einem Brennelement im Vergleich zur Effizienz 100. Effizienz von 50 bedeutet halbe Energieausbeute +signal-reactor-cell-bonus=Produktionsrate der zusätzlichen verbrannten Brennelemente. Das Signal wird mit 100 multipliziert. 50 bedeutet, es wird 1 extra Brennelement pro 2 verbrannte Brennelemente produziert +signal-coolant-amount=Menge des Wassers im ECCS +signal-reactor-electric-power=Ladezustand des internen Akkumulators in % +signal-neighbour-bonus=Anzahl der Reaktoren, welche mit Wärmeröhren verbunden sind. Der Bonus gilt für bis zu 4 verbundene Reaktoren + +[technology-name] +breeder-reactors=Brutreaktoren + +[technology-description] +breeder-reactors=Erforsche den Brutreaktor, welcher in der Lage ist, zusätzliche Brennelemente produzieren zu können + +[item-name] +rr-clowns-mox-cell=MOX Brennelement + +[mod-setting-name] +realistic-reactors-disable-reactor-light=Reaktorbeleuchtung deaktivieren +realistic-reactors-disable-vanilla-reactor=Standardreaktor deaktivieren +realistic-reactors-reactor-scram-duration=SCRAM Dauer +realistic-reactors-reactor-starting-duration=Start Dauer +realistic-reactors-energy-consumption-multiplier=Multiplikator des Energieverbrauchs +realistic-reactors-calculate-stats-function=Kontrollgesetz der Reaktoren +realistic-reactors-static-cooling-power-consumption=Statischer Stromverbrauch im ECCS +realistic-reactors-scram-behaviour=SCRAM Verhalten +realistic-reactors-explosion-mode=Modus der Kernschmelze + +realistic-reactors-clouds-duration=Lebensdauer der radioaktiven Wolken +realistic-reactors-clouds-generation=Dauer der Wolkenemission nach der Kernschmelze +realistic-reactors-fallout-appearance=Aussehen der radioaktiven Niederschläge +realistic-reactors-fallout-duration=Lebensdauer der radioaktiven Niederschläge +realistic-reactors-sarcophagus-duration=Lebensdauer des Sarkophags + +[mod-setting-description] +realistic-reactors-disable-reactor-light=Unterleuchtung des Reaktors deaktivieren +realistic-reactors-disable-vanilla-reactor=Entfernt den Standardreaktor von Factorio aus dem Spiel +realistic-reactors-reactor-scram-duration=Dauer der SCRAM Phase in Sekunden +realistic-reactors-reactor-starting-duration=Dauer der Startphase in Sekunden +realistic-reactors-energy-consumption-multiplier=Multiplikator des Energieverbrauchs in der Startphase, Notkühlung mit ECCS und der Reaktorschnittstelle.\nMin: 0, Standard: 1.0, Max: 2.5 (Realistisch: 2.5) +realistic-reactors-calculate-stats-function=Hier kann das Kontrollgesetz für die Reaktoren festgelegt werden.\n\nOwnly's Formeln besitzen recht differenziertes Verhalten, in Abhängigkeit von Nuklearbrennstoff. Die maximale Abgabe und Effizienz werden mit den höheren Kerntemperaturen besser sein, sowie zusätzlich 10% pro Nachbar.\n\nIngo's Formeln zwingen die Kerntemperatur genauer zu kontrollieren. Die optimale Leistungabgabe/Effizienz haben je nach Konfiguration unterschiedliche optimale Temperatur.\n\nWeitere Informationen über die beiden Formeln gibt es im Forum oder aus ODT-Dokument im Modarchiv +realistic-reactors-static-cooling-power-consumption=Wenn dies ausgewählt ist, verbraucht das ECCS des Reaktors 1 MW. Wenn dies nicht ausgewählt ist, verbraucht das ECCS 1 MW pro 20 MW Kühlleistung + +realistic-reactors-scram-behaviour=auf aktuelles Brennelement begrenzen = Der Reaktor wird im SCRAM runterfahren bis die SCRAM Dauer vorbei ist oder das Brennelement verbrannt ist.\n\nstop halb-leer = Der Reaktor wird das nächste Brennelement nehmen wenn das aktuelle Element während des SCRAM abbrennt. Das Brennelement verbleibt im Reaktor halb-verbraucht nach Ablauf des SCRAM.\n\nverbrauche zus. Element = So wie oben, jedoch wird das Brennelement verbraucht wenn der SCRAM endet.\n\nZerfallswärme (v1.0.x) = Aktuelles Brennelement wird entfernt und der Reaktor produziert Restwärme, wie in version 1.0.x + +realistic-reactors-explosion-mode=Methode der Kernschmelze + +realistic-reactors-clouds-generation=Wenn der Reaktorkern schmilzt, entsteht eine Ruine, welche radioaktive Wolken emittiert. Die Wolken bewegen sich auf der Karte entsprechend der Windrichtung/-Stärke und verteilen radioaktiven Niederschlag.\nMit der Einstellung wird die Dauer der Emission der Ruine eingestellt\n(0 - 100 - 9999999) +realistic-reactors-clouds-duration=Lebensdauer der radioaktiven Wolken (in Sekunden)\n(1 - 80 - 9999999) +realistic-reactors-fallout-appearance=Unsichtbar = nur der Geigerzähler weist auf Radioaktivität hin\n\nHalbtransparent = schwebende orangene Partikel\n\nWeniger Transprent = so wie oben, nur noch besser sichtbar\n\nGrüner Schleier = Die Bereiche werden grün eingefärbt. Könnte schwer zu erkennen sein, wenn die Belastung gering ist. +realistic-reactors-fallout-duration=Lebensdauer der Kontamination durch den radioaktiven Niederschlag\n(1 - 600 - 9999999) +realistic-reactors-sarcophagus-duration=Lebensdauer des Sarkophags. Nach Ablauf dieser Zeit\nkann der Sarkophag abgebaut werden (in Sekunden) + + +[string-mod-setting] +realistic-reactors-calculate-stats-function-ownly=Ownly's Formeln +realistic-reactors-calculate-stats-function-ingo=Ingo's Formeln +realistic-reactors-scram-behaviour-limit-to-current-cell=Auf aktuelles Brennelement begrenzen +realistic-reactors-scram-behaviour-stop-half-empty=Stop halb-leer +realistic-reactors-scram-behaviour-consume-additional-cell=Verbrauche zusätzliches Brennelement +realistic-reactors-scram-behaviour-decay-heat-v1=Zerfallswärme (v1.0.x) +realistic-reactors-explosion-mode-meltdown=Realistisch +realistic-reactors-explosion-mode-really-very-small-atomic-bomb-projectile=Atombombe mit sehr geringer Sprengkraft (2t) +realistic-reactors-explosion-mode-very-small-atomic-bomb-projectile=Atombombe mit geringer Sprengkraft (4t) +realistic-reactors-explosion-mode-small-atomic-bomb-projectile=Atombombe mit kleiner Sprengkraft (8t) +realistic-reactors-explosion-mode-atomic-rocket=Atombombe mit Sprengkraft (20t) +realistic-reactors-explosion-mode-big-atomic-bomb-projectile=Atombombe mit großer Sprengkraft (500t) +realistic-reactors-explosion-mode-very-big-atomic-bomb-projectile=Atombombe mit sehr großer Sprengkraft (1kt) +realistic-reactors-explosion-mode-thermobaric-rocket=Thermobarische Bombe +realistic-reactors-explosion-mode-plutonium-atomic-rocket=Plutonium Bombe + +realistic-reactors-fallout-appearance-invisible=Unsichtbar +realistic-reactors-fallout-appearance-half-transparent=Halbtransparent +realistic-reactors-fallout-appearance-less-transparent=Weniger transparent +realistic-reactors-fallout-appearance-green-veil=Grüner Schleier + +[tips-and-tricks-item-name] +RealisticReactors_Title=Realistische Reaktoren +RealisticReactors_Operate=Wie wird der Reaktor betrieben? +RealisticReactors_Output=Leistungsabgabe und Effizienz +RealisticReactors_Meltdown=Kernschmelze +RealisticReactors_OutputReactor=Leistungsparameter des Kernreaktors +RealisticReactors_OutputBreeder=Leistungsparameter des Brutreaktors + +[tips-and-tricks-item-description] +RealisticReactors_Title=Glückwunsch! Mit diesem Mod machst Du Dein Kernreaktoraufbau ein Bißchen spannender!\n\nRealistische Reaktoren [entity=realistic-reactor] produzieren Wärmeenergie und verbrauchen dabei Brennstoffzellen, genau so wie die vanilla Kernreaktoren in Factorio. Jedoch hängen ihre Leistungsabgabe [virtual-signal=signal-reactor-power-output] und Effizienz [virtual-signal=signal-reactor-efficiency] von der Kerntemperatur des Kernreaktors ab [virtual-signal=signal-reactor-core-temp], sowie vom physikalischen Layout des Wärmenetzwerks ab [virtual-signal=signal-neighbour-bonus].\nDer Brutreaktor [entity=realistic-reactor-breeder] funktioniert vergleichbar mit unserem Kernreaktor. Der Brutreaktor hat mittlere Wärmeabgabe, produziert jedoch zusätzliche verbrannte Brennelemente. Das ist nützlich zur Produktion von weiteren Brennstoffzellen oder für Massenvernichtungswaffen.\n\nBeide Reaktoren brauchen entsprechende Kühlung. Wenn ihre Kerntemperatur 1000°C erreicht, geschieht eine Kernschmelze. Um dem vorzubeugen, haben die Reaktoren ein Notkühlsystem, das ECCS (Emergency Core Cooling System) integriert. Das ECCS muss mit kaltem Wasser gespeist werden und dabei heisses Wasser aus dem Reaktorkern abgeführt werden. Die Kühltürme [entity=rr-cooling-tower] werden zur Kühlung von heißem Wasser oder Dampf verwendet. + +RealisticReactors_Operate=Steuerung und Überwachung der Reaktoren geschieht über das Schaltnetzwerk, welches mit dem [entity=realistic-reactor-interface] verbunden wird. Die Schnittstelle liefert Signale über den Zustand des Reaktorkerns und der Subsysteme:\n[virtual-signal=signal-state-stopped]\n[virtual-signal=signal-state-starting]\n[virtual-signal=signal-state-running]\n[virtual-signal=signal-state-scramed]\n[virtual-signal=signal-reactor-core-temp]\n[virtual-signal=signal-coolant-amount]\n[virtual-signal=signal-reactor-power-output]\n[virtual-signal=signal-reactor-efficiency]\n[virtual-signal=signal-neighbour-bonus]\n[virtual-signal=signal-reactor-cell-bonus]\n[virtual-signal=signal-reactor-electric-power]\n\nUm den __CONTROL_STYLE_BEGIN__Reaktor zu starten__CONTROL_STYLE_END__ müssen einige Brennelemente bestückt werden und dann das Startsignal [virtual-signal=signal-control-start] an den Reaktor gesendet werden. Der Reaktor schaltet in die Startphase und verbraucht dabei etwas elektrische Energie.\nNach der Startphase geht der Reaktor in den Normalbetrieb über. Jetzt ändert sich die Leistungsabgabe und die Effizienz in Abhängigkeit von der Kerntemperatur des Reaktors.\n\nUm den __CONTROL_STYLE_BEGIN__Reaktor zu stoppen__CONTROL_STYLE_END__ kann er die letzte Brennstoffzelle verbrauchen und danach anhalten. Um den Reaktor im Notfall anzuhalten muss das SCRAM Signal [virtual-signal=signal-control-scram] an die Schnittstelle des Reaktors gesendet werden. Der Reaktor geht sofort in die SCRAM Phase über, währenddessen produziert er weiterhin thermische Energie. Während dieser Zeit muss die Kühlung des Reaktorkerns sichergestellt sein! \n\nUm den __CONTROL_STYLE_BEGIN__Reaktor zu kühlen__CONTROL_STYLE_END__ kann die gesamte Wärme über das Wärmenetzwerk und Wärmetauscher abgeführt werden. Die andere Variante ist die Kühlung mit dem [entity=realistic-reactor-eccs].\nBeachte, dass bei hoher thermischer Abgabeleistung die Kühlung mit dem ECCS allein nicht mehr ausreichend sein kann. In diesem Fall müssen beide Varianten im Design vorgesehen werden, um auf der sicheren Seite zu bleiben.\n\n__CONTROL_STYLE_BEGIN__Anmerkungen__CONTROL_STYLE_END__ \n - Der Reaktor braucht elektrische Energie um betrieben zu werden. Diese Energie wird für interne Komponenten des Reaktors benötigt. Das schließt das Reaktorinterface und ECCS mit ein. Weiterhin hat jeder Reaktor einen internen Akku um kurze Stromausfälle zu überbrücken.\n - Die Informtionen über den Reaktor können auch in einer GUI angesehen werden. Dafür muss der Anwender auf das Reaktorinterface clicken. Dort gibt es auch einen Liniengraph über die wichtigsten Parameter. + +RealisticReactors_Output=Die Signale [virtual-signal=signal-reactor-power-output], [virtual-signal=signal-reactor-efficiency] und [virtual-signal=signal-reactor-cell-bonus] verändern sich in Abhängigkeit von der Kerntemperatur des Reaktors.\nDie allgemeine Regel für die Brutreaktoren ist einfach: je höher die Temperatur, desto besser. Mit Effizienz ist es anders: beste Effizienz ist bei einer bestimmten Temperatur erreicht, darunter oder darüber wird sie sinken. \nDie Effizienz hat einen Minimum von 50% und einen Maximum von 200%. \n\nAlle Werte hängen im Endeffekt vom Reaktorsetup ab, also Anzahl der mit Wärmeröhren verbundenen Reaktoren (dies ersetzt den aus vanilla bekannten Nachbarbonus). Das Maximum sind 4 verbundene Reaktoren, der aktuelle Zustand wird als [virtual-signal=signal-neighbour-bonus] gesendet. \n\nDie folgenden 2 Seiten zeigen detailierte Informationen über Reaktorkombinationen, Temperaturen, Effizienzen etc. Weitere Information ist im Modarchiv gespeichert, im Ordner DOCUMENTATION. \n\n__CONTROL_STYLE_BEGIN__Spoiler alarm!__CONTROL_STYLE_END__\nWenn Du im Spiel die Leistungsgrenzen selbst ermitteln willst, empfehlen wir die folgende 2 Seiten zu überspringen. \n\nDie folgenden Tabellen gelten für __CONTROL_STYLE_BEGIN__[Ingo's Formeln]__CONTROL_STYLE_END__. Das kann zur Zeit noch in den Einstellungen geändert werden zu __CONTROL_STYLE_BEGIN__[Ownly's Formeln]__CONTROL_STYLE_END__. + +RealisticReactors_OutputReactor=Die Tabelle zeigt die Ausbeute der Reaktoren bei bester Effizienz für alle 4 Installationsmöglichkeiten. Dem folgt noch die Stromproduktion mit der nötigen Anzahl der Wärmetauscher und Dampfturbinen (beides gerundet).\n\n[img=tab-r-1]\n[img=tab-r-2]\n[img=tab-r-3]\n[img=tab-r-4]\n[img=tab-r-5]\n *das ist theoretischer Wert, da der Reaktor bei 1000°C explodiert. + +RealisticReactors_OutputBreeder=Die Tabelle zeigt die Ausbeute der Reaktoren bei bester Effizienz für alle 4 Installationsmöglichkeiten. Dem folgt noch die Stromproduktion mit der nötigen Anzahl der Wärmetauscher und Dampfturbinen (beides gerundet).\n\n[img=tab-b-1]\n[img=tab-b-2]\n[img=tab-b-3]\n[img=tab-b-4]\n[img=tab-b-5]\n *das ist theoretischer Wert, da der Reaktor bei 1000°C explodiert. + + +RealisticReactors_Meltdown=Wenn der Reaktor 1000°C erreicht, passiert eine Kernschmelze. Das führt unweigerlich zu einer Explosion und es verbleibt eine [entity=reactor-ruin]. Die Ruine emittiert radioaktive Wolken, welche sich in Abhängigkeit von der Windrichtung und -Stärke sich über dem Gelände verteilen und es mit radioaktivem Niederschlag verseuchen. \nUm die weiteren Emissionen zu stoppen muss [entity=reactor-sarcophagus] auf die Ruine platziert werden. Nach einer Weile läuft die Lebensdauer des Sarkophags ab. Dann kann er entfernt werden und der Platz erneut bebaut werden. + diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg new file mode 100644 index 0000000..130a0df --- /dev/null +++ b/locale/en/locale.cfg @@ -0,0 +1,152 @@ +[mod-name] +RealisticReactors=Realistic Reactors + +[mod-description] +RealisticReactors=Add more realistic nuclear reactors with a breeder reactor and a cooling tower. The reactors have to be controlled via integrated circuit interface signals. The reactors have a dynamic heat output depending on their temperature. They need proper cooling, otherwise a nuclear meltdown will occur + +[entity-name] +realistic-reactor-interface=Nuclear reactor circuit interface +realistic-breeder-interface=Nuclear breeder circuit interface +realistic-reactor-eccs=Emergency Core Cooling System (ECCS) +rr-cooling-tower=Cooling tower +realistic-reactor-normal=Nuclear reactor +realistic-reactor-power-normal=Nuclear reactor +realistic-reactor-breeder=Nuclear breeder reactor +realistic-reactor-power-breeder=Nuclear breeder reactor +reactor-ruin=Nuclear reactor ruin +breeder-ruin=Nuclear breeder reactor ruin +reactor-sarcophagus=Reactor sarcophagus +realistic-reactor=Nuclear reactor + +[entity-description] +realistic-reactor-interface=Provides signals of all internal reactor data and is used to send control signals to the reactor +realistic-breeder-interface=Provides signals of all internal reactor data and is used to send control signals to the reactor +realistic-reactor-eccs=Contains the water to directly cool down the reactor core. Pump cold water in and the hot water out if the core needs to be cooled! The cooling process consumes electric energy +rr-cooling-tower=Cools steam or hot water down to 15° water +realistic-reactor-normal=Produces heat. Has a high power output +realistic-reactor-breeder=Has a medium power output but produces extra empty fuel cells (i.e. materials). Output, efficiency and the rate of additional empty fuel cells change depending on the reactor core temperature +reactor-ruin=Constantly produces radioactivity. Build a sarcophagus over it! +breeder-ruin=Constantly produces radioactivity. Build a sarcophagus over it! +reactor-sarcophagus=Needs to be built over a reactor ruin +realistic-reactor=Has a high power output. Output and efficiency change depending on the reactor core temperature + +[virtual-signal-name] +signal-reactor-core-temp=Reactor core temperature +signal-uranium-fuel-cells=Fuel cells +signal-used-uranium-fuel-cells=Used up fuel cells +signal-state-stopped=[Reactor state] Stopped +signal-state-starting=[Reactor state] Starting +signal-state-running=[Reactor state] Running +signal-state-scramed=[Reactor state] SCRAM +signal-control-start=[Reactor control] Start signal +signal-control-scram=[Reactor control] SCRAM signal +signal-reactor-power-output=Reactor core power output +signal-reactor-efficiency=Reactor core efficiency +signal-reactor-cell-bonus=Breeder reactor bonus production +signal-coolant-amount=ECCS coolant amount +signal-reactor-electric-power=Reactor accumulator charge +signal-neighbour-bonus=Reactor neighbour bonus + +[virtual-signal-description] +signal-reactor-core-temp=The core temperature of the nuclear reactor. Don't let it reach 1000°! +signal-uranium-fuel-cells=The amount of uranium fuel cells in the nuclear reactor +signal-used-uranium-fuel-cells=The amount of used uranium fuel cells in the nuclear reactor +signal-state-stopped=When the nuclear reactor is currently stopped this signal is 1, otherwise it is 0 +signal-state-starting=When the nuclear reactor is currently starting this signal is counting down the time until the start phase is over, otherwise it is 0 +signal-state-running=When the nuclear reactor is currently running this signal is 1, otherwise it is 0 +signal-state-scramed=When the nuclear reactor is currently in SCRAM, this signal is counting down the time until the SCRAM phase is over, otherwise it is 0 +signal-control-start=When this signal is send to the nuclear reactor, it enters the starting state +signal-control-scram=When this signal is send to the nuclear reactor, it enters the SCRAM state +signal-reactor-power-output=Current power output (in MWth) of the reactor core. +signal-reactor-efficiency=Current efficiency (in percent) of the reactor core. An efficiency of 200 means that you are doubling the amount of energy out of one fuel cell compared to an efficiency of 100. 50 efficiency means half the energy. +signal-reactor-cell-bonus=The bonus production of a breeder reactor in additional used-up fuel cells per burn cycle. The signal is multiplied with 100. 50 means you get one additional used-up cell for every two fuel cells burnt +signal-coolant-amount=The amount of coolant water in the ECCS +signal-reactor-electric-power=Charge level of the internal reactor accumulator in percent +signal-neighbour-bonus=The number of reactors connected by heat pipes. Boni are applied to a maximum of 4 connected reactors + +[technology-name] +breeder-reactors=Nuclear breeder reactors + +[technology-description] +breeder-reactors=Adds a nuclear breeder reactor, which produces more burnt materials + + +[item-name] +rr-clowns-mox-cell=MOX fuel cell + +[tips-and-tricks-item-name] +RealisticReactors_Title=Realistic Reactors +RealisticReactors_Operate=How to operate a reactor +RealisticReactors_Output=Power output and efficiency +RealisticReactors_Meltdown=Meltdown +RealisticReactors_OutputReactor=Reactor output levels +RealisticReactors_OutputBreeder=Breeder output levels + +[tips-and-tricks-item-description] +RealisticReactors_Title=Congratulations! With this mod you have made your nuclear reactor setup a little bit more exciting!\n\nRealistic reactors [entity=realistic-reactor] produce heat and consume nuclear power cells just like the vanilla reactors. But their power output [virtual-signal=signal-reactor-power-output] and efficiency [virtual-signal=signal-reactor-efficiency] changes depending on the reactor core temperature [virtual-signal=signal-reactor-core-temp] and the reactor setup [virtual-signal=signal-neighbour-bonus].\nThe breeder reactor [entity=realistic-reactor-breeder] works similar to the normal reactor. It has a little less power output but produces additional empty fuel cells. This is useful if you need to produce specific products from empty fuel cells (like for example plutonium with GotLags Nuclear Fuel Mod - which I recommend using with this mod by the way...).\n\nBoth reactors require proper cooling. If their core reaches 1000° a nuclear meltdown will occur. To prevent this the reactors have an integrated ECCS (Emergency Core Cooling System), where you can insert cold water to cool the reactor core. Cooling towers [entity=rr-cooling-tower] are used to cool down the hot water. + +RealisticReactors_Operate=Reactor control is done via signals. You have to send them to the [entity=realistic-reactor-interface], the interface also tells you all important data to your reactor:\n[virtual-signal=signal-state-stopped]\n[virtual-signal=signal-state-starting]\n[virtual-signal=signal-state-running]\n[virtual-signal=signal-state-scramed]\n[virtual-signal=signal-reactor-core-temp]\n[virtual-signal=signal-coolant-amount]\n[virtual-signal=signal-reactor-power-output]\n[virtual-signal=signal-reactor-efficiency]\n[virtual-signal=signal-neighbour-bonus]\n[virtual-signal=signal-reactor-cell-bonus]\n[virtual-signal=signal-reactor-electric-power]\n\nTo __CONTROL_STYLE_BEGIN__start a reactor__CONTROL_STYLE_END__ you need to insert a fuel cell and send the [virtual-signal=signal-control-start] to the reactor. The reactor then enters its starting state, at which it will consume additional electric energy. \nAt the end of the starting phase it will enter the running state. Now you can see the power output and efficiency slowly changing depending on the core temperature.\n\nTo __CONTROL_STYLE_BEGIN__stop a reactor__CONTROL_STYLE_END__ you can simply let it consume the inserted fuel cell. To force an emergency stop you need to send the [virtual-signal=signal-control-scram] to the circuit interface. The reactor will then enter the SCRAM state, in which it will continue to produce heat for a certain amount of time. Make sure the reactor is cooled until the end of the SCRAM phase!\n\nTo __CONTROL_STYLE_BEGIN__cool the reactor__CONTROL_STYLE_END__ you can either use all of its produced heat with heat exchangers or cool it via the [entity=realistic-reactor-eccs]. \nPlease note that at the highest reactor output levels the ECCS alone is not able to cool down the core temperature anymore. That means you need to combine both cooling methods to be safe.\n\n__CONTROL_STYLE_BEGIN__Additional notes__CONTROL_STYLE_END__\n - A reactor needs to be connected to the electric network. It consumes electric power to do certain operations like signal output or ECCS cooling. It has an internal accumulator to store a certain amount of energy.\n - The reactor information can also be viewed in a GUI. Simply click on the circuit interface of a reactor to open the GUI. In the GUI you can also enable a graph which shows you the output over a time period. + +RealisticReactors_Output=[virtual-signal=signal-reactor-power-output], [virtual-signal=signal-reactor-efficiency] and [virtual-signal=signal-reactor-cell-bonus] change dynamically depending on the reactor core temperature.\nThe general rule for power output and breeder bonus production is: the higher the temperature, the better. For the efficiency it's different: maximum efficiency is reached at a certain temperature, above and also below that point the efficiency is going to drop.\nEfficiency has a fixed minimum of 50%, and a fixed maximum of 200%.\n\nAll three values depend on the reactor setup, meaning the number of reactors connected by heat pipes (this feature replaces the vanilla neighbor bonus). The maximum is 4 connected reactors, the current state is visible in the [virtual-signal=signal-neighbour-bonus].\n\nThe following two pages show you the exact output values of the reactor and the breeder reactor. More information comes with the mod, it is stored in the mod archive under DOCUMENTATION. \n\n__CONTROL_STYLE_BEGIN__Spoiler alarm!__CONTROL_STYLE_END__\nIf you want to find out the outputs in-game (it's possible via the signals or the GUI), then don't look at this. \n\nPlease note that all of this is only valid if you play in the standard mode __CONTROL_STYLE_BEGIN__[Ingo's formulas]__CONTROL_STYLE_END__. You can change the mode in the settings if you prefer __CONTROL_STYLE_BEGIN__[Ownlys Formulas]__CONTROL_STYLE_END__. + +RealisticReactors_OutputReactor=The following table shows the power production at the point of maximum efficiency for the four setups, and following from the power production the number of heat exchangers and steam turbines (both rounded up),for which this setup can provide heat.\n\n[img=tab-r-1]\n[img=tab-r-2]\n[img=tab-r-3]\n[img=tab-r-4]\n[img=tab-r-5]\n *this is of course theoretical as the reactor explodes at 1000° + +RealisticReactors_OutputBreeder=The following table shows the power production at the point of maximum efficiency for the four setups, and following from the power production the number of heat exchangers and steam turbines (both rounded up),for which this setup can provide heat.\n\n[img=tab-b-1]\n[img=tab-b-2]\n[img=tab-b-3]\n[img=tab-b-4]\n[img=tab-b-5]\n *this is of course theoretical as the reactor explodes at 1000° + +RealisticReactors_Meltdown=When the reactor reaches 1000° its core will melt. This will cause the reactor to explode leaving behind a [entity=reactor-ruin]. The ruin will constantly produce radioactivity in form of a radioactive cloud, which will - depending on the wind - spread in the area around the ruin and leave radioactive fallout behind. \nTo stop the radioactivity you need to build a [entity=reactor-sarcophagus] over the reactor ruin. After a long time (visible in its health) the sarcophagus can be removed. + +[mod-setting-name] +realistic-reactors-disable-reactor-light=Disable Reactor Light +realistic-reactors-disable-vanilla-reactor=Disable vanilla reactor +realistic-reactors-reactor-scram-duration=Reactor SCRAM duration +realistic-reactors-reactor-starting-duration=Reactor starting duration +realistic-reactors-energy-consumption-multiplier=Energy consumption multiplier +realistic-reactors-calculate-stats-function=Reactor power output and efficiency +realistic-reactors-static-cooling-power-consumption=Static power consumption for ECCS +realistic-reactors-scram-behaviour=SCRAM behavior +realistic-reactors-explosion-mode=Meltdown Mode +realistic-reactors-clouds-duration=Lifetime of the radioactive clouds +realistic-reactors-clouds-generation=How long to emit radioactive clouds on meltdown? +realistic-reactors-fallout-appearance=Nuclear fallout appearance +realistic-reactors-fallout-duration=Lifetime of nuclear fallout +realistic-reactors-sarcophagus-duration=Reactor ruin / sarcophagus duration + +[mod-setting-description] +realistic-reactors-disable-reactor-light=Toggle Reactor glow light +realistic-reactors-disable-vanilla-reactor=Removes the vanilla nuclear reactor recipe from the game +realistic-reactors-reactor-scram-duration=The duration of the reactor SCRAM phase in seconds +realistic-reactors-reactor-starting-duration=The duration of the reactor starting phase in seconds +realistic-reactors-energy-consumption-multiplier=Sets a multiplier for the energy consumption of the starting-phase, cooling and interface\nMin: 0, Default: 1.0, Max: 2.5 (Realistic: 2.5) +realistic-reactors-calculate-stats-function=This setting controls the power output and fuel efficiency of the reactors.\n\nOwnly's calculation gives you slightly different behaviour for different fuel cell types. The maximum output and efficiency will always be on the highest temperature (+10% per neighbour).\n\nWith Ingo's calculation you have to monitor the temperature more carefully. If you want to have a maximum output with good efficiency, you have to connect your reactors (max 4). On a single reactor the efficiency will drop quickly with high temperatures.\n\nSee the forum post or the included ODT-charts (-> mod folder) for more details on both modes +realistic-reactors-static-cooling-power-consumption=When static is chosen, the reactor will consume 1 MW while the core is being cooled by ECCS.\nOtherwise it will consume 1 MW per 20 MW of cooling + +realistic-reactors-scram-behaviour=limit to current cell = The reactor will slowly power down until the scam duration is over or the current fuel cell is depleted.\n\nstop half-empty = Reactor will take another fuel cell if current one depletes before SCRAM duration is over. The cell will stay half-consumed in the reactor when it stops.\n\nconsume additional cell = Same as above but the cell will be depleted when the reactor stops.\n\ndecay heat (v1.0.x)= Current fuel is removed and decay heat is produced by script (like in version 1.0.x) + +realistic-reactors-explosion-mode=Method of reactor meltdown + +realistic-reactors-clouds-generation=When the reactor melts, it will emit radioactive clouds from the condensed water. The clouds will move over the map affected by the wind.\nSet a duration for how many seconds this will happen\n(0 - 100 - 9999999) +realistic-reactors-clouds-duration=Sets the duration (in seconds) how long radioactive clouds will stay\n(1 - 80 - 9999999) +realistic-reactors-fallout-appearance=Invisible = You will only notice the contaminated area by the geiger-counter sound\n\nHalf-transparent = floating orange particles\n\nLess transparent = Same as above, but better visible\n\nGreen veil = The area will get colored green, but very hard to see in zones with low radiation +realistic-reactors-fallout-duration=Sets the duration (in seconds) how long fallout contamination will stay\n(1 - 600 - 9999999) +realistic-reactors-sarcophagus-duration=The time before a sarcophagus or ruin can be removed + +[string-mod-setting] +realistic-reactors-fallout-appearance-invisible=invisible +realistic-reactors-fallout-appearance-half-transparent=half transparent +realistic-reactors-fallout-appearance-less-transparent=less transparent +realistic-reactors-fallout-appearance-green-veil=green veil +realistic-reactors-calculate-stats-function-ownly=Ownly's formulas +realistic-reactors-calculate-stats-function-ingo=Ingo's formulas +realistic-reactors-scram-behaviour-limit-to-current-cell=Limit to current cell +realistic-reactors-scram-behaviour-stop-half-empty=Stop half-empty +realistic-reactors-scram-behaviour-consume-additional-cell=Consume additional cell +realistic-reactors-scram-behaviour-decay-heat-v1=Decay heat (v1.0.x) +realistic-reactors-explosion-mode-meltdown=Realistic +realistic-reactors-explosion-mode-really-very-small-atomic-bomb-projectile=Low yield atomic bomb (2t) +realistic-reactors-explosion-mode-very-small-atomic-bomb-projectile=Low yield atomic bomb (4t) +realistic-reactors-explosion-mode-small-atomic-bomb-projectile=Low yield atomic bomb (8t) +realistic-reactors-explosion-mode-atomic-rocket=Atomic Bomb (20t) +realistic-reactors-explosion-mode-big-atomic-bomb-projectile=High yield atomic bomb (500t) +realistic-reactors-explosion-mode-very-big-atomic-bomb-projectile=Super high yield atomic bomb (1kt) +realistic-reactors-explosion-mode-thermobaric-rocket=Thermobaric Bomb +realistic-reactors-explosion-mode-plutonium-atomic-rocket=Plutonium Bomb diff --git a/locale/ko/locale.cfg b/locale/ko/locale.cfg new file mode 100644 index 0000000..ad746cb --- /dev/null +++ b/locale/ko/locale.cfg @@ -0,0 +1,152 @@ +[mod-name] +RealisticReactors=현실적인 원자로 + +[mod-description] +RealisticReactors=증식로와 냉각탑으로 보다 사실적인 원자로를 추가합니다. 원자로는 집적 회로 인터페이스 신호를 통해 제어되어야 합니다. 원자로는 온도에 따라 동적 열 출력을 가집니다. 적절한 냉각이 필요합니다. 그렇지 않으면 핵 용해가 발생합니다. + +[entity-name] +realistic-reactor-interface=원자로 회로 인터페이스 +realistic-breeder-interface=핵증식로 회로 인터페이스 +realistic-reactor-eccs=비상 노심 냉각 시스템 (ECCS) +rr-cooling-tower=냉각탑 +realistic-reactor-normal=원자로 +realistic-reactor-power-normal=원자로 +realistic-reactor-breeder=핵증식로 +realistic-reactor-power-breeder=핵증식로 +reactor-ruin=원자로 폐허 +breeder-ruin=핵증식로 폐허 +reactor-sarcophagus=원자로 석관 +realistic-reactor=원자로 + +[entity-description] +realistic-reactor-interface=모든 내부 원자로 데이터의 신호를 제공하며 원자로에 제어 신호를 보내는 데 사용됩니다. +realistic-breeder-interface=모든 내부 원자로 데이터의 신호를 제공하며 원자로에 제어 신호를 보내는 데 사용됩니다. +realistic-reactor-eccs=원자로 노심을 직접 냉각시키는 물을 포함합니다. 코어를 냉각해야 하는 경우 찬물을 펌핑하고 뜨거운 물을 빼냅니다! 냉각 과정은 전기 에너지를 소비합니다 +rr-cooling-tower=증기 또는 뜨거운 물을 15° 물까지 냉각 +realistic-reactor-normal=열을 발생시킵니다. 높은 출력을 가지고 있습니다 +realistic-reactor-breeder=중간 출력을 가지고 있지만 여분의 빈 연료 전지(예: 재료)를 생성합니다. 원자로 노심 온도에 따라 출력, 효율 및 추가 빈 연료 전지 비율이 변경됨 +reactor-ruin=지속적으로 방사능을 생성합니다. 그 위에 석관을 지으세요! +breeder-ruin=지속적으로 방사능을 생성합니다. 그 위에 석관을 지으세요! +reactor-sarcophagus=원자로 폐허 위에 건설해야 함 +realistic-reactor=고출력입니다. 원자로 노심 온도에 따른 출력 및 효율 변화 + +[virtual-signal-name] +signal-reactor-core-temp=원자로 노심 온도 +signal-uranium-fuel-cells=연료전지 +signal-used-uranium-fuel-cells=소진된 연료전지 +signal-state-stopped=[원자로 상태] 정지됨 +signal-state-starting=[원자로 상태] 시동 중 +signal-state-running=[원자로 상태] 운전 중 +signal-state-scramed=[원자로 상태] SCRAM +signal-control-start=[원자로 제어] 시작 신호 +signal-control-scram=[원자로 제어] SCRAM 신호 +signal-reactor-power-output=노심 전원 출력 +signal-reactor-efficiency=노심 효율 +signal-reactor-cell-bonus=증식로 보너스 생산 +signal-coolant-amount=ECCS 냉각수 양 +signal-reactor-electric-power=원자로 축전지 충전 +signal-neighbour-bonus=원자로 이웃 보너스 + +[virtual-signal-description] +signal-reactor-core-temp=원자로의 노심온도. 1000°에 도달하지 마십시오! +signal-uranium-fuel-cells=원자로 내 우라늄 연료전지의 양 +signal-used-uranium-fuel-cells=원자로 내 사용된 우라늄 연료전지의 양 +signal-state-stopped=원자로가 현재 정지된 경우 이 신호는 1이고 그렇지 않으면 0입니다. +signal-state-starting=원자로가 현재 시작 중일 때 이 신호는 시작 단계가 끝날 때까지 시간을 카운트다운하고 그렇지 않으면 0입니다. +signal-state-running=원자로가 현재 가동 중일 때 이 신호는 1이고 그렇지 않으면 0입니다. +signal-state-scramed=원자로가 현재 SCRAM에 있을 때 이 신호는 SCRAM 단계가 끝날 때까지 시간을 카운트다운하고 그렇지 않으면 0입니다. +signal-control-start=이 신호를 원자로에 보내면 시동상태로 들어갑니다. +signal-control-scram=이 신호가 원자로에 보내지면 SCRAM 상태가 됩니다. +signal-reactor-power-output=원자로 노심의 현재 전력 출력(MWth). +signal-reactor-efficiency=원자로 노심의 현재 효율(%). 200의 효율은 100의 효율과 비교하여 하나의 연료 전지에서 나오는 에너지 양이 두 배라는 것을 의미합니다. 50의 효율은 에너지의 절반을 의미합니다. +signal-reactor-cell-bonus=연소 사이클당 소모된 추가 연료 전지에서 증식 원자로의 보너스 생산. 신호에 100을 곱합니다. 50은 연료 전지 2개를 태울 때마다 소모된 전지 1개를 추가로 얻는다는 의미입니다. +signal-coolant-amount=ECCS의 냉각수 양 +signal-reactor-electric-power=내부 원자로 축전지의 충전 수준(%) +signal-neighbour-bonus=히트 파이프로 연결된 원자로의 수. 보니는 최대 4개의 연결된 리액터에 적용됩니다. + +[technology-name] +breeder-reactors=핵증식로 + +[technology-description] +breeder-reactors=더 많은 연소 물질을 생성하는 핵증식로를 추가합니다. + + +[item-name] +rr-clowns-mox-cell=혼합산화물 연료전지 + +[tips-and-tricks-item-name] +RealisticReactors_Title=현실적인 원자로 +RealisticReactors_Operate=원자로 운전방법 +RealisticReactors_Output=전력 출력 및 효율 +RealisticReactors_Meltdown=멜트다운 +RealisticReactors_OutputReactor=원자로 출력 레벨 +RealisticReactors_OutputBreeder=증식로 출력 수준 + +[tips-and-tricks-item-description] +RealisticReactors_Title=축하합니다! 이 모드를 사용하면 원자로 설정을 좀 더 흥미롭게 만들 수 있습니다!\n\n현실적인 원자로 [entity=realistic-reactor]는 바닐라 원자로처럼 열을 생산하고 원자력 전지를 소비합니다. 그러나 출력 [virtual-signal=signal-reactor-power-output]과 효율 [virtual-signal=signal-reactor-efficiency]은 원자로 노심 온도 [virtual-signal=signal-reactor-core-temp]와 원자로 설정 [virtual-signal=signal-neighbour-bonus]에 따라 달라집니다.\n핵증식로 [entity=realistic-reactor-breeder]는 일반 원자로와 비슷하게 작동합니다. 출력은 약간 떨어지지만 빈 연료 전지를 추가로 생성합니다. 이것은 빈 연료 전지에서 특정 제품을 생산해야 하는 경우에 유용합니다(예: GotLags Nuclear Fuel Mod가 있는 플루토늄 - 이 모드와 함께 사용하는 것이 좋습니다...).\n\n두 원자로는 적절한 냉각이 필요합니다. 코어가 1000°에 도달하면 핵 용해가 발생합니다. 이를 방지하기 위해 원자로에는 통합 ECCS(비상 노심 냉각 시스템)가 있어 냉수를 삽입하여 원자로 노심을 냉각할 수 있습니다. 냉각탑 [entity=rr-cooling-tower]은 뜨거운 물을 식히는 데 사용됩니다. + +RealisticReactors_Operate=원자로 제어는 신호를 통해 이루어집니다. [entity=realistic-reactor-interface]로 보내야 하며 인터페이스는 또한 모든 중요한 데이터를 원자로에 알려줍니다.\n[virtual-signal=signal-state-stopped]\n[virtual-signal=signal-state-starting]\n[virtual-signal=signal-state-running]\n[virtual-signal=signal-state-scramed]\n[virtual-signal=signal-reactor-core-temp]\n[virtual-signal=signal-coolant-amount]\n[virtual-signal=signal-reactor-power-output]\n[virtual-signal=signal-reactor-efficiency]\n[virtual-signal=signal-neighbour-bonus]\n[virtual-signal=signal-reactor-cell-bonus]\n[virtual-signal=signal-reactor-electric-power]\n\n__CONTROL_STYLE_BEGIN__원자로__CONTROL_STYLE_END__를 시작하려면 연료 전지를 삽입하고 [virtual-signal=signal-control-start]를 원자로에 보내야 합니다. 그런 다음 원자로는 추가 전기 에너지를 소비하는 시작 상태로 들어갑니다.\n시작 단계가 끝나면 실행 상태로 들어갑니다. 이제 코어 온도에 따라 전력 출력과 효율이 천천히 변하는 것을 볼 수 있습니다.\n\n원자로 __CONTROL_STYLE_BEGIN__정지__CONTROL_STYLE_END__는 단순히 삽입된 연료 전지를 소모하게 할 수 있습니다. 비상 정지를 강제하려면 [virtual-signal=signal-control-scram]를 회로 인터페이스에 보내야 합니다. 그러면 원자로가 SCRAM 상태로 들어가 특정 시간 동안 계속해서 열을 생성합니다. SCRAM 단계가 끝날 때까지 원자로가 냉각되었는지 확인하십시오!\n\n원자로 __CONTROL_STYLE_BEGIN__냉각__CONTROL_STYLE_END__을 위해 생성된 모든 열을 열교환기로 사용하거나 [entity=realistic-reactor-eccs]를 통해 냉각할 수 있습니다.\n가장 높은 원자로 출력 수준에서 ECCS만으로는 더 이상 노심 온도를 냉각할 수 없습니다. 즉, 안전하려면 두 냉각 방법을 결합해야 합니다.\n\n__CONTROL_STYLE_BEGIN__추가사항__CONTROL_STYLE_END__\n - 원자로는 전기 네트워크에 연결되어야 합니다. 신호 출력 또는 ECCS 냉각과 같은 특정 작업을 수행하기 위해 전력을 소비합니다. 일정량의 에너지를 저장하는 내부 축전지가 있습니다.\n - 원자로 정보는 GUI에서도 볼 수 있습니다. GUI를 열려면 원자로의 회로 인터페이스를 클릭하기만 하면 됩니다. GUI에서 일정 기간 동안의 출력을 보여주는 그래프를 활성화할 수도 있습니다. + +RealisticReactors_Output=[virtual-signal=signal-reactor-power-output], [virtual-signal=signal-reactor-efficiency] 및 [virtual-signal=signal-reactor-cell-bonus]는 원자로 노심 온도에 따라 동적으로 변경됩니다.\n출력 및 증식로 보너스 생산에 대한 일반적인 규칙은 다음과 같습니다. 온도가 높을수록 좋습니다. 효율성의 경우 다릅니다. 특정 온도에서 최대 효율성에 도달하고 그 온도보다 높거나 낮으면 효율성이 떨어집니다.\n효율성은 고정된 최소값이 50%이고 고정된 최대값이 200%입니다.\n\n세 값 모두 열 파이프로 연결된 원자로의 수를 의미하는 원자로 설정에 따라 다릅니다(이 기능은 기본 이웃 보너스를 대체함). 최대 연결된 원자로는 4개이며 현재 상태는 [virtual-signal=signal-neighbour-bonus]에서 볼 수 있습니다.\n\n다음 두 페이지는 원자로와 증식 원자로의 정확한 출력 값을 보여줍니다. 더 많은 정보는 모드와 함께 제공되며 DOCUMENTATION 아래의 모드 아카이브에 저장됩니다.\n\n__CONTROL_STYLE_BEGIN__스포일러 알람!__CONTROL_STYLE_END__\n게임 내 출력(신호 또는 GUI를 통해 가능)을 찾고 싶다면 이것을 보지 마십시오.\n\n이 모든 것은 표준 모드 __CONTROL_STYLE_BEGIN__[Ingo의 공식]__CONTROL_STYLE_END__에서 플레이하는 경우에만 유효합니다. __CONTROL_STYLE_BEGIN__[Ownly의 공식]__CONTROL_STYLE_END__을 원하시면 설정에서 모드를 변경할 수 있습니다. + +RealisticReactors_OutputReactor=다음 표는 4가지 설정에 대한 최대 효율 지점에서의 전력 생산을 보여주고, 전력 생산에서 이 설정이 수행할 수 있는 열교환기 및 증기 터빈의 수(둘 다 반올림)를 보여줍니다. 열을 제공합니다.\n\n[img=tab-r-1]\n[img=tab-r-2]\n[img=tab-r-3]\n[img=tab-r-4]\n[img=tab-r-5]\n *이것은 원자로가 1000°에서 폭발하기 때문에 물론 이론적인 것입니다. + +RealisticReactors_OutputBreeder=다음 표는 4가지 설정에 대한 최대 효율 지점에서의 전력 생산을 보여주고, 전력 생산 이후 이 설정이 수행할 수 있는 열교환기 및 증기 터빈의 수(둘 다 반올림)를 보여줍니다. 열을 제공합니다.\n\n[img=tab-b-1]\n[img=tab-b-2]\n[img=tab-b-3]\n[img=tab-b-4]\n[img=tab-b-5]\n *이것은 원자로가 1000°에서 폭발하기 때문에 물론 이론적인 것입니다. + +RealisticReactors_Meltdown=원자로가 1000°에 도달하면 핵이 녹습니다. 이렇게 하면 원자로가 폭발하여 [entity=reactor-ruin]를 남깁니다. 폐허는 바람에 따라 폐허 주변으로 퍼져 방사성 낙진을 남기는 방사성 구름의 형태로 방사능을 지속적으로 생성합니다.\n방사능을 멈추려면 원자로 폐허 위에 [entity=reactor-sarcophagus]를 세워야 합니다. 오랜 시간이 지나면(체력 상태에서 볼 수 있음) 석관을 제거할 수 있습니다. + +[mod-setting-name] +realistic-reactors-disable-reactor-light=원자로 조명 비활성화 +realistic-reactors-disable-vanilla-reactor=바닐라 원자로 비활성화 +realistic-reactors-reactor-scram-duration=원자로 SCRAM 지속 시간 +realistic-reactors-reactor-starting-duration=원자로 기동시간 +realistic-reactors-energy-consumption-multiplier=에너지 소비 배수 +realistic-reactors-calculate-stats-function=원자로 출력 및 효율 +realistic-reactors-static-cooling-power-consumption=ECCS의 정적 소비 전력 +realistic-reactors-scram-behaviour=SCRAM 동작 +realistic-reactors-explosion-mode=멜트다운 모드 +realistic-reactors-clouds-duration=방사성 구름의 수명 +realistic-reactors-clouds-generation=멜트다운 시 방사성 구름을 방출하는 기간은? +realistic-reactors-fallout-appearance=방사능 낙진 외형 +realistic-reactors-fallout-duration=방사능 낙진 수명 +realistic-reactors-sarcophagus-duration=원자로 폐허/석관 지속시간 + +[mod-setting-description] +realistic-reactors-disable-reactor-light=리액터 글로우 라이트 토글 +realistic-reactors-disable-vanilla-reactor=게임에서 바닐라 원자로 제조법을 제거합니다. +realistic-reactors-reactor-scram-duration=원자로 SCRAM 단계의 지속 시간(초) +realistic-reactors-reactor-starting-duration=원자로 시작 단계의 지속 시간(초) +realistic-reactors-energy-consumption-multiplier=시작 단계, 냉각 및 인터페이스의 에너지 소비에 대한 배수를 설정합니다.\n최소: 0, 기본: 1.0, 최대: 2.5(현실적: 2.5) +realistic-reactors-calculate-stats-function=이 설정은 원자로의 출력과 연료 효율을 제어합니다.\n\nOwnly의 계산은 다양한 연료 전지 유형에 대해 약간 다른 동작을 제공합니다. 최대 출력과 효율성은 항상 가장 높은 온도에서 유지됩니다(이웃당 +10%).\n\nIngo의 계산을 사용하면 온도를 더 주의 깊게 모니터링해야 합니다. 좋은 효율로 최대 출력을 얻으려면 리액터(최대 4개)를 연결해야 합니다. 단일 원자로에서 효율은 고온에서 빠르게 떨어집니다.\n\n두 모드에 대한 자세한 내용은 포럼 게시물 또는 포함된 ODT 차트(-> mod 폴더)를 참조하십시오. +realistic-reactors-static-cooling-power-consumption=정적이 선택되면 원자로는 ECCS로 노심을 냉각하는 동안 1MW를 소비합니다.\n그렇지 않으면 냉각 20MW당 1MW를 소비합니다. + +realistic-reactors-scram-behaviour=현재 셀로 제한 = 사기 지속 시간이 끝나거나 현재 연료 전지가 고갈될 때까지 원자로의 전원이 천천히 꺼집니다.\n\nstop half-empty = SCRAM 지속 시간이 끝나기 전에 현재 연료 전지가 고갈되면 원자로가 다른 연료 전지를 사용합니다. 세포는 정지할 때 원자로에서 반쯤 소모된 상태로 유지됩니다.\n\n추가 셀 소모 = 위와 동일하지만 원자로가 정지하면 셀이 고갈됩니다.\n\n붕괴열(v1.0.x)= 현재 연료가 제거되고 스크립트에 의해 붕괴열이 생성됩니다(버전 1.0.x에서와 같이). + +realistic-reactors-explosion-mode=원자로용해방법 + +realistic-reactors-clouds-generation=원자로가 녹으면 응축수에서 방사성 구름을 방출합니다. 구름은 바람의 영향을 받은 지도 위로 이동합니다.\n몇 초 동안 발생할 것인지 설정하십시오.\n(0 - 100 - 9999999) +realistic-reactors-clouds-duration=방사성 구름이 머무는 시간(초)을 설정합니다.\n(1 - 80 - 9999999) +realistic-reactors-fallout-appearance=보이지 않음 = 가이거 계수기 소리로 오염된 부분만 알 수 있음\n\n반투명 = 떠 있는 주황색 입자\n\n덜 투명 = 위와 동일하지만 더 잘 보입니다.\n\n녹색 베일 = 해당 영역이 녹색으로 표시되지만 복사량이 적은 영역에서는 보기가 매우 어렵습니다. +realistic-reactors-fallout-duration=낙진 오염이 지속되는 기간(초)을 설정합니다.\n(1 - 600 - 9999999) +realistic-reactors-sarcophagus-duration=석관이나 폐허를 제거할 수 있는 시간 + +[string-mod-setting] +realistic-reactors-fallout-appearance-invisible=보이지 않음 +realistic-reactors-fallout-appearance-half-transparent=반투명 +realistic-reactors-fallout-appearance-less-transparent=덜 투명 +realistic-reactors-fallout-appearance-green-veil=녹색 베일 +realistic-reactors-calculate-stats-function-ownly=Ownly의 공식 +realistic-reactors-calculate-stats-function-ingo=Ingo의 공식 +realistic-reactors-scram-behaviour-limit-to-current-cell=현재 셀로 제한 +realistic-reactors-scram-behaviour-stop-half-empty=반이 비면 멈춤 +realistic-reactors-scram-behaviour-consume-additional-cell=추가 셀 소모 +realistic-reactors-scram-behaviour-decay-heat-v1=감퇴 열 (v1.0.x) +realistic-reactors-explosion-mode-meltdown=현실감 +realistic-reactors-explosion-mode-really-very-small-atomic-bomb-projectile=저수율 원자폭탄(2t) +realistic-reactors-explosion-mode-very-small-atomic-bomb-projectile=저수율 원자폭탄(4t) +realistic-reactors-explosion-mode-small-atomic-bomb-projectile=저수량 원자폭탄(8t) +realistic-reactors-explosion-mode-atomic-rocket=원자폭탄(20t) +realistic-reactors-explosion-mode-big-atomic-bomb-projectile=고수율 원자폭탄(500t) +realistic-reactors-explosion-mode-very-big-atomic-bomb-projectile=초고수율 원자폭탄(1kt) +realistic-reactors-explosion-mode-thermobaric-rocket=열압 폭탄 +realistic-reactors-explosion-mode-plutonium-atomic-rocket=플루토늄 폭탄 diff --git a/locale/ru/locale.cfg b/locale/ru/locale.cfg new file mode 100644 index 0000000..01640a1 --- /dev/null +++ b/locale/ru/locale.cfg @@ -0,0 +1,141 @@ +[mod-name] +RealisticReactors=Реалистичные Реакторы + +[mod-description] +RealisticReactors=Переосмыслите технологии ванильной ядерной энергетики, добавьте градирню, новый ядерный реактор и реактор-размножитель к игре! Реалистичные Реакторы имеют интерфейс для взаимодействия с внутренними системами. Интерфейс позволяет автоматизацию контроля над реактором и управление динамической теплоотдачей в зависимости от температуры ядра реактора. По этой причине Реалистичные Реакторы требуют внедрения соответствующих систем охлаждения и теплообмена, в противном случае активная зона реактора расплавится и последствия будут поистине неприятными. + +[entity-name] +realistic-reactor-interface=Интерфейс систем реактора +realistic-breeder-interface=Интерфейс систем реактора-размножителя +realistic-reactor-eccs=Система аварийного охлаждения ТВС (ECCS) +rr-cooling-tower=Градирня +realistic-reactor-normal=Ядерный реактор +realistic-reactor-power-normal=Ядерный реактор +realistic-reactor-breeder=Ядерный реактор-размножитель +realistic-reactor-power-breeder=Ядерный реактор-размножитель +reactor-ruin=Руина реактора +breeder-ruin=Руина реактора-размножителя +reactor-sarcophagus=Саркофаг + +[entity-description] +realistic-reactor-interface=Используется для получения всех данных этого реактора в виде соответствующих логических сигналов комбинаторной сети ([item=decider-combinator]) и для отправки управляющих логических сигналов во внутренние системы этого реактора. +realistic-breeder-interface=Используется для получения всех данных этого реактора в виде соответствующих логических сигналов комбинаторной сети ([item=decider-combinator]) и для отправки управляющих логических сигналов во внутренние системы этого реактора. +realistic-reactor-eccs=Принимает хладагент с низкой температурой в виде воды [fluid=water] для циркуляции в системе аварийного охлаждения активной зоны этого реактора. Выводит нагретый хладагент, который следует охладить в градирне ([entity=rr-cooling-tower]). +rr-cooling-tower=Принимает на вход хладагент в виде нагретой воды ([fluid=water]) или пара ([fluid=steam]) и [color=green]охлаждает[/color] его до температуры [color=blue]15°C[/color]. +realistic-reactor-normal=Использует ядерные топливные элементы (ТВС) для генерации тепла. При установке рядом с другими ядерными реакторами этого-же типа, эффективность выделения тепла увеличивается.\n[color=yellow][font=default-bold]Для обеспечения безопасной работы на всех режимах настоятельно рекомендуется интеграция систем циркуляционного охлаждения[/font][/color].\nОтносительная выходная мощность: [color=red]высокая[/color]. +realistic-reactor-breeder=Использует ядерные топливные элементы (ТВС) для генерации тепла. При установке рядом с другими ядерными реакторами-разможителями эффективность выделения тепла увеличивается.\n[color=yellow][font=default-bold]Для обеспечения безопасной работы на всех режимах настоятельно рекомендуется интеграция систем циркуляционного охлаждения[/font][/color].\nОтносительная выходная мощность: [color=yellow]средняя[/color].\nБонус: производит [color=green]дополнительные[/color] отработанные ТВС ([item=used-up-uranium-fuel-cell]). +reactor-ruin=Эту руину [color=yellow]невозможно убрать[/color], необходимо построить саркофаг ([entity=reactor-sarcophagus]) на этой руине. [color=yellow]Бесконтрольно эмиттирует радиоактивные облака[/color]. +breeder-ruin=Эту руину [color=yellow]невозможно убрать[/color], необходимо построить саркофаг ([entity=reactor-sarcophagus]) на этой руине. [color=yellow]Бесконтрольно эмиттирует радиоактивные облака[/color]. +reactor-sarcophagus=Саркофаг предназначен для строительства над руиной реактора ([entity=reactor-ruin]) или реактора-размножителя ([entity=breeder-ruin]). [color=yellow]Прекращает бесконтрольную эмиссию радиоактивных облаков[/color] и в последствии даёт возможность убрать останки. + +[virtual-signal-name] +signal-reactor-core-temp=Температура АЗ реактора +signal-uranium-fuel-cells=ТВС +signal-used-uranium-fuel-cells=Израсходованные ТВС +signal-state-stopped=[Реактор: режим] Остановлен +signal-state-starting=[Реактор: режим] Стартует +signal-state-running=[Реактор: режим] Работает +signal-state-scramed=[Реактор: режим] Аварийная остановка +signal-control-start=[Реактор: управление] Старт +signal-control-scram=[Реактор: управление] Аварийная остановка +signal-reactor-power-output=[Реактор: состояние] Выходная тепловая мощность +signal-reactor-efficiency=[Реактор: состояние] Эффективность АЗ +signal-reactor-cell-bonus=[Реактор: бонус] Производительность бонусных ТВС +signal-coolant-amount=[Реактор: состояние] Кол-во хладагента +signal-reactor-electric-power=[Реактор: состояние] Заряд аккумулятора +signal-neighbour-bonus=[Реактор: бонус] Бонус текущего соседа + +[virtual-signal-description] +signal-reactor-core-temp=Температура активной зоны ядерного реактора (АЗ). [color=yellow]При достижении температуры ядра 1000°C реактор разрушается![/color] +signal-uranium-fuel-cells=Количество топливных элементов [item=uranium-fuel-cell] в ядерном реакторе. +signal-used-uranium-fuel-cells=Количество использованных топливных элементов [item=uranium-fuel-cell] в ядерном реакторе. +signal-state-stopped=Если реактор остановлен, этот сигнал равен 1, в противном случае - 0. +signal-state-starting=Если реактор запускается, этот сигнал отсчитывает время до начала фазы старта. В противном случае он равен 0. +signal-state-running=Если реактор работает, этот сигнал равен 1, в противном случае - 0. +signal-state-scramed=Если реактор аварийно остановлен, этот сигнал отсчитывает время до окончания фазы аварийной остановки, иначе он равен 0. +signal-control-start=Если этот не нулевой сигнал отправляется в реактор, он переходит в состояние запуска. +signal-control-scram=Если этот не нулевой сигнал отправляется в реактор, он переходит в состояние аварийной остановки. +signal-reactor-power-output=Текущая выходная мощность реактора (MWth). +signal-reactor-efficiency=Текущая эффективность активной зоны реактора (АЗ) в процентах (максимум в стандартном режиме - 200). +signal-reactor-cell-bonus=Бонусная производительность реактора-размножителя в дополнительных использованных топливных элементах (ТВС) за цикл использования ТВС. Сигнал умножается на 100. 50 означает, что реактор производит одну дополнительную использованную ТВС при расходе двух топливных элементов. +signal-coolant-amount=Количество хладагента в системе аварийного охлаждения ТВС в ядре реактора (ECCS). +signal-reactor-electric-power=Уровень заряда внутреннего аккумулятора реактора в процентах. +signal-neighbour-bonus=Количество реакторов, соединённых тепловыми трубами в единую сеть. Максимальный бонус — 4 реактора в одной тепловой сети. + +[technology-name] +breeder-reactors=Реактор-размножитель + +[technology-description] +breeder-reactors=Добавляет ядерный реактор-размножитель, который производит [color=green]дополнительные[/color] отработанные ТВС ([item=used-up-uranium-fuel-cell]) во время работы. + +[mod-setting-name] +realistic-reactors-disable-reactor-light=Отключить подсветку реакторов +realistic-reactors-disable-vanilla-reactor=Отключить ванильный реактор +realistic-reactors-reactor-scram-duration=Продолжительность фазы аварийной остановки +realistic-reactors-reactor-starting-duration=Продолжительность фазы запуска +realistic-reactors-energy-consumption-multiplier=Множитель энергопотребления +realistic-reactors-calculate-stats-function=Формула выходной мощности и эффективности +realistic-reactors-static-cooling-power-consumption=Статическое энергопотребление системы аварийного охлаждения +realistic-reactors-scram-behaviour=Поведение при аварийной остановке +realistic-reactors-explosion-mode=Mетод подрыва ядра реактора +realistic-reactors-clouds-duration=Время жизни радиоактивных облаков +realistic-reactors-clouds-generation=Продолжительность выделений радиоактивных облаков при расплавлении АЗ +realistic-reactors-fallout-appearance=Явление радиоактивных осадков +realistic-reactors-fallout-duration=Время жизни радиоактивных осадков +realistic-reactors-sarcophagus-duration=Продолжительность разрушения саркофага + +[mod-setting-description] +realistic-reactors-disable-reactor-light=Реакторы не светятся в сумерках и темноте. +realistic-reactors-disable-vanilla-reactor=Игра без ванильного реактора. +realistic-reactors-reactor-scram-duration=Продолжительность фазы аварийной остановки реактора в секундах. +realistic-reactors-reactor-starting-duration=Продолжительность стартовой фазы реактора в секундах. +realistic-reactors-energy-consumption-multiplier=Множитель потребления энергии стартовой фазы, охлаждения и интерфейса.\n\nМин: 0.0, По умолчанию: 1.0, Макс: 2.5 (Реалистично: 2.5) +realistic-reactors-calculate-stats-function=Настройка управляет выходной мощностью и топливной экономичностью реакторов.\n\nИнтересный расчёт дает несколько другое поведение для разных типов топливных элементов. Максимальный выход и эффективность всегда будут на самой высокой температуре (+10% на соседа).\n\nС помощью формул Ingo есть необходимость более тщательно контролировать температуру. Для получения максимальной производительности при хорошей эффективности необходимо подключить реакторы в единую систему тепловых труб. На одном реакторе эффективность быстро снижается при высоких температурах.\n\nПодробнее об этих двух режимах читайте документацию в советах и подсказках. +realistic-reactors-static-cooling-power-consumption=При выборе статического потребления реактор будет потреблять 1 МВт электричества при работе системы аварийного охлаждения ТВС в ядре реактора (ECCS). В противном случае реактор будет потреблять 1 МВт электричества на 20 МВт охлаждения мощности АЗ. +realistic-reactors-scram-behaviour=Ограничить на текущую ТВС — Реактор будет выключаться, пока ТВС не израсходуется или продолжительность аварийной остановки закончится.\n\nОстановить полупустым — ТВС останется полуизрасходованной. Если ТВС закончится процессе аварийной остановки, то реактор возьмет еще одну ТВС\n\nПотребить дополнительную ТВС — Тоже самое, только реактор израсходует дополнительную ТВС при остановке.\n\nОстаточное тепловыделение — Текущая ТВС вынимается и остаточное тепловыделение происходит полуреалистично, как в версии 1.0.х. +realistic-reactors-explosion-mode=Метод взрыва реактора при достижении критической температуры 1000°C +realistic-reactors-clouds-generation=При расплавлении АЗ реактора начинается выделение радиоактивных облаков из конденсированной воды. Облака перемещаются по карте в направлении ветра.\nУстанaвливает продолжительность паровделения (в секундах)\n(0 - 100 - 9999999) +realistic-reactors-clouds-duration=Продолжительность жизни радиоактивных облаков на местности (в секундах)\n(1 - 80 - 9999999) +realistic-reactors-fallout-appearance=Невидимая — загрязненную радиацией область возможно заметить только звуком счётчика гейгера (реалистично).\n\nПолупрозрачная — плавающие оранжевые частицы.\n\nМенее прозрачная — плавающие оранжевые частицы с улучшенной видимостью.\n\nЗелёная пелена — поверхность окрашена в зелёный цвет, однако бывает трудно увидеть в зонах с низким излучением. +realistic-reactors-fallout-duration=Продолжительность заражения радиоактивных осадков (в секундах)\n(1 - 600 - 9999999) +realistic-reactors-sarcophagus-duration=Истечение жизни саркофага (в секундах). + +[string-mod-setting] +realistic-reactors-fallout-appearance-invisible=Невидимая +realistic-reactors-fallout-appearance-half-transparent=Полупрозрачная +realistic-reactors-fallout-appearance-less-transparent=Менее прозрачная +realistic-reactors-fallout-appearance-green-veil=Зелёная пелена +realistic-reactors-calculate-stats-function-ownly=Формулы Ownly +realistic-reactors-calculate-stats-function-ingo=Формулы Ingo +realistic-reactors-scram-behaviour-limit-to-current-cell=Ограничить на текущую ТВС +realistic-reactors-scram-behaviour-stop-half-empty=Остановить полупустым +realistic-reactors-scram-behaviour-consume-additional-cell=Потребить дополнительную ТВС +realistic-reactors-scram-behaviour-decay-heat-v1=Остаточное тепловыделение +realistic-reactors-explosion-mode-meltdown=Реалистичный +realistic-reactors-explosion-mode-really-very-small-atomic-bomb-projectile=Атомная бомба (2т) +realistic-reactors-explosion-mode-very-small-atomic-bomb-projectile=Атомная бомба (4т) +realistic-reactors-explosion-mode-small-atomic-bomb-projectile=Атомная бомба (8т) +realistic-reactors-explosion-mode-atomic-rocket=Атомная бомба (20т) +realistic-reactors-explosion-mode-big-atomic-bomb-projectile=Атомная бомба (500т) +realistic-reactors-explosion-mode-very-big-atomic-bomb-projectile=Атомная бомба (1кт) +realistic-reactors-explosion-mode-thermobaric-rocket=Термобарический заряд +realistic-reactors-explosion-mode-plutonium-atomic-rocket=Плутониевая бомба + +[item-name] +rr-clowns-mox-cell=МОX ТВС + +[tips-and-tricks-item-name] +RealisticReactors_Title=Реалистичные Реакторы +RealisticReactors_Operate=Принципы работы с реактором +RealisticReactors_Output=Эффективность и энергоотдача реактора +RealisticReactors_Meltdown=Расплавление ядра реактора +RealisticReactors_OutputReactor=Мощность реактора +RealisticReactors_OutputBreeder=Мощность реактора-размножителя + +[tips-and-tricks-item-description] +RealisticReactors_Title=Поздравляем! С этим модом вы сделали ядерные реакторы более реалистичными в игре!\n\n Реалистичные Реакторы [entity=realistic-reactor-normal] вырабатывают тепло и потребляют ТВС [item=uranium-fuel-cell] в точности так, как знакомые ванильные реакторы но\n* энергоотдача [virtual-signal=signal-reactor-power-output] и\n* эффективность [virtual-signal=signal-reactor-efficiency]\nменяются в зависимости от температуры [virtual-signal=signal-reactor-core-temp], расположения и соединения реакторов [virtual-signal=signal-neighbour-bonus].\nРабота реактора-размножителя [entity=realistic-reactor-breeder] схожа с работой обычного реактора. Его выходная мощность средняя, при этом реактор-размножитель вырабатывает дополнительные израсходованные ТВС [item=used-up-uranium-fuel-cell] в процессе эксплуатации. По необходимости эти ТВС можно переработать, например в уран для новых ТВС, компонентов оружия массового поражения и так далее.\n\nОба типа реакторов требуют грамотное внедрение системы распределения тепла и охлаждения. По достижении температуры 1000°C активная зона реактора (АЗ) расплавляется. Дла предотвращения такой ситуации наши реакторы имеют систему аварийного охлаждения (ECCS). В эту систему необходимо подавать холодную воду и забирать горячую для произведения охлаждения АЗ реактора. Градирни [entity=rr-cooling-tower] помогут вам справиться с охлаждением хладагента на выходе из реактора для последуйщей рециркуляции. +RealisticReactors_Operate=Реактор необходимо подключить к электросети для снабжения внутренних систем. Каждый реактор имеет встроенный аккумулятор, который снабжает внутренние системы необходимой энергией на непродолжительное время в случае остановки основного источника питания в аварийных случаях. Каждый реактор имеет графический интерфейс, который можно вызвать с помощю щелчка на [entity=realistic-reactor-interface]. Контроль над реактором осуществляется с помощю сигналов логической комбинаторной сети. Для этого оба типа реакторов имеют соответствующий интерфейс [entity=realistic-reactor-interface]. С помощю этого интерфейса есть возможность узнать все рабочие параметры реактора, а именно:\n\n[virtual-signal=signal-state-stopped]\n[virtual-signal=signal-state-starting]\n[virtual-signal=signal-state-running]\n[virtual-signal=signal-state-scramed]\n[virtual-signal=signal-reactor-core-temp]\n[virtual-signal=signal-coolant-amount]\n[virtual-signal=signal-reactor-power-output]\n[virtual-signal=signal-reactor-efficiency]\n[virtual-signal=signal-neighbour-bonus]\n[virtual-signal=signal-reactor-cell-bonus]\n[virtual-signal=signal-reactor-electric-power]\n\nДля __CONTROL_STYLE_BEGIN__запуска реактора__CONTROL_STYLE_END__ необходимо загрузить в реактор несколько ТВС и дать сигнал [virtual-signal=signal-control-start] в интерфейс реактора. Реактор переходит в состояние старта, в котором его энергопотребление несколько выше нормы.\nПосле окончнания стартовой фазы реактор переходит в нормальный рабочий режим. Энергоотдача и эффективность меняются соответсвенно с температурой и установленными формулами.\n\nДля __CONTROL_STYLE_BEGIN__остановки реактора__CONTROL_STYLE_END__ есть возможность дать ему израсходовать последнюю ТВС. Другой вариант – включение аварийной защиты: необходимо послать сигнал [virtual-signal=signal-control-scram] в интрфейс реактора. Реактор перейдёт в режим аварийной остановки, в котором он будет некоторое время вырабатывать тепловую энергию. Необходимо продолжение охлаждения АЗ реактора в течении всей работы реактора в режиме аварийной остановки.\n\nДля __CONTROL_STYLE_BEGIN__охлаждения реактора__CONTROL_STYLE_END__ возможно использование остаточного тепла из тепловых труб, и/или использование системы аварийного охлажденя ТВС в ядре реактора (ECCS) [entity=realistic-reactor-eccs].\nНа высоких уровнях выходной мощности реактора ОДНОЙ системы аварийного охлажденя ТВС в ядре реактора (ECCS) может быть не достаточно для охлаждения ядра реактора в полной мере. Этoт комплекс проблем требует интеллигентных инженерных решений. +RealisticReactors_Output=[virtual-signal=signal-reactor-power-output], [virtual-signal=signal-reactor-efficiency] и [virtual-signal=signal-reactor-cell-bonus] изменяются в зависимости от температуры ядра реактора.\nОсновное правило для выработки энергии и дополнительных ТВС: чем выше температура - тем лучше. Но с эффективностью всё по-другому: наилучшая эффективность достигается при определённой температуре ядра реактора. При температуре выше или ниже идеальной, эффективность ухудшается.\nЭффективность имеет абсолютный минимум в 50%, а также абсолютный максимум в 200%.\n\nВсё зависит от расположения и подключения реакторов тепловыми трубами (чем-то схоже с ванильным реактором). Эффективность возрастает с количеством реакторов в одной теплопроводной сети. Возможно подключение до 4 реакторов для достижения максимаьной эффективности. Об этом можно узнать с помощю сигнала [virtual-signal=signal-neighbour-bonus].\n\n__CONTROL_STYLE_BEGIN__ВНИМАНИЕ!__CONTROL_STYLE_END__\nНа следующих страницах содержится информация о температурных режимах и эффективности реакторов в тепловых сетях различных величин. Ещё более подробная информация содержится в архиве мода в папке DOCUMENTATION.\n\nЕсли ваша цель в процессе игры подробнее изучить эффективность и температурные режимы, то мы советуем не пересматривать следующие 2 страницы, как раз там эти вопросы решаются. Мы вас предупреждали.\n\nПоследующая информация соответствует __CONTROL_STYLE_BEGIN__[Формулы Ingo]__CONTROL_STYLE_END__. На данный момент это ещё возможно изменить на __CONTROL_STYLE_BEGIN__[Формулы Ownly]__CONTROL_STYLE_END__. +RealisticReactors_OutputReactor=В этой таблице указана выработка тепловой энергии в момент наилучшей эффективности в 4-х конфигурациях, а также последующая выработка электроэнергии через теплообменники и паровые турбины (последние округлены).\n\n[img=tab-r-1]\n[img=tab-r-2]\n[img=tab-r-3]\n[img=tab-r-4]\n[img=tab-r-5]\n *теоретически, соответственно, т.к. реактор взрывается при температуре свыше 1000°C. +RealisticReactors_OutputBreeder=В этой таблице указана выработка тепловой энергии в момент наилучшей эффективности в 4-х конфигурациях, а также последующая выработка электроэнергии через теплообменники и паровые турбины (последние округлены).\n\n[img=tab-b-1]\n[img=tab-b-2]\n[img=tab-b-3]\n[img=tab-b-4]\n[img=tab-b-5]\n *теоретически, соответственно, т.к. реактор взрывается при температуре свыше 1000°C. +RealisticReactors_Meltdown=При достижении критической температуры активной зоны реактора (1000°C) ядро реактора расплавляется. Этот процесс вызывает разрушение реактора и реактор превращается в [entity=reactor-ruin]. Эта руина эмиттирует радиацию в форме паровых радиоактивных облаков. Облака расходятся по карте в соответствии с направлением ветра, так-же эти облака разносят радиоактивные осадки.\n\nДля предотвращения дальнейшего загрязнения необходимо построить [entity=reactor-sarcophagus] на руину реактора. После некоторого времени саркофаг можно убрать, а освобождённое место застраивать в дальнейшем. diff --git a/migrations/RealisticReactors_1.0.2.lua b/migrations/RealisticReactors_1.0.2.lua new file mode 100644 index 0000000..17fd941 --- /dev/null +++ b/migrations/RealisticReactors_1.0.2.lua @@ -0,0 +1,9 @@ +game.reload_script() + +for index, force in pairs(game.forces) do + force.reset_recipes() + force.reset_technologies() + if force.technologies["nuclear-power"].researched then + force.recipes["rr-cooling-tower"].enabled = true + end +end diff --git a/migrations/RealisticReactors_3.0.0.json b/migrations/RealisticReactors_3.0.0.json new file mode 100644 index 0000000..469e12d --- /dev/null +++ b/migrations/RealisticReactors_3.0.0.json @@ -0,0 +1,8 @@ +{ + "mod_setting": [ + ["clouds_generation", "clouds-generation"], + ["rr-clouds-duration", "clouds-duration"], + ["rr-fallout-duration", "fallout-duration"], + ["rr_energy_consumption_multiplier", "energy-consumption-multiplier"] + ] +} diff --git a/migrations/RealisticReactors_3.1.0.json b/migrations/RealisticReactors_3.1.0.json new file mode 100644 index 0000000..ee322ec --- /dev/null +++ b/migrations/RealisticReactors_3.1.0.json @@ -0,0 +1,16 @@ +{ + "mod_setting": [ + ["fallout-appearance", "realistic-reactors-fallout-appearance"], + ["clouds-generation", "realistic-reactors-clouds-generation"], + ["clouds-duration", "realistic-reactors-clouds-duration"], + ["fallout-duration", "realistic-reactors-fallout-duration"], + ["sarcophagus-duration", "realistic-reactors-sarcophagus-duration"], + ["calculate-stats-function", "realistic-reactors-calculate-stats-function"], + ["reactor-scram-duration", "realistic-reactors-reactor-scram-duration"], + ["reactor-starting-duration", "realistic-reactors-reactor-starting-duration"], + ["energy-consumption-multiplier", "realistic-reactors-energy-consumption-multiplier"], + ["static-cooling-power-consumption", "realistic-reactors-static-cooling-power-consumption"], + ["scram-behaviour", "realistic-reactors-scram-behaviour"] + ] +} + diff --git a/prototypes/apm_fix.lua b/prototypes/apm_fix.lua new file mode 100644 index 0000000..e8ceb22 --- /dev/null +++ b/prototypes/apm_fix.lua @@ -0,0 +1,7 @@ +if mods["apm_nuclear"] then + data.raw.reactor["realistic-reactor-breeder"].energy_source.burnt_inventory_size = 2 + data.raw.item["realistic-reactor"].group = data.raw.item["apm_nuclear_breeder"].group + data.raw.item["realistic-reactor"].subgroup = data.raw.item["apm_nuclear_breeder"].subgroup + data.raw.item["breeder-reactor"].group = data.raw.item["apm_nuclear_breeder"].group + data.raw.item["breeder-reactor"].subgroup = data.raw.item["apm_nuclear_breeder"].subgroup +end \ No newline at end of file diff --git a/prototypes/category_fix.lua b/prototypes/category_fix.lua new file mode 100644 index 0000000..5f427b9 --- /dev/null +++ b/prototypes/category_fix.lua @@ -0,0 +1,26 @@ +local categories = { + "deuterium", + "thorium", + "nuclear", + "PE-MOX", + "MOX", +} + + +local function insert_categories(reactor) + for _, category in pairs(categories) do + if data.raw["fuel-category"][category] then + table.insert(reactor.energy_source.fuel_categories, category) + end + end +end + + + +insert_categories(data.raw.reactor["realistic-reactor"]) +insert_categories(data.raw.reactor["realistic-reactor-normal"]) +insert_categories(data.raw.reactor["realistic-reactor-breeder"]) + +for i=1, 250 do + insert_categories(data.raw.reactor["realistic-reactor-"..i]) +end diff --git a/prototypes/connections_hack.lua b/prototypes/connections_hack.lua new file mode 100644 index 0000000..9d6a580 --- /dev/null +++ b/prototypes/connections_hack.lua @@ -0,0 +1,46 @@ +local math2d = require("math2d") +local bigpack = require("__big-data-string__.pack") + + +local function map(t,f) + local r = {} for k,v in pairs(t) do r[k] = f(v) end return r +end + +local function encode(t) + return table.concat(map(t,tostring), ",") +end + + + +local function hack_connections(prototype) + local connections = {} + if prototype.heat_buffer then + for _,connection in ipairs(prototype.heat_buffer.connections or {}) do + local d = assert(connection.direction, "direction missing") + local p = assert(connection.position, "position missing") + p = math2d.position.ensure_xy(p) + -- append triple + table.insert(connections, d) + table.insert(connections, p.x) + table.insert(connections, p.y) + end + end + data:extend{bigpack(prototype.name .. "-heat-buffer-connections", encode(connections))} +end + + +local function is_internal(prototype) + return prototype.type == "reactor" + and string.sub(prototype.name, 1,17) == "realistic-reactor" + and not (prototype.name == "realistic-reactor") +end + + +for _,type in ipairs{"reactor","heat-pipe"} do + for _,prototype in pairs(data.raw[type]) do + if not is_internal(prototype) then + hack_connections(prototype) + end + end +end + diff --git a/prototypes/entities.lua b/prototypes/entities.lua new file mode 100644 index 0000000..c016642 --- /dev/null +++ b/prototypes/entities.lua @@ -0,0 +1,880 @@ +local Setting = require "scripts.setting" +local util = require "__core__.lualib.util" + +local debug_core = false + +local REACTOR_GLOW_COLOR = {r = 0, g = 1, b = 0.94} +local BREEDER_GLOW_COLOR = {r = 0.94, g = 1, b = 0} + +local REACTOR_LIGHT = {intensity = 0.9, size = 3, color = REACTOR_GLOW_COLOR} +local BREEDER_LIGHT = {intensity = 0.9, size = 3, color = BREEDER_GLOW_COLOR} + +if Setting.startup("disable-reactor-light") then + REACTOR_LIGHT = nil + BREEDER_LIGHT = nil +end + +local REACTOR_DEFAULT_PICTURE_ATTRS = { + width = 288, height = 348, + scale = 0.3542, + shift = util.by_pixel(1,-12), +} + +local REACTOR_LIGHT_FLICKER_ATTRS = { + color = {0,0,0}, + minimum_intensity = 0.7, + maximum_intensity = 0.95, +} + +local REACTOR_RESISTANCES_ATTRS = { + { type = "physical", percent = 80, }, + { type = "impact", percent = 80, }, + { type = "fire", percent = 80, }, + { type = "acid", percent = 80, }, + { type = "poison", percent = 80, }, + { type = "explosion", percent = 70, }, + { type = "laser", percent = 70, }, +} + +-- https://wiki.factorio.com/Types/EntityPrototypeFlags +local REACTOR_TEMPLATE_ENTITY_FLAGS = { + "not-rotatable", + "placeable-player", +-- "placeable-neutral", +-- "placeable-enemy", + "placeable-off-grid", + "player-creation", +-- "building-direction-8-way", +-- "filter-directions", +-- "fast-replaceable-no-build-while-moving", +-- "breaths-air", +-- "not-repairable", + "not-on-map", + "not-blueprintable", +-- "not-deconstructable", -- added where needed + "hidden", + "hide-alt-info", +-- "fast-replaceable-no-cross-type-while-moving", +-- "no-gap-fill-while-building", + "not-flammable", +-- "no-automated-item-removal", +-- "no-automated-item-insertion", + "no-copy-paste", +-- "not-selectable-in-game", + "not-upgradable", + "not-in-kill-statistics", +} + +local REACTOR_ENTITY_FLAGS = { +-- "not-rotatable", -- added where needed + "placeable-player", +-- "placeable-neutral", +-- "placeable-enemy", +-- "placeable-off-grid", + "player-creation", +-- "building-direction-8-way", +-- "filter-directions", +-- "fast-replaceable-no-build-while-moving", +-- "breaths-air", +-- "not-repairable", +-- "not-on-map", +-- "not-blueprintable", +-- "not-deconstructable", +-- "hidden", +-- "hide-alt-info", -- added where needed +-- "fast-replaceable-no-cross-type-while-moving", +-- "no-gap-fill-while-building", +-- "not-flammable", +-- "no-automated-item-removal", +-- "no-automated-item-insertion", +-- "no-copy-paste", +-- "not-selectable-in-game", +-- "not-upgradable", +-- "not-in-kill-statistics", +} + +local REACTOR_RUIN_ENTITY_FLAGS = { + "not-rotatable", +-- "placeable-player", + "placeable-neutral", +-- "placeable-enemy", +-- "placeable-off-grid", + "player-creation", +-- "building-direction-8-way", +-- "filter-directions", +-- "fast-replaceable-no-build-while-moving", +-- "breaths-air", + "not-repairable", +-- "not-on-map", + "not-blueprintable", + "not-deconstructable", +-- "hidden", + "hide-alt-info", +-- "fast-replaceable-no-cross-type-while-moving", +-- "no-gap-fill-while-building", + "not-flammable", +-- "no-automated-item-removal", +-- "no-automated-item-insertion", + "no-copy-paste", +-- "not-selectable-in-game", + "not-upgradable", + "not-in-kill-statistics", +} + +local REACTOR_INTERNAL_ENTITY_FLAGS = { + "not-rotatable", + "placeable-player", +-- "placeable-neutral", +-- "placeable-enemy", +-- "placeable-off-grid", + "player-creation", +-- "building-direction-8-way", +-- "filter-directions", +-- "fast-replaceable-no-build-while-moving", +-- "breaths-air", + "not-repairable", + "not-on-map", +-- "not-blueprintable", --added where required + "not-deconstructable", + "hidden", +-- "hide-alt-info", --added where required +-- "fast-replaceable-no-cross-type-while-moving", +-- "no-gap-fill-while-building", + "not-flammable", +-- "no-automated-item-removal", +-- "no-automated-item-insertion", + "no-copy-paste", +-- "not-selectable-in-game", --added where required + "not-upgradable", + "not-in-kill-statistics", +} + +local COOLING_TOWER_SMOKE_FLAGS = { +-- "not-rotatable", +-- "placeable-player", + "placeable-neutral", +-- "placeable-enemy", + "placeable-off-grid", +-- "player-creation", +-- "building-direction-8-way", +-- "filter-directions", +-- "fast-replaceable-no-build-while-moving", +-- "breaths-air", + "not-repairable", + "not-on-map", + "not-blueprintable", + "not-deconstructable", + "hidden", + "hide-alt-info", +-- "fast-replaceable-no-cross-type-while-moving", +-- "no-gap-fill-while-building", + "not-flammable", +-- "no-automated-item-removal", +-- "no-automated-item-insertion", + "no-copy-paste", + "not-selectable-in-game", + "not-upgradable", + "not-in-kill-statistics", +} + +local empty_sprite = { + filename = "__core__/graphics/empty.png", + flags = { "always-compressed" }, + priority = "extra-high", + frame_count = 1, + width = 1, + height = 1, +} + +local interface_led = { + filename = "__base__/graphics/entity/combinator/activity-leds/decider-combinator-LED-S.png", + width = 8, + height = 8, + frame_count = 1, + --shift = {-0.28125, -0.34375} + shift = {-0.15, -0.24}, + scale = 0.3, +} + +local red_point = {x=6.9,y=10.8} +local green_point = {x=-6.1,y=10.8} + +interface_connection = { + shadow = { + red = util.by_pixel(red_point.x+16, red_point.y+12.5),--{0.796875, 0.5}, + green = util.by_pixel(green_point.x+16, green_point.y+12.5)--{0.203125, 0.5}, + }, + wire = { + red = util.by_pixel(red_point.x, red_point.y),--{0.296875, 0.0625}, + green = util.by_pixel(green_point.x, green_point.y)--{-0.296875, 0.0625}, + } +} + +-- default reactor (running reactor) +reactor_template = { + type = "reactor", + name = "realistic-reactor", + icon = "__RealisticReactors__/graphics/icons/nuclear-reactor.png", + icon_size = 32, + order = "f[nuclear-energy]-a[reactor]", + flags = {"not-deconstructable", unpack(REACTOR_TEMPLATE_ENTITY_FLAGS)}, + max_health = 500, + corpse = "big-remnants", + consumption = "40MW", + neighbour_bonus = 1, + selectable_in_game = false, + vehicle_impact_sound = {filename = "__base__/sound/car-metal-impact.ogg", volume = 0.65}, + meltdown_action = nil, + collision_box = {{-1.4, -1.4}, {1.4, 1.4}}, + --collision_box = {{-2.2, -2.2}, {2.2, 2.2}}, + --selection_box = {{-2.5, -2.5}, {2.5, 2.5}}, + --collision_box = {{-1.4, -1.4}, {1.4, 0.7}}, + --selection_box = {{-1.5, -3}, {1.5, 0.5}}, + --selection_box = {{-1.4, -3}, {1.4, 1.4}}, + working_light_picture = empty_sprite, + energy_source = { + type = "burner", + fuel_categories = {}, + effectivity = 1, + fuel_inventory_size = 1, + burnt_inventory_size = 1, + light_flicker = { + minimum_intensity = 0, + maximum_intensity = 0, + minimum_light_size = 0, + light_intensity_to_size_coefficient = 0, + } + }, + working_sound = { + sound = {filename = "__RealisticReactors__/sound/reactor-active.ogg", volume = 0.6}, + idle_sound = {filename = "__base__/sound/idle1.ogg", volume = 0.6}, + apparent_volume = 1.5, + }, + heat_buffer = { + max_temperature = 1005, + specific_heat = "5MJ", + max_transfer = "10GW", + minimum_glow_temperature = 350, + connections = { + { position = { 1.0, -1.5}, direction = defines.direction.north }, + { position = { 0.0, -1.5}, direction = defines.direction.north }, + { position = {-1.0, -1.5}, direction = defines.direction.north }, + { position = { 1.3, -1.0}, direction = defines.direction.east }, + { position = { 1.3, 0.0}, direction = defines.direction.east }, + { position = {-1.3, 0.0}, direction = defines.direction.west }, + { position = {-1.3, -1.0}, direction = defines.direction.west }, + } + }, +-- connection_patches_connected = { +-- sheet = { +-- filename = "__RealisticReactors__/graphics/entity/reactor-connect-patches-empty.png", +-- width = 32, +-- height = 32, +-- variation_count = 12 +-- } +-- }, +-- connection_patches_disconnected = { +-- sheet = { +-- filename = "__RealisticReactors__/graphics/entity/reactor-connect-patches-empty.png", +-- width = 32, +-- height = 32, +-- variation_count = 12, +-- y = 32 +-- } +-- }, +-- pipe_covers = pipecoverspictures(), +} + + +-- display dummy for normal reactor +reactor_normal = table.deepcopy(reactor_template) +reactor_normal.name = "realistic-reactor-normal" +reactor_normal.flags = {"not-rotatable", "hide-alt-info", unpack(REACTOR_ENTITY_FLAGS)} +reactor_normal.placeable_by = {item="realistic-reactor", count = 1} +reactor_normal.resistances = REACTOR_RESISTANCES_ATTRS +reactor_normal.minable = {mining_time = 1.5, result = "realistic-reactor"} +reactor_normal.consumption = "0.00001W" +reactor_normal.collision_box = {{-1.3, -1.3}, {1.3, 1.4}} +--reactor_normal.selection_box = {{-1.4, -1.9}, {1.4, 0.5}} +reactor_normal.selection_box = {{-1.4, -1.8}, {1.4, 1.35}} --eccs would not be selectable, but interface would. +reactor_normal.selectable_in_game = true +reactor_normal.light = REACTOR_LIGHT +reactor_normal.energy_source.light_flicker = REACTOR_LIGHT_FLICKER_ATTRS + +reactor_normal.picture = { layers = { + { + filename = "__RealisticReactors__/graphics/entity/reactor.png", + width = REACTOR_DEFAULT_PICTURE_ATTRS.width, + height = REACTOR_DEFAULT_PICTURE_ATTRS.height, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = REACTOR_DEFAULT_PICTURE_ATTRS.shift, + }, + { + filename = "__RealisticReactors__/graphics/entity/reactor-shadow.png", + draw_as_shadow = true, + flags = {"shadow"}, + width = 510, + height = 360, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = util.by_pixel(40,-10), + }, +}} + +reactor_normal.working_light_picture = { + filename = "__RealisticReactors__/graphics/entity/reactor-lights.png", + blend_mode = "additive", + draw_as_glow = true, + flags = {"light"}, + tint = REACTOR_GLOW_COLOR, + width = REACTOR_DEFAULT_PICTURE_ATTRS.width, + height = REACTOR_DEFAULT_PICTURE_ATTRS.height, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = REACTOR_DEFAULT_PICTURE_ATTRS.shift, +} + +reactor_normal.lower_layer_picture = { + filename = "__RealisticReactors__/graphics/entity/reactor-pipes.png", + width = REACTOR_DEFAULT_PICTURE_ATTRS.width, + height = REACTOR_DEFAULT_PICTURE_ATTRS.height, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = REACTOR_DEFAULT_PICTURE_ATTRS.shift, +} + +reactor_normal.heat_lower_layer_picture = apply_heat_pipe_glow{ + filename = "__RealisticReactors__/graphics/entity/reactor-pipes-heated.png", + priority = "extra-high", + width = REACTOR_DEFAULT_PICTURE_ATTRS.width, + height = REACTOR_DEFAULT_PICTURE_ATTRS.height, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = REACTOR_DEFAULT_PICTURE_ATTRS.shift, +} + +reactor_normal.heat_buffer.heat_picture = apply_heat_pipe_glow{ + filename = "__RealisticReactors__/graphics/entity/reactor-heated.png", + priority = "extra-high", + blend_mode = "additive-soft", +-- tint = {r = 255, g = 200, b = 0}, -- works, white by default + width = REACTOR_DEFAULT_PICTURE_ATTRS.width, + height = REACTOR_DEFAULT_PICTURE_ATTRS.height, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = REACTOR_DEFAULT_PICTURE_ATTRS.shift, +} + + -- display dummy for breeder reactor +reactor_breeder = table.deepcopy(reactor_normal) +reactor_breeder.name = "realistic-reactor-breeder" +reactor_breeder.icon = "__RealisticReactors__/graphics/icons/breeder-reactor.png" +reactor_breeder.minable = {mining_time = 1.5, result = "breeder-reactor"} +reactor_breeder.placeable_by = {item="breeder-reactor", count = 1} +reactor_breeder.resistances = REACTOR_RESISTANCES_ATTRS +reactor_breeder.picture.layers[1].filename = "__RealisticReactors__/graphics/entity/breeder.png" +reactor_breeder.heat_buffer.heat_picture = apply_heat_pipe_glow{ + filename = "__RealisticReactors__/graphics/entity/breeder-heated.png", + priority = "extra-high", + blend_mode = "additive-soft", +-- tint = {r = 255, g = 200, b = 0}, -- works, white by default + width = REACTOR_DEFAULT_PICTURE_ATTRS.width, + height = REACTOR_DEFAULT_PICTURE_ATTRS.height, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = REACTOR_DEFAULT_PICTURE_ATTRS.shift, +} + +reactor_breeder.working_light_picture.filename = "__RealisticReactors__/graphics/entity/breeder-lights.png" +reactor_breeder.working_light_picture.tint = BREEDER_GLOW_COLOR +reactor_breeder.light = BREEDER_LIGHT +reactor_breeder.energy_source.light_flicker = REACTOR_LIGHT_FLICKER_ATTRS + +reactor_default = table.deepcopy(reactor_template) +reactor_default.flags = REACTOR_TEMPLATE_ENTITY_FLAGS -- deconstructable required for explosion-mode ~= meltdown +reactor_default.collision_mask = {} +reactor_default.resistances = {} + + +table.insert(reactor_template.flags, "no-automated-item-insertion") +table.insert(reactor_template.flags, "no-automated-item-removal") + +-- Nuclear reactor x, running phase +for i=1, 250 do + + local temp_reactor = table.deepcopy(reactor_template) + temp_reactor.name = "realistic-reactor-"..i + temp_reactor.collision_mask = {"item-layer"} + temp_reactor.consumption = i.."MW" + + if debug_core then + temp_reactor.selection_box = {{-1.4, -2.5}, {1.4, 1.35}} + temp_reactor.selectable_in_game = true + end + + data:extend({temp_reactor}) + +end + +-- Circuit interface entity for nuclear reactor +reactor_interface = { + type = "constant-combinator", + name = "realistic-reactor-interface", + selection_priority = 255, + icon = reactor_template.icon, + icon_size = reactor_template.icon_size, + order = "f[nuclear-energy]-a[reactor]", + flags = {"hide-alt-info", unpack(REACTOR_INTERNAL_ENTITY_FLAGS)}, + max_health = reactor_template.max_health, + collision_box = {{-1.4, -0.25}, {1.4, 0.4}}, + selection_box = {{-0.5, -0.5}, {0.5, 0.5}}, + item_slot_count = 14, + sprites = { + north = empty_sprite, + east = empty_sprite, + south = empty_sprite, + west = empty_sprite, + }, + activity_led_sprites = { + north = util.draw_as_glow(interface_led), + east = util.draw_as_glow(interface_led), + south = util.draw_as_glow(interface_led), + west = util.draw_as_glow(interface_led), + }, + activity_led_light = { + intensity = 0.4, + size = 0.3, + color = {r = 0.02, g = 0.05, b = 0.55} + }, + activity_led_light_offsets = { + interface_led.shift, + interface_led.shift, + interface_led.shift, + interface_led.shift, + }, + circuit_wire_connection_points = { + interface_connection, + interface_connection, + interface_connection, + interface_connection, + }, + circuit_wire_max_distance = 7.5, + order = "z", +} + +local breeder_interface = table.deepcopy(reactor_interface) +breeder_interface.name = "realistic-breeder-interface" + + +-- ECCS entity for nuclear reactor +reactor_eccs = { + type = "storage-tank", + name = "realistic-reactor-eccs", + collision_mask = {"item-layer"}, + icon = reactor_template.icon, + icon_size = reactor_template.icon_size, + order = "f[nuclear-energy]-a[reactor]", + flags = {"not-blueprintable", unpack(REACTOR_INTERNAL_ENTITY_FLAGS)}, + max_health = reactor_template.max_health, + collision_mask = {"ghost-layer"}, + collision_box = {{-1.3, -1.3}, {1.3, 1.3}}, + selection_box = {{-1.4,0.5},{1.4,1.7}}, + --drawing_box = {{-1.4,-1.4},{-1,-1}}, --doesnt affect alt-info-overlay + fluid_box = { + base_area = 50, + pipe_covers = pipecoverspictures(), + pipe_connections = { + --{position = {-2, -1}}, + {position = {-2, 1}}, + --{position = {-1, -2}}, + {position = {-1, 2}}, + --{position = {1, -2}}, + {position = {1, 2}}, + --{position = {2, -1}}, + {position = {2, 1}}, + } + }, + window_bounding_box = {{-0.1,-0.1}, {0.1,0.1}}, + pictures = { + picture = { + north = empty_sprite, + east = empty_sprite, + south = empty_sprite, + west = empty_sprite, + }, + fluid_background = empty_sprite, + window_background = empty_sprite, + flow_sprite = empty_sprite, + gas_flow = empty_sprite, + }, + flow_length_in_ticks = 360, + circuit_wire_max_distance = 0, + order = "z", +} + +-- Power draining entity for normal reactor +reactor_power_normal = { + type = "electric-energy-interface", + name = "realistic-reactor-power-normal", + icon = reactor_normal.icon, + icon_size = reactor_normal.icon_size, + flags = {"not-blueprintable", "hide-alt-info", unpack(REACTOR_INTERNAL_ENTITY_FLAGS)}, + order = "f[nuclear-energy]-a[reactor]", + max_health = 150, + collision_mask = {"ghost-layer"}, + collision_box = {{-1.4, -1.4}, {1.4, 1.4}}, + selection_box = {{-1.4, -1.4}, {1.4, 1.4}}, + drawing_box = {{-0.5, -2.5}, {0.5, 0.3}}, + selectable_in_game = false, + energy_source = { + type = "electric", + usage_priority = "secondary-input", + input_flow_limit= "17MW", + buffer_capacity = "17MJ" + }, + energy_production = "0kW", + energy_usage = "0kW", +} + +-- Power draining entity 2 for nuclear reactor +reactor_power_breeder = table.deepcopy(reactor_power_normal) +reactor_power_breeder.name = "realistic-reactor-power-breeder" +reactor_power_breeder.icon = reactor_breeder.icon +reactor_power_breeder.icon_size = reactor_breeder.icon_size + + + +-- Ruins +reactor_ruin = { + type = "simple-entity-with-owner", + name = "reactor-ruin", + icon = "__RealisticReactors__/graphics/icons/nuclear-reactor.png", + icon_size = 32, + max_health = 1000, + order = "f[nuclear-energy]-a[reactor]", + flags = REACTOR_RUIN_ENTITY_FLAGS, + placeable_by = { item="reactor-sarcophagus", count = 1}, + collision_box = {{-1.4, -1.4}, {1.4, 1.4}}, +-- collision_box = {{-1.5, -1.5}, {1.5, 1.5}}, +-- collision_box = {{-2.2, -2.2}, {2.2, 2.2}}, + selection_box = {{-2.5, -2.5}, {2.5, 2.5}}, + selection_priority = 0, + minable = {hardness = 999999, mining_time = 999999}, + fast_replaceable_group = "reactor-ruins", + pictures = { layers = { + { + filename = "__RealisticReactors__/graphics/entity/reactor-ruin.png", + width = 174, + height = 160, + shift = {1.2185, -0.59375}, + frame_count=1 + }, + { + filename = "__RealisticReactors__/graphics/entity/reactor-shadow.png", + draw_as_shadow = true, + flags = {"shadow"}, + width = 510, + height = 360, + scale = REACTOR_DEFAULT_PICTURE_ATTRS.scale, + shift = util.by_pixel(40,-10), + }, + }} +} + + +breeder_ruin = table.deepcopy(reactor_ruin) +breeder_ruin.name = "breeder-ruin" +breeder_ruin.icon = "__RealisticReactors__/graphics/icons/breeder-reactor.png" +breeder_ruin.pictures.layers[1].filename = "__RealisticReactors__/graphics/entity/breeder-ruin.png" + +-- Sarcophagus +sarcophagus = table.deepcopy(reactor_ruin) +sarcophagus.name = "reactor-sarcophagus" +sarcophagus.icon = "__RealisticReactors__/graphics/icons/sarcophagus2.png" +sarcophagus.flags = {"player-creation", "not-blueprintable", "not-repairable"} +sarcophagus.minable = {mining_time = 1} +sarcophagus.collision_box = {{-1.5, -1.5}, {1.5, 1.5}} +-- sarcophagus.collision_box = {{-2.2, -2.1}, {2.2, 2.1}} +sarcophagus.selection_box = {{-2.3, -2.4}, {2.1, 2.0}} +sarcophagus.pictures = { + layers = { + { + filename = "__RealisticReactors__/graphics/entity/sarcophagus2.png", + width = 1024, + height = 768, + shift = {-0.1, -0.4}, + frame_count=1, + scale=0.19 + }, + { + filename = "__RealisticReactors__/graphics/entity/sarcophagus2-shadow.png", + width = 1024, + height = 768, + shift = {0.8, -0.07}, + frame_count=1, + scale=0.18, + draw_as_shadow = true + }, + } +} + + + +-- Cooling tower +cooling_tower = { + type = "furnace", + name = "rr-cooling-tower", + icon = "__RealisticReactors__/graphics/icons/cooling-tower.png", + icon_size = 32, + flags = {"hide-alt-info", unpack(REACTOR_ENTITY_FLAGS)}, + minable = {hardness = 0.2, mining_time = 0.5, result = "rr-cooling-tower"}, + max_health = 500, + corpse = "medium-remnants", + resistances = { + { + type = "fire", + percent = 70 + } + }, + collision_box = {{-1.3, -1.3}, {1.3, 1.3}}, + selection_box = {{-1.5, -1.5}, {1.5, 1.5}}, + drawing_box = {{-1.5, -3}, {1.5, 1.5}}, + fluid_boxes = { + { + production_type = "input", + base_area = 25, + base_level = -1, + pipe_covers = pipecoverspictures(), + pipe_connections = { + {position = {-2, -1},type = "input"}, + {position = {-2, 1},type = "input"}, + {position = {-1, -2},type = "input"}, + {position = {-1, 2},type = "input"} + } + }, + { + production_type = "output", + base_area = 25, + base_level = 1, + pipe_covers = pipecoverspictures(), + pipe_connections = { + {position = {1, -2},type = "output"}, + {position = {1, 2},type = "output"}, + {position = {2, -1},type = "output"}, + {position = {2, 1},type = "output"} + } + } + }, + source_inventory_size = 0, + result_inventory_size = 0, + crafting_categories = {"water-cooling"}, + energy_usage = "120kW", + crafting_speed = 1, + energy_source = { + type = "electric", + usage_priority = "primary-input", + emissions = 0, + }, + animation = { + layers = { + { + filename = "__RealisticReactors__/graphics/entity/cooling-tower-shadow.png", + width = 430, height = 310, + shift = util.by_pixel(52, -21), + scale = 0.5, + draw_as_shadow = true, + flags = {"shadow"}, + }, + { + filename = "__RealisticReactors__/graphics/entity/cooling-tower.png", + width = 308, height = 310, + shift = {0.695, -0.66}, + scale = 0.505, + }, + { + filename = "__RealisticReactors__/graphics/entity/cooling-tower-light.png", + scale = 0.505, + width = 308, height = 310, + shift = {0.695, -0.66}, + tint = {r = 1, g = 0, b = 0}, + draw_as_light = true, + priority = "extra-high", + flags = {"light"}, + }, + { + filename = "__RealisticReactors__/graphics/entity/cooling-tower-glow.png", + scale = 0.505, + width = 308, height = 310, + shift = {0.695, -0.66}, + tint = {r = 1, g = 0, b = 0}, + draw_as_light = true, + priority = "extra-high", + flags = {"light"}, + }, + } + } +} + +-- Steam creator for cooling tower +cooling_tower_smoke = { + type = "furnace", + name = "rr-cooling-tower-steam", + selectable_in_game = false, + icon = cooling_tower.icon, + icon_size = 32, + order = "f[nuclear-energy]-a[reactor]", + flags = COOLING_TOWER_SMOKE_FLAGS, + max_health = cooling_tower.max_health, + collision_mask = {"ghost-layer"}, + collision_box = {{-0.5,-0.5},{0.5,0.5}}, + selection_box = {{-0.5,-0.5},{0.5,0.5}}, + fluid_boxes = { + { + production_type = "input", + base_area = 0.1, + pipe_connections = { } + }, + { + production_type = "output", + base_area = 0.1, + pipe_connections = { } + } + }, + source_inventory_size = 0, + result_inventory_size = 0, + crafting_categories = {"steaming"}, + energy_usage = "1W", + crafting_speed = 1, + energy_source = { + type = "burner", + effectivity = 1, + fuel_inventory_size = 1, + emissions = 0, + light_flicker = { + minimum_intensity = 0, + maximum_intensity = 0, + }, + smoke = { + { + name = "cooling-tower-smoke-type", + type = "trivial-smoke", + deviation = {0.05, 0.05}, + frequency = 10, + position = {0.15, -1.95}, + starting_vertical_speed = 0.011, + starting_vertical_speed_deviation = 0.004, + starting_frame_deviation = 60, + } + } + }, + animation = empty_sprite, +} + +ruin_smoke = table.deepcopy(cooling_tower_smoke) +ruin_smoke.name = "ruin-smoke" +ruin_smoke.energy_source.smoke[1].name = "ruin-smoke-type" +ruin_smoke.energy_source.smoke[1].position = {0.0, -1.8} + +local function trivial_smoke(opts) return { + type = "trivial-smoke", + flags = {"placeable-off-grid","not-on-map"}, + name = opts.name, + color = opts.color, + render_layer = "smoke", + blend_mode = opts.blend_mode, + duration = opts.duration or 600, + fade_in_duration = opts.fade_in_duration or 0, + fade_away_duration = opts.fade_away_duration or ((opts.duration or 600) - (opts.fade_in_duration or 0)), + spread_duration = opts.spread_duration or 600, + start_scale = opts.start_scale or 0.20, + end_scale = opts.end_scale or 1.0, + cyclic = opts.cyclic == nil or opts.cyclic, + affected_by_wind = opts.affected_by_wind == nil or opts.affected_by_wind, + show_when_smoke_off = opts.show_when_smoke_off, + animation = { + width = 152, + height = 120, + line_length = 5, + frame_count = 60, + shift = {-0.53125, -0.4375}, + priority = "extra-high", + animation_speed = 0.25, + filename = "__base__/graphics/entity/smoke/smoke.png", + flags = { "smoke" }, + scale = opts.scale or 1, + } +} end +local warning = { + type = "simple-entity", + name = "rr-electricity-warning", + flags = {"placeable-off-grid"}, + picture = { + filename = "__core__/graphics/icons/alerts/electricity-icon-red.png", + priority = "high", + width = 64, + height = 64, + frame_count = 1, + animation_speed = 1, + scale = 0.44, + }, + duration = 60, + fade_in_duration = 3, + fade_away_duration = 3, + spread_duration = 0, + start_scale = 1, + end_scale = 1, + cyclic = true, + affected_by_wind = false, + movement_slow_down_factor = 0.5, + show_when_smoke_off = true, + render_layer = "entity-info-icon-above", +} +local cooling_warning = table.deepcopy(warning) +cooling_warning.name = "rr-cooling-warning" +cooling_warning.picture.filename = "__core__/graphics/icons/alerts/recharge-icon.png" +data:extend({ + -- electricity warning + warning, + cooling_warning, + -- smoke for cooling tower + trivial_smoke{ + name = "cooling-tower-smoke-type", + color = {r=0.65, g=0.65, b=0.65, a=0.3}, + start_scale = 0.77, + end_scale = 1.3, + duration = 260, + spread_duration = 260, + fade_away_duration = 100, + fade_in_duration = 20, + affected_by_wind = true, + show_when_smoke_off = true, + }, + -- smoke for reactor ruin + trivial_smoke{ + name = "ruin-smoke-type", + blend_mode = "additive-soft", + color = {r=0.8, g=0.9, b=0.9, a=0.9}, + start_scale = 0.4, + end_scale = 6.0, + duration = 1000, + spread_duration = 1000, + fade_away_duration = 990, + fade_in_duration = 10, + affected_by_wind = true, + show_when_smoke_off = true, + }, + +}) + + +data:extend{ + reactor_default, + reactor_interface, + breeder_interface, + reactor_eccs, + reactor_power_normal, + reactor_power_breeder, + reactor_normal, + reactor_breeder, + cooling_tower, + cooling_tower_smoke, + ruin_smoke, + reactor_ruin, + breeder_ruin, + sarcophagus, +} + diff --git a/prototypes/fx.lua b/prototypes/fx.lua new file mode 100644 index 0000000..ee02853 --- /dev/null +++ b/prototypes/fx.lua @@ -0,0 +1,162 @@ +local Setting = require "scripts.setting" + +-- ENTITIES FOR CLOUDS AND FALLOUT AND ATOMIC EXPLOSION +local function make_action(radius, effect_id) return { + type = "direct", + action_delivery = { + type = "instant", + target_effects = { + type = "nested-result", + action = { + type = "area", + radius = radius, + entity_flags = {"placeable-off-grid"}, + action_delivery = { + type = "instant", + target_effects = { + type = "script", + effect_id = effect_id, + --probability = 0.5, + } + } + } + } + } +} end + + +-- fallout radiation +local perma_radiation = { + type = "smoke-with-trigger", + name = "permanent-radiation", + flags = {"not-on-map"}, + render_layer = "item-in-inserter-hand", + show_when_smoke_off = true, + random_animation_offset = true, + animation = { + filename = "__RealisticReactors__/graphics/fallout/fallout_spritesheet.png", + random_animation_offset = true, + + priority = "low", + width = 249, + height = 211, + frame_count = 14, + animation_speed = 0.15, + line_length = 7, + scale = 2, + blend_mode = "additive-soft", + apply_runtime_tint=true, + }, + slow_down_factor = 0, + affected_by_wind = false, + cyclic = true, + duration = Setting.protoduration("fallout") * 60, + fade_away_duration = math.min(180,Setting.protoduration("fallout")/3) * 60, + --fade_in_duration = math.min(5,Setting.protoduration("fallout")/3) * 60, --doesn't work + spread_duration = math.min(5,Setting.protoduration("fallout")/3) * 60, + movement_slow_down_factor = 0, + color = { r = 1, g = 1, b = 1}, + action = make_action(7,"radiation-damage"), + action_cooldown = 30 +} + +-- fallout appearance +if Setting.appearance("fallout") == "invisible" then + perma_radiation.animation = { + filename = "__RealisticReactors__/graphics/transparent32.png", + random_animation_offset = true, + flags = { "compressed" }, + priority = "low", + width = 32, + height = 32, + frame_count = 1, + animation_speed = 0.2, + line_length = 1, + scale = 1, + } + perma_radiation.action = make_action(14,"radiation-damage") + +elseif Setting.appearance("fallout") == "half-transparent" then + perma_radiation.animation = { + filename = "__RealisticReactors__/graphics/fallout/fallout_spritesheet_half.png", + random_animation_offset = true, + priority = "low", + width = 249, + height = 211, + frame_count = 14, + animation_speed = 0.15, + line_length = 7, + scale = 2, + blend_mode = "additive-soft", + apply_runtime_tint = true, + } + +elseif Setting.appearance("fallout") == "green-veil" then + perma_radiation.animation = { + filename = "__RealisticReactors__/graphics/fallout/fallout-green.png", + random_animation_offset = true, + flags = { "compressed" }, + priority = "low", + width = 256, + height = 256, + frame_count = 1, + animation_speed = 0.2, + line_length = 1, + scale = 4, + blend_mode = "additive-soft", + apply_runtime_tint = true, + --premul_alpha= false, + tint = {r=0, g=1, b=0, a=0.01}, + } + perma_radiation.action = make_action(15,"radiation-damage") + +end + +data:extend{ + perma_radiation, + + -- fallout cloud + { + type = "smoke-with-trigger", + name = "fallout-cloud", + flags = {"not-on-map","placeable-off-grid"}, + render_layer = "entity-info-icon-above", + show_when_smoke_off = true, + animation = { + filename = "__RealisticReactors__/graphics/fallout/cloud-45-frames.png", + flags = {"compressed"}, + priority = "low", + width = 256, + height = 256, + frame_count = 45, + animation_speed = 0.2, + line_length = 7, + scale = 6, + }, + slow_down_factor = 0, + affected_by_wind = true, + cyclic = true, + duration = Setting.protoduration("clouds") * 60, + --fade_in_duration = math.min(20,Setting.protoduration("clouds")/3) * 60, --doesnt work + fade_away_duration = math.min(20,Setting.protoduration("clouds")/3) * 60, + --spread_duration = 50, + spread_duration = 300, + movement_slow_down_factor = 1, + color = { r = 1, g = 1, b = 1}, + action = make_action(20,"radiation-damage-strong"), + action_cooldown = 30, + }, + + -- geiger counter sounds + { + type = "sound", + name = "RR-geiger-0", + filename = "__RealisticReactors__/sound/geiger0.ogg", + }, + { + type = "sound", + name = "RR-geiger-1", + filename = "__RealisticReactors__/sound/geiger1.ogg", + }, + +} diff --git a/prototypes/items.lua b/prototypes/items.lua new file mode 100644 index 0000000..31ca930 --- /dev/null +++ b/prototypes/items.lua @@ -0,0 +1,109 @@ +data:extend{ + +-- ITEM SUBGROUP -- +-- + { + type = "item-subgroup", + name = "realistic-reactors-energy", + group = "production", + order = "b-a", + }, + + +-- ITEMS -- +-- + -- Nuclear Reactor + { + type = "item", + name = "realistic-reactor", + icon = "__RealisticReactors__/graphics/icons/nuclear-reactor.png", + icon_size = 32, + subgroup = "realistic-reactors-energy", + order = "f[nuclear-energy]-b[a-realistic-reactor]-a[normal]", + place_result = "realistic-reactor-normal", + stack_size = 10, + }, + -- Reactor Ruin +-- { -- not needed +-- type = "item", +-- name = "reactor-ruin", +-- icon = "__RealisticReactors__/graphics/icons/nuclear-reactor.png", +-- icon_size = 32, +-- subgroup = "realistic-reactors-energy", +-- order = "f[nuclear-energy]-b[a-realistic-reactor]-z[ruin]", +-- place_result = "reactor-ruin", +-- stack_size = 10, +-- }, + -- Breeder Reactor + { + type = "item", + name = "breeder-reactor", + icon = "__RealisticReactors__/graphics/icons/breeder-reactor.png", + icon_size = 32, + subgroup = "realistic-reactors-energy", + order = "f[nuclear-energy]-b[a-realistic-reactor]-b[breeder]", + place_result = "realistic-reactor-breeder", + stack_size = 10, + }, + -- Cooling Tower + { + type = "item", + name = "rr-cooling-tower", + icon = "__RealisticReactors__/graphics/icons/cooling-tower.png", + icon_size = 32, + subgroup = "realistic-reactors-energy", + order = "f[nuclear-energy]-d[cooling-tower]", + place_result = "rr-cooling-tower", + stack_size = 10, + }, + -- Sarcophagus + { + type = "item", + name = "reactor-sarcophagus", + icon = "__RealisticReactors__/graphics/icons/sarcophagus2.png", + icon_size = 32, + subgroup = "realistic-reactors-energy", + order = "f[nuclear-energy]-s[sarcophagus]", + place_result = "reactor-sarcophagus", + stack_size = 1, + }, + -- Interface Blueprint Placables + { + type = "item", + name = "realistic-reactor-interface", + icon = "__RealisticReactors__/graphics/icons/nuclear-reactor-interface.png", + icon_size = 32, + flags = {"hidden","primary-place-result","only-in-cursor"}, + subgroup = "realistic-reactors-energy", + order = "z[nuclear-energy]-i[interface]-a[reactor]-a[normal]", + place_result = "realistic-reactor-interface", + stack_size = 50, + }, + { + type = "item", + name = "realistic-breeder-interface", + icon = "__RealisticReactors__/graphics/icons/breeder-interface.png", + icon_size = 32, + flags = {"hidden","primary-place-result","only-in-cursor"}, + subgroup = "realistic-reactors-energy", + order = "z[nuclear-energy]-i[interface]-a[reactor]-b[breeder]", + place_result = "realistic-breeder-interface", + stack_size = 50, + }, + + -- Dummy Fuel Cell for Reactor Core + { + type = "item", + name = "rr-dummy-fuel-cell", + flags = {"hidden","hide-from-bonus-gui","hide-from-fuel-tooltip"}, + icon = "__base__/graphics/icons/uranium-fuel-cell.png", + icon_size = 32, + subgroup = "intermediate-product", + order = "r[uranium-processing]-a[uranium-fuel-cell]", + fuel_category = "nuclear", + burnt_result = "used-up-uranium-fuel-cell", + fuel_value = "9223372035GJ", + stack_size = 50, + }, + +} diff --git a/prototypes/lamps.lua b/prototypes/lamps.lua new file mode 100644 index 0000000..491e335 --- /dev/null +++ b/prototypes/lamps.lua @@ -0,0 +1,177 @@ +--red_light = { +-- type = "lamp", +-- name = "rr-red-light", +-- order = "z", +-- alert_icon_scale =0, +-- icon = "__RealisticReactors__/graphics/lamps/red.png", +-- icon_size = 32, +-- collision_mask={"layer-13"}, +-- flags = {"placeable-neutral", "player-creation", "placeable-off-grid","not-deconstructable"}, +-- max_health = 100, +-- corpse = "small-remnants", +-- --collision_box = {{-1.4, -1.9}, {1.4, 1.4}}, +0,62 -0,6 +-- collision_box = {{-0.78, -2.1}, {2.02, 0.8}}, +-- --selection_box = {{-0.78, -2.1}, {2.02, 0.8}}, +-- vehicle_impact_sound = { filename = "__base__/sound/car-metal-impact.ogg", volume = 0.65 }, +-- energy_source ={ +-- type = "electric", +-- usage_priority = "lamp", +-- render_no_power_icon = false , +-- render_no_network_icon = false,}, +-- energy_usage_per_tick = "5KW", +-- darkness_for_all_lamps_on = 0.00001, +-- darkness_for_all_lamps_off = 0, +-- light = {intensity = 0.6, size = 3, color = {r=1.0, g=0.0, b=0.0}}, +-- light_when_colored = {intensity = 1, size = 6, color = {r=1.0, g=1.0, b=1.0}}, +-- glow_size = 2, +-- glow_color_intensity = 0.135, +-- picture_off = +-- { +-- layers = +-- { +-- { +-- filename = "__RealisticReactors__/graphics/transparent32.png", +-- priority = "high", +-- width = 32, +-- height = 32, +-- frame_count = 1, +-- axially_symmetrical = false, +-- direction_count = 1, +-- --shift = {-0.62, 0.6} +-- }, +-- } +-- }, +-- picture_on = +-- { +-- filename = "__RealisticReactors__/graphics/transparent32.png", +-- priority = "high", +-- width = 32, +-- height = 32, +-- frame_count = 1, +-- axially_symmetrical = false, +-- direction_count = 1, +-- }, +-- }----------------------------------------------------------------------------------------------------------------------------------------------- +local function setfilename(picture, filename) + if picture.filename then + picture.filename = filename + end + for _,layer in pairs(picture.layers or {}) do + setfilename(layer, filename) + end +end + + +local orange_light = { + type = "explosion", + name = "rr-red-light", + flags = {"not-on-map", "placeable-off-grid"}, + animations = + { + { + filename = "__RealisticReactors__/graphics/transparent32.png", + priority = "high", + width = 1, + height = 1, + frame_count = 255, + line_length = 32, + shift = {-0.56, -0.96}, + animation_speed = 0.0000001 + } + }, + light = {intensity = 0.6, size = 1, color = {r=1.0, g=0.7, b=0.0}}, +} +local orange_lamp = { + type = "simple-entity-with-force", + name = "rr-red-lamp", + flags = {"placeable-neutral", "player-creation", "placeable-off-grid","not-deconstructable"}, + render_layer = "object", + collision_mask={"layer-13"}, + icon = "__base__/graphics/icons/steel-chest.png", + icon_size = 32, + order = "s-e-w-f", + max_health = 100, + corpse = "small-remnants", + picture = {layers={ + { + filename = "__RealisticReactors__/graphics/lamps/red.png", + priority = "extra-high", + width = 32, + height = 32, + scale = 0.41, --0.3542 + --shift = {-0.5, -0.5} + }, + { + filename = "__RealisticReactors__/graphics/lamps/red.png", + priority = "extra-high", + width = 32, + height = 32, + scale = 0.41, --0.3542 + --shift = {-0.5, -0.5} + draw_as_light = true, + }, + }} +} + + +yellow_light = table.deepcopy(orange_light) +yellow_light.name="rr-yellow-light" +yellow_light.light = {intensity = 0.6, size = 1, color = {r=1.0, g=1.0, b=0.0}} +--yellow_light.icon = "__RealisticReactors__/graphics/lamps/yellow.png" + +yellow_lamp = table.deepcopy(orange_lamp) +yellow_lamp.name="rr-yellow-lamp" +setfilename(yellow_lamp.picture, "__RealisticReactors__/graphics/lamps/yellow.png") + + + +green_light = table.deepcopy(orange_light) +green_light.name="rr-green-light" +green_light.light = {intensity = 0.6, size = 1, color = {r=0.0, g=1.0, b=0.0}} +--green_light.icon = "__RealisticReactors__/graphics/lamps/green.png" + +green_lamp = table.deepcopy(orange_lamp) +green_lamp.name="rr-green-lamp" +setfilename(green_lamp.picture, "__RealisticReactors__/graphics/lamps/green.png") + + + +red_light = table.deepcopy(orange_light) +red_light.name="rr-black-light" +red_light.light = {intensity = 0.6, size = 1, color = {r=1, g=0, b=0}} +--orange_light.icon = "__RealisticReactors__/graphics/lamps/black.png" + +red_lamp = table.deepcopy(orange_lamp) +red_lamp.name="rr-black-lamp" +setfilename(red_lamp.picture, "__RealisticReactors__/graphics/lamps/black.png") + +ruin_glow = table.deepcopy(orange_light) +ruin_glow.name = "rr-ruin-glow" +ruin_glow.light = {intensity = 0.22, size = 6.5, shift = {0.0, 0.0}, color = {r = 0.35, g = 0.8, b = 1.0}} + + +ruin_glow.animations = { + { + filename = "__RealisticReactors__/graphics/transparent32.png", + priority = "high", + width = 1, + height = 1, + frame_count = 255, + line_length = 32, + --shift = {-0.56, -0.96}, + animation_speed = 0.0000001, + }, +} + + +data:extend{ +red_light, +red_lamp, +yellow_light, +yellow_lamp, +green_light, +green_lamp, +orange_light, +orange_lamp, +ruin_glow, +} diff --git a/prototypes/patch_other_mods.lua b/prototypes/patch_other_mods.lua new file mode 100644 index 0000000..a443daf --- /dev/null +++ b/prototypes/patch_other_mods.lua @@ -0,0 +1,60 @@ +local Setting = require "scripts.setting" + + + do local recipe = data.raw.recipe["mixed-oxide"] + if recipe and recipe.icon == "__Clowns-Nuclear__/graphics/icons/nuclear-fuel-mixed-oxide.png" then + data:extend({{ + type = "item", + name = "rr-clowns-mox-cell", + icon = "__RealisticReactors__/graphics/icons/mox_fuel_cell.png", + icon_size = 32, + --flags = {"goes-to-main-inventory"}, + subgroup = "intermediate-product", + order = "r[uranium-processing]-a[uranium-fuel-cell]", + fuel_category = "nuclear", + burnt_result = "used-up-uranium-fuel-cell", + fuel_value = "7GJ", + stack_size = 50 + }}) + recipe.icon = "__RealisticReactors__/graphics/icons/clowns_mox_recipe.png" + recipe.results[1].name="rr-clowns-mox-cell" +end end + + do local recipe = data.raw.recipe["MOX-fuel-reprocessing"] + if recipe +and recipe.results +and recipe.results[2] +and recipe.results[2].amount > 3 then + recipe.results[2].amount = 3 +end end + + + +do + local energy_items = mods["base"] and { + "heat-pipe", + "heat-exchanger", + "steam-turbine", + "nuclear-reactor", + } or {} + if mods["PlutoniumEnergy"] then table.insert(energy_items, "MOX-reactor") end + + for _,name in ipairs(energy_items) do + data.raw.item[name].subgroup = "realistic-reactors-energy" + end +end + + + +if mods["base"] and Setting.startup("disable-vanilla-reactor") then + data.raw.recipe["nuclear-reactor"].hidden = true + data.raw.reactor["nuclear-reactor"].minable = {mining_time = 1.5, result = "realistic-reactor"} + local effects = data.raw.technology["nuclear-power"].effects + for i,effect in ipairs(effects) do + if effect.recipe == "nuclear-reactor" and effect.type == "unlock-recipe" then + table.remove(effects, i) + break + end + end +end + diff --git a/prototypes/recipes.lua b/prototypes/recipes.lua new file mode 100644 index 0000000..09ac0e1 --- /dev/null +++ b/prototypes/recipes.lua @@ -0,0 +1,142 @@ +data:extend{ + +-- RECIPE CATEGORIES -- +-- + +{ + type = "recipe-category", + name = "steaming", +}, +{ + type = "recipe-category", + name = "water-cooling", +}, + + +-- ITEM RECIPIES -- +-- +-- Nuclear Reactor +{ + + type = "recipe", + name = "realistic-reactor", + enabled = false, + energy_required = 20, + ingredients = { + {"concrete", 500}, + {"steel-plate", 500}, + {"advanced-circuit", 500}, + {"copper-plate", 500}, + {"effectivity-module-2", 3}, + }, + result = "realistic-reactor", +}, +-- Breeder Reactor +{ + type = "recipe", + name = "breeder-reactor", + enabled = false, + energy_required = 20, + ingredients = { + {"concrete", 500}, + {"steel-plate", 500}, + {"advanced-circuit", 500}, + {"copper-plate", 500}, + {"productivity-module-3", 3}, + }, + result = "breeder-reactor", +}, + +-- Cooling Tower +{ + type = "recipe", + name = "rr-cooling-tower", + enabled = false, + energy_required = 20, + ingredients = { + {"concrete", 200}, + {"steel-plate", 100}, + {"pipe", 100}, + {"pump", 10}, + }, + result = "rr-cooling-tower", +}, + +-- Sarcophagus +{ + type = "recipe", + name = "reactor-sarcophagus", + enabled = false, + energy_required = 100, + ingredients = { + {"concrete", 1000}, + {"steel-plate", 600}, + {"pipe", 50}, + {"pump", 10}, + {"advanced-circuit", 100}, + }, + result = "reactor-sarcophagus", +}, + + +-- OTHER RECIPIES +-- + +-- Cooling tower water cooling recipe +{ + type = "recipe", + name = "water-cooling", + category = "water-cooling", + enabled = true, + hidden = true, + energy_required = 0.5, + ingredients = { + {type="fluid", name="water", amount=500}, + }, + results = { + {type="fluid", name="water", amount=480}, + }, + icon = "__base__/graphics/icons/fluid/water.png", + icon_size = 32, + subgroup = "fluid-recipes", + order = "z", +}, +{ + type = "recipe", + name = "steam-cooling", + category = "water-cooling", + enabled = true, + hidden = true, + energy_required = 0.7, + ingredients = { + {type="fluid", name="steam", amount=300}, + }, + results = { + {type="fluid", name="water", amount=50}, + }, + icon = "__base__/graphics/icons/fluid/water.png", + icon_size = 32, + subgroup = "fluid-recipes", + order = "z", +}, +-- Cooling tower steam dummy recipe +{ + type = "recipe", + name = "rr-cooling-tower-steam", + category = "steaming", + enabled = true, + hidden = true, + energy_required = 600, + ingredients = { + {type="fluid", name="water", amount=0.1}, + }, + results = { + {type="fluid", name="water", amount=0}, + }, + icon = "__base__/graphics/icons/fluid/water.png", + icon_size = 32, + subgroup = "fluid-recipes", + order = "z", +} + +} diff --git a/prototypes/signals.lua b/prototypes/signals.lua new file mode 100644 index 0000000..5b962ba --- /dev/null +++ b/prototypes/signals.lua @@ -0,0 +1,205 @@ + BLUE_BACKGROUND = "__RealisticReactors__/graphics/icons/signal_blue.png" + RED_BACKGROUND = "__RealisticReactors__/graphics/icons/signal_red_background.png" + GREEN_BACKGROUND = "__RealisticReactors__/graphics/icons/signal_green_background.png" +YELLOW_BACKGROUND = "__RealisticReactors__/graphics/icons/signal_yellow_background.png" + GREY_BACKGROUND = "__RealisticReactors__/graphics/icons/signal_grey_background.png" +ORANGE_BACKGROUND = "__RealisticReactors__/graphics/icons/signal_orange_background.png" + +data:extend{ + +-- SIGNAL SUBGROUP -- +-- +{ + type = "item-subgroup", + name = "reactor-signals", + group = "signals", + order = "f" +}, + + +-- SIGNALS -- +-- + +-- signal-reactor-core-temp +{ + type = "virtual-signal", + name = "signal-reactor-core-temp", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_core_temp.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-a" +}, + +--signal-reactor-power-output +{ + type = "virtual-signal", + name = "signal-reactor-power-output", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_reactor_power_output.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-b" +}, + +--signal-reactor-electric-power +{ + type = "virtual-signal", + name = "signal-reactor-electric-power", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_reactor_electric_power.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-d" +}, + +-- signal-reactor-efficiency +{ + type = "virtual-signal", + name = "signal-reactor-efficiency", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_reactor_efficiency.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-c" +}, + +-- signal-breeder_cell-bonus +{ + type = "virtual-signal", + name = "signal-reactor-cell-bonus", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_cell_bonus.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-d" +}, + +-- signal-coolant-amount" +{ + type = "virtual-signal", + name = "signal-coolant-amount", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_coolant_amount.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-e" +}, + +-- signal-state-stopped +{ + type = "virtual-signal", + name = "signal-state-stopped", + icons = + { + {icon = GREY_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_state.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-f" +}, + +-- signal-state-starting +{ + type = "virtual-signal", + name = "signal-state-starting", + icons = + { + {icon = YELLOW_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_state.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-g" +}, + +-- signal-state-running +{ + type = "virtual-signal", + name = "signal-state-running", + icons = + { + {icon = GREEN_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_state.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-h" +}, + +-- signal-state-scramed +{ + type = "virtual-signal", + name = "signal-state-scramed", + icons = + { + {icon = RED_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_state.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-i" +}, + +-- signal-control-start +{ + type = "virtual-signal", + name = "signal-control-start", + icons = + { + {icon = ORANGE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_control_start.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "b-a" +}, + +-- signal-control-scram +{ + type = "virtual-signal", + name = "signal-control-scram", + icons = + { + {icon = ORANGE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/signal_control_scram.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "b-c" +}, + +-- signal-neighbour-bonus +{ + type = "virtual-signal", + name = "signal-neighbour-bonus", + icons = + { + {icon = BLUE_BACKGROUND}, + {icon = "__RealisticReactors__/graphics/icons/neighbours.png"} + }, + icon_size = 32, + subgroup = "reactor-signals", + order = "a-f" +}, + +} diff --git a/prototypes/sprites_font.lua b/prototypes/sprites_font.lua new file mode 100644 index 0000000..5fee78b --- /dev/null +++ b/prototypes/sprites_font.lua @@ -0,0 +1,174 @@ +local colors = { +"r" , +"y" , +"b" , +"a" , +"ry" , +"rb" , +"ra" , +"yb" , +"ya" , +"ba" , +"rya", +"ryb" , +"rba" , +"yba" , +"ryba" } + +for _, color in pairs(colors) do + data:extend{ + { + type="sprite", + name="rr-"..color, + filename = "__RealisticReactors__/graphics/sprites/"..color..".png", + priority = "extra-high", + width = 1, + height = 1, + } + } +end + + +data:extend({ + { + type = "font", + name = "rr-small-bold", + from = "default-bold", + size = 9, + }, + { + type="sprite", + name="rr-black", + filename = "__RealisticReactors__/graphics/sprites/black.png", + priority = "extra-high", + width = 1, + height = 1, + }, + { + type="sprite", + name="rr-black-background", + filename = "__RealisticReactors__/graphics/black192x101.png", + priority = "extra-high", + width = 192, + height = 101, + flags = {"linear-magnification"}, + }, + { + type="sprite", + name="rr-button-x", + filename = "__RealisticReactors__/graphics/sprites/button_x.png", + priority = "extra-high", + width = 32, + height = 32, + }, + { + type="sprite", + name="rr-button-graph", + filename = "__RealisticReactors__/graphics/sprites/button_graph.png", + priority = "extra-high", + width = 61, + height = 32, + }, + { + type="sprite", + name="rr-button-graph-off", + filename = "__RealisticReactors__/graphics/sprites/button_graph_off.png", + priority = "extra-high", + width = 61, + height = 32, + }, + { + type="sprite", + name="rr-button-signals", + filename = "__RealisticReactors__/graphics/sprites/button_signals.png", + priority = "extra-high", + width = 90, + height = 32, + }, + { + type="sprite", + name="rr-button-signals-off", + filename = "__RealisticReactors__/graphics/sprites/button_signals_off.png", + priority = "extra-high", + width = 90, + height = 32, + }, + { + type="sprite", + name="rr-button-progress", + filename = "__RealisticReactors__/graphics/sprites/button_progress.png", + priority = "extra-high", + width = 61, + height = 32, + }, + { + type="sprite", + name="rr-button-progress-off", + filename = "__RealisticReactors__/graphics/sprites/button_progress_off.png", + priority = "extra-high", + width = 61, + height = 32, + }, + { + type="sprite", + name="rr-bonuscell-sprite", + filename = "__RealisticReactors__/graphics/icons/bonuscell_sprite.png", + priority = "extra-high", + width = 32, + height = 32, + }, + { + type="sprite", + name="rr-transparent-sprite", + filename = "__RealisticReactors__/graphics/transparent32.png", + priority = "extra-high", + width = 32, + height = 32, + }, + { + type="sprite", + name="rr-mini-sprite", + filename = "__core__/graphics/empty.png", + priority = "high", + width = 1, + height = 1, + }, +}) + +data.raw["gui-style"].default.rr_button = { + name = "rr_button", + type = "button_style", + parent = "image_tab_slot", + scalable = false, + width = 25, + height = 25, + padding = 0, + default_graphical_set = + { + filename = "__core__/graphics/gui.png", + priority = "extra-high-no-scale", + width = 36, + height = 36, + x = 111, + y = 144 + }, + hovered_graphical_set = + { + filename = "__core__/graphics/gui.png", + priority = "extra-high-no-scale", + width = 36, + height = 36, + x = 148, + y = 144 + }, + clicked_graphical_set = + { + filename = "__core__/graphics/gui.png", + priority = "extra-high-no-scale", + width = 36, + height = 36, + x = 111, + y = 0 + } +} + diff --git a/prototypes/technology.lua b/prototypes/technology.lua new file mode 100644 index 0000000..8803cfe --- /dev/null +++ b/prototypes/technology.lua @@ -0,0 +1,65 @@ +data:extend{ + { + type = "technology", + name = "breeder-reactors", + icon = "__RealisticReactors__/graphics/technology/breeder-reactors.png", + icon_size = 128, + effects = { + { + type = "unlock-recipe", + recipe = "breeder-reactor", + }, + }, + prerequisites = {"nuclear-power","productivity-module-3","military-4"}, + unit = { + ingredients = { + {"automation-science-pack", 1}, + {"logistic-science-pack", 1}, + {"chemical-science-pack", 1}, + {"military-science-pack", 1}, + {"production-science-pack", 1}, + {"utility-science-pack", 1}, + }, + time = 30, + count = 800, + }, + order = "a-h-d", + }, +} + + +do + local nuclear = data.raw.technology["nuclear-power"] + table.insert(nuclear.prerequisites, "effectivity-module-2") + local add = {} + for i,effect in ipairs(nuclear.effects) do + if effect.type == "unlock-recipe" then + if effect.recipe == "nuclear-reactor" then + table.insert(add, { + index = 1+i, + type = "unlock-recipe", + recipe = "realistic-reactor", + }) + elseif effect.recipe == "steam-turbine" then + table.insert(add, { + index = 1+i, + type = "unlock-recipe", + recipe = "rr-cooling-tower", + }) + end + end + end + + table.insert(add, { + index = 1+#add, + type = "unlock-recipe", + recipe = "reactor-sarcophagus", + }) + + for i = #add,1,-1 do + local effect = add[i] + table.insert(nuclear.effects, effect.index, effect) + effect.index = nil + end +end + diff --git a/prototypes/tips_and_tricks.lua b/prototypes/tips_and_tricks.lua new file mode 100644 index 0000000..493cb9b --- /dev/null +++ b/prototypes/tips_and_tricks.lua @@ -0,0 +1,223 @@ +data:extend( +{ + { + type = "sprite", + name = "signal_control_start", + filename = "__RealisticReactors__/graphics/tips_and_tricks/signal_start.png", + priority = "extra-high-no-scale", + width = 32, + height = 32, + flags = {"gui-icon"}, + }, + { + type = "sprite", + name = "signal_reactor_power_output", + filename = "__RealisticReactors__/graphics/tips_and_tricks/signal_power.png", + priority = "extra-high-no-scale", + width = 32, + height = 32, + flags = {"gui-icon"}, + }, + { + type = "sprite", + name = "signal_reactor_efficiency", + filename = "__RealisticReactors__/graphics/tips_and_tricks/signal_efficiency.png", + priority = "extra-high-no-scale", + width = 32, + height = 32, + flags = {"gui-icon"}, + }, + -- reactor table + { + type = "sprite", + name = "tab-r-1", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-r-1.png", + priority = "extra-high-no-scale", + width = 1414, + height = 39, + }, + { + type = "sprite", + name = "tab-r-2", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-r-2.png", + priority = "extra-high-no-scale", + width = 1414, + height = 39, + }, + { + type = "sprite", + name = "tab-r-3", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-r-3.png", + priority = "extra-high-no-scale", + width = 1414, + height = 39, + }, + { + type = "sprite", + name = "tab-r-4", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-r-4.png", + priority = "extra-high-no-scale", + width = 1414, + height = 39, + }, + { + type = "sprite", + name = "tab-r-5", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-r-5.png", + priority = "extra-high-no-scale", + width = 1414, + height = 39, + }, + -- breeder table + { + type = "sprite", + name = "tab-b-1", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-b-1.png", + priority = "extra-high-no-scale", + width = 1641, + height = 39, + }, + { + type = "sprite", + name = "tab-b-2", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-b-2.png", + priority = "extra-high-no-scale", + width = 1641, + height = 39, + }, + { + type = "sprite", + name = "tab-b-3", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-b-3.png", + priority = "extra-high-no-scale", + width = 1641, + height = 39, + }, + { + type = "sprite", + name = "tab-b-4", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-b-4.png", + priority = "extra-high-no-scale", + width = 1641, + height = 39, + }, + { + type = "sprite", + name = "tab-b-5", + filename = "__RealisticReactors__/graphics/tips_and_tricks/tab-b-5.png", + priority = "extra-high-no-scale", + width = 1641, + height = 39, + }, + +}) + + + +data:extend( +{ + { + type = "tips-and-tricks-item-category", + name = "RealisticReactors", + order = "z-[RealisticReactors]" + }, + + -- Title + { + type = "tips-and-tricks-item", + name = "RealisticReactors_Title", + category = "RealisticReactors", + tag = "[entity=realistic-reactor]", + order = "a", + is_title = true, + trigger = + { + type = "research", + technology = "nuclear-power" + }, + image = "__RealisticReactors__/graphics/tips_and_tricks/title_pic.png" + }, + + + -- How to operate a reactor + { + type = "tips-and-tricks-item", + name = "RealisticReactors_Operate", + category = "RealisticReactors", + tag = "[img=signal_control_start]", + order = "b", + indent = 1, + trigger = + { + type = "research", + technology = "nuclear-power" + }, + image = "__RealisticReactors__/graphics/tips_and_tricks/reactor_interface.png" + }, + + -- Meltdown + { + type = "tips-and-tricks-item", + name = "RealisticReactors_Meltdown", + category = "RealisticReactors", + tag = "[entity=reactor-sarcophagus]", + order = "c", + indent = 1, + trigger = + { + type = "research", + technology = "nuclear-power" + }, + image = "__RealisticReactors__/graphics/tips_and_tricks/meltdown.png" + }, + + -- Power output and efficiency + { + type = "tips-and-tricks-item", + name = "RealisticReactors_Output", + category = "RealisticReactors", + tag = "[img=signal_reactor_power_output][img=signal_reactor_efficiency]", + order = "d", + indent = 1, + trigger = + { + type = "research", + technology = "nuclear-power" + }, + }, + + -- Output Reactor + { + type = "tips-and-tricks-item", + name = "RealisticReactors_OutputReactor", + category = "RealisticReactors", + tag = "[entity=realistic-reactor]", + order = "e", + indent = 2, + trigger = + { + type = "research", + technology = "nuclear-power" + }, + image = "__RealisticReactors__/graphics/tips_and_tricks/output_reactor.png" + }, + + -- Output Breeder + { + type = "tips-and-tricks-item", + name = "RealisticReactors_OutputBreeder", + category = "RealisticReactors", + tag = "[entity=realistic-reactor-breeder]", + order = "f", + indent = 2, + trigger = + { + type = "research", + technology = "nuclear-power" + }, + image = "__RealisticReactors__/graphics/tips_and_tricks/output_breeder.png" + }, + + + +}) \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..964c075 --- /dev/null +++ b/readme.md @@ -0,0 +1,115 @@ +We introduce a nuclear reactor, a breeder reactor and a cooling tower. The nuclear reactor type has high power output while the breeder reactor type has medium power output and produces bonus materials. The thermal energy output of both reactor types as well as fuel efficiencies are dynamic and depend on operational temperature. The reactors have to be controlled with signals through the integrated circuit interface and depend on the cooling infrastructure through the Emergency Core Cooling System (ECCS) interface. Without cooling the reactor core will finally overheat and cause a meltdown. Cooling towers are designed to cool down hot water or steam coming from the ECCS or other parts of the plant. Physical layout of the power plant also plays a significant role. + + +# Reactor operation +--------------------------------------------------------------------------------------------------------------------------------------------- +#### Starting and stopping +To start a nuclear reactor insert fuel cells and send the ``Start Control Signal`` to the circuit interface of the reactor. The reactor will start up and produce heat at a certain efficiency (see the two corresponding signals on the circuit interface). +To shut down the reactor either let it run out of fuel cells or send the ``SCRAM Control Signal`` to the reactor circuit interface. +Be aware that the SCRAM process takes some time and the reactor will still produce heat until it is fully stopped. + +#### The point of maximum output and efficiency +Power output and fuel efficiency are changing dynamically depending on the reactor core temperature and the number of connected neighbor reactors. Additionally, the output of extra empty fuel cells of the breeder reactor type changes according to the temperature. To connect a reactor neighbor simply connect them with heat-pipes into one heat network. The reactors don't have to touch each other. + +There are two possible options how output and efficiency are calculated, which can be changed in the settings. +By default **Ownly's formulas** will be used. In short this means: the hotter, the better. The higher the reactor core temperature is, the higher power output and fuel efficiency will be. +The alternative **Ingo's formulas** work a little different. Power output will also increase with higher core temperature, but efficiency will have a certain maximum, after which it will start to drop quickly again. + +#### Reactor core meltdown +When the reactor core reaches 1000°C a reactor core meltdown is caused. The reactor explodes and leaves a ruin behind. That ruin produces permanent radiation around and generates radioactive clouds. These clouds pollute large areas around the reactor ruin. The radiation effect is damage to all lifeforms in proximity. To stop the radiation spreading, a sarcophagus must be built over the reactor ruin, then the radiation will slowly decay. + + +# Documentation +--------------------------------------------------------------------------------------------------------------------------------------------- +Find more details about Realistic Reactors operation [in the forum post](https://forums.factorio.com/viewtopic.php?f=93&t=56621), the [«FAQ» section](https://mods.factorio.com/mod/RealisticReactors/faq) and the ``DOCUMENTATION`` folder of the mod archive. Finally, talk to us! Our communication channel is the [«Discussion» section of the «Realistic Reactors» mod page](https://mods.factorio.com/mod/RealisticReactors/discussion). + + +# Supported languages +--------------------------------------------------------------------------------------------------------------------------------------------- +* English +* German +* Russian +* Korean + +# Supported mods +--------------------------------------------------------------------------------------------------------------------------------------------- +* [GotLag's Nuclear Fuel](https://mods.factorio.com/mod/Nuclear%20Fuel) +* [JohnTheCoolingFan's Plutonium Energy](https://mods.factorio.com/mod/PlutoniumEnergy) +* [MadClown01's Nuclear Extension](https://mods.factorio.com/mod/Clowns-Nuclear) +* [BicycleEater's True Nukes](https://mods.factorio.com/mod/True-Nukes) +* [Nuclear Operators Utilities](https://mods.factorio.com/mod/Nuclear_Operators_Utilities) +* [Realistic Heat Glow](https://mods.factorio.com/mod/Realistic_Heat_Glow) + + +# Compatibility +--------------------------------------------------------------------------------------------------------------------------------------------- +* limited compatibility with mods which modify the connections or sizes of heat-pipe entities (e.g. [Schall Pipe Scaling](https://mods.factorio.com/mod/SchallPipeScaling) and similar) – when using such mods with Realistic Reactors please make sure the vanilla heat-pipes are used for the reactor heat-pipe network +* blueprints made with RealisticReactors before version 3.0.0 need to be updated due to fixes in the reactor's combinator network interface +* please report on our mod discussion page if any incompatibilities are discovered + + +# Included materials +--------------------------------------------------------------------------------------------------------------------------------------------- +* geiger counter sounds based on the [work by Mike Koenig](http://soundbible.com/1113-Radio-Active.html), under [CC BY 3.0](https://creativecommons.org/licenses/by/3.0/) +* reactor sarcophagus graphic based on the [design by Anthony Garcellano](https://www.artstation.com/artwork/nbPvO) +* see the included license.txt file + + +# License +--------------------------------------------------------------------------------------------------------------------------------------------- +This mod was made by [dodo.the.last](https://mods.factorio.com/user/dodo.the.last), [max2344](https://mods.factorio.com/user/max2344), [IngoKnieto](https://mods.factorio.com/user/ingo), [OwnlyMe](https://mods.factorio.com/user/OwnlyMe) and published under the MIT license. + + +# Credits +--------------------------------------------------------------------------------------------------------------------------------------------- +Original idea and implementation by [IngoKnieto](https://mods.factorio.com/user/IngoKnieto) and [OwnlyMe](https://mods.factorio.com/user/OwnlyMe). +Thanks to [GotLag](https://mods.factorio.com/mods/GotLag) for the [original reactors mod](https://mods.factorio.com/mods/GotLag/Reactors), which this mod is based upon. +Thanks to [Sigma1](https://mods.factorio.com/user/Sigma1) for making the awesome cooling tower picture. +The refactored version 3 was made by [dodo.the.last](https://mods.factorio.com/user/dodo.the.last) and [max2344](https://mods.factorio.com/user/max2344) who are currently maintaining the mod. +The Korean translation was made by [x2605](https://mods.factorio.com/user/x2605). + +# Notes +--------------------------------------------------------------------------------------------------------------------------------------------- +The information on this mod page represents the state of the current release and might be updated without prior notification or public announcement. Please refer to the «Documentation» section of this page for details. + + +# FAQ +--------------------------------------------------------------------------------------------------------------------------------------------- + +# Found a bug, issue or a missing feature? +--------------------------------------------------------------------------------------------------------------------------------------------- +Please report bugs or issues to our [mod discussion page](https://mods.factorio.com/mod/RealisticReactors/discussion) in accordance with [factorio bug report rules](https://forums.factorio.com/viewtopic.php?f=7&t=3638) or talk with us about the desired features. + + +# Why are blueprints placed with missing combinator network connections? +--------------------------------------------------------------------------------------------------------------------------------------------- +Blueprints made with RealisticReactors before version 3.0.0 need to be updated due to fixes in the reactor's combinator network interface. When using blueprints or copy/paste in the game editor, the combinator connections aren't placed and need to be added manually, probably because not all events are sent in the editor. + + +# How to operate a reactor? +--------------------------------------------------------------------------------------------------------------------------------------------- +To start a nuclear reactor insert fuel cells and send the ``Start Control Signal`` to the circuit interface of the reactor. The reactor will start up and produce heat at a certain efficiency (see the two corresponding signals on the circuit interface). +To shut down the reactor either let it run out of fuel cells or send the ``SCRAM Control Signal`` to the reactor circuit interface. +Be aware that the SCRAM process takes some time and the reactor will still produce heat until it is fully stopped. + +# What is the point of maximum output and efficiency? +--------------------------------------------------------------------------------------------------------------------------------------------- +Power output and fuel efficiency are changing dynamically depending on the reactor core temperature and the number of connected neighbor reactors. Additionally, the output of extra empty fuel cells of the breeder reactor type changes according to the temperature. To connect a reactor neighbor simply connect them with heat-pipes into one heat network. The reactors don't have to touch each other. + +There are two possible options how output and efficiency are calculated, which can be changed in the settings. +By default **Ownly's formulas** will be used. In short this means: the hotter, the better. The higher the reactor core temperature is, the higher power output and fuel efficiency will be. +The alternative **Ingo's formulas** work a little different. Power output will also increase with higher core temperature, but efficiency will have a certain maximum, after which it will start to drop quickly again. + + +# What is a reactor core meltdown? +--------------------------------------------------------------------------------------------------------------------------------------------- +When the reactor core reaches 1000°C a reactor core meltdown is caused. The reactor explodes and leaves a ruin behind. That ruin produces permanent radiation around and generates radioactive clouds. These clouds pollute large areas around the reactor ruin. The radiation effect is damage to all lifeforms in proximity. To stop the radiation spreading, a sarcophagus must be built over the reactor ruin, then the radiation will slowly decay. + + +# Where do I find more information? +--------------------------------------------------------------------------------------------------------------------------------------------- +Find [more details in the forum post](https://forums.factorio.com/viewtopic.php?f=93&t=56621) and the ``DOCUMENTATION`` folder of the mod archive. + +Finally, talk to us! + + diff --git a/scripts/const.lua b/scripts/const.lua new file mode 100644 index 0000000..e648186 --- /dev/null +++ b/scripts/const.lua @@ -0,0 +1,114 @@ +-- all globals + +-- CONTROL DECLARATION + +REMOTE_INTERFACE_NAME = "rr-interface" + +-- VARIABLE DECLARATION + +TICKS_PER_UPDATE = 15 + -- each reactor and cooling tower gets updated once every 15 ticks (60 ticks = 1 s) +CHANGE_MULTIPLIER = 0.2 + -- used to multiply the temperature change + -- CHANGE_MULTIPLIER and TICKS_PER_UPDATE work together and are balanced: + -- 0.2 CHANGE_MULTIPLIER = 15 TICKS_PER_UPDATE +REACTOR_MASS = 6000 --increased from 4000 because of the reactor output nerf + -- used to calculate temperature changes when emergency cooling is used + -- the mass is an estimate best guess based on many tries and errors +BONUS_CELL_MULTIPLIER = 0.5 + -- multiplier for breeder bonus cell production. + -- BONUS_CELL_MULTIPLIER=1 and bonus_cell_Production=100 means 1 additional empty cell per minute +POWER_USAGE_STARTING=3600000 -- 3600 KW +POWER_USAGE_INTERFACE=200000 -- 200 KW +POWER_USAGE_COOLING=1000000 -- 1 MW when when reactor was cooled (static) or 1 MW per 20 MW cooling (non-static) + -- electric power usage of the reactor +TEMPERATURE = 15 -- standard of factorio +DYING_REACTOR_CORE_TEMPERATURE = 1000 + +-- entity names +REACTOR_ENTITY_NAME = "realistic-reactor-normal" +BREEDER_ENTITY_NAME = "realistic-reactor-breeder" +REACTOR_POWER_NAME = "realistic-reactor-power-normal" +BREEDER_POWER_NAME = "realistic-reactor-power-breeder" +REACTOR_RUIN_NAME = "reactor-ruin" +BREEDER_RUIN_NAME = "breeder-ruin" +REACTOR_INTERFACE_ENTITY_NAME = "realistic-reactor-interface" +BREEDER_INTERFACE_ENTITY_NAME = "realistic-breeder-interface" +SARCOPHAGUS_ENTITY_NAME="reactor-sarcophagus" +BOILER_ENTITY_NAME = "realistic-reactor-eccs" +TOWER_ENTITY_NAME = "rr-cooling-tower" +STEAM_ENTITY_NAME = "rr-cooling-tower-steam" +RUIN_SMOKE_NAME = "ruin-smoke" +-- signal names +SIGNAL_CORE_TEMP = {type="virtual", name="signal-reactor-core-temp"} +SIGNAL_STATE_STOPPED = {type="virtual", name="signal-state-stopped"} +SIGNAL_STATE_STARTING = {type="virtual", name="signal-state-starting"} +SIGNAL_STATE_RUNNING = {type="virtual", name="signal-state-running"} +SIGNAL_STATE_SCRAMED = {type="virtual", name="signal-state-scramed"} +SIGNAL_CONTROL_START = {type="virtual", name="signal-control-start"} +SIGNAL_CONTROL_SCRAM = {type="virtual", name="signal-control-scram"} +SIGNAL_COOLANT_AMOUNT = {type="virtual", name="signal-coolant-amount"} +--SIGNAL_COOLANT_TEMP = {type="virtual", name="signal-coolant-temperature"} +SIGNAL_URANIUM_FUEL_CELLS = {type="item", name="uranium-fuel-cell"} +SIGNAL_USED_URANIUM_FUEL_CELLS = {type="item", name="used-up-uranium-fuel-cell"} +SIGNAL_REACTOR_POWER_OUTPUT = {type="virtual", name="signal-reactor-power-output"} +SIGNAL_REACTOR_EFFICIENCY = {type="virtual", name="signal-reactor-efficiency"} +SIGNAL_REACTOR_CELL_BONUS = {type="virtual", name="signal-reactor-cell-bonus"} +SIGNAL_REACTOR_ELECTRIC_POWER = {type="virtual", name="signal-reactor-electric-power"} +SIGNAL_NEIGHBOUR_BONUS = {type="virtual", name="signal-neighbour-bonus"} + + +-- NAMESPACE DECLARATION + +RUIN_NAME = { + [REACTOR_ENTITY_NAME] = REACTOR_RUIN_NAME, + [BREEDER_ENTITY_NAME] = BREEDER_RUIN_NAME, +} + +POWER_NAME = { + [REACTOR_ENTITY_NAME] = REACTOR_POWER_NAME, + [BREEDER_ENTITY_NAME] = BREEDER_POWER_NAME, +} + +E2I_NAME = { + [REACTOR_ENTITY_NAME] = REACTOR_INTERFACE_ENTITY_NAME, + [BREEDER_ENTITY_NAME] = BREEDER_INTERFACE_ENTITY_NAME, +} + +I2E_NAME = { + [REACTOR_INTERFACE_ENTITY_NAME] = REACTOR_ENTITY_NAME, + [BREEDER_INTERFACE_ENTITY_NAME] = BREEDER_ENTITY_NAME, +} + +R2E_NAME = { + [REACTOR_RUIN_NAME] = REACTOR_ENTITY_NAME, + [BREEDER_RUIN_NAME] = BREEDER_ENTITY_NAME, +} + + + + +N = defines.direction.north +S = defines.direction.south +E = defines.direction.east +W = defines.direction.west +D = {N,E,S,W} -- like css +R = {[N]=S,[S]=N,[E]=W,[W]=E} +O = { + [N] = {x= 0, y=-1}, + [S] = {x= 0, y= 1}, + [W] = {x=-1, y= 0}, + [E] = {x= 1, y= 0}, +} + +V = {x='y',y='x'} -- reverse vector +A = { -- areal lookup + left_top = {x=W, y=N}, + right_bottom = {x=E, y=S}, +} +M = { + [W] = {left_top='x'}, + [N] = {left_top='y'}, + [E] = {right_bottom='x'}, + [S] = {right_bottom='y'}, +} diff --git a/scripts/entity/fallout.lua b/scripts/entity/fallout.lua new file mode 100644 index 0000000..4f74a38 --- /dev/null +++ b/scripts/entity/fallout.lua @@ -0,0 +1,163 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local Setting = require(rroot .. "setting") +local remove_reactor_ruin = require(rpath .. "ruin").remove +local next_delay = require(rpath .. "util").next_delay +local fx = require(rroot .. "fx") +local periodic_pollution = fx.periodic_pollution +local circular_radiation = fx.circular_radiation + + +-- FIXME merge with code from TrueNukes for fallout +-- TODO add radiation damage in diverse form + + +local function create_cloud(surface, position) -- create fallout cloud + return surface.create_entity{ + name = "fallout-cloud", + position = position, + force = "radioactivity-strong", + } +end + +local function create_radiation(cloud) + -- FIXME use created_effect in data from entity to trigger script to create periodic_pollution + periodic_pollution(cloud, 0.05) + cloud.surface.create_entity{ + name = "permanent-radiation", + position = cloud.position, + force = "radioactivity", + } +end + +local function create_fallout(surface, position, tick) + local fallout_size = 4 + local id = surface.index + local fallout = global.fallout[id] + if not fallout then + fallout = {id=id, surface=surface, clouds={}, positions={}} + global.fallout[id] = fallout + end + fallout.positions[tick] = position + table.insert(fallout.clouds, create_cloud(surface, position)) + + --create delayed event for fallout around the dead_reactor_core + local delay = next_delay(tick, 2) + global.delayed_fallout[tick+delay] = { + surface = surface, + position = position, + min_radius = 0, + fallout_size = math.floor(fallout_size/2), + } + + local delay = next_delay(tick, 180) + global.delayed_fallout[tick+delay] = { + surface = surface, + position = position, + min_radius = 0, + fallout_size = fallout_size, + } +end + +local function is_generating_clouds(tick) + return tick + 60 * Setting.startup("clouds-generation") > game.tick +end + +local function on_tick(tick) + + -- TODO ruins should have 100% radioactivity resistance + -- using optimized-particle regular_trigger_effect could help creating clouds over time + + -- radiation around reactor ruin + if game.tick % 180 == 0 then -- max 2x radiation + for _,ruin in pairs(global.ruins) do + local entity = ruin.entity + if entity and entity.valid then + periodic_pollution(entity,0.1) + if ruin.tick + (Setting.protoduration("fallout")*30) < game.tick then + + ruin.tick = game.tick + ruin.spread = ruin.spread + 1 + circular_radiation(entity.surface,entity.position,0,math.min(16,ruin.spread)) + end + end + end + end + + -- radioactive cloud + if tick % 600 == 0 then + + -- FIXME TODO create clouds with trigger effects + + for _,ruin in pairs(global.ruins) do + if is_generating_clouds(ruin.meltdown_tick) then + -- create fallout cloud + local fallout = ruin.entity.valid and global.fallout[ruin.entity.surface.index] + if fallout then + table.insert(fallout.clouds, create_cloud(ruin.entity.surface, ruin.entity.position)) + end + else + -- disable steamer entity of ruin + ruin.steam.active = false + end + end + + for _,fallout in pairs(global.fallout) do + if fallout.surface.valid then + local remove = {} + for tick,position in pairs(fallout.positions) do + if is_generating_clouds(tick) then + table.insert(fallout.clouds, create_cloud(fallout.surface, position)) + else + table.insert(remove, tick) + end + end + --remove cloud creating entry + for _,tick in ipairs(remove) do + fallout.positions[tick] = nil + end + end + end + + end + + -- fallout radiation (from clouds) + if tick % 79 == 0 then + local remove = {} + for id,fallout in pairs(global.fallout) do + if not fallout.surface.valid or #fallout.clouds + table_size(fallout.positions) == 0 then + table.insert(remove, id) + else + local index = global.random(1,#fallout.clouds) + local cloud = fallout.clouds[index] + if cloud and cloud.valid then + create_radiation(cloud) + else + table.remove(fallout.clouds, index) + if #fallout.clouds + table_size(fallout.positions) == 0 then + table.insert(remove, id) + end + end + end + end + for _,id in ipairs(remove) do + global.fallout[id] = nil + end + end + + -- FIXME this can be possibly merged with projectile logic of TrueNukes + + -- delayed fallout + local delay = global.delayed_fallout[tick] + if delay then + circular_radiation(delay.surface, delay.position, delay.min_radius or 0, delay.fallout_size) + global.delayed_fallout[tick] = nil + end +end + + +return { -- exports + tick = on_tick, + create = create_fallout, + cloud = create_cloud, +} diff --git a/scripts/entity/interface.lua b/scripts/entity/interface.lua new file mode 100644 index 0000000..75fb964 --- /dev/null +++ b/scripts/entity/interface.lua @@ -0,0 +1,121 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local set3d = require(rroot .. "heat.util").set3d +local get3d = require(rroot .. "heat.util").get3d +local isempty = require(rroot .. "util").isempty +local util = require(rpath .. "util") +local find_nuclear_entity = util.find_nuclear_entity +local find_nuclear_ghost = util.find_nuclear_ghost + + + + +local function store_interface(ghost) + local x,y,z = ghost.position.x,ghost.position.y,ghost.surface.index +-- log(string.format("added interface ghost at x=%s y=%s z=%s", x,y,z)) + local definitions = {} + for _,target in pairs(ghost.circuit_connection_definitions) do + target.name = target.target_entity.name == "entity-ghost" and target.target_entity.ghost_name or target.target_entity.name + target.position = target.target_entity.position + target.target_entity = nil + table.insert(definitions, target) + end + set3d(global.interfaces, z,x,y-1, { -- position of reactor + name = ghost.ghost_name, + position = ghost.position, + definitions = definitions, + }) +end + +local function get_interface_definitions(x,y,z) + local ghost = get3d(global.interfaces, z,x,y) + if not ghost then return nil end + --- now its used up + set3d(global.interfaces, z,x,y, nil) + return ghost +end + + +local function create_interface(surface, options) + local x,y,z = options.position.x,options.position.y,surface.index -- of reactor + options.position = {x=x,y=y+1} + local ghost = get_interface_definitions(x,y,z) + local interface = surface.create_entity(options) + if not ghost then return interface end + for _,target in pairs(ghost.definitions) do + local entity = find_nuclear_entity(surface, target.position, target.name) + or find_nuclear_ghost (surface, target.position, target.name) + if entity then + target.name = nil + target.position = nil + target.target_entity = entity + interface.connect_neighbour(target) + end + end + return interface +end + + +local function get_nuclear_entity(surface, position) + return find_nuclear_entity(surface, position, REACTOR_ENTITY_NAME) + or find_nuclear_entity(surface, position, BREEDER_ENTITY_NAME) +end + +local function get_nuclear_ghost(surface, position) + return find_nuclear_ghost(surface, position, REACTOR_ENTITY_NAME) + or find_nuclear_ghost(surface, position, BREEDER_ENTITY_NAME) +end + + +local function find_interface(surface, position, name) + return find_nuclear_entity(surface, {position.x,position.y+1}, name) +end + +local function find_interface_ghost(surface, position, name) + return find_nuclear_ghost(surface, {position.x,position.y+1}, name) +end + + +local function get_interface(surface, position) + return find_interface(surface, position, REACTOR_INTERFACE_ENTITY_NAME) + or find_interface(surface, position, BREEDER_INTERFACE_ENTITY_NAME) +end + +local function get_interface_ghost(surface, position) + return find_interface_ghost(surface, position, REACTOR_INTERFACE_ENTITY_NAME) + or find_interface_ghost(surface, position, BREEDER_INTERFACE_ENTITY_NAME) +end + +local function on_tick(tick) + -- FIXME this should never happen + if tick % 9000 == 0 then + local is = global.interfaces + for x,xs in pairs(is) do + local surface = game.surfaces[x] -- actually z :P + for y,ys in pairs(xs) do + for z,ghost in pairs(ys) do + if not find_nuclear_ghost(surface, ghost.position, ghost.name) then + log(string.format("had to remove dead ghost from x=%s y=%s z=%s", x,y,z)) + game.print("[BUG] Found dead ghost. Please report and send log file!") + ys[z] = nil -- cleanup dead ghosts, rofl + end + end + if isempty(ys) then xs[y] = nil end + end + if isempty(xs) then is[x] = nil end + end + end +end + + +return { -- exports + tick = on_tick, + create = create_interface, + remember = store_interface, + definitions = get_interface_definitions, + get = get_interface, + get_ghost = get_interface_ghost, + find = find_interface, + find_ghost = find_interface_ghost, +} + diff --git a/scripts/entity/reactor.lua b/scripts/entity/reactor.lua new file mode 100644 index 0000000..d615524 --- /dev/null +++ b/scripts/entity/reactor.lua @@ -0,0 +1,939 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local Setting = require(rroot .. "setting") +local ruin = require(rpath .. "ruin") +local fallout = require(rpath .. "fallout") +local network = require(rroot .. "heat.network") +local FORMULA = require(rroot .. "formulas") +local msg = require(rroot .. "util").msg +local util = require(rpath .. "util") +local find_nuclear_ghost = util.find_nuclear_ghost +local create_warning = util.create_warning + + +local function Signals() return { parameters = { -- signals for interface + ["core_temperature" ] = {signal=SIGNAL_CORE_TEMP, count=TEMPERATURE, index=1}, + ["state_stopped" ] = {signal=SIGNAL_STATE_STOPPED, count=1, index=2}, + ["state_starting" ] = {signal=SIGNAL_STATE_STARTING, count=0, index=3}, + ["state_running" ] = {signal=SIGNAL_STATE_RUNNING, count=0, index=4}, + ["state_scramed" ] = {signal=SIGNAL_STATE_SCRAMED, count=0, index=5}, + ["coolant-amount" ] = {signal=SIGNAL_COOLANT_AMOUNT, count=0, index=6}, + ["power-output" ] = {signal=SIGNAL_REACTOR_POWER_OUTPUT, count=0, index=7}, + ["uranium-fuel-cells"] = {signal=SIGNAL_URANIUM_FUEL_CELLS, count=0, index=8}, + --["used-uranium-fuel-cells"] = {signal=SIGNAL_USED_URANIUM_FUEL_CELLS, count=0, index=9}, + ["efficiency" ] = {signal=SIGNAL_REACTOR_EFFICIENCY, count=0, index=10}, + ["cell-bonus" ] = {signal=SIGNAL_REACTOR_CELL_BONUS, count=0, index=11}, + ["electric-power" ] = {signal=SIGNAL_REACTOR_ELECTRIC_POWER, count=0, index=12}, + ["neighbour-bonus" ] = {signal=SIGNAL_NEIGHBOUR_BONUS, count=0, index=13}, +} } end + + +-- adds core, eccs, lamp, light, electric interface to the reactor core when it is build +local function add_reactor(entity,interface) + local surface = entity.surface + local p,f = entity.position,entity.force + local reactor_core = surface.create_entity{ + name = "realistic-reactor-1", + position = p, + force = f, + } + --reactor is actually the reactor core + reactor_core.destructible = false + --logging("---------------------------------------------------------------") + --logging("Adding new reactor: "..entity.name) + --logging("Reactor core ID: " .. reactor_core.unit_number) + --interface.operable = false + interface.destructible = false + interface.minable = false + + --logging("Reactor ID: " .. interface.unit_number) + + --add the eccs + local eccs = surface.create_entity{ + name = BOILER_ENTITY_NAME, + position = p, + force = f, + } + eccs.operable = false + eccs.destructible = false + eccs.minable = false + + --add power entity + local power = surface.create_entity{ + name = POWER_NAME[entity.name], + position = p, + force = f, + } + power.destructible = false +-- power.energy = math.max(0,(1.5-Setting.map("energy-consumption-multiplier"))) * entity.electric_buffer_size + --add light + local reactor_lamp = surface.create_entity{ + name = "rr-black-lamp", + position = {p.x+0.017,p.y+0.88}, + force = f, + } + local reactor_light = surface.create_entity{ + name = "rr-black-light", + position = {p.x+0.017,p.y+0.88}, + force = f, + } + reactor_lamp.destructible = false + reactor_light.destructible = false + + -- reactor is not active when it is build (state=stopped) + reactor_core.active = false + + --max power for gui + if Setting.ingos_formula() then + reactor_max_power = 123 + reactor_max_efficiency = 200 + elseif Setting.ownlys_formula() then + reactor_max_power = 250 + reactor_max_efficiency = 210 + else + error("formula missing") + end + local reactor = { + id = entity.unit_number, -- ID of the reactor (doesn't change) + core_id = reactor_core.unit_number, -- ID of the core (changes when core is replaced) + core = reactor_core, -- core entity + interface = interface, -- interface entity + eccs = eccs, -- eccs entity + power = power, -- power entity + entity = entity, -- displayer entity + position = reactor_core.position, -- core position = reactor position + state = 0, -- reactor state + state_active_since = game.tick, -- state begin + neighbours = 1, -- number of connected reactors and itself + control = interface.get_or_create_control_behavior(), -- control behaviour for interface signals + efficiency = 0, -- reactor efficiency + bonus_cells = {}, -- list of bonus cell amount for breeder + cooling_history = 0, -- average power loss through cooling in the last TICKS_PER_UPDATE*8 ticks + lamp = reactor_lamp, --reactor lamp dummy + light = reactor_light, --light dummy for the lamp + interface_warning_tick = 0, --last tick an electricity warning was displayed on the interface (for imitating the game's behaviour) + interface_warning = nil, --electricity warning dummy (interface) + cooling_warning_tick = 0, --last tick an electricity warning was displayed on the cooling + cooling_warning = nil, --electricity warning dummy (cooling) + power_usage = {starting = 0,cooling = 0, interface = 0}, --portions of the power consumption + last_states_update = game.tick, + fuel_last_tick = 0, -- fuel value in the core on last tick + --fluid_amount_last_tick = 0, -- fluid amount in eccs on last tick + power_output_last_tick = 0, --power output in MW on last tick (only updated in start or running phase) + last_temp_update = game.tick, + max_power = reactor_max_power, --dynamic maximum value for gui + max_efficiency = reactor_max_efficiency, --dynamic maximum value for gui + signals = Signals(), + } + global.reactors[reactor.id] = reactor + reactor.core.get_fuel_inventory().insert{name="rr-dummy-fuel-cell", count = 50} + reactor.control.parameters = reactor.signals.parameters + --logging("-> reactor successfully added") + --logging("") + return reactor +end + +local function reactor_meltdown(surface, position, tick) + -- do the meltdown explosion + surface.create_entity{ + name = "medium-explosion", + position = position, + } + --create radiation and cloud + fallout.create(surface, position, tick) +end + +-- removes other reactor parts when its reactor core is removed +local function remove_reactor(entity, tick, has_died) + local reactor = global.reactors[entity.unit_number] + local dead_reactor_name = reactor.entity.name + local current = {surface=entity.surface,position=entity.position,force=entity.force} + local meltdown = Setting.meltdown() + + -- cause meltdown? + if reactor.state ~= 0 then + current.surface.pollute(current.position, 150000) --0.006 evo @ 0.80 + --if game.forces["enemy"] then + -- game.forces["enemy"].evolution_factor=math.min(1,game.forces["enemy"].evolution_factor+0.007) + --end + -- working reactor destroyed, causing meltdown + if meltdown then + reactor_meltdown(current.surface, current.position, tick) + else + reactor.entity.destroy() -- to make space for dummy + local dummy = current.surface.create_entity{ + name = "realistic-reactor", + position = current.position, + force = "enemy", + } + current.surface.create_entity{ + type = "projectile", + name = Setting.map("explosion-mode"), + position = current.position, + force = current.force, + target = dummy, + max_range = 23, + speed = 0.1, + } + end + end --meltdown + + -- create reactor ruin + if has_died and reactor.state ~= 0 then + reactor.interface.destroy() -- remove interface entity + if meltdown then + reactor.entity.destroy() --only destroy when there should not remain a ghost + ruin.add(dead_reactor_name, current) + end + else + reactor.interface.destroy() -- remove interface entity + end + + --remove other stuff + if reactor.power.valid then reactor.power.destroy() end -- remove power entity + if reactor.eccs.valid then reactor.eccs.destroy() end -- remove eccs + if reactor.core.valid then reactor.core.destroy() end + if reactor.lamp.valid then reactor.lamp.destroy() end + if reactor.light.valid then reactor.light.destroy() end + if reactor.interface_warning and reactor.interface_warning.valid then reactor.interface_warning.destroy() end + if reactor.cooling_warning and reactor.cooling_warning.valid then reactor.cooling_warning.destroy() end + + --remove global table entry + global.reactors[reactor.id] = nil +end + + +-- replaces the reactor core entity with another one +local function replace_reactor_core(reactor, new_reactor_entity_name) + local temp = reactor.core.temperature + --logging("Building reactor model: "..new_reactor_entity_name) + + --logging("- old reactor ID: " .. reactor.id) + --logging("- old reactor core ID: " .. reactor.core_id) + + -- create new reactor core + local new_reactor_core = reactor.core.surface.create_entity{ + name = new_reactor_entity_name, + position = reactor.core.position, + force = reactor.core.force, + create_build_effect_smoke = false, + } + --logging("- new reactor core ID: " .. new_reactor_core.unit_number) + + -- copy everything from old core to new core + new_reactor_core.copy_settings(reactor.core) --(what is this actually copying???) -- on/off state for example + new_reactor_core.temperature = temp + new_reactor_core.destructible = false + --logging("-> updated temperature: " .. new_reactor_core.temperature) + -- transfer burner heat and remaining fuel in burner + + --if reactor.state == 0 and not (Setting.behavior("scram") == "stop-half-empty") then + -- -- do nothing (don't transfer burner heat to a stopped or scramed reactor) + -- if Setting.behavior("scram") == "consume-additional-cell" and reactor.entity.burner.currently_burning then + -- reactor.entity.get_burnt_result_inventory().insert({name = reactor.entity.burner.currently_burning.burnt_result.name, count = 1}) + -- reactor.entity.burner.currently_burning = nil + -- end + -- + -- --logging("-> burner settings not transferred, state: stopped or scramed") + --else + -- -- transfer current burner settings + -- --if reactor.core.burner.heat > 0 then --not reliable! + -- if reactor.entity.burner.remaining_burning_fuel > 0 then + --new_reactor_core.burner.currently_burning = game.item_prototypes["uranium-fuel-cell"] + --new_reactor_core.burner.currently_burning = reactor.entity.burner.currently_burning + new_reactor_core.burner.currently_burning = "rr-dummy-fuel-cell" + new_reactor_core.burner.remaining_burning_fuel = 9223372035000000000 + new_reactor_core.burner.heat = reactor.entity.burner.heat + --logging("-> updated burner heat: " .. new_reactor_core.burner.heat) + --new_reactor_core.burner.remaining_burning_fuel = reactor.entity.burner.remaining_burning_fuel + --logging("-> updated burner remaining_burning_fuel: " .. new_reactor_core.burner.remaining_burning_fuel) + -- else + -- --logging("-> burner settings not transferred, empty") + -- end + --end + + new_reactor_core.minable = false -- core should be unminable in any case cause its removed via script + + new_reactor_core.get_fuel_inventory().insert{name="rr-dummy-fuel-cell", count = 50} + + + -- destroy old core + reactor.core.destroy() + -- store new core + reactor.core = new_reactor_core + --update reactor core id with new core unit_number + reactor.core_id = new_reactor_core.unit_number + + --logging("-> reactor replaced") + --logging("- new reactor ID: " .. reactor.id) + --logging("- new reactor core ID: " .. reactor.core_id) +end + +local function update_reactor_signals(reactor, tick) + local inventory = reactor.entity.get_fuel_inventory() + local cells = reactor.signals.parameters["uranium-fuel-cells"] + if inventory.is_empty() or reactor.entity.status == defines.entity_status.full_output then + cells.count = 0 + cells.signal = {type="item", name="uranium-fuel-cell"} + else + cells.signal = {type="item", name=inventory[1].name} + cells.count = inventory[1].count + end + -- destroy energy warning entity + if game.tick - reactor.interface_warning_tick >=60 and reactor.interface_warning and reactor.interface_warning.valid then + reactor.interface_warning.destroy() + end + reactor.signals.parameters["core_temperature"].count = reactor.core.temperature + + local fluid = reactor.eccs.fluidbox[1] + if fluid then + reactor.signals.parameters["coolant-amount"].count = fluid.amount + else + reactor.signals.parameters["coolant-amount"].count = 0 + end + -- check if interface has enough power + if reactor.power.energy >= (POWER_USAGE_INTERFACE * TICKS_PER_UPDATE / 60* Setting.map("energy-consumption-multiplier")) then --- TICKS_PER_UPDATE // TODO: compare with correct power usage (not critical) + -- show updated signals on interface + + reactor.signals.parameters["electric-power"].count = (reactor.power.energy/reactor.power.electric_buffer_size)*100 + reactor.control.parameters = reactor.signals.parameters + + -- apply power consumption for interface + reactor.power_usage.interface = math.floor(POWER_USAGE_INTERFACE/60) + else + reactor.power_usage.interface = 0 + -- disable interface signals + --reactor.control.parameters = {parameters={}} +-- reactor.control.parameters = {} + reactor.control.parameters = Signals().parameters + + -- show energy warning entity + if game.tick - reactor.interface_warning_tick >= 120 then + reactor.interface_warning = create_warning(reactor.interface, "electricity") + reactor.interface_warning_tick = game.tick + end + end +end + +local function update_reactor_temperature(reactor, tick) + local powerusagecooling = POWER_USAGE_COOLING + local time_passed = math.max(1,tick - reactor.last_temp_update) + reactor.last_temp_update = tick + local change_mult = CHANGE_MULTIPLIER * (time_passed / 15) + local reactor_core_temperature = reactor.core.temperature + local reactor_state = reactor.state + local powersignal = reactor.signals.parameters["power-output"].count + local reactor_efficiency = reactor.efficiency + local reactor_power_energy = reactor.power.energy + + --apply efficiency by adding a fuel bonus + if reactor_efficiency > 0 then + local fuel_consumption = (powersignal/60*1000000)*(time_passed)*(100/reactor_efficiency) + local reactor_entity_burner_remaining_burning_fuel = reactor.entity.burner.remaining_burning_fuel + + if reactor_entity_burner_remaining_burning_fuel - fuel_consumption > 0 then + reactor.entity.burner.remaining_burning_fuel = reactor_entity_burner_remaining_burning_fuel - fuel_consumption + else + reactor.entity.burner.remaining_burning_fuel = 0 + end + + else + --no fuel bonus, efficiency=0 (reactor stopped) + end + + --apply starting power consumption + if reactor_state == 1 then + --apply cooling power consumption + reactor.power_usage.starting = math.floor(POWER_USAGE_STARTING / 60) + else + reactor.power_usage.starting = 0 + end + + -- do environment cooling if state is stopped + if reactor_state == 0 then + if reactor_core_temperature > 15 then + --logging("Reactor state is stopped, apply cooling by the environment") + + --[[ + local Tmix_env = ((reactor_core_temperature * REACTOR_MASS) + (15 * 10000)) / (REACTOR_MASS + 10000) + --logging("Tmix_env: " .. Tmix_env) + local Tdelta_reactor_env = reactor_core_temperature - Tmix_env + --logging("Tdelta_reactor_env: " .. Tdelta_reactor_env) + local Tchange_reactor_env = (Tdelta_reactor_env * change_mult * 0.1) + --logging("-> Tchange_reactor_env: " .. Tchange_reactor_env) + if (reactor_core_temperature - Tchange_reactor_env) > 15 then + reactor.core.temperature = reactor_core_temperature - Tchange_reactor_env + reactor_core_temperature = reactor_core_temperature - Tchange_reactor_env + else + reactor.core.temperature = 15 + reactor_core_temperature = 15 + end + ]] + + if reactor_core_temperature > 15.125 then + reactor_core_temperature = reactor_core_temperature - 0.125 * (time_passed / 15) + else + reactor_core_temperature = 15 + end + + --logging("Reactor core temperature after environment cooling: " .. reactor_core_temperature) + end + end + + -- do decay heat effect if state is scramed + if reactor_state == 3 and Setting.behavior("scram") == "decay-heat-v1" then + reactor_core_temperature = reactor_core_temperature + powersignal/20 + end + + reactor.power_usage.cooling = 0 -- supposed to be overwritten + local cooling_history = reactor.cooling_history + cooling_history = cooling_history*7/8 --devaluing previous history (only used by graph) + + --remove cooling power alert (gfx) + if game.tick - reactor.cooling_warning_tick >= 60 and reactor.cooling_warning and reactor.cooling_warning.valid then + reactor.cooling_warning.destroy() + end + + -- check fluid level in eccs and calculate temperature changes + local fluid = reactor.eccs.fluidbox[1] + local Tchange_reactor = 0 + local Tchange_fluid = 0 + + if fluid == nil then + --do nothing, no coolant + reactor.signals.parameters["coolant-amount"].count = 0 + + else + --reactor has coolant + + local fluid_temperature = fluid.temperature + local fluid_amount = fluid.amount + + reactor.signals.parameters["coolant-amount"].count = fluid_amount + + if fluid_amount < 100 then + --do nothing, not enough coolant + + else + + --calculate the mixing temperature with richmann's mixing rule + local Tmix = ((reactor_core_temperature * REACTOR_MASS) + (fluid_amount * fluid_temperature)) / (REACTOR_MASS + fluid_amount) + + -- check which is hotter and cool/heat accordingly + local Tdelta_reactor = reactor_core_temperature - Tmix + local Tdelta_fluid = fluid_temperature - Tmix + + if Tdelta_reactor > Tdelta_fluid then + -- reactor is hotter than fluid (that is how it should be) + + Tchange_reactor = (Tdelta_reactor * change_mult) + Tchange_fluid = (Tdelta_fluid * change_mult) + + if fluid_temperature - Tchange_fluid > 100 then + -- resulting fluid would be too hot (max 100°, set by game) + + --reduce both temperature changes by the same factor, so that resulting fluid is = 100° + efficiency_factor = (100 - fluid_temperature) / (Tdelta_fluid * change_mult * -1) + Tchange_reactor = (Tchange_reactor * efficiency_factor) + Tchange_fluid = (Tchange_fluid * efficiency_factor) + + end + + else + -- fluid is hotter than reactor (it's possible to heat the reactor with a hot fluid) + + Tchange_reactor = (Tdelta_reactor * change_mult) + Tchange_fluid = (Tdelta_fluid * change_mult) + + if reactor_core_temperature - Tchange_reactor > 100 then + Tchange_reactor = 0 + Tchange_fluid = 0 + --do nothing, reactor is already 100° + -- this is necessary, because theoretically it would be possible to heat the reactor with steam to 1000°, thus causing the meltdown + + end + + end + + + -- --save the fluid back to the eccs + -- fluid.temperature = fluid_temperature + -- fluid.amount = fluid_amount + -- reactor.eccs.fluidbox[1] = fluid + + end -- not enough coolant + + + --do the actual cooling + local cooled = true + if Tchange_reactor ~= 0 then + if Setting.map("static-cooling-power-consumption") then + -- static cooling power consumption + + if reactor_power_energy < (powerusagecooling * time_passed / 60* Setting.map("energy-consumption-multiplier")) then + -- not enough energy - don't cool + cooled = false + else + -- cool/heat the core and the fluid + reactor_core_temperature = reactor_core_temperature - Tchange_reactor + fluid_temperature = fluid_temperature - Tchange_fluid + -- cooling power consumption + if Tchange_reactor>0.1 then + reactor.power_usage.cooling = math.floor(powerusagecooling / 60) + end + cooling_history = cooling_history + (Tchange_reactor * 20 / 8/ (time_passed/15)) + end + else + local max_cooling = reactor_power_energy / (powerusagecooling * time_passed / 60* Setting.map("energy-consumption-multiplier")) + local cooling_mult = 1 + if max_cooling < math.abs(Tchange_reactor) then + cooled = false + cooling_mult = max_cooling/math.abs(Tchange_reactor) + end + cooling_history = cooling_history + (Tchange_reactor * cooling_mult * 20 / 8/ (time_passed/15)) + reactor_core_temperature = reactor_core_temperature - Tchange_reactor * cooling_mult + --cool the fluid + fluid_temperature = fluid_temperature - Tchange_fluid * cooling_mult + + --cooling power consumption + + --dynamic cooling power consumption + reactor.power_usage.cooling = math.floor(Tchange_reactor * cooling_mult * powerusagecooling / 60 / (time_passed/15)) + cooling_history = cooling_history + reactor.power_usage.cooling*60/1000000/8* Setting.map("energy-consumption-multiplier") + + end + + end + + --save the fluid back to the eccs + fluid.temperature = fluid_temperature + fluid.amount = fluid_amount + reactor.eccs.fluidbox[1] = fluid + + if not cooled then + -- core wasn't cooled - show energy warning signal + if game.tick - reactor.cooling_warning_tick >= 120 then + reactor.cooling_warning = create_warning(reactor.entity, "cooling") + reactor.cooling_warning_tick = game.tick + end + end + + end --empty eccs + + + reactor.cooling_history = cooling_history + + -- update reactor signals + reactor.signals.parameters["electric-power"].count = (reactor_power_energy/reactor.power.electric_buffer_size)*100 + + -- set power usage value of power entity + reactor.power.power_usage = (reactor.power_usage.starting + reactor.power_usage.cooling + reactor.power_usage.interface) * Setting.map("energy-consumption-multiplier") + + -- start nuclear meltdown + if reactor_core_temperature >= DYING_REACTOR_CORE_TEMPERATURE then + --logging("Core temperature > 1000°, MELTDOWN") + -- destroy the reactor core (will trigger meltdown) + reactor.entity.die() + else + reactor.core.temperature = reactor_core_temperature + end + +end + + +-- changes reactor state +local function change_reactor_state(new_state, reactor, tick) + --logging("-> changing reactor state to: " .. new_state) + reactor.state = new_state + reactor.state_active_since = tick + reactor.lamp.destroy() + reactor.light.destroy() + local light_color = "black" + if new_state == 0 then + -- set signals + reactor.signals.parameters["state_stopped"].count = 1 + reactor.signals.parameters["state_starting"].count = 0 + reactor.signals.parameters["state_running"].count = 0 + reactor.signals.parameters["state_scramed"].count = 0 + -- configure reactor + replace_reactor_core(reactor,"realistic-reactor-1") + reactor.entity.active = false + reactor.core.active = false + reactor.entity.minable = true + elseif new_state == 1 then + --set signals + reactor.signals.parameters["state_stopped"].count = 0 + reactor.signals.parameters["state_starting"].count = 1 + reactor.signals.parameters["state_running"].count = 0 + reactor.signals.parameters["state_scramed"].count = 0 + -- configure reactor + reactor.entity.active = true + reactor.core.active = true + --reactor.debug_start_cell = true + reactor.entity.minable = false + light_color = "yellow" + elseif new_state == 2 then + -- set signals + reactor.signals.parameters["state_stopped"].count = 0 + reactor.signals.parameters["state_starting"].count = 0 + reactor.signals.parameters["state_running"].count = 1 + reactor.signals.parameters["state_scramed"].count = 0 + -- configure reactor + reactor.entity.active = true + reactor.core.active = true + + reactor.entity.minable = false + light_color = "green" + elseif new_state == 3 then + --set signals + reactor.signals.parameters["state_stopped"].count = 0 + reactor.signals.parameters["state_starting"].count = 0 + reactor.signals.parameters["state_running"].count = 0 + reactor.signals.parameters["state_scramed"].count = 1 + -- configure reactor + if Setting.behavior("scram") == "decay-heat-v1" then + reactor.core.active = false + reactor.entity.active = false + if reactor.entity.burner.currently_burning then + reactor.entity.get_burnt_result_inventory().insert({name = reactor.entity.burner.currently_burning.burnt_result.name, count = 1}) + end + reactor.entity.burner.currently_burning = nil + reactor.entity.burner.remaining_burning_fuel = 0 + else + reactor.core.active = true + reactor.entity.active = true + end + reactor.entity.minable = false + light_color = "red" + end + local p = reactor.core.position + reactor.lamp = reactor.core.surface.create_entity{ + name = "rr-"..light_color.."-lamp", + position = {p.x+0.017,p.y+0.88}, + force = reactor.core.force.name, + } + reactor.light = reactor.core.surface.create_entity{ + name = "rr-"..light_color.."-light", + position = {p.x+0.017,p.y+0.88}, + force = reactor.core.force.name, + } + reactor.lamp.destructible = false + reactor.light.destructible = false +end + + +local function update_reactor_states(reactor, tick) + --logging("---") + --logging("Updating reactor ID: " .. reactor.id) + --logging("Reactor core ID: " .. reactor.core_id) + --logging("Reactor type: ".. reactor.entity.name) + --logging("Reactor model: " .. reactor.core.name) + --logging("Reactor state: " .. reactor.state) + local running_time = math.ceil((tick - reactor.state_active_since)/60) + --logging("-> state active for (s): " .. running_time) + + + -- get control signals + --logging("Checking circuit network signals") + local signal_start = false + local signal_scram = false + for _,wire in ipairs{defines.wire_type.green, defines.wire_type.red} do + local network = reactor.control.get_circuit_network(wire) + if network then + --logging("-> Found circuit network. Network ID: " .. network.network_id) + if network.get_signal(SIGNAL_CONTROL_SCRAM) > 0 then + signal_scram = true + --logging("--> found SCRAM signal") + elseif network.get_signal(SIGNAL_CONTROL_START) > 0 then + signal_start = true + --logging("--> found START signal") + end + end + end + + -- check for changed states + --logging("Checking for changed reactor state") + local reactor_state = reactor.state + local state_changed = false + if reactor_state == 0 then + + if reactor.entity.get_fuel_inventory().is_empty() == false + and signal_start == true + and reactor.power.energy >= (POWER_USAGE_STARTING * TICKS_PER_UPDATE * 4 / 60* Setting.map("energy-consumption-multiplier")) + and signal_scram == false + and reactor.entity.get_burnt_result_inventory().can_insert({count = 1, name = game.item_prototypes[next(reactor.entity.get_fuel_inventory().get_contents())].burnt_result.name}) then + state_changed = true + change_reactor_state(1, reactor, tick) + end + + elseif reactor_state == 1 then + + if signal_scram == true or reactor.entity.status ~= defines.entity_status.working then + state_changed = true + change_reactor_state(3, reactor, tick) + elseif running_time >= Setting.duration("starting") then + state_changed = true + change_reactor_state(2, reactor, tick) + elseif reactor.power.energy < (POWER_USAGE_STARTING * TICKS_PER_UPDATE * 4 / 60* Setting.map("energy-consumption-multiplier")) then + --ToDo: show alarm on map? + -- FIXME use messaging with localization +-- game.players[1].print("The starting phase of a nuclear reactor was aborted, because the reactor core didn't get enough electric energy from the grid. The fuel cell is lost.") + state_changed = true + change_reactor_state(0, reactor, tick) + end + + elseif reactor_state == 2 then + + if signal_scram == true or reactor.entity.status ~= defines.entity_status.working then + state_changed = true + change_reactor_state(3, reactor, tick) + end + + elseif reactor_state == 3 then + if running_time >= Setting.duration("scram") then + state_changed = true + local usedfuel = reactor.entity.burner.currently_burning and reactor.entity.burner.currently_burning.burnt_result.name + if Setting.behavior("scram") ~= "stop-half-empty" then + if usedfuel then + reactor.entity.get_burnt_result_inventory().insert({name = usedfuel, count = 1}) + end + reactor.entity.burner.currently_burning = nil + end + change_reactor_state(0, reactor, tick) + elseif reactor.entity.get_fuel_inventory().is_empty() and reactor.entity.burner.remaining_burning_fuel == 0 then + state_changed = true + change_reactor_state(0, reactor, tick) + elseif Setting.behavior("scram") == "limit-to-current-cell" and reactor.entity.burner.remaining_burning_fuel < (reactor.signals.parameters["power-output"].count/60*1000000*(300)*(100/reactor.signals.parameters["efficiency"].count)) then --200 ticks at 200 reactors, but lets say 300 + + local usedfuel = reactor.entity.burner.currently_burning and reactor.entity.burner.currently_burning.burnt_result.name + state_changed = true + if usedfuel then + reactor.entity.get_burnt_result_inventory().insert({name = usedfuel, count = 1}) + end + reactor.entity.burner.currently_burning = nil + change_reactor_state(0, reactor, tick) + end + + end + running_time = math.ceil((tick - reactor.state_active_since)/60) + + if state_changed == false then + --logging("-> reactor state unchanged") + else + reactor_state = reactor.state + end + + --update reactor signals + if reactor_state == 1 then + reactor.signals.parameters["state_starting"].count = Setting.duration("starting") - running_time + reactor.signals.parameters["neighbour-bonus"].count = 0 + end + if reactor_state == 3 then + reactor.signals.parameters["state_scramed"].count = Setting.duration("scram") - running_time + reactor.signals.parameters["neighbour-bonus"].count = 0 + end + if reactor_state == 0 then + -- reactor is not running + reactor.signals.parameters["power-output"].count = 0 + reactor.signals.parameters["efficiency"].count = 0 + reactor.signals.parameters["cell-bonus"].count = 0 + reactor.signals.parameters["neighbour-bonus"].count = 0 + end + + + -- check reactor and replace it with the appropriate version depending on temperature and connected reactors + if reactor_state == 1 or reactor_state == 2 or reactor_state == 3 then + + --logging("---Updating reactor stats---") + + -- check how many running reactors are connected + local neighbours = 1 + --logging("Checking running reactor neighbours:") + + for id in pairs(network.get_connected_reactors(reactor.entity)) do + --logging("- checking connected reactor ID: " .. id) + + if reactor.id ~= id then + -- found another reactor, check if it is running + local other = global.reactors[id] + --if other then logging("-> loaded connected reactor ID: " .. other.id) end + if other and other.state == 2 then + neighbours = neighbours + 1 + --logging("--> reactor is running, bonus: " .. neighbours) + end + end + end + --logging("-> Running reactor neighbours: " .. neighbours) + reactor.neighbours = neighbours + + --load reactor parameters: + -- power , efficiency, bonus_cells, (max_power, max_efficiency) + --logging("Calculating reactor stats:") + local reactor_parameters = FORMULA[Setting.formula()](reactor,running_time) + + -- -> in stats function + -- if reactor.state == 1 then + -- reactor.signals.parameters["state_starting"].count = Setting.duration("starting") - running_time + 1 --ToDo: starting phase should end, when first fuel cell is half empty + -- reactor_parameters.power = math.floor(reactor_parameters.power * ((running_time)/Setting.duration("starting"))^2) + -- --reactor_parameters.efficiency = reactor_parameters.efficiency + -- reactor_parameters.bonus_cells = 0 + -- end + -- if reactor.state == 3 then + -- reactor.signals.parameters["state_scramed"].count = Setting.duration("scram") - running_time + 1 + -- reactor_parameters.power = math.floor(reactor_parameters.power * ((Setting.duration("scram") - (running_time)/3.5)/Setting.duration("scram"))^11+0.45) + -- --reactor_parameters.efficiency = reactor_parameters.efficiency + -- reactor_parameters.bonus_cells = 0 + -- end + + --logging("-> Temperature="..reactor.core.temperature.." PowerOutput="..reactor_parameters.power.." Efficiency="..math.floor(reactor_parameters.efficiency).." BonusCellAmount: "..reactor_parameters.bonus_cells) + + --apply material bonus by adding empty fuel cell + local burnt_result + --logging("Applying empty fuel cell bonus:") + if reactor.entity.burner.currently_burning == nil then + -- burner is empty, can't read fuel type + -- cell bonus is skipped for now + -- ToDo: save it in global.reactors and add it during the next update + else + --logging("- currently burning: "..reactor.entity.burner.currently_burning.name) + if reactor_parameters.bonus_cells == 0 then + -- do nothing + -- normal reactor or breeder too cold + --logging("-> adding nothing, too cold or no breeder reactor") + else + -- add bonus to current empty fuel cell amount + burnt_result = reactor.entity.burner.currently_burning.burnt_result + if burnt_result.name == "apm_fuel_cell_mox_used" then + burnt_result = game.item_prototypes["apm_nuclear_breeder_uranium_inventory_enriched"] + end + local burn_duration = ((reactor.entity.burner.currently_burning.fuel_value/1000000)*(reactor_parameters.efficiency/100))/reactor_parameters.power*15/TICKS_PER_UPDATE -- burn duration in ticks + local cell_bonus_amount + + if Setting.ingos_formula() then + --adding bonus cell per time + cell_bonus_amount = ((reactor_parameters.bonus_cells*TICKS_PER_UPDATE)/900)*BONUS_CELL_MULTIPLIER -- reactor_parameters.bonus_cells=100 means one cell per minute + elseif Setting.ownlys_formula() then + --adding bonus per burn duration + cell_bonus_amount = reactor_parameters.bonus_cells / burn_duration + else + error("formula missing") + end + + reactor.bonus_cells[burnt_result.name] = (reactor.bonus_cells[burnt_result.name] or 0) + cell_bonus_amount * (game.tick-reactor.last_states_update) / 60 + --logging("- bonus cell amount: "..cell_bonus_amount) + + -- msg("reactor.bonus_cells: "..reactor.bonus_cells[burnt_result.name]) + -- msg("bonus amount (time): "..((reactor_parameters.bonus_cells*TICKS_PER_UPDATE)/900)*BONUS_CELL_MULTIPLIER) + -- msg("") + -- msg("bonus amount (duration): "..reactor_parameters.bonus_cells / burn_duration) + -- msg("---") + + end + end + + + if burnt_result and reactor.bonus_cells[burnt_result.name] and reactor.bonus_cells[burnt_result.name] > 1 then + -- add used-up-uranium-fuel-cell + --logging("Inserting used-up-uranium-fuel-cell:") + --game.players[1].print("bonuscell") + if not reactor.entity.get_burnt_result_inventory().is_empty() then + --burnt fuel inventory not empty + if reactor.entity.get_burnt_result_inventory().can_insert{name = burnt_result.name, count = 1} then + -- same cell + reactor.entity.get_burnt_result_inventory().insert({name=burnt_result.name, count=1}) + reactor.bonus_cells[burnt_result.name] = reactor.bonus_cells[burnt_result.name] -1 + --logging("-> inserted") + else + --different cell, wait + --logging("-> can't insert, different used up cell present. waiting...") + end + else + --burnt fuel inventory empty + reactor.entity.get_burnt_result_inventory().insert({name=burnt_result.name,count=1}) + reactor.bonus_cells[burnt_result.name] = reactor.bonus_cells[burnt_result.name] -1 + --logging("-> inserted") + end + + end + + + -- update reactor signals + reactor.signals.parameters["power-output"].count = reactor_parameters.power + reactor.signals.parameters["efficiency"].count = math.floor(reactor_parameters.efficiency) + reactor.signals.parameters["cell-bonus"].count = reactor_parameters.bonus_cells*100 + reactor.efficiency = reactor_parameters.efficiency + if reactor_state == 2 or reactor_state == 1 then + reactor.power_output_last_tick = reactor_parameters.power + end + + + -- replace reactor with updated level version + --logging("Replace reactor model:") + local reactor_to_build = "realistic-reactor-" .. math.max(1,reactor_parameters.power) + --logging("-Reactor to be: "..reactor_to_build) + --logging("-Current reactor: "..reactor.core.name) + if reactor.core.name == reactor_to_build then + --logging("-> Reactor already build.") + elseif not (reactor_state == 3 and Setting.behavior("scram") == "decay-heat-v1") then + replace_reactor_core(reactor,reactor_to_build) + end + + end + + + -- UPDATE DISPLAYER -- + reactor.entity.temperature = reactor.core.temperature + + reactor.last_states_update = game.tick +end + + +local function is_core(entity) + return string.sub(entity.name, 1, 18) == "realistic-reactor-" + and not E2I_NAME[entity.name] +end + +local function is_reactor(entity) + return entity.type == "reactor" + and not is_core(entity) +end + +local function reactor_is_empty(entity) + return entity.get_fuel_inventory().is_empty() + and entity.get_burnt_result_inventory().is_empty() +end + +local function reactor_is_minable(entity) + return entity.minable and reactor_is_empty(entity) +end + + +local function find_reactor_ghost_of_interface(surface, position, name) + return find_nuclear_ghost(surface, {position.x,position.y-1}, name) +end + + +local function on_tick(tick) + -- reactor update events + if tick % TICKS_PER_UPDATE == 0 then + for _,reactor in pairs(global.reactors) do + if reactor.entity and reactor.entity.valid then + update_reactor_states(reactor,tick) + update_reactor_signals(reactor,tick) + update_reactor_temperature(reactor,tick) -- reactor may die here + end + end + end +end + + +return { -- exports + is = is_reactor, + tick = on_tick, + add = add_reactor, + remove = remove_reactor, + is_empty = reactor_is_empty, + is_minable = reactor_is_minable, + find_ghost = find_reactor_ghost_of_interface, +} + diff --git a/scripts/entity/ruin.lua b/scripts/entity/ruin.lua new file mode 100644 index 0000000..135d5e7 --- /dev/null +++ b/scripts/entity/ruin.lua @@ -0,0 +1,98 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local Setting = require(rroot .. "setting") +local create_steam = require(rpath .. "util").create_steam + + + +local function add_reactor_ruin(dead_reactor_name, dead_reactor_core) + local surface = dead_reactor_core.surface + + -- create reactor ruin + local entity = surface.create_entity{ + name = RUIN_NAME[dead_reactor_name], + position = dead_reactor_core.position, + force = dead_reactor_core.force, + create_build_effect_smoke = false, + } + entity.destructible = false + + -- create steam producing entity + local p = dead_reactor_core.position + local steam = create_steam(surface, { + name = RUIN_SMOKE_NAME, + position = {p.x,p.y+0.3}, + force = dead_reactor_core.force, + }) + steam.active = true -- start active - it's always active + local glow = surface.create_entity{ + name = "rr-ruin-glow", + position = dead_reactor_core.position, + force = dead_reactor_core.force, + } + + -- reactor ruin is saved in global table + local ruin = { + id = entity.unit_number, --same id as reactor + entity = entity, + steam = steam, + glow = glow, + tick = game.tick,--changes during radiation creation + meltdown_tick = game.tick, + spread = 6, + } + global.ruins[ruin.id] = ruin + return ruin +end + +-- remove reactor ruin +local function remove_reactor_ruin(entity) + --when the reactor ruin was replaced by a sarcophagus, the steam creator entity needs to be removed + --logging("---------------------------------------------------------------") + --logging("Removing reactor ruin ID: " .. entity.unit_number) + local ruin = global.ruins[entity.unit_number] + if ruin then + --logging("-> found matching entry") + if ruin.steam and ruin.steam.valid then ruin.steam.destroy() end + if ruin.glow and ruin.glow.valid then ruin.glow.destroy() end + global.ruins[ruin.id] = nil + end +end + + +local function update_reactor_ruin(ruin) + if ruin and ruin.entity.valid and ruin.steam and ruin.steam.valid then + -- reset steam puff crafting progress so it never actually finishes + ruin.steam.crafting_progress = 0.1 + end +end + + +local function on_tick(tick) + --sarcophagus healing + if tick % 180 == 0 then + for key, entity in pairs(global.sarcophagus) do + entity.health = entity.health + 1000/math.max(1,Setting.protoduration("sarcophagus")) * 3 + if entity.health == 1000 then + entity.minable = true + entity.destructible = true + global.sarcophagus[key] = nil + end + end + end + -- reactor ruins + if (tick-7) % (TICKS_PER_UPDATE*4) == 0 then + for _,ruin in pairs(global.ruins) do + update_reactor_ruin(ruin) + end + end +end + + + +return { -- exports + tick = on_tick, + add = add_reactor_ruin, + remove = remove_reactor_ruin, +} + diff --git a/scripts/entity/tower.lua b/scripts/entity/tower.lua new file mode 100644 index 0000000..a04ebf0 --- /dev/null +++ b/scripts/entity/tower.lua @@ -0,0 +1,80 @@ +local rpath = (...):match("(.-)[^%.]+$") +local util = require(rpath .. "util") +local create_steam = util.create_steam + + +-- adds the steam entity to the cooling tower when it is build +local function add_cooling_tower(entity) + -- the steam entity makes happy clouds when the tower is active + -- this is needed because the cooling tower is an electric furnace, and only burner furnaces can produce smoke + --logging("---------------------------------------------------------------") + --logging("Adding new cooling tower with ID: " .. entity.unit_number) + local steam = create_steam(entity.surface, { + name = STEAM_ENTITY_NAME, + position = entity.position, + force = entity.force, + }) + steam.active = false -- start inactive + local tower = { + id = entity.unit_number, + entity = entity, + steam = steam, + } + global.towers[tower.id] = tower + --logging("-> tower successfully added") + --logging("") + return tower +end + +-- removes the steam entity when its cooling tower is removed +local function remove_cooling_tower(entity) + --logging("---------------------------------------------------------------") + --logging("Removing cooling tower ID: " .. entity.unit_number) + local tower = global.towers[entity.unit_number] + if tower then + --logging("-> found tower, removing it and all its parts") + if tower.steam and tower.steam.valid then tower.steam.destroy() end -- remove happy cloud maker + global.towers[tower.id] = nil -- remove table entry so we stop trying to update this tower + end + --logging("-> tower successfully removed") + --logging("") +end + + +local function update_cooling_tower(tower) + if not (tower.entity.valid and tower.steam.valid) then return end + -- enable or disable steamer + if tower.entity.is_crafting() then + tower.steam.active = true + -- reset steam puff crafting progress so it never actually finishes + tower.steam.crafting_progress = 0.1 + else + tower.steam.active = false + end + + -- if tower and tower.entity.valid then + -- -- only show steam puffs if cooling tower is actively working and not backed up + -- tower.steam.active = tower.entity.is_crafting() and tower.entity.crafting_progress < 1 and tower.name == TOWER_ENTITY_NAME + -- -- reset steam puff crafting progress so it never actually finishes + -- tower.steam.crafting_progress = 0.1 + -- end +end + + +local function on_tick(tick) + -- cooling towers + if (tick-4) % 5 == 0 then + for _,tower in pairs(global.towers) do + update_cooling_tower(tower) + end + end +end + + +return { -- exports + tick = on_tick, + add = add_cooling_tower, + remove = remove_cooling_tower, +} + + diff --git a/scripts/entity/util.lua b/scripts/entity/util.lua new file mode 100644 index 0000000..d7a3781 --- /dev/null +++ b/scripts/entity/util.lua @@ -0,0 +1,59 @@ + + +local function next_delay(tick, delay) + while global.delayed_fallout[tick + delay] do + delay = delay + 1 + end + return delay +end + + +local function find_nuclear_entity(surface, position, name) + return surface.find_entity(name, position) +end + +local function find_nuclear_ghost(surface, position, name) + return surface.find_entities_filtered({ + ghost_name = name, + position = position, + limit = 1, + })[1] +end + + +local function get_reactor_core_power(entity) + if string.sub(entity.name,1,18) ~= "realistic-reactor-" then return nil end + return tonumber(string.sub(entity.name, 19)) +end + + +local function create_warning(entity,kind) + local warning = entity.surface.create_entity{ + name = "rr-" .. kind .. "-warning", + position = entity.position, + force = entity.force, + } + warning.destructible = false + return warning +end + +local function create_steam(surface, options) + local steam = surface.create_entity(options) + steam.operable = false -- disable opening the happy cloud maker's GUI + steam.destructible = false -- it can't be destroyed (we remove it when the cooling tower dies) + steam.get_fuel_inventory().insert{name="solid-fuel", count=50} -- at 1 watt, this is enough fuel to run for 39 years, should suffice + steam.fluidbox[1] = {name="water", amount=1} -- water for dummy steam puff recipe + steam.active = false -- start inactive + return steam +end + + + +return { -- exports + create_steam = create_steam, + create_warning = create_warning, + get_reactor_core_power = get_reactor_core_power, + next_delay = next_delay, + find_nuclear_entity = find_nuclear_entity, + find_nuclear_ghost = find_nuclear_ghost, +} diff --git a/scripts/events/construct.lua b/scripts/events/construct.lua new file mode 100644 index 0000000..1103a35 --- /dev/null +++ b/scripts/events/construct.lua @@ -0,0 +1,125 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local on = require(rpath .. "util").on +local network = require(rroot .. "heat.network") +local reactor = require(rroot .. "entity.reactor") +local interface = require(rroot .. "entity.interface") +local tower = require(rroot .. "entity.tower") +local sameposition = require(rroot .. "heat.util").sameposition +local util = require(rroot .. "entity.util") +local find_nuclear_entity = util.find_nuclear_entity +local find_nuclear_ghost = util.find_nuclear_ghost + + +local event_filters = {} +local event_listener = {} + + +local function on_ghost_interface_added(ghost) + -- this should tell factorio to not delete it between on_pre_built and on_built_entity + interface.remember(ghost) +end + +local function on_reactor_added(entity, tick) + entity.active = false + entity.minable = true + reactor.add(entity, interface.create(entity.surface, { + name = E2I_NAME[entity.name], + position = entity.position, + force = entity.force, + })) +end + + +local function on_heatpipe_added(entity, tick) + network.add_heat_pipe(entity) +end + + +local function on_coolingtower_added(entity, tick) + tower.add(entity) +end + + +local function on_sarcophagus_added(entity) + entity.health = 0.1 + entity.destructible = false + entity.minable = false + global.sarcophagus[entity.unit_number] = entity + local fallout = entity.surface.find_entities_filtered{name="permanent-radiation",area={{entity.position.x-5,entity.position.y-5},{entity.position.x+5,entity.position.y+5}}} + for i, ent in pairs (fallout) do + if i % 20 ~= 1 then + ent.destroy() + end + end +end + + +event_filters.ghost = {} +event_listener.ghost = { + [REACTOR_INTERFACE_ENTITY_NAME] = on_ghost_interface_added, + [BREEDER_INTERFACE_ENTITY_NAME] = on_ghost_interface_added, +} + +local ghost_added = event_listener.ghost + +for ghost_name in pairs(ghost_added) do + table.insert(event_filters.ghost, {mode = "or", filter = "ghost_name", name = ghost_name}) +end + +local function on_ghost_added(entity, ...) + ghost_added[entity.ghost_name](entity, ...) +end + + +event_listener.entity = { + ["entity-ghost"] = on_ghost_added, + [REACTOR_ENTITY_NAME] = on_reactor_added, + [BREEDER_ENTITY_NAME] = on_reactor_added, + [TOWER_ENTITY_NAME] = on_coolingtower_added, + [SARCOPHAGUS_ENTITY_NAME] = on_sarcophagus_added, +} + +event_filters.entity = { + {mode = "or", filter = "type", type = "heat-pipe"}, + {mode = "or", filter = "type", type = "reactor"}, + unpack(event_filters.ghost) +} + +event_filters.ghost = nil -- dont set them, cuz not used + +local entity_added = event_listener.entity + +for entity_name,_ in pairs(entity_added) do + if entity_name ~= "entity-ghost" then + table.insert(event_filters.entity, {mode = "or", filter = "name", name = entity_name}) + end +end + +local function on_entity_added(entity, ...) + if reactor.is(entity) then + network.add_reactor(entity) + end + if entity.type == "heat-pipe" then + on_heatpipe_added(entity, ...) + else + on(entity_added, entity, ...) + end +end + + + +local function on_player_pipette(player_index, item) + if I2E_NAME[item.name] then -- reactor interface + local player = game.players[player_index] + player.pipette_entity(I2E_NAME[item.name]) + end +end + + +return { -- exports + entity = on_entity_added, + pipette = on_player_pipette, + filters = event_filters, + listeners = event_listener, +} diff --git a/scripts/events/destruct.lua b/scripts/events/destruct.lua new file mode 100644 index 0000000..ad63504 --- /dev/null +++ b/scripts/events/destruct.lua @@ -0,0 +1,130 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local on = require(rpath .. "util").on +local network = require(rroot .. "heat.network") +local reactor = require(rroot .. "entity.reactor") +local interface = require(rroot .. "entity.interface") +local tower = require(rroot .. "entity.tower") +local ruin = require(rroot .. "entity.ruin") +local isempty = require(rroot .. "util").isempty + +local event_filters = {} +local event_listener = {} + + +local function on_ghost_interface_removed(ghost) --interface ghost has been mined via deconstruction planner or player mouse + local x,y,z = ghost.position.x,ghost.position.y,ghost.surface.index + interface.definitions(x,y-1,z) -- remove known interface ghosts + --remove reactor on interface ghost removal + local ghosts = { + reactor.find_ghost(ghost.surface, ghost.position, REACTOR_ENTITY_NAME), + reactor.find_ghost(ghost.surface, ghost.position, BREEDER_ENTITY_NAME), + } + for _,entity in pairs(ghosts) do + entity.destroy() + end +end + + +local function on_ghost_reactor_removed(ghost) + local x,y,z = ghost.position.x,ghost.position.y,ghost.surface.index + interface.definitions(x,y,z) -- remove known interface ghosts + --remove interface on reactor ghost removal + local ghosts = { + interface.find_ghost(ghost.surface, ghost.position, REACTOR_INTERFACE_ENTITY_NAME), + interface.find_ghost(ghost.surface, ghost.position, BREEDER_INTERFACE_ENTITY_NAME), + } + for _,entity in pairs(ghosts) do + entity.destroy() + end +end + + + +local function reactor_is_not_robot_minable(entity, event_name) + return event_name == defines.events.on_robot_pre_mined + and not reactor.is_minable(entity) +end + +local function on_reactor_removed(entity, tick, has_died, event_name) + if reactor_is_not_robot_minable(entity, event_name) then return end + reactor.remove(entity, tick, has_died) +end + + +local function on_heatpipe_removed(entity, tick, has_died) + network.remove_heat_pipe(entity) +end + + +local function on_coolingtower_removed(entity, tick, has_died) + tower.remove(entity) +end + + +local function on_ruin_removed(entity, tick, has_died) + ruin.remove(entity) +end + + +event_filters.ghost = {} +event_listener.ghost = { + [REACTOR_INTERFACE_ENTITY_NAME] = on_ghost_interface_removed, + [BREEDER_INTERFACE_ENTITY_NAME] = on_ghost_interface_removed, + [REACTOR_ENTITY_NAME] = on_ghost_reactor_removed, + [BREEDER_ENTITY_NAME] = on_ghost_reactor_removed, +} + + +local ghost_removed = event_listener.ghost + +for ghost_name in pairs(ghost_removed) do + table.insert(event_filters.ghost, {mode = "or", filter = "ghost_name", name = ghost_name}) +end + + +local function on_ghost_removed(entity, ...) + ghost_removed[entity.ghost_name](entity, ...) +end + + +event_filters.entity = { + {mode = "or", filter = "type", type = "heat-pipe"}, + {mode = "or", filter = "type", type = "reactor"}, + unpack(event_filters.ghost), +} + +event_listener.entity = { + ["entity-ghost"] = on_ghost_removed, + [REACTOR_ENTITY_NAME] = on_reactor_removed, + [BREEDER_ENTITY_NAME] = on_reactor_removed, + [TOWER_ENTITY_NAME] = on_coolingtower_removed, + [REACTOR_RUIN_NAME] = on_ruin_removed, + [BREEDER_RUIN_NAME] = on_ruin_removed, +} + +local entity_removed = event_listener.entity + +for entity_name in pairs(entity_removed) do + if entity_name ~= "entity-ghost" then + table.insert(event_filters.entity, {mode = "or", filter = "name", name = entity_name}) + end +end + +local function on_entity_removed(entity, ...) + if reactor.is(entity) then + network.remove_reactor(entity) + end + if entity.type == "heat-pipe" then + on_heatpipe_removed(entity, ...) + else + on(entity_removed, entity, ...) + end +end + +return { -- exports + ghost = on_ghost_removed, + entity = on_entity_removed, + filters = event_filters, + listeners = event_listener, +} diff --git a/scripts/events/init.lua b/scripts/events/init.lua new file mode 100644 index 0000000..e902192 --- /dev/null +++ b/scripts/events/init.lua @@ -0,0 +1,71 @@ +local rpath = (...):match("(.-)[^%.]+$") +local construct = require(rpath .. "construct") +local destruct = require(rpath .. "destruct") + + +local events = { -- defined + added = { + entity = { + defines.events.on_robot_built_entity, + defines.events.script_raised_revive, + defines.events.script_raised_built, + defines.events.on_built_entity, + }, + }, + removed = { + surface = { + defines.events.on_surface_cleared, + defines.events.on_surface_deleted, + }, + chunk = { + defines.events.on_pre_chunk_deleted, + }, + entity = { + defines.events.script_raised_destroy, + defines.events.on_pre_player_mined_item, + defines.events.on_robot_pre_mined, + defines.events.on_entity_died, + }, + ghost = { + defines.events.on_pre_ghost_deconstructed, + }, + }, + gui = { + opened = { + defines.events.on_gui_opened, + }, + clicked = { + defines.events.on_gui_click, + }, + }, + trigger = { + effect = { + defines.events.on_script_trigger_effect, + }, + }, + pipette = { + defines.events.on_player_pipette, + }, + tick = { + defines.events.on_tick, + }, +} + +local filters = { + added = construct.filters, + removed = destruct.filters, +} + +local listeners = { + added = construct.listeners, + removed = destruct.listeners, +} + + +return { -- exports + defined = events, + filters = filters, + listeners = listeners, + construct = construct, + destruct = destruct, +} diff --git a/scripts/events/util.lua b/scripts/events/util.lua new file mode 100644 index 0000000..5febfb2 --- /dev/null +++ b/scripts/events/util.lua @@ -0,0 +1,10 @@ + +local function on(listeners, entity, ...) + local callback = listeners[entity.name] + if callback then callback(entity, ...) end +end + + +return { -- exports + on = on, +} diff --git a/scripts/forces.lua b/scripts/forces.lua new file mode 100644 index 0000000..87f49ab --- /dev/null +++ b/scripts/forces.lua @@ -0,0 +1,13 @@ + +local function init() + for _,force in ipairs{"radioactivity","radioactivity-strong"} do + if not game.forces[force] then + game.create_force(force) + end + end +end + + +return { -- exports + init = init, +} diff --git a/scripts/formulas.lua b/scripts/formulas.lua new file mode 100644 index 0000000..2d3936b --- /dev/null +++ b/scripts/formulas.lua @@ -0,0 +1,350 @@ +local rpath = (...):match("(.-)[^%.]+$") +local Setting = require(rpath .. "setting") + +-- calculate power output, efficiency, bonus cell production + +local function calculate_stats_ingo(reactor,state_running_time) + local temperature = reactor.core.temperature + local reactor_neighbours = math.min(reactor.neighbours,4) + local power + local power_breeder + local efficiency + local BonusCellAmount + local fuel_cell = reactor.entity.burner.currently_burning + local bonus_cells_multiplier = 1 + + reactor.signals.parameters["neighbour-bonus"].count = reactor_neighbours + + --set or calculate values + if reactor.state == 2 and temperature<500 then --running below 500 + + if reactor_neighbours == 1 then + power = 30 + power_breeder = 30 + elseif reactor_neighbours == 2 then + power = 33 + power_breeder = 33 + elseif reactor_neighbours == 3 then + power = 36 + power_breeder = 36 + else + power = 39 + power_breeder = 39 + end + efficiency = 100 + BonusCellAmount = 0 + + elseif reactor.state == 1 then --start + local duration = Setting.duration("starting") + + if temperature <= 500 then + --power on a cold reactor + if reactor_neighbours == 1 then + power_target = 30 + power_breeder_target = 30 + elseif reactor_neighbours == 2 then + power_target = 33 + power_breeder_target = 33 + elseif reactor_neighbours == 3 then + power_target = 36 + power_breeder_target = 36 + else + power_target = 39 + power_breeder_target = 39 + end + --power=math.min(((39/duration)*state_running_time)+1,40) -- running time: 0s=1MW, start_duration(default:30s)=40MW + --power_breeder=math.min(((39/duration)*state_running_time)+1,40) -- running time: 0s=1MW, start_duration(default:30s)=40MW + else + -- power on a warm reactor + power_target = math.max(((1/6)*temperature)-(130/3),40) -- 500°=40MW, 980°=120MW + power_breeder_target = math.max(((1/12)*temperature)-(5/3),40) --500°=40MW, 980°=80MW + end + + power=math.min((((power_target-1)/duration)*state_running_time)+1,power_target) -- running time: 0s=1MW, start_duration(default:30s)= output of a running reactor at this temp + power_breeder=math.min((((power_breeder_target-1)/duration)*state_running_time)+1,power_breeder_target) -- running time: 0s=1MW, start_duration(default:30s)= output of a running breeder at this temp + efficiency=100 + BonusCellAmount=0 + + elseif reactor.state == 3 then --scram + local duration = Setting.duration("scram") + + power=(((reactor.power_output_last_tick-1)/duration)*(duration-state_running_time))+1 --running time: scram_duration(default:180s)=power_output_last_tick, 0s=1MW + power_breeder=(((reactor.power_output_last_tick-1)/duration)*(duration-state_running_time))+1 --running time: scram_duration(default:180s)=power_output_last_tick, 0s=1MW + efficiency=200 + BonusCellAmount=0 + + else --running above 500 + + power = math.max(((1/6)*temperature)-(130/3),40) -- 500°=40MW, 980°=120MW + power_breeder = math.max(((1/12)*temperature)-(5/3),40) --500°=40MW, 980°=80MW + BonusCellAmount = math.max(((1/680)*temperature)-(15/34),0) --350°=0 Cells, 980°=1 Cell + + if reactor_neighbours==1 then + -- one reactor + if temperature <= 620 then + efficiency = math.max((5/6)*temperature-(950/3),100) + else + efficiency = math.max(-(5/6)*temperature+(2150/3),50) + end + + elseif reactor_neighbours==2 then + -- two reactors + if temperature <= 620 then + efficiency = math.max((5/6)*temperature-(950/3),100) + elseif temperature > 620 and temperature <= 740 then + efficiency = 200 + else + efficiency = math.max(-(5/6)*temperature+(2450/3),50) + end + + elseif reactor_neighbours==3 then + -- three reactors + if temperature <= 620 then + efficiency = math.max((5/6)*temperature-(950/3),100) + elseif temperature > 620 and temperature <= 860 then + efficiency = 200 + else + efficiency = math.max(-(5/6)*temperature+(2750/3),50) + end + + else + -- four reactors + if temperature <= 620 then + efficiency = math.max((5/6)*temperature-(950/3),100) + else + efficiency = 200 + end + + end + + end + + --reactor count modifier + power = power*(0.7+0.075*reactor_neighbours) + power_breeder = power_breeder*(0.7+0.075*reactor_neighbours) + + --nerf bonus cell amount depending on what fuel (mods) is used + if not fuel_cell then + --nerf nothing + elseif fuel_cell.name == "mox-fuel-cell" and reactor.entity.burner.currently_burning.fuel_value == 8500000000 then --mox + --SigmaOne's Mods: Nuclear fuel (same stats as uranium power) + bonus_cells_multiplier = 0.825 + + elseif fuel_cell.name == "breeder-fuel-cell" then + --"Plutonium fuel cell" - Nuclear Fuel (4000000000) and "Breeder fuel cell (Pu)" - Nuclear Fuel Cycle (3500000000) + bonus_cells_multiplier = 0.625 + + elseif fuel_cell.name == "fuel-assembly-mox" then + --Uranium Power + + elseif fuel_cell.name == "mox-fuel-cell" then + --"MOX fuel cell" - Advanced Atomics (12000000000) and "MOX fuel cell" - Nuclear Fuel Cycle (8000000000) + bonus_cells_multiplier = 1.8 + + elseif fuel_cell.name == "MOX-fuel" then -- by Plutonium Energy + --"MOX fuel cell" - plutonium energy (20GJ) + bonus_cells_multiplier = 0.6 + + else + -- default uranium fuel cell or other mods + if game.active_mods["Nuclear Fuel"] then + bonus_cells_multiplier = 0.25 + else + bonus_cells_multiplier = 1 + end + + end + BonusCellAmount = BonusCellAmount * bonus_cells_multiplier + + + -- return values + if reactor.entity.name == REACTOR_ENTITY_NAME then + reactor.max_power = 123 + reactor.max_efficiency = 200 + return {power = math.floor(power), efficiency = efficiency, bonus_cells = 0, max_power = 123, max_efficiency = 200} + end + if reactor.entity.name == BREEDER_ENTITY_NAME then + reactor.max_power = 82 + reactor.max_efficiency = 200 + return {power = math.floor(power_breeder), efficiency = efficiency, bonus_cells = BonusCellAmount, max_power = 82, max_efficiency = 200} + end + reactor.max_efficiency = 200 --under optimal conditions - 4 reactors + +end + + + + + + + + + + +local function calculate_stats_ownly(reactor,running_time) + local fuel_cell = reactor.entity.burner.currently_burning + local fuel_cell_name = "" + if fuel_cell ~= nil then + fuel_cell_name = fuel_cell.name + end + local temperature = reactor.core.temperature + local neighbours = reactor.neighbours + local reactors = neighbours + reactors = math.min(4,reactors) +-- reactors = math.max(1,reactors) -- obsolete since neighbours are always >= 1 + local efficiency + local output + local min_efficiency = 1 + local max_efficiency = 200 + local max_output = 150 + local bonus_cells = 0 + local bonus_cells_multiplier = 1 + temperature = math.max(0,temperature - 500) + -- https://rechneronline.de/funktionsgraphen/ + + reactor.signals.parameters["neighbour-bonus"].count = neighbours + if fuel_cell == nil then + efficiency=0 + output=0 + + --SigmaOne's Mods: Nuclear fuel (same stats as uranium power) + elseif fuel_cell_name == "mox-fuel-cell" and reactor.entity.burner.currently_burning.fuel_value == 8500000000 then --mox + efficiency = (3+temperature*0.11)^1.19+80 + output = (1+temperature*0.16)^1.08+35 + min_efficiency = 84 + max_efficiency = 205 + max_output = 150 + bonus_cells_multiplier = 0.66 + + + --"Plutonium fuel cell" - Nuclear Fuel (4000000000) and "Breeder fuel cell (Pu)" - Nuclear Fuel Cycle (3500000000) + elseif fuel_cell_name == "breeder-fuel-cell" then --plutonium (although breeder cells are usually mox i think) + output = (2.2+temperature*0.034)^0.57*25+10 + efficiency = (2.5+temperature*0.034)^0.45*39.5+45 + min_efficiency = 102 + max_efficiency = 195 + max_output = 145 + bonus_cells_multiplier = 0.5 + + --Uranium Power & Patched Mad Clowns + elseif fuel_cell_name == "fuel-assembly-mox" or fuel_cell_name == "rr-clowns-mox-cell" then --mox + efficiency = (3+temperature*0.11)^1.19+80 + output = (1+temperature*0.16)^1.08+35 + min_efficiency = 84 + max_efficiency = 205 + max_output = 150 + + --"MOX fuel cell" - Advanced Atomics (12000000000) and "MOX fuel cell" - Nuclear Fuel Cycle (8000000000) + elseif fuel_cell_name == "mox-fuel-cell" then --mox + efficiency = (3+temperature*0.11)^1.19+80 + output = (1+temperature*0.16)^1.08+35 + min_efficiency = 84 + max_efficiency = 205 + max_output = 150 + bonus_cells_multiplier = 1.45 + + --Plutonium Energy +-- elseif fuel_cell_name == "plutonium-fuel-cell" then --plutonium -- this is pre PlutoniumEnergy 1.0.0 +-- output = (2.2+temperature*0.034)^0.57*25+10 +-- efficiency = (2.5+temperature*0.034)^0.45*39.5+45 +-- min_efficiency = 102 +-- max_efficiency = 195 +-- max_output = 145 + + -- "MOX fuel cell" - plutonium energy (20GJ) + elseif fuel_cell_name == "MOX-fuel" then + efficiency = ((3+temperature*0.11)^1.19+80) + output = ((1+temperature*0.16)^1.08+35)*0.5 + min_efficiency = 84 + max_efficiency = 205 + max_output = 150*0.5 + bonus_cells_multiplier = 0.6 + + -- "Thorium fuel cell" - MadClown01's AngelBob Nuclear Extension + elseif fuel_cell_name == "thorium-fuel-cell" or fuel_cell_name == "apm_breeder_thorium_loaded" or fuel_cell_name == "apm_fuel_rod_thorium" then + efficiency= 205-(1.5+temperature*0.1)^1.45*0.2 + output = 155-(1.5+temperature*0.1)^1.6*0.08 + min_efficiency = 79 + max_efficiency = 204 + max_output = 155 + bonus_cells_multiplier = 0.8 + + elseif fuel_cell_name == "apm_fuel_cell_mox" or fuel_cell_name == "apm_fuel_rod_mox" then + efficiency = ((3+temperature*0.11)^1.19+80) + output = ((1+temperature*0.16)^1.08+35)*0.5 + min_efficiency = 84 + max_efficiency = 205 + max_output = 150*0.5 + bonus_cells_multiplier = 0.8 + + elseif fuel_cell_name:sub(1,22) == "apm_nuclear_fuel_cell_" then + efficiency=(800+temperature*5)^0.64*0.96+29 + output=(1+temperature*0.07)^1.15*1.52*tonumber(fuel_cell_name:sub(23))/45+45 + min_efficiency = 94 + max_efficiency = 200 + max_output = 138 + + --Default/other fuel cells + else --uranium + efficiency=(800+temperature*5)^0.64*0.96+29 + output=(1+temperature*0.07)^1.15*1.52+45 + min_efficiency = 94 + max_efficiency = 200 + max_output = 138 + if game.active_mods["Nuclear Fuel"] then + bonus_cells_multiplier = 0.2 + else + bonus_cells_multiplier = 0.8 + end + end + + --breeder + if reactor.entity.name == BREEDER_ENTITY_NAME then + bonus_cells = (0.15+(efficiency-min_efficiency)/(max_efficiency-min_efficiency)*0.85)*bonus_cells_multiplier --15% - 100% *multiplier + output = output /(1+(efficiency-min_efficiency)/(max_efficiency-min_efficiency)*0.75) + max_output = max_output /(1+(max_efficiency-min_efficiency)/(max_efficiency-min_efficiency)*0.75) + efficiency = efficiency/(1.15+(efficiency-min_efficiency)/(max_efficiency-min_efficiency)*0.85) + max_efficiency = max_efficiency/(1.15+(max_efficiency-min_efficiency)/(max_efficiency-min_efficiency)*0.85) + end + --reactor count modifier + efficiency = efficiency*(0.7+0.075*reactors) + --max_efficiency = math.floor(max_efficiency*(0.7+0.075*reactors)) + output = output / 1.1 -- why did i do this? Oo + max_output = max_output/1.1 -- why did i do this? Oo + output = output *(0.5+0.125*reactors) + max_output = max_output*(0.5+0.125*reactors) + output = output * (1+neighbours/100) + max_output = max_output * (1+neighbours/100) + output = math.min(250,output) + max_output = math.min(250,max_output) + + + if reactor.state == 1 then + local duration = Setting.duration("starting") + output = math.floor(output * ((running_time)/duration)^2) + bonus_cells = bonus_cells * ((running_time)/duration)^2 + end + if reactor.state == 3 then + local duration = Setting.duration("scram") + output = math.floor(reactor.power_output_last_tick * ((duration - (running_time)/3.5)/duration)^11+0.45) + bonus_cells = bonus_cells * ((duration - (running_time)/3.5)/duration)^11 + end + --game.print(output) + reactor.max_power = max_output + reactor.max_efficiency = max_efficiency --under optimal conditions - 4 reactors + if reactor.entity.name == REACTOR_ENTITY_NAME then + return {power=math.floor(output),efficiency = efficiency, bonus_cells = 0, max_power = max_power, max_efficiency = max_efficiency} + else + return {power=math.floor(output),efficiency = efficiency, bonus_cells = bonus_cells, max_power = max_output, max_efficiency = max_efficiency} + end +end + + + + + + +return { -- exports + [ "ingo"] = calculate_stats_ingo, + ["ownly"] = calculate_stats_ownly, +} diff --git a/scripts/fx.lua b/scripts/fx.lua new file mode 100644 index 0000000..234e59e --- /dev/null +++ b/scripts/fx.lua @@ -0,0 +1,160 @@ +local rpath = (...):match("(.-)[^%.]+$") +local Setting = require(rpath .. "setting") + + +-- TODO merge geiger into geiger-counter +-- FIXME use damage type resistances for radiation via data + + +local function periodic_pollution(entity,mult) --on_tick + entity.surface.pollute(entity.position, math.floor(20000*mult)) --0.002% evo +end + + +-- radiation damage function +local function radio_damage(entity,force,tick) + if entity.type == "character" then + --sound + if entity.player then + if global.geigers[entity.player.index] == nil + or global.geigers[entity.player.index]+3 < tick then + + local index = math.floor(global.random()*1.99) + global.geigers[entity.player.index] = tick + + entity.player.play_sound{ + path = "RR-geiger-" .. index, + volume_modifier =0.7, + } + + end + end + --damage + local resist = 1 --resistances + if entity.grid then + if entity.grid.max_shield > 0 then + resist = 1 - math.min(35,entity.grid.shield)/35 + entity.damage(math.min(entity.grid.shield, 35)+0.0001,game.forces.neutral,"electric") + else + entity.damage(0.0001,game.forces.neutral,"electric") + end + if entity.grid.shield > 0 then return end + if entity.grid.prototype.name == "radiation-suit-grid" then + resist = resist*0.1 + elseif entity.grid.prototype.name == "small-equipment-grid" then + resist = resist*0.85 + elseif entity.grid.prototype.name == "medium-equipment-grid" then + resist = resist*0.7 + else + resist = resist*0.55 + end + else + entity.damage(0.0001,game.forces.neutral,"electric") + end + if force == "radioactivity" then + entity.health = entity.health -0.25*resist + entity.damage(0.13*resist,game.forces.neutral,"electric") + else + entity.health = entity.health -0.4*resist + entity.damage(0.2*resist,game.forces.neutral,"electric") + end + if entity.health < 1 then + entity.die(force) + end + else + if force == "radioactivity" then + entity.health = entity.health -0.4 + else + entity.health = entity.health -0.6 + end + if entity.health < 1 then + entity.die(force) + end + + end +end + + +-- radiation damage (event) +local function on_script_trigger_effect(effect_id, entity, force, tick) + if (effect_id == "radiation-damage" or effect_id == "radiation-damage-strong") and entity then + if effect_id == "radiation-damage" then + force = "radioactivity" + else + force = "radioactivity-strong" + end + if entity.type == "car" then + + local passenger = entity.get_passenger() --passenger + if passenger and passenger.type == "character" and passenger.has_flag("breaths-air") then + radio_damage(passenger, force, tick) + end + + local passenger = entity.get_driver() --driver + if passenger and passenger.name == "character" and passenger.has_flag("breaths-air") then + radio_damage(passenger, force, tick) + end + + elseif entity.type == "character" and entity.has_flag("breaths-air") then + radio_damage(entity, force, tick) + + elseif entity.has_flag("breaths-air") then + radio_damage(entity, force, tick) + end + end +end + + +--fallout stuff... +local function circular_radiation(surface,position,min_radius,size) + local step = 3.2 + if min_radius == 0 then + surface.create_entity{ + name = "permanent-radiation", + position = position, + force = "radioactivity", + } + surface.create_entity{ + name = "permanent-radiation", + position = position, + force = "radioactivity", + } + min_radius = min_radius + 1 + end + for spread = min_radius, size do --each run adds another layer + local x = position.x-step/2*spread + local y = position.y-step/2*spread + for i=1, spread*4 do + + surface.create_entity{ + name = "permanent-radiation", + position = {x,y}, + force = "radioactivity", + } + if i <= spread then + x=x+step + y=y-step*0.7*(1- i / ((spread+1)/2)) --almost perfect circle + + elseif i <= spread * 2 then + y=y+step + x=x+step*0.7*(1- (i-spread) / ((spread+1)/2)) + + elseif i <= spread * 3 then + x=x-step + y=y+step*0.7* (1- (i-spread*2) / ((spread+1)/2)) + + elseif i <= spread * 4 then + y=y-step + x=x-step*0.7*(1- (i-spread*3) / ((spread+1)/2)) + + end + end + end +end + + +return { -- exports + effect = on_script_trigger_effect, + periodic_pollution = periodic_pollution, + circular_radiation = circular_radiation, +} diff --git a/scripts/gui/init.lua b/scripts/gui/init.lua new file mode 100644 index 0000000..8f83899 --- /dev/null +++ b/scripts/gui/init.lua @@ -0,0 +1,21 @@ +local rpath = (...):match("(.-)[^%.]+$") +local stats = require(rpath .. "stats") + + +local function init() + global.gui_count = 0 + global.stats = {} + global.guis = {} +end + + +local exports = { -- exports + init = init, +} + +for name, export in pairs(stats) do + exports[name] = export +end + +return exports + diff --git a/scripts/gui/stats.lua b/scripts/gui/stats.lua new file mode 100644 index 0000000..a18d871 --- /dev/null +++ b/scripts/gui/stats.lua @@ -0,0 +1,824 @@ +--copyright ownlyme, written for realistic reactors (free to use/distribute/edit/build upon as a module of this mod or other mods by ingoknieto) +--with minor_ bugfixes from dodo + +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local Setting = require(rroot .. "setting") +local tablemax = require(rroot .. "util").tablemax +local noop = require(rroot .. "util").noop +local util = require(rpath .. "util") +local string2sprite = util.string2sprite +local string2color = util.string2color +local splitty = util.splitty + + +-- local gui constants +local COLUMN_COUNT = 70 +local PIXEL_WIDTH = 2 +local FONT_SIZE = 12 +local GRAPH_HEIGHT = 101 + +local function create_graph(gui) + + gui.add{type = "sprite", name = "graph", sprite="rr-black-background", direction = "vertical"} + gui.graph.style.width=math.floor(80*PIXEL_WIDTH)+32 + gui.graph.style.height=GRAPH_HEIGHT + gui.graph.style.stretch_image_to_widget_size =true + --gui.graph.style.scaleable = false + gui.graph.add{type = "table", column_count=COLUMN_COUNT+1, name = "table", direction = "vertical"} + gui.graph.table.style.horizontal_spacing=0 + gui.graph.table.style.vertical_spacing=0 + --gui.graph.table.style.scaleable = false + + for x=1,COLUMN_COUNT+1 do + gui.graph.table.add{type = "table", column_count=1, name = "col_"..x, direction = "vertical"} + gui.graph.table["col_"..x].style.horizontal_spacing=0 + gui.graph.table["col_"..x].style.vertical_spacing=0 + gui.graph.table["col_"..x].style.minimal_height=GRAPH_HEIGHT + gui.graph.table["col_"..x].style.maximal_height=GRAPH_HEIGHT + --gui.graph.table["col_"..x].style.scaleable = false + if x>COLUMN_COUNT then + gui.graph.table["col_"..x].style.left_padding = 3 + end + --gui.graph.table["col_"..x].add{type = "flow", name = "space_5", direction = "vertical"} + --gui.graph.table["col_"..x]["space_5"].style.maximal_height=100 + --gui.graph.table["col_"..x]["space_5"].style.minimal_height=1 + --gui.graph.table["col_"..x]["space_5"].style.maximal_width=PIXEL_WIDTH + --gui.graph.table["col_"..x]["space_5"].style.minimal_width=PIXEL_WIDTH + --gui.graph.table["col_"..x]["space_5"].style.scaleable = false + + end + for x=1,COLUMN_COUNT+1 do + for y=4,1,-1 do + gui.graph.table["col_"..x].add{type = "flow", name = "space_"..y, direction = "vertical"} + gui.graph.table["col_"..x]["space_"..y].style.maximal_height=100 + gui.graph.table["col_"..x]["space_"..y].style.minimal_height=1 + gui.graph.table["col_"..x]["space_"..y].style.maximal_width=PIXEL_WIDTH + gui.graph.table["col_"..x]["space_"..y].style.minimal_width=PIXEL_WIDTH + --gui.graph.table["col_"..x]["space_"..y].style.scaleable = false + if x <=COLUMN_COUNT then + gui.graph.table["col_"..x].add{type = "sprite", name = "dot_"..y.."_2", direction = "vertical"} + gui.graph.table["col_"..x]["dot_"..y.."_2"].style.maximal_height=1 + gui.graph.table["col_"..x]["dot_"..y.."_2"].style.maximal_width=PIXEL_WIDTH + gui.graph.table["col_"..x]["dot_"..y.."_2"].style.minimal_width=PIXEL_WIDTH + gui.graph.table["col_"..x]["dot_"..y.."_2"].sprite="rr-black" + --gui.graph.table["col_"..x]["dot_"..y.."_2"].value=1 + --gui.graph.table["col_"..x]["dot_"..y.."_2"].style.color={r=0,g=0,b=0} + --gui.graph.table["col_"..x]["dot_"..y.."_2"].style.scaleable = false + + gui.graph.table["col_"..x].add{type = "sprite", name = "dot_"..y.."_1", direction = "vertical"} + gui.graph.table["col_"..x]["dot_"..y.."_1"].style.maximal_height=1 + gui.graph.table["col_"..x]["dot_"..y.."_1"].style.maximal_width=PIXEL_WIDTH + gui.graph.table["col_"..x]["dot_"..y.."_1"].style.minimal_width=PIXEL_WIDTH + gui.graph.table["col_"..x]["dot_"..y.."_1"].sprite="rr-black" + --gui.graph.table["col_"..x]["dot_"..y.."_1"].value=1 + --gui.graph.table["col_"..x]["dot_"..y.."_1"].style.color={r=0,g=0,b=0} + --gui.graph.table["col_"..x]["dot_"..y.."_1"].style.scaleable = false + else + gui.graph.table["col_"..x].add{type = "label", name = "text_"..y, direction = "vertical"} + gui.graph.table["col_"..x]["text_"..y].caption = "" + gui.graph.table["col_"..x]["text_"..y].style.height = FONT_SIZE + --gui.graph.table["col_"..x]["text_"..y].style.vertical_align = "top" + --gui.graph.table["col_"..x]["text_"..y].style.single_line =false + --gui.graph.table["col_"..x]["text_"..y].style.want_ellipsis =false + gui.graph.table["col_"..x]["text_"..y].style.font = "rr-small-bold" + gui.graph.table["col_"..x]["text_"..y].style.top_padding = 0 + gui.graph.table["col_"..x]["text_"..y].style.right_padding = 0 + gui.graph.table["col_"..x]["text_"..y].style.bottom_padding = 0 + gui.graph.table["col_"..x]["text_"..y].style.left_padding = 0 + --gui.graph.table["col_"..x]["text_"..y].style.scaleable = false + + end + end + end +end + +local function create_signals(gui,reactor_key) + gui.add{type = "table", column_count = 6, name = "signals", direction = "horizontal"} + gui.signals.style.top_padding = 0 + gui.signals.style.right_padding = 0 + gui.signals.style.bottom_padding = 0 + gui.signals.style.left_padding = 0 + gui.signals.style.horizontal_spacing= 0 + gui.signals.style.vertical_spacing=0 + --gui.signals.style.scaleable = false + + gui.signals.add{type = "sprite-button", name = "temperature", sprite="virtual-signal/signal-reactor-core-temp", direction = "vertical", style = "slot_button"} + gui.signals.temperature.number=0 + gui.signals.temperature.style.height = 32 + gui.signals.temperature.style.width = 32 + gui.signals.temperature.style.top_padding = 0 + gui.signals.temperature.style.right_padding = 0 + gui.signals.temperature.style.bottom_padding = 0 + gui.signals.temperature.style.left_padding = 0 + --gui.signals.temperature.style.scaleable = false + gui.signals.temperature.ignored_by_interaction =true + + gui.signals.add{type = "sprite-button", name = "power", sprite="virtual-signal/signal-reactor-power-output", direction = "vertical", style = "slot_button"} + gui.signals.power.number=0 + gui.signals.power.style.height = 32 + gui.signals.power.style.width = 32 + gui.signals.power.style.top_padding = 0 + gui.signals.power.style.right_padding = 0 + gui.signals.power.style.bottom_padding = 0 + gui.signals.power.style.left_padding = 0 + --gui.signals.power.style.scaleable = false + gui.signals.power.ignored_by_interaction =true + + gui.signals.add{type = "sprite-button", name = "efficiency", sprite="virtual-signal/signal-reactor-efficiency", direction = "vertical", style = "slot_button"} + gui.signals.efficiency.number=0 + gui.signals.efficiency.style.height = 32 + gui.signals.efficiency.style.width = 32 + gui.signals.efficiency.style.top_padding = 0 + gui.signals.efficiency.style.right_padding = 0 + gui.signals.efficiency.style.bottom_padding = 0 + gui.signals.efficiency.style.left_padding = 0 + --gui.signals.efficiency.style.scaleable = false + gui.signals.efficiency.ignored_by_interaction =true + + if global.reactors[reactor_key].entity.name == BREEDER_ENTITY_NAME then + gui.signals.add{type = "sprite-button", name = "bonuscells", sprite="virtual-signal/signal-reactor-cell-bonus", direction = "vertical", style = "slot_button"} + gui.signals.bonuscells.number=0 + gui.signals.bonuscells.style.height = 32 + gui.signals.bonuscells.style.width = 32 + gui.signals.bonuscells.style.top_padding = 0 + gui.signals.bonuscells.style.right_padding = 0 + gui.signals.bonuscells.style.bottom_padding = 0 + gui.signals.bonuscells.style.left_padding = 0 + --gui.signals.bonuscells.style.scaleable = false + gui.signals.bonuscells.show_percent_for_small_numbers =true + gui.signals.bonuscells.ignored_by_interaction =true + end + + gui.signals.add{type = "sprite-button", name = "coolant", sprite="virtual-signal/signal-coolant-amount", direction = "vertical", style = "slot_button"} + gui.signals.coolant.number=0 + gui.signals.coolant.style.height = 32 + gui.signals.coolant.style.width = 32 + gui.signals.coolant.style.top_padding = 0 + gui.signals.coolant.style.right_padding = 0 + gui.signals.coolant.style.bottom_padding = 0 + gui.signals.coolant.style.left_padding = 0 + --gui.signals.coolant.style.scaleable = false + gui.signals.coolant.ignored_by_interaction = true + +-- gui.signals.add{type = "sprite-button", name = "ups", sprite="virtual-signal/signal-reactor-electric-power", direction = "vertical", style = "slot_button"} +-- gui.signals.ups.number = 0 +-- gui.signals.ups.style.height = 32 +-- gui.signals.ups.style.width = 32 +-- gui.signals.ups.style.top_padding = 0 +-- gui.signals.ups.style.right_padding = 0 +-- gui.signals.ups.style.bottom_padding = 0 +-- gui.signals.ups.style.left_padding = 0 +-- --gui.signals.ups.style.scaleable = false +-- gui.signals.ups.ignored_by_interaction = true + + gui.signals.add{ + type = "sprite-button", + name = "status", + sprite = "virtual-signal/signal-state-stopped", + style = "slot_button", + number = 0, + ignored_by_interaction = true, -- FIXME add status button anywhere else or use text + } + gui.signals.status.style.height = 32 + gui.signals.status.style.width = 32 + gui.signals.status.style.top_padding = 0 + gui.signals.status.style.right_padding = 0 + gui.signals.status.style.bottom_padding = 0 + gui.signals.status.style.left_padding = 0 + --gui.signals.status.style.scaleable = false + +end + +local function create_progress(gui,reactor_key) + local cols = 3 + if #global.reactors[reactor_key].entity.get_burnt_result_inventory() > 1 then + cols = 4 + end + gui.add{type = "table", column_count = cols, name = "progress", direction = "horizontal"} + gui.progress.style.top_padding = 0 + gui.progress.style.right_padding = 0 + gui.progress.style.bottom_padding = 0 + gui.progress.style.left_padding = 0 + gui.progress.style.horizontal_spacing= 1 + gui.progress.style.vertical_spacing=1 + --gui.progress.style.scaleable = false + + + gui.progress.add{type = "sprite-button", name = "cells", sprite="rr-mini-sprite", direction = "vertical", style = "rr_button"} + gui.progress.cells.style.maximal_height = 25 + gui.progress.cells.style.maximal_width = 25 + gui.progress.cells.style.top_padding = 0 + gui.progress.cells.style.right_padding = 0 + gui.progress.cells.style.bottom_padding = 0 + gui.progress.cells.style.left_padding = 0 + --gui.progress.cells.style.scaleable = false + gui.progress.cells.ignored_by_interaction =true + + + gui.progress.add{type = "table", column_count = 1, name = "bars", direction = "vertical"} + gui.progress.bars.style.maximal_height = 28 + gui.progress.bars.style.maximal_width = 140 + gui.progress.bars.style.horizontal_spacing=0 + gui.progress.bars.style.vertical_spacing=0 + gui.progress.bars.style.top_padding = 4 + gui.progress.bars.style.right_padding = 0 + gui.progress.bars.style.bottom_padding = 0 + gui.progress.bars.style.left_padding = 0 + --gui.progress.bars.style.scaleable = false + + + gui.progress.bars.add{type = "progressbar", name = "progress", direction = "vertical"} + gui.progress.bars.progress.style.height=12 + gui.progress.bars.progress.style.width=140 + gui.progress.bars.progress.style.top_padding = 0 + gui.progress.bars.progress.style.right_padding = 0 + gui.progress.bars.progress.style.bottom_padding = 0 + gui.progress.bars.progress.style.left_padding = 0 + gui.progress.bars.progress.value=0 + gui.progress.bars.progress.style.color={r=1,g=0,b=0} + --gui.progress.bars.progress.style.scaleable = false + + if global.reactors[reactor_key].entity.name == BREEDER_ENTITY_NAME then + gui.progress.bars.add{type = "progressbar", name = "bonus_cells", direction = "vertical"} + gui.progress.bars.bonus_cells.style.height=12 + gui.progress.bars.bonus_cells.style.width=140 + gui.progress.bars.bonus_cells.style.top_padding = 0 + gui.progress.bars.bonus_cells.style.right_padding = 0 + gui.progress.bars.bonus_cells.style.bottom_padding = 0 + gui.progress.bars.bonus_cells.style.left_padding = 0 + gui.progress.bars.bonus_cells.value=0 + gui.progress.bars.bonus_cells.style.color={r=0.8,g=0,b=0.8} + --gui.progress.bars.bonus_cells.style.scaleable = false + else + gui.progress.bars.style.top_padding = 2 + end + + + gui.progress.add{type = "sprite-button", name = "used_cells", sprite="rr-transparent-sprite", direction = "vertical", style = "rr_button"} + gui.progress.used_cells.style.height = 25 + gui.progress.used_cells.style.width = 25 + gui.progress.used_cells.style.top_padding = 0 + gui.progress.used_cells.style.right_padding = 0 + gui.progress.used_cells.style.bottom_padding = 0 + gui.progress.used_cells.style.left_padding = 0 + --gui.progress.used_cells.style.scaleable = false + gui.progress.used_cells.ignored_by_interaction =true + if #global.reactors[reactor_key].entity.get_burnt_result_inventory() > 1 then + gui.progress.add{type = "sprite-button", name = "used_cells2", sprite="rr-transparent-sprite", direction = "vertical", style = "rr_button"} + gui.progress.used_cells2.style.height = 25 + gui.progress.used_cells2.style.width = 25 + gui.progress.used_cells2.style.top_padding = 0 + gui.progress.used_cells2.style.right_padding = 0 + gui.progress.used_cells2.style.bottom_padding = 0 + gui.progress.used_cells2.style.left_padding = 0 + --gui.progress.used_cells.style.scaleable = false + gui.progress.used_cells2.ignored_by_interaction = true + gui.progress.bars.progress.style.width=115 + gui.progress.bars.bonus_cells.style.width = 115 + + end + +end + +local function create_gui(player, reactor_key) + local reactor = global.reactors[reactor_key] + local gui = player.gui.left.add{type = "frame", name = "rr_gui_"..reactor_key, caption = "", direction = "vertical"} + gui.style.top_padding = 1 + gui.style.right_padding = 4 + gui.style.bottom_padding = 4 + gui.style.left_padding = 4 + --gui.style.scaleable = false + + if not global.stats[reactor_key] then + global.stats[reactor_key] = {} + global.stats[reactor_key].temperature = {} + global.stats[reactor_key].efficiency = {} + global.stats[reactor_key].production = {} + global.stats[reactor_key].aqua = {} + global.stats[reactor_key].max = 1 + end + gui.add{type = "table", column_count = 4, name = "rr_buttons_flow", direction = "horizontal"} + gui.rr_buttons_flow.style.top_padding = 0 + gui.rr_buttons_flow.style.right_padding = 0 + gui.rr_buttons_flow.style.bottom_padding = 0 + gui.rr_buttons_flow.style.left_padding = 13 + gui.rr_buttons_flow.style.horizontal_spacing = 20 + --gui.rr_buttons_flow.style.scaleable = false + + gui.rr_buttons_flow.add{type = "sprite-button",name = "rr_button_signals", sprite = "rr-button-signals", style = "rr_button"} + gui.rr_buttons_flow.rr_button_signals.style.top_padding = 0 + gui.rr_buttons_flow.rr_button_signals.style.right_padding = 0 + gui.rr_buttons_flow.rr_button_signals.style.bottom_padding = 0 + gui.rr_buttons_flow.rr_button_signals.style.left_padding = 0 + gui.rr_buttons_flow.rr_button_signals.style.height = 14 + gui.rr_buttons_flow.rr_button_signals.style.width = 40 + --gui.rr_buttons_flow.rr_button_signals.style.scaleable = false + + gui.rr_buttons_flow.add{type = "sprite-button",name = "rr_button_progress", sprite = "rr-button-progress", style = "rr_button"} + gui.rr_buttons_flow.rr_button_progress.style.top_padding = 0 + gui.rr_buttons_flow.rr_button_progress.style.right_padding = 0 + gui.rr_buttons_flow.rr_button_progress.style.bottom_padding = 0 + gui.rr_buttons_flow.rr_button_progress.style.left_padding = 0 + gui.rr_buttons_flow.rr_button_progress.style.height = 14 + gui.rr_buttons_flow.rr_button_progress.style.width = 30 + --gui.rr_buttons_flow.rr_button_progress.style.scaleable = false + + gui.rr_buttons_flow.add{type = "sprite-button",name = "rr_button_graph", sprite = "rr-button-graph-off", style = "rr_button"} + gui.rr_buttons_flow.rr_button_graph.style.top_padding = 0 + gui.rr_buttons_flow.rr_button_graph.style.right_padding = 0 + gui.rr_buttons_flow.rr_button_graph.style.bottom_padding = 0 + gui.rr_buttons_flow.rr_button_graph.style.left_padding = 0 + gui.rr_buttons_flow.rr_button_graph.style.height = 14 + gui.rr_buttons_flow.rr_button_graph.style.width = 30 + --gui.rr_buttons_flow.rr_button_graph.style.scaleable = false + + + --gui.rr_buttons_flow.add{type = "flow", name = "titlebar_spacer", direction = "horizontal"} + --gui.rr_buttons_flow.titlebar_spacer.style.width=2 + --gui.rr_buttons_flow.titlebar_spacer.style.height=1 + --gui.rr_buttons_flow.titlebar_spacer.style.scaleable = false + + + gui.rr_buttons_flow.add{type = "sprite-button",name = "rr_button_exit", sprite = "rr-button-x", style = "rr_button"} + gui.rr_buttons_flow.rr_button_exit.style.top_padding = 0 + gui.rr_buttons_flow.rr_button_exit.style.right_padding = 0 + gui.rr_buttons_flow.rr_button_exit.style.bottom_padding = 0 + gui.rr_buttons_flow.rr_button_exit.style.left_padding = 0 + gui.rr_buttons_flow.rr_button_exit.style.height = 18 + gui.rr_buttons_flow.rr_button_exit.style.width = 18 + --gui.rr_buttons_flow.rr_button_exit.style.scaleable = false + + create_signals(gui,reactor_key) + create_progress(gui,reactor_key) + + global.gui_count = global.gui_count + 1 + global.guis[reactor_key.."-"..player.index] = gui + +end + + +local function clear_invalid_gui() + clear_invalid_gui = noop + for key, gui in pairs(global.guis) do + if not gui.valid then -- might be opened in map editor + log("removed invalid gui " .. key) + local reactor_key, playerid = splitty(key,"-") + global.stats[reactor_key] = nil + global.guis[key] = nil + global.gui_count = global.gui_count - 1 + end + end +end + +local function on_tick(tick) + clear_invalid_gui() + + if tick % (TICKS_PER_UPDATE*2) == 0 then + --if not global.stats[reactor_key] then global.stats[reactor_key] = {efficiency = {},production = {},temperature = {}, aqua = {}} end + for reactor_key, stats in pairs (global.stats) do + if not global.reactors[reactor_key] then + global.stats[reactor_key] = nil + else + signals = global.reactors[reactor_key].signals.parameters + if global.reactors[reactor_key].power_usage.interface > 1 then + stats.temperature[COLUMN_COUNT] = math.min(100,math.max(1,math.floor((signals["core_temperature"].count or 1)/10))) + stats.efficiency[COLUMN_COUNT] = math.min(100,math.max(1,math.floor((signals["efficiency"].count or 1)/global.reactors[reactor_key].max_efficiency*100))) + + stats.production[COLUMN_COUNT] = signals["power-output"].count or 1 --math.min(100,math.max(1, + stats.aqua[COLUMN_COUNT] = global.reactors[reactor_key].cooling_history + else + stats.efficiency[COLUMN_COUNT] = -1 + stats.production[COLUMN_COUNT] = -1 + stats.temperature[COLUMN_COUNT] = -1 + stats.aqua[COLUMN_COUNT] = -1 + + end + --local cooling = 0 + --for i=1,math.floor(1/(TICKS_PER_UPDATE/30)) do + -- cooling = cooling + global.reactors[reactor_key].cooling_history[i] + --end + --stats.aqua[COLUMN_COUNT] = math.floor(cooling / math.floor(1/(TICKS_PER_UPDATE/30))/2) + + --game.players[1].print(stats.aqua[COLUMN_COUNT]) + for k=1,COLUMN_COUNT-1 do + stats.efficiency[k] = stats.efficiency[k+1] + stats.production[k] = stats.production[k+1] + stats.temperature[k] = stats.temperature[k+1] + stats.aqua[k]=stats.aqua[k+1] + end + local maxpower = {tablemax(stats.production), tablemax(stats.aqua),global.reactors[reactor_key].max_power} + stats.max = tablemax(maxpower) + + end + end + end + + if tick % TICKS_PER_UPDATE == 0 then + for key, gui in pairs(global.guis) do + local reactor_key, playerid = splitty(key,"-") + + local reactor = global.reactors[reactor_key] + + if not (reactor and reactor.entity.valid and global.stats[reactor_key]) then + global.stats[reactor_key] = nil + gui.destroy() + global.guis[key] = nil + global.gui_count = global.gui_count - 1 + else + local signals = reactor.signals.parameters + if gui.signals then + gui.signals.temperature.number = math.floor(signals["core_temperature"].count) +-- gui.signals.ups.number = math.floor(signals["electric-power"].count) + gui.signals.power.number = math.floor(signals["power-output"].count) + gui.signals.efficiency.number = math.floor(signals["efficiency"].count) + if reactor.entity.name == BREEDER_ENTITY_NAME then + gui.signals.bonuscells.number = math.floor(signals["cell-bonus"].count)/100 + end + gui.signals.coolant.number=signals["coolant-amount"].count + if signals["state_stopped"].count > 0 then + gui.signals.status.number=signals["state_stopped"].count + gui.signals.status.sprite="virtual-signal/signal-state-stopped" + elseif signals["state_starting"].count > 0 then + gui.signals.status.number=signals["state_starting"].count + gui.signals.status.sprite="virtual-signal/signal-state-starting" + elseif signals["state_running"].count > 0 then + gui.signals.status.number=signals["state_running"].count + gui.signals.status.sprite="virtual-signal/signal-state-running" + elseif signals["state_scramed"].count > 0 then + gui.signals.status.number=signals["state_scramed"].count + gui.signals.status.sprite="virtual-signal/signal-state-scramed" + end + end + if gui.progress then + if reactor.entity.get_fuel_inventory().is_empty() then + gui.progress.cells.number = nil + gui.progress.cells.sprite = "rr-transparent-sprite" + else + gui.progress.cells.number = reactor.entity.get_fuel_inventory()[1].count + gui.progress.cells.sprite = "item/"..reactor.entity.get_fuel_inventory()[1].name + end + + if reactor.entity.get_burnt_result_inventory().is_empty() then + gui.progress.used_cells.number = nil + gui.progress.used_cells.sprite = "rr-transparent-sprite" + if gui.progress.used_cells2 then + gui.progress.used_cells2.number = nil + gui.progress.used_cells2.sprite = "rr-transparent-sprite" + end + else + local inv1 = reactor.entity.get_burnt_result_inventory()[1] + gui.progress.used_cells.number = inv1.valid_for_read and inv1.count or nil + gui.progress.used_cells.sprite = inv1.valid_for_read and "item/"..inv1.name or "rr-transparent-sprite" + if gui.progress.used_cells2 then + local inv2 = reactor.entity.get_burnt_result_inventory()[2] + gui.progress.used_cells2.number = inv2.valid_for_read and inv2.count or nil + gui.progress.used_cells2.sprite = inv2.valid_for_read and "item/"..inv2.name or "rr-transparent-sprite" + end + end + + if reactor.entity.burner.currently_burning == nil then + gui.progress.bars.progress.value=0 + if reactor.entity.name == BREEDER_ENTITY_NAME then + gui.progress.bars.bonus_cells.value=0 + end + else + local burnt_result = reactor.entity.burner.currently_burning.burnt_result.name + if burnt_result == "apm_fuel_cell_mox_used" then + burnt_result = "apm_nuclear_breeder_uranium_inventory_enriched" + end + --gui.progress.cells.sprite = "item/"..reactor.entity.burner.currently_burning.name + --gui.progress.used_cells.sprite = "item/"..burnt_result.name + gui.progress.bars.progress.value = reactor.entity.burner.remaining_burning_fuel / reactor.entity.burner.currently_burning.fuel_value + + if reactor.entity.name == BREEDER_ENTITY_NAME then + gui.progress.bars.bonus_cells.value = math.min(1,reactor.bonus_cells[burnt_result] or 0) + end + end + end + end + end + end + + --process graphs + if tick % (TICKS_PER_UPDATE*2) == 0 then + for key, gui in pairs(global.guis) do + if gui.graph then + local reactor_key,playerid = splitty(key,"-") + if not (global.reactors[reactor_key] and global.stats[reactor_key]) then + global.stats[reactor_key] = nil + gui.destroy() + global.guis[key] = nil + global.gui_count = global.gui_count - 1 + else + if gui.graph.valid then + local stats = global.stats[reactor_key] + for k, temperature in pairs(stats.temperature) do + local colors={} + local production = math.min(100,math.max(1,math.floor( stats.production[k] / stats.max * 100 ))) + local aqua = math.min(100,math.max(1,math.floor( stats.aqua[k] / stats.max * 100 ))) + + colors[production] = "" + colors[stats.efficiency[k]] = "" + colors[aqua] = "" + colors[production+1] = "" + colors[stats.efficiency[k]+1] = "" + colors[aqua+1] = "" + + colors[stats.temperature[k]] = "r" + colors[stats.temperature[k]+1] = "r" + colors[production] = colors[production] .."y" + colors[production+1] = colors[production+1] .."y" + colors[stats.efficiency[k]] = colors[stats.efficiency[k]] .."b" + colors[stats.efficiency[k]+1] = colors[stats.efficiency[k]+1] .."b" + colors[aqua] = colors[aqua].."a" + colors[aqua+1] = colors[aqua+1].."a" + + local height = 0 + local counter = 1 + local value,color = next(colors) + + while value do + if value >0 then + if counter > 1 then + gui.graph.table["col_".. k]["space_"..counter-1].style.minimal_height=math.max(0,value-height) + end + + height=value+2 + gui.graph.table["col_".. k]["dot_"..counter.."_1"].sprite=string2sprite(color) + if colors[value+1] then + gui.graph.table["col_".. k]["dot_"..counter.."_2"].sprite=string2sprite(colors[value+1]) + value,color = next(colors,value) + else + gui.graph.table["col_".. k]["dot_"..counter.."_2"].sprite="rr-black" + end + counter=counter+1 + end + value,color = next(colors,value) + end + for i=counter,4 do + if i~=1 then + gui.graph.table["col_".. k]["space_"..i-1].style.minimal_height=0 + end + gui.graph.table["col_".. k]["dot_"..i.."_1"].sprite = "rr-black" + gui.graph.table["col_".. k]["dot_"..i.."_2"].sprite = "rr-black" + height=height+2 + + end + gui.graph.table["col_".. k]["space_".. 4].style.minimal_height=math.max(0,103-height) + end + local temp_stats = {} + local signals = global.reactors[reactor_key].signals.parameters + + local production = math.min(100,math.max(1,math.floor( stats.production[COLUMN_COUNT] / stats.max * 100 ))) + local aqua = math.min(100,math.max(1,math.floor( stats.aqua[COLUMN_COUNT] / stats.max * 100 ))) + + freespot = 0 + while temp_stats[math.min(100,production+math.floor(FONT_SIZE/2))+freespot] do + freespot = freespot + 1 + end + temp_stats[math.min(100,production+math.floor(FONT_SIZE/2))+freespot] = "y" + --game.players[1].print(game.tick.." y = "..stats.production[COLUMN_COUNT]) + + freespot = 0 + while temp_stats[math.min(100,stats.efficiency[COLUMN_COUNT]+math.floor(FONT_SIZE/2))+freespot] do + freespot = freespot + 1 + end + temp_stats[math.min(100,stats.efficiency[COLUMN_COUNT]+math.floor(FONT_SIZE/2))+freespot] = "b" + --game.players[1].print(game.tick.." b = "..stats.efficiency[COLUMN_COUNT]) + + freespot = 0 + while temp_stats[math.min(100,aqua+math.floor(FONT_SIZE/2))+freespot] do + freespot = freespot + 1 + end + temp_stats[math.min(100,aqua+math.floor(FONT_SIZE/2))+freespot] = "a" + --game.players[1].print(game.tick.." a = "..stats.aqua[COLUMN_COUNT]) + + freespot = 0 + while temp_stats[math.min(100,stats.temperature[COLUMN_COUNT]+math.floor(FONT_SIZE/2))+freespot] do + freespot = freespot + 1 + end + temp_stats[math.min(100,stats.temperature[COLUMN_COUNT]+math.floor(FONT_SIZE/2))+freespot] = "r" + --game.players[1].print(game.tick.." r = "..stats.temperature[COLUMN_COUNT]) + + --local checked_stats = {} + local groups = {} + local i = 1 + + local currentvalue = next(temp_stats, nil) + while currentvalue do + groups[i] = {min_value = currentvalue, max_value = currentvalue, avg_value = currentvalue, count = 1, values = {}} + groups[i].values[currentvalue] = temp_stats[currentvalue] + local nextvalue = next(temp_stats,currentvalue) + local values = 1 + --game.players[1].print(game.tick.." group "..i.." "..currentvalue.." = "..temp_stats[currentvalue]) -- + --if nextvalue then -- + -- game.players[1].print(game.tick..i.." next: "..nextvalue) -- + --end -- + --game.players[1].print(i.." "..nextvalue - groups[i].min_value) + --if nextvalue and nextvalue - groups[i].min_value < 10 * groups[i].count then + -- game.players[1].print(game.tick.."GROUP: ") + --end + while nextvalue and nextvalue - groups[i].min_value < FONT_SIZE * groups[i].count do + groups[i].count = groups[i].count + 1 + groups[i].values[nextvalue] = temp_stats[nextvalue] + --game.players[1].print (game.tick.." "..groups[i].count..": "..nextvalue .." = "..temp_stats[nextvalue]) + local values_sum = 0 + for value, stat in pairs(groups[i].values) do + values_sum = values_sum + value + end + + groups[i].avg_value = values_sum / groups[i].count + groups[i].min_value = values_sum / groups[i].count - math.floor(FONT_SIZE/2) * (groups[i].count-1) + groups[i].max_value = values_sum / groups[i].count + math.floor(FONT_SIZE/2) * (groups[i].count-1) + if groups[i].min_value < 1 then + groups[i].max_value = groups[i].max_value + (1 - groups[i].min_value) + groups[i].min_value = 1 + end + if groups[i].max_value > 100 then + groups[i].min_value = groups[i].min_value - (groups[i].max_value - 100) + groups[i].max_value = 100 + end + nextvalue = next(temp_stats,nextvalue) + end + + if nextvalue then + currentvalue = nextvalue + i=i+1 + else + currentvalue = nil + end + end + + local last_maximal = 0 + for key, group in pairs(groups) do + if group.min_value < last_maximal+FONT_SIZE then + group.min_value = group.min_value + last_maximal - group.min_value+FONT_SIZE + group.max_value = group.max_value + last_maximal - group.min_value+FONT_SIZE + end + last_maximal = group.max_value + end + + local last_minimal = 100+FONT_SIZE + for key=i, 1, -1 do + local group = groups[key] + if group.max_value > last_minimal-FONT_SIZE then + group.min_value = group.min_value - (group.max_value - (last_minimal-FONT_SIZE)) + group.max_value = group.max_value - (group.max_value - (last_minimal-FONT_SIZE)) + end + last_minimal = group.min_value + end + + local temp_stats2 = {} + for key, group in pairs(groups) do + i = 0 + for _, stat in pairs(group.values) do + --if temp_stats2[math.floor(group.min_value + i*12)] then + -- game.players[1].print(i.."occupied") + --end + table.insert(temp_stats2,{value = math.floor(group.min_value + i*FONT_SIZE), stat = stat}) + --game.players[1].print(game.tick.." ("..i..") "..math.floor(group.min_value + i*FONT_SIZE).." = "..stat) + i = i+1 + end + end + local height = 100 + --for value,stat in pairs(temp_stats2) do + for counter = 4, 1, -1 do + local value = temp_stats2[counter].value + local stat = temp_stats2[counter].stat + + gui.graph.table["col_".. COLUMN_COUNT+1]["space_"..counter].style.height=math.max(0,height-value-1) + + --game.players[1].print(game.tick.." spacer "..counter.." = "..gui.graph.table["col_".. COLUMN_COUNT+1]["space_"..counter].style.minimal_height.."px, value = "..value) + height=height-math.max(0,height-value-1)-FONT_SIZE + --game.players[1].print(game.tick .." height:"..height-16 .." value:"..value.." color:"..stat) + if global.reactors[reactor_key].power_usage.interface >1 then + if stat=="r" then + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].caption = " "..math.floor(signals["core_temperature"].count).."°" + elseif stat=="y" then + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].caption = " "..signals["power-output"].count.."MW" + elseif stat=="b" then + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].caption = " "..signals["efficiency"].count.."%" + else + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].caption = math.floor(global.reactors[reactor_key].cooling_history)*-1 .."MW" + end + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].style.font_color = string2color(stat) + else + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].caption = "" + gui.graph.table["col_".. COLUMN_COUNT+1]["text_"..counter].style.font_color = {r=0,g=0,b=0} + end + end + --gui.graph.table["col_".. COLUMN_COUNT+1]["space_".. 5].style.height= 0 --height + --game.players[1].print(game.tick.." spacer 5 = "..gui.graph.table["col_".. COLUMN_COUNT+1]["space_5"].style.minimal_height.."px") + --game.players[1].print(100-height) + end + end + end + end + end +end + +local function on_gui_opened(player_index) + local player = game.players[player_index] + if player.opened_gui_type ~= defines.gui_type.entity then return end + local entity = player.opened + if entity and I2E_NAME[entity.name] then + local reactor_key + for key, reactor in pairs(global.reactors) do + if reactor.interface == entity then + reactor_key = key + break + end + end + if reactor_key then + local frame = player.gui.left["rr_gui_"..reactor_key] + if frame then + frame.destroy() + global.guis[reactor_key.."-"..player_index] = nil + global.gui_count = global.gui_count - 1 + else + create_gui(player,reactor_key) + end + end + player.opened = nil + end +end + +local function on_gui_clicked(player_index, element, tick) + if not element.parent or (element.parent.name ~="rr_buttons_flow" and element.parent.name ~="signals") then return end + local reactor_key = tonumber(string.sub(element.parent.parent.name,8)) -- cut off "rr_gui_" + if (not global.reactors[reactor_key]) or element.name == "rr_button_exit" then + global.guis[reactor_key.."-"..player_index] = nil + global.gui_count = global.gui_count -1 + element.parent.parent.destroy() + + elseif element.name == "rr_button_signals" then + if element.parent.parent.signals then + element.parent.parent.signals.destroy() + element.parent.rr_button_signals.sprite = "rr-button-signals-off" + else + create_signals(element.parent.parent,reactor_key) + element.parent.rr_button_signals.sprite = "rr-button-signals" + end + + elseif element.name == "rr_button_progress" then + if element.parent.parent.progress then + element.parent.parent.progress.destroy() + element.parent.rr_button_progress.sprite = "rr-button-progress-off" + else + create_progress(element.parent.parent,reactor_key) + element.parent.rr_button_progress.sprite = "rr-button-progress" + end + + elseif element.name == "rr_button_graph" then + if element.parent.parent.graph then + element.parent.parent.graph.destroy() + element.parent.rr_button_graph.sprite = "rr-button-graph-off" + else + create_graph(element.parent.parent) + element.parent.rr_button_graph.sprite = "rr-button-graph" + end + elseif element.name == "status" then + local reactor = global.reactors[reactor_key] + + local signal_scram = false + for _,wire in ipairs{defines.wire_type.green,defines.wire_type.red} do + local network = reactor.control.get_circuit_network(wire) + if network then + --logging("-> Found circuit network. Network ID: " .. network.network_id) + if network.get_signal(SIGNAL_CONTROL_SCRAM) > 0 then + signal_scram = true + --logging("--> found SCRAM signal") + end + end + end + game.players[player_index].print("[RealisticReactors] Reactor greets you!") + + if reactor.entity.get_fuel_inventory().is_empty() and reactor.entity.burner.remaining_burning_fuel == 0 then + game.players[player_index].print("[RealisticReactors] Reactor can't start, because it has no fuel cell.") + elseif reactor.power.energy < (POWER_USAGE_STARTING * TICKS_PER_UPDATE * 4 / 60 * Setting.map("energy-consumption-multiplier")) then + game.players[player_index].print("[RealisticReactors] Reactor can't start, because it has not enough electricity.") + elseif signal_scram then + game.players[player_index].print("[RealisticReactors] Reactor can't start, because there's a scram control signal on its circuit interface.") + else + if reactor.state == 3 then +-- if reactor.state == 0 then +-- change_reactor_state(1, reactor, tick) +-- update_reactor_states(reactor, tick) +-- elseif reactor.state == 1 or reactor.state == 2 then +-- change_reactor_state(3, reactor, tick) +-- update_reactor_states(reactor, tick) +-- else + game.players[player_index].print("[RealisticReactors] Reactor is already scramed.") + end + end + + end +end + + +return { -- exports + tick = on_tick, + opened = on_gui_opened, + clicked = on_gui_clicked, +} diff --git a/scripts/gui/util.lua b/scripts/gui/util.lua new file mode 100644 index 0000000..edb5dcf --- /dev/null +++ b/scripts/gui/util.lua @@ -0,0 +1,44 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local split = require(rroot .. "heat.util").split + + +local function string2sprite(ryba) + if ryba == "" then + return "rr-black" + else + return "rr-"..ryba + end +end + +local function string2color(ryba) + local color = {r=0,g=0,b=0} + if ryba == "r" then + color.r=1 + end + if ryba == "y" then + color.r=1 + color.g=1 + end + if ryba == "b" then + color.b=1 + end + if ryba == "a" then + color.r=0.4 + color.g=0.75 + color.b=1 + end + return color +end + + +local function splitty(s,delimiter) + return unpack(split(s..delimiter,delimiter,tonumber)) +end + + +return { -- exports + string2sprite = string2sprite, + string2color = string2color, + splitty = splitty, +} diff --git a/scripts/heat/buffer.lua b/scripts/heat/buffer.lua new file mode 100644 index 0000000..0a597c3 --- /dev/null +++ b/scripts/heat/buffer.lua @@ -0,0 +1,117 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local bigunpack = require("__big-data-string__.unpack") +local util = require(rpath .. "util") +local moveposition = util.moveposition +local get2d = util.get2d +local set2d = util.set2d +local split = util.split +local memo = util.memo + + +local function decode(s) + return split(s or "",",",tonumber) +end + +local function is_internal(prototype) + return prototype.type == "reactor" + and string.sub(prototype.name, 1,17) == "realistic-reactor" + and not (prototype.name == "realistic-reactor") +end + +local function heat_buffer_prototype_connections(prototype) + local data = decode(bigunpack(prototype.name .. "-heat-buffer-connections")) + local connections = {} + for i = 1,#data,3 do + local d,x,y = unpack(data,i) + table.insert(connections, {direction=d, position={x=x,y=y}}) + end + return connections +end + +local function all_heat_buffer_prototype_connections() + local connections = {} + local filters = { + {mode="or", filter="type", type="reactor"}, + {mode="or", filter="type", type="heat-pipe"}, + } + for name,prototype in pairs(game.get_filtered_entity_prototypes(filters)) do + if not is_internal(prototype) then + connections[name] = heat_buffer_prototype_connections(prototype) + end + end + -- add connections for internals + connections["realistic-reactor-normal" ] = connections["realistic-reactor"] + connections["realistic-reactor-breeder"] = connections["realistic-reactor"] + return connections +end + +local function get_heat_buffer_prototype_connections(name) + return memo(all_heat_buffer_prototype_connections)[name] or {} +end + +local function get_heat_buffer_prototype_transitions(name) + local transitions = {} + for i,connection in ipairs(get_heat_buffer_prototype_connections(name)) do + transitions[i] = { + p = connection.position, -- inside entity + o = moveposition({x=0,y=0}, connection.direction, 1), -- outlet offset + } + end + return transitions +end + +local function vecadd(a,b) return {x=a.x+b.x, y=a.y+b.y} end +local function vecfmut(f,v) v.x = f(v.x) v.y = f(v.y) return v end +local function iterate_heat_buffer_prototype_transitions(s, i) + i = i + 1 local t = s.t[i] -- transition + if not t then return end -- stop iteration + local p = vecfmut(math.floor, vecadd(s,t.p)) + return i, p, vecadd(p,t.o) +end + +local TRANSITIONS = setmetatable({}, { __index = function (T, name) + local transitions = get_heat_buffer_prototype_transitions(name) + T[name] = transitions -- memoize + return transitions +end}) + +local function heat_buffer_transitions(entity) + local index = 0 + local iterator = iterate_heat_buffer_prototype_transitions + local state = { + t = TRANSITIONS[entity.name], + x = entity.position.x, + y = entity.position.y, + } + return iterator, state, index +end + + +local function get_heat_buffer_prototype_transition_lookup(name) + local lookup = {} + for _,transition in ipairs(TRANSITIONS[name]) do + local p = vecfmut(math.floor,vecadd({x=0.5,y=0.5},transition.p)) + local v = vecadd(transition.o, p) + set2d(lookup, v.x,v.y, p) + end + return lookup +end + +local TRANSITION_LOOKUP = setmetatable({}, {__index = function (T, name) + local lookup = get_heat_buffer_prototype_transition_lookup(name) + T[name] = lookup -- memoize + return lookup +end}) + +local function heat_buffer_transition_position(entity, o,p) + local t = get2d(TRANSITION_LOOKUP[entity.name], p.x-o.x, p.y-o.y) + if t then return vecadd(t,o) end +end + +return { -- exports + get_heat_buffer_prototype_connections = get_heat_buffer_prototype_connections, + heat_buffer_transition_position = heat_buffer_transition_position, + heat_buffer_transitions = heat_buffer_transitions, +} + diff --git a/scripts/heat/debug.lua b/scripts/heat/debug.lua new file mode 100644 index 0000000..8457d26 --- /dev/null +++ b/scripts/heat/debug.lua @@ -0,0 +1,379 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local isempty = require(rroot .. "util").isempty +local distance = require(rpath .. "math").distance +local cell_nodes = require(rpath .. "math").cell_nodes +local sameposition = require(rpath .. "util").sameposition + +local PRETTYCHECK = "%s (%s)%s" +local function check(value,actual) + return value, actual, (value == actual and "" or " FAIL") +end +local function Coordinates(entity) + local x,y,z = entity.position.x,entity.position.y,entity.surface.index + return math.floor(x), math.floor(y), z +end +local function Vector(x,y,z) return { + x = x, + y = y, + z = z, +} end + +-- debugging pretty prints + +function dbgLinked(others) + local strings = {} + for _,cells in pairs(others) do + local str = {} + for _,cell in pairs(cells) do + table.insert(str, string.format("{entity = %s}", cell.entity.unit_number)) + end + table.insert(strings, string.format("{%s}", table.concat(str, ", "))) + end + return string.format("{%s}", table.concat(strings, ", ")) +end + +function dbgVector(vector) return "Vector" .. serpent.line(vector) end +function dbgChunkArea(area) return "ChunkArea" .. serpent.line(area) end +function dbgReactorPosition(reactor) + local strings = {} + table.insert(strings, "id = " .. tostring(reactor.id)) + table.insert(strings, "position = " .. dbgVector(reactor.position)) + if reactor.cell and reactor.cell.chunk and reactor.cell.chunk.network then + table.insert(strings, "network = " .. tostring(reactor.cell.chunk.network.id)) + end + table.insert(strings, "name = " .. tostring(reactor.name)) + return string.format("ReactorPosition{%s}", table.concat(strings, ", ")) +end +function dbgHeatNetworkCell(cell) + local strings = {} + table.insert(strings, "\tnetwork = " .. tostring(cell.chunk.network.id)) + local entities = {} + local counter = {entity = 0, reactor = 0, outlet = 0} + for x,xs in pairs(cell.entities) do + for y,entity in pairs(xs) do + local str = {} + if not entity.valid then + table.insert(str, "\t\t\tvalid = false") + else + table.insert(str, "\t\t\tname = " .. entity.name) + table.insert(str, "\t\t\ttype = " .. entity.type) + table.insert(str, "\t\t\tposition = " .. serpent.line(entity.position)) + table.insert(str, "\t\t\tunit_number = " .. entity.unit_number) + if entity.type ~= 'reactor' then + counter.entity = counter.entity + 1 + elseif sameposition(Vector(x,y),Vector(Coordinates(entity))) then + counter.reactor = counter.reactor + 1 + else + counter.outlet = counter.outlet + 1 + end + end + table.insert(entities, string.format("\t\t[%s,%s] = Entity{\n%s\n\t\t}", x,y, table.concat(str, ",\n"))) + end + end + local reactors = {} + for _,reactor in pairs(cell.reactors) do + table.insert(reactors, "\t\t" .. dbgReactorPosition(reactor)) + end + local counts = {} + for k,count in pairs(cell.count) do + actual = 0 + if k == 'entity' then + actual = counter.entity + elseif k == 'reactor' then + actual = counter.reactor + elseif k == 'outlet' then + actual = counter.outlet + end + table.insert(counts, string.format("\t\t%s = "..PRETTYCHECK, k, check(count, actual))) + end + if isempty(entities) then + table.insert(strings, "\tentities = {}") + else + table.insert(strings, "\tentities = {\n" .. table.concat(entities, ",\n") .. "\n\t} " .. string.format(PRETTYCHECK, check(#entities, counter.entity + counter.reactor + counter.outlet))) + end + if isempty(reactors) then + table.insert(strings, "\treactors = {}") + else + table.insert(strings, "\treactors = {\n" .. table.concat(reactors, ",\n") .. "\n\t} " .. string.format(PRETTYCHECK, check(#reactors, counter.reactor))) + end + table.insert(strings, "\tcount = {\n" .. table.concat(counts, ",\n") .. "\n\t}") + return "HeatNetworkCell{\n" .. table.concat(strings, ",\n") .. "\n}" +end +function dbgHeatChunk(chunk) + local direction = {[N]="north", [S]="south", [E]="east", [W]="west"} + local strings = {} + table.insert(strings, "\tnetwork = " .. tostring(chunk.network.id)) + table.insert(strings, "\tposition = " .. dbgVector(chunk.position)) + table.insert(strings, "\tarea = " .. dbgChunkArea(chunk.area)) + local cells,lookup = {},{} + for cell in pairs(chunk.cells) do + table.insert(cells, "\t\t" .. string.gsub(dbgHeatNetworkCell(cell), "\n", "\n\t\t")) + lookup[cell] = #cells + end + if isempty(cells) then + table.insert(strings, "\tcells = {}") + else + table.insert(strings, "\tcells = {\n" .. table.concat(cells, ",\n") .. "\n\t}") + end + local border = {} + for d,border_cells in pairs(chunk.border) do + if not isempty(border_cells) then + local cells = {} + for c,cell in pairs(border_cells) do + table.insert(cells, string.format("[%s] = %s", c,tostring(lookup[cell]))) + end + table.insert(border, string.format("\t\t%s = {%s}", direction[d], table.concat(cells, ", "))) + end + end + if isempty(border) then + table.insert(strings, "\tborder = {}") + else + table.insert(strings, "\tborder = {\n" .. table.concat(border, ",\n") .. "\n\t}") + end + local counts = {} + for k,count in pairs(chunk.count) do + actual = 0 + if k == 'cell' then + actual = #cells + else + for _,cell in pairs(chunk.border[k]) do + actual = actual + (cell and 1 or 0) + end + end + table.insert(counts, string.format("\t\t%s = "..PRETTYCHECK, direction[k] or k, check(count, actual))) + end + table.insert(strings, "\tcount = {\n" .. table.concat(counts, ",\n") .. "\n\t}") + return "HeatChunk{\n" .. table.concat(strings, ",\n") .. "\n}" +end +function dbgHeatNetwork(network) + local strings = {} + table.insert(strings, "\tid = " .. network.id) + local chunks = {} + local cell_count = 0 + for x,xs in pairs(network.chunks) do + for z,chunk in pairs(xs) do + table.insert(chunks, "\t\t" .. string.gsub(dbgHeatChunk(chunk), "\n", "\n\t\t")) + for _,cell in pairs(chunk.cells) do + cell_count = cell_count + 1 + end + end + end + local reactors = {} + for _,reactor in pairs(network.reactors) do + table.insert(reactors, "\t\t" .. dbgReactorPosition(reactor)) + end + local counts = {} + for k,count in pairs(network.count) do + actual = 0 + if k == 'chunk' then + actual = #chunks + elseif k == 'cell' then + actual = cell_count + elseif k == 'reactor' then + actual = #reactors + end + table.insert(counts, string.format("\t\t%s = "..PRETTYCHECK, k, check(count, actual))) + end + if isempty(chunks) then + table.insert(strings, "\tchunks = {}") + else + table.insert(strings, "\tchunks = {\n" .. table.concat(chunks, ",\n") .. "\n\t}") + end + if isempty(reactors) then + table.insert(strings, "\treactors = {}") + else + table.insert(strings, "\treactors = {\n" .. table.concat(reactors, ",\n") .. "\n\t}") + end + table.insert(strings, "\tcount = {\n" .. table.concat(counts, ",\n") .. "\n\t}") + return "HeatNetwork{\n" .. table.concat(strings, ",\n") .. "\n}" +end + + +-- debug rendering + +local WIDTH = 2 -- in pixels +local F = 0.5 - WIDTH/32 -- in tiles +local function debug_entity(cell, x,y, entity, color) + local ids = {} + if not entity.valid then return ids end + local p = entity.position + local surface = entity.surface + cell_nodes(cell.entities)(entity,function (other,q) + local o = {x=q.x+0.5, y=q.y+0.5} + if o.x - p.x > 0 or o.y - p.y > 0 or entity.type == 'reactor' then + table.insert(ids, rendering.draw_line{ + surface = surface, + color = color, + width = WIDTH, + from = p, + to = o, + }) + end + end) + table.insert(ids, rendering.draw_text{ + surface = entity.surface, + color = color, + target = {p.x-0.3, p.y-0.2}, + text = tostring(cell.chunk.network.id), + }) + return ids +end + +local function debug_cell(cell, color) + local ids = {} + for x,xs in pairs(cell.entities) do + for y,entity in pairs(xs) do + for _,id in ipairs(debug_entity(cell, x,y, entity, color)) do + table.insert(ids, id) + end + end + end + return ids +end + +local function debug_chunk(chunk, color) + local surface = game.surfaces[chunk.position.z] + local ids = {} + for cell in pairs(chunk.cells) do + for _,id in ipairs(debug_cell(cell, color)) do + table.insert(ids, id) + end + end + for d,border in pairs(chunk.border) do + local corner,k = next(M[d]) + local a = chunk.area[corner][k] + local o = O[d] + for c,cell in pairs(border) do + local p = {[k]=a+0.5, [V[k]]=c+0.5} + table.insert(ids, rendering.draw_line{ + surface = surface, + color = color, + width = WIDTH, + from = {p.x-o.y*F+o.x*F, p.y-o.x*F+o.y*F}, + to = {p.x+o.y*F+o.x*F, p.y+o.x*F+o.y*F}, + }) + end + end + return ids +end + + +local function debug_reactor(reactor, color) + local p = reactor.position + local surface = game.surfaces[reactor.cell.chunk.position.z] + local entity = surface.find_entity(reactor.name, p) + local area = entity and entity.bounding_box + if not area then return {} end + local ids = {rendering.draw_rectangle{ + surface = surface, + color = color, + filled = false, + left_top = area.left_top, + right_bottom = area.right_bottom, + }} + table.insert(ids, rendering.draw_text{ + surface = surface, + color = color, + target = {p.x-0.3, p.y-0.2}, + text = tostring(reactor.cell.chunk.network.id), + }) + return ids +end + + +local function debug_network(network, color) + if HEAT.debug_logging ~= false then log(dbgHeatNetwork(network)) end + local ids = {} + for x,xs in pairs(network.chunks) do + for y,chunk in pairs(xs) do + for _,id in ipairs(debug_chunk(chunk, color)) do + table.insert(ids, id) + end + end + end + for _,reactor in pairs(network.reactors) do + for _,id in ipairs(debug_reactor(reactor, color)) do + table.insert(ids, id) + end + end + return ids +end + +local function HSL(h, s, l, a) + if s<=0 then return l,l,l,a end + h, s, l = h*6, s, l + local c = (1-math.abs(2*l-1))*s + local x = (1-math.abs(h%2-1))*c + local m,r,g,b = (l-.5*c), 0,0,0 + if h < 1 then r,g,b = c,x,0 + elseif h < 2 then r,g,b = x,c,0 + elseif h < 3 then r,g,b = 0,c,x + elseif h < 4 then r,g,b = 0,x,c + elseif h < 5 then r,g,b = x,0,c + else r,g,b = c,0,x + end return (r+m),(g+m),(b+m),a +end + +local function random_color(id) +-- return {r=global.random(),g=global.random(),b=global.random(),a=1} + local r,g,b = HSL(((id * 5) % 23) / 23, 0.9, 0.5) + return {r=r,g=g,b=b,a=1} +end + +local function remove_debug(id, debug) + HEAT.debug[id] = nil + for _,id in ipairs(debug.ids) do + rendering.destroy(id) + end + debug.ids = {} +end + +local function create_debug(network) + local id = network.id + if not HEAT.network[id] then return end + local debug = HEAT.debug[id] or {color=random_color(id)} + debug.ids = debug_network(network, debug.color) + HEAT.debug[id] = debug +end + +local function debug_cleanup() + if not HEAT.debug then return end + for id,debug in pairs(HEAT.debug) do + remove_debug(id, debug) + end + HEAT.debug = nil +end + +local function debug_heat() + if HEAT.debug then debug_cleanup() end + HEAT.debug = {} + for _,network in pairs(HEAT.network) do + create_debug(network) + end +end + +local function update_debug() + if not HEAT.debug then return end + if isempty(HEAT.network) then log("no networks") end + debug_heat() +end + +local function debug_log(enabled) + HEAT.debug_logging = enabled + debug_heat() +end + + + +return { -- exports +-- create = create_debug, + update = update_debug, +-- remove = remove_debug, + -- toggle + enable = debug_heat, + disable = debug_cleanup, + logging = debug_log, +} + + diff --git a/scripts/heat/init.lua b/scripts/heat/init.lua new file mode 100644 index 0000000..5773195 --- /dev/null +++ b/scripts/heat/init.lua @@ -0,0 +1,37 @@ +local mod = {} -- init here for circular dependencies + + +local function populate_heat_network() + for _,surface in pairs(game.surfaces) do + for _,entity in pairs(surface.find_entities_filtered{type={'reactor','heat-pipe'}}) do + if entity.type == "reactor" then + mod.add_reactor(entity) + else + mod.add_heat_pipe(entity) + end + end + end +end + + +HEAT = nil -- global cache +function mod.load() +-- log("heat network load") + HEAT = global.heat +end + + +function mod.init() +-- log("heat network init") + global.heat = { + network = {}, -- array of HeatNetwork indexed by id + outlets = {}, -- matrix of array of ReactorPosition indexed by z,x,y and id + cells = {}, -- matrix of HeatNetworkCell indexed by z,x,y + ids = {}, -- array of HeatNetworkId + } + mod.load() + populate_heat_network() +end + + +return mod -- exports diff --git a/scripts/heat/math.lua b/scripts/heat/math.lua new file mode 100644 index 0000000..0f80d28 --- /dev/null +++ b/scripts/heat/math.lua @@ -0,0 +1,364 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local heat_buffer_transition_position = require(rpath .. "buffer").heat_buffer_transition_position +local heat_buffer_transitions = require(rpath .. "buffer").heat_buffer_transitions +local isempty = require(rroot .. "util").isempty +local noop = require(rroot .. "util").noop +local util = require(rpath .. "util") +local imerge = util.imerge +local set3d = util.set3d +local get3d = util.get3d +local get2d = util.get2d +local rm2d = util. rm2d + +local inf = 1/0 + +local function distance(x1,y1,x2,y2) + -- pythagoras + -- return math.sqrt(math.pow(x2 - x1,2) + math.pow(y2 - y1, 2)) + + -- and + + -- chebyshev +-- return math.max(math.abs(x2 - x1), math.abs(y2 - y1)) -- I AM THE KING! + + -- in a + + -- taxicab + return math.abs(x2 - x1) + math.abs(y2 - y1) +end + +local function heuristic(a,b) + return distance(a.x, a.y, b.x, b.y) +end + +local function lowest_score(set, score) + local lowest, best = inf + for node,_ in pairs(set) do + local value = score[node] + if value < lowest then + lowest, best = value, node + end + end + return best +end + +local function calculate_score(score, node, goal) + local a = node.position or node.chunk.position + local b = goal.position or goal.chunk.position + return score[node] + heuristic(a,b) +end + +local function astar(callback, start, goal, neighbours, ...) -- A* + if start == goal then return start end -- connected + local current + local count = 1 + local closedSet = {} + local openSet = {[start] = true} + local gScore = {[start] = 0} + local fScore = {[start] = calculate_score(gScore,start,goal)} + local function score(node) + if not closedSet[node] then + local tentative_gScore = calculate_score(gScore, current, node) + if not openSet[node] or tentative_gScore < gScore[node] then + callback(current, node) + gScore[node] = tentative_gScore + fScore[node] = calculate_score(gScore, node, goal) + if not openSet[node] then + openSet[node] = true + count = count + 1 + end + end + end + end + while count > 0 do + current = lowest_score(openSet, fScore) + if current == goal then return goal end -- connected + count = count - 1 + openSet[current] = nil + closedSet[current] = true + neighbours(current, score, ...) + end + return nil -- not connected +end + +local function testpath(...) -- A* → bool + return astar(noop, ...) +end + +local function getpath(...) -- A* → {node,...} + local cameFrom = {} + local goal = astar(function (current,node) cameFrom[node] = current end, ...) + if not goal then return nil end + local path = {goal} + while cameFrom[goal] do + goal = cameFrom[goal] + table.insert(path, 1, goal) -- prepend + end + return path +end + +local function is_connected_outside(path) + if not path then return false end + local before = path[1] + for i = 2,#path do + local current = path[i] + if heuristic(before.position, current.position) > 1 then -- distance + return true -- must have moved trough linked entities + end + end + return false +end + + + +local function cleanup_heat_network(id) +-- log("cleanup heat network " .. tostring(id)) + HEAT.network[id] = nil + HEAT.ids[id] = nil +end + +local function clear_heat_network_for_surface(z) + HEAT.cells [z] = nil + HEAT.outlets[z] = nil + for id,network in pairs(HEAT.network) do + local chunks = select(2,next(network.chunks)) + local chunk = chunks and select(2,next(chunks)) + if chunk and chunk.position.z == z then + cleanup_heat_network(id) + end + end +end + +-- x,y,z = position.x, position.y, surface.index +-- using z,x,y order for easier removal +local function rm_heat_network_cell(x,y,z) return rm2d(HEAT.cells[z] or {}, x,y) end + +local function get_heat_network_cell(x,y,z,d) return get3d(HEAT.cells, z,x,y, d) end +local function set_heat_network_cell(x,y,z,v) return set3d(HEAT.cells, z,x,y, v) end + +local function get_heat_outlet(x,y,z,d) return get3d(HEAT.outlets, z,x,y, d) end +local function set_heat_outlet(x,y,z,v) return set3d(HEAT.outlets, z,x,y, v) end + + +-- cps instead of coroutine +local function direct_neighbour_nodes(entities, entity, callback, ...) + local z = entity.surface.index + local is_reactor = entity.type == 'reactor' + for i,p,o in heat_buffer_transitions(entity) do +-- local neighbour = get2d(is_reactor and (get_heat_network_cell(o.x,o.y,z) or {}).entities or entities, o.x,o.y) + local neighbour = get2d(entities, o.x,o.y) + if neighbour and neighbour.valid then + callback(neighbour, o, ...) + end + if is_reactor or i == 1 then + if is_reactor then callback(entity, p, ...) end -- THIS LINE IS MAGIC + local reactors = get_heat_outlet(p.x,p.y,z, {}) + for _,reactor in pairs(reactors) do + local r = reactor.position + local neighbour = get2d(reactor.cell.entities, r.x,r.y) + if neighbour and neighbour.valid then + if get2d(entities, r.x,r.y) then -- same cell + callback(neighbour, r, ...) + else + local position = heat_buffer_transition_position(reactor, r,p) + if position then callback(neighbour, position, ...) end + end + end + end + end + end +end +local function cell_nodes(entities) + return function (...) return direct_neighbour_nodes(entities, ...) end +end + +local function linked_neighbour_nodes(entities, linked, entity, callback, ...) + direct_neighbour_nodes(entities, entity, callback, ...) + if not linked[entity] then return end + for position,neighbour in pairs(linked[entity]) do + if neighbour ~= entity then + callback(neighbour, position, ...) + end + end +end +local function chunk_nodes(entities, linked) + return function (...) return linked_neighbour_nodes(entities, linked, ...) end +end + + +local function neighbour_chunks(chunk, callback, chunks) + local x,y = chunk.position.x, chunk.position.y + for _,o in pairs(O) do + local neighbour = get2d(chunks, o.x+x, o.y+y) + if neighbour then callback(neighbour) end + end +end + +local function neighbour_cells(chunk, filter, callback, ...) + local network,area = chunk.network,chunk.area + local x,y,z = chunk.position.x, chunk.position.y + for d,o in pairs(O) do + local corner,k = next(M[d]) + local a = area[corner][k] + local other = get2d(network.chunks, o.x+x, o.y+y) + if other then + for c,cell in pairs(chunk.border[d]) do + if cell == filter then + local neighbour = other.border[R[d]][c] -- of same network + if neighbour then + local p = {[k]=a, [V[k]]=c} + callback(neighbour,p,o,...) + end + end + end + end + end +end + +local function connected_cells(cell, callback, ignore) + if ignore == cell then return end + neighbour_cells(cell.chunk, cell, callback) +end + +local function chunked(v) return math.floor(v/32) end +local function get_chunk_neighbour(cell, x,y, callback, ...) + local p = {x=x,y=y} -- entity.position + local cx,cy = chunked(x), chunked(y) -- chunk position + for _,corner in ipairs{'left_top','right_bottom'} do + for k,v in pairs(p) do + if cell.chunk.area[corner][k] == v then + local d = A[corner][k] + local o = O[d] + local neighbour = get2d(cell.chunk.network.chunks, cx+o.x, cy+o.y) + local other = neighbour and neighbour.border[R[d]][p[V[k]]] -- of same network + if other then callback(other,p,o,...) end + end + end + end +end + + +local function Linked(others) + -- gather result +-- log"linked entities:" + local linked = {} + for _,cells in pairs(others) do + if #cells > 1 then + local entities = {} +-- log"linked:" + for _,other in ipairs(cells) do + local entity = other.entity + entities[other.position] = entity + linked[entity] = entities +-- log(string.format("entity=%s x=%s y=%s %s", entity.unit_number, entity.position.x, entity.position.y, dbgVector(other.position))) + end + end + end + return linked +end + +local function inside_linked_entities(cell) + local entities = cell.entities + local others,neighbours = {},cell_nodes(entities) + -- test if entities are connected inside chunk + neighbour_cells(cell.chunk, cell, function (cell,position) +-- log(dbgVector(position)) +-- log(dbgHeatNetworkCell(cell)) + local entity = get2d(entities, position.x, position.y) + local found = nil + for _,cells in ipairs(others) do + if testpath(entity, cells[1].entity, neighbours) then -- inner +-- log"connected inside" + found = cells + break + end + end + if not found then + found = {} + table.insert(others, found) + end + table.insert(found, {cell=cell, entity=entity, position=position}) + end) +-- log("Inside ".. dbgLinked(others)) + return others +end + +local function outside_linked_entities(cell, others) + -- test if entities are connected outside chunk + local n = #others + if n > 1 then + for i = 1,n do + local current = others[i] + if current then + local start = current[1].cell + for j,cells in pairs(others) do + if current ~= cells then + for _,goal in pairs(cells) do goal = goal.cell + if testpath(start, goal, connected_cells, cell) then -- outer +-- log"connected outside" + current = imerge(current, cells) + others[current == cells and i or j] = nil + break + end + end + end + end + end + end + end +-- log("Outside ".. dbgLinked(others)) + return others +end + +local function get_linked_entities(cell) + local others = inside_linked_entities(cell) + local inside = Linked(others) + others = outside_linked_entities(cell, others) + local outside = Linked(others) + return outside, inside +end + +local function get_entity_neighbour_cells(entity,callback,...) + local z = entity.surface.index + local is_reactor = entity.type == 'reactor' + for i,p,o in heat_buffer_transitions(entity) do + local cell = get_heat_network_cell(o.x,o.y,z) + if cell then callback(cell,o,...) end + if is_reactor or i == 1 then + for _,reactor in pairs(get_heat_outlet(p.x,p.y,z,{})) do + local position = heat_buffer_transition_position(reactor, reactor.position,p) + local cell = get_heat_network_cell(position.x,position.y,z) + if cell then callback(cell, position, ...) end + end + if is_reactor then + local cell = get_heat_network_cell(p.x,p.y,z) + if cell then callback(cell, p, ...) end + end + end + end +end + + +return { -- exports + testpath = testpath, + getpath = getpath, + is_connected_outside = is_connected_outside, + chunk_nodes = chunk_nodes, + cell_nodes = cell_nodes, + neighbour_chunks = neighbour_chunks, + neighbour_cells = neighbour_cells, + connected_cells = connected_cells, + get_chunk_neighbour = get_chunk_neighbour, + get_linked_entities = get_linked_entities, + get_entity_neighbour_cells = get_entity_neighbour_cells, + clear_heat_network_for_surface = clear_heat_network_for_surface, + cleanup_heat_network = cleanup_heat_network, + rm_heat_network_cell = rm_heat_network_cell, + get_heat_network_cell = get_heat_network_cell, + set_heat_network_cell = set_heat_network_cell, + get_heat_outlet = get_heat_outlet, + set_heat_outlet = set_heat_outlet, + distance = heuristic, +} + diff --git a/scripts/heat/network.lua b/scripts/heat/network.lua new file mode 100644 index 0000000..a830cd6 --- /dev/null +++ b/scripts/heat/network.lua @@ -0,0 +1,665 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local get_reactor_core_power = require(rroot .. "entity.util").get_reactor_core_power +local mod = require(rpath .. "init") +local heat_buffer_transition_position = require(rpath .. "buffer").heat_buffer_transition_position +local heat_buffer_transitions = require(rpath .. "buffer").heat_buffer_transitions +local netmath = require(rpath .. "math") +local getpath = netmath.getpath +local testpath = netmath.testpath +local is_connected_outside = netmath.is_connected_outside +local chunk_nodes = netmath.chunk_nodes +local cell_nodes = netmath. cell_nodes +local connected_cells = netmath.connected_cells +local neighbour_cells = netmath.neighbour_cells +local get_chunk_neighbour = netmath.get_chunk_neighbour +local get_linked_entities = netmath.get_linked_entities +local get_entity_neighbour_cells = netmath.get_entity_neighbour_cells +local clear_heat_network_for_surface = netmath.clear_heat_network_for_surface +local cleanup_heat_network = netmath.cleanup_heat_network +local rm_heat_network_cell = netmath.rm_heat_network_cell +local get_heat_network_cell = netmath.get_heat_network_cell +local set_heat_network_cell = netmath.set_heat_network_cell +local get_heat_outlet = netmath.get_heat_outlet +local set_heat_outlet = netmath.set_heat_outlet +local isempty = require(rroot .. "util").isempty +local debug = require(rpath .. "debug") +local util = require(rpath .. "util") +local get2d = util.get2d +local set2d = util.set2d +local rm2d = util. rm2d +local sameposition = util.sameposition +-- local growarea = util. growarea +-- local subarea = util. subarea +-- local shrinkarea = util.shrinkarea + + +local function Coordinates(entity) + local x,y,z = entity.position.x,entity.position.y,entity.surface.index + return math.floor(x), math.floor(y), z +end + +local CHUNK_SIZE = 32 -- in tiles +local function ChunkCoordinates(x,y,z) + return math.floor(x / CHUNK_SIZE), math.floor(y / CHUNK_SIZE), z +end + +local function Vector(x,y,z) return { + x = x, + y = y, + z = z, +} end + +-- FIXME area not used! could be removed or used to pimp split? +-- function Area(...) return { +-- left_top = Vector(...), +-- right_bottom = Vector(...), +-- } end + +local function ChunkArea(x,y) return { + left_top = Vector(CHUNK_SIZE * (0 + x) - 0, CHUNK_SIZE * (0 + y) - 0), + right_bottom = Vector(CHUNK_SIZE * (1 + x) - 1, CHUNK_SIZE * (1 + y) - 1), +} end + +local function ReactorPosition(entity) return { + id = entity.unit_number, + name = entity.name, + position = Vector(Coordinates(entity)), +-- cell = HeatNetworkCell(), -- HeatNetworkCell which the reactor belongs to +} end + +local function HeatChunk(...) return { + position = Vector(ChunkCoordinates(...)), + area = ChunkArea(ChunkCoordinates(...)), + cells = {}, -- array of HeatNetworkCell indexed by itself +-- network = HeatNetwork(), -- network this chunk belongs to + border = { + [N] = {}, -- array of HeatNetworkCell indexed by x + [E] = {}, -- array of HeatNetworkCell indexed by y + [S] = {}, -- array of HeatNetworkCell indexed by x + [W] = {}, -- array of HeatNetworkCell indexed by y + }, + count = { + cell = 0, -- number of HeatNetworkCell + [N] = 0, -- number of HeatNetworkCell + [E] = 0, -- number of HeatNetworkCell + [S] = 0, -- number of HeatNetworkCell + [W] = 0, -- number of HeatNetworkCell + }, +} end + + +-- only required for debugging +local function HeatNetworkId() + local i = -1 + for id in pairs(HEAT.ids) do + i = math.max(i,id) + end + local id = i + 1 + HEAT.ids[id] = id +-- log("create new network " .. tostring(id)) + return id -- uniq number +end + +local function HeatNetwork() return { + id = HeatNetworkId(), + reactors = {}, -- array of ReactorPosition indexed by entity.unit_number + chunks = {}, -- array of HeatChunk indexed by x,y + count = { + chunk = 0, -- number of HeatChunk + cell = 0, -- number of HeatNetworkCell + reactor = 0, -- number of reactors + }, +} end + +local function HeatNetworkCell() return { +-- chunk = HeatChunk(), -- chunk this cell belongs to + entities = {}, -- matrix of heat-pipe and reactor entities indexed by x,y + reactors = {}, -- array of ReactorPosition indexed by entity.unit_number +-- area = Area(), -- bounding box of network + count = { + entity = 0, -- number of heat-pipes + reactor = 0, -- number of reactors + outlet = 0, -- number of reactor outlets + }, +} end + + + +local function set_chunk_borders(chunk, x,y, cell) + local p = Vector(x,y) + local count = cell and 1 or -1 + for _,corner in ipairs{'left_top','right_bottom'} do + for k,v in pairs(p) do + if chunk.area[corner][k] == v then + local d = A[corner][k] + chunk.border[d][p[V[k]]] = cell + chunk.count[d] = chunk.count[d] + count +-- log(string.format("%s chunk border in network %s at x=%s y=%s", cell and "add cell to" or "remove cell from", chunk.network.id, x,y)) + end + end + end +end + +local function get_or_create_heat_network_chunk(x,y,z, network) + local cx,cy = ChunkCoordinates(x,y,z) + local chunk = get2d(network.chunks, cx,cy) + if chunk then return chunk end + chunk = HeatChunk(x,y,z) + chunk.network = network +-- log(string.format("create new chunk in network %s at x=%s y=%s z=%s", network.id, chunk.position.x,chunk.position.y,chunk.position.z)) + set2d(network.chunks, cx,cy, chunk) + network.count.chunk = network.count.chunk + 1 + return chunk +end + +local function create_heat_network_cell(x,y,z, network) + local chunk = get_or_create_heat_network_chunk(x,y,z, network) + local cell = HeatNetworkCell() +-- log(string.format("create heat network cell in network %s at x=%s y=%s z=%s", network.id, x,y,z)) +-- cell.area = growarea(cell.area, Area(x,y)) + network.count.cell = network.count.cell + 1 + chunk.count.cell = chunk.count.cell + 1 + chunk.cells[cell] = cell + cell.chunk = chunk + return cell +end + +local function create_heat_network(x,y,z) + local network = HeatNetwork() + local cell = create_heat_network_cell(x,y,z, network) + HEAT.network[network.id] = network +-- log(string.format("created heat network %s", network.id)) + return cell +end + +local function remove_heat_network(x,y,z, cell) + local chunk,network = cell.chunk,cell.chunk.network + rm_heat_network_cell(x,y,z) + set_chunk_borders(chunk, x,y, nil) +-- log(string.format("removed heat network %s", network.id)) + if cell.count.entity + cell.count.reactor + cell.count.outlet > 0 then return end + chunk.cells[cell] = nil + chunk.count.cell = chunk.count.cell - 1 + network.count.cell = network.count.cell - 1 + if chunk.count.cell > 0 then return end + rm2d(network.chunks, chunk.position.x,chunk.position.y) + network.count.chunk = network.count.chunk - 1 + if network.count.chunk > 0 then return end + -- network gets garbage collected + cleanup_heat_network(network.id) +end + + +local move_entities +local function move_cells(cell, position, offset, network, neighbours, inside, last, dst, ignore) + local old = cell.chunk + local x,y,z = position.x+offset.x,position.y+offset.y,old.position.z + if cell == ignore then + local entity = get2d(cell.entities, x,y) + if entity then + if inside[entity] == inside[last] then +-- log(string.format("leafing recursion cuz cell is connected inside at x=%s y=%s", x,y)) + return + end +-- log(string.format("return to crawling at x=%s y=%s", x,y)) + dst = create_heat_network_cell(x,y,z, network) + move_entities(entity, Vector(x,y), dst,cell, neighbours, inside) + end + return -- to crawling + end + local new = get_or_create_heat_network_chunk(x,y,z, network) + if old.network == new.network then return end +-- log(string.format("move cells into network %s from %s at x=%s y=%s z=%s", network.id,old.network.id, x,y,z)) + cell.chunk = new + old.cells[cell] = nil + new.cells[cell] = cell + old.count.cell = old.count.cell - 1 + new.count.cell = new.count.cell + 1 + old.network.count.cell = old.network.count.cell - 1 + network.count.cell = network.count.cell + 1 + for id,reactor in pairs(cell.reactors) do + if not network.reactors[id] then + old.network.reactors[id] = nil + network.reactors[id] = reactor + old.network.count.reactor = old.network.count.reactor - 1 + network.count.reactor = network.count.reactor + 1 + end + end + if not (old.count.cell > 0) then + rm2d(old.network.chunks, ChunkCoordinates(x,y)) + old.network.count.chunk = old.network.count.chunk - 1 + end + neighbour_cells(old, cell, move_cells, network, neighbours, inside, last, dst, ignore) + set_chunk_borders(old, x,y, nil) + set_chunk_borders(new, x,y, cell) + for d in pairs(O) do + for c,other in pairs(old.border[d]) do + if other == cell then + old.border[d][c] = nil + new.border[d][c] = cell + old.count[d] = old.count[d] - 1 + new.count[d] = new.count[d] + 1 + end + end + end +end + +move_entities = function (entity, position, new,old, neighbours, inside) -- salami tactics + local x,y,z = position.x,position.y,old.chunk.position.z -- use this instead of entity.position which could be in another cell + if not get2d(old.entities, x,y) then + local other = get_heat_network_cell(x,y,z) + if other ~= old then -- found outside + move_cells(other, position, Vector(0,0), new.chunk.network, neighbours,inside,entity,new,old) + end + return + end +-- log(string.format("move entity between networks from %s to %s at x=%s y=%s z=%s %s", old.chunk.network.id, new.chunk.network.id, x,y,z, serpent.line{from=old.count,to=new.count})) +-- new.area = growarea(new.area, Area(x,y)) + if entity.type ~= "reactor" then + old.count.entity = old.count.entity - 1 + new.count.entity = new.count.entity + 1 + elseif old.reactors[entity.unit_number] then + old.count.reactor = old.count.reactor - 1 + new.count.reactor = new.count.reactor + 1 + local reactor = old.reactors[entity.unit_number] + old.reactors[reactor.id] = nil + new.reactors[reactor.id] = reactor + reactor.cell = new + old.chunk.network.reactors[reactor.id] = nil + new.chunk.network.reactors[reactor.id] = reactor + old.chunk.network.count.reactor = old.chunk.network.count.reactor - 1 + new.chunk.network.count.reactor = new.chunk.network.count.reactor + 1 + else + old.count.outlet = old.count.outlet - 1 + new.count.outlet = new.count.outlet + 1 + end + rm2d(old.entities, x,y) + set2d(new.entities, x,y, entity) + if old.chunk.network ~= new.chunk.network then + get_chunk_neighbour(old, x,y, move_cells, new.chunk.network, neighbours,inside,entity,new,old) + end + remove_heat_network(x,y,z, old) + set_heat_network_cell(x,y,z, new) + set_chunk_borders(new.chunk, x,y, new) + neighbours(entity, move_entities, new,old, neighbours, inside) +end + + +local function is_border(area,p) + for _,corner in ipairs{'left_top','right_bottom'} do + for k,v in pairs(p) do + if v == area[corner][k] then return true end + end + end + return false +end +local function counter(cell,_,count) count[cell], count.value = true, count.value + 1 end +local function insert_table(e,p,a,b) table.insert(a,e) table.insert(b,p) end +local function split_heat_network_cell(cell,x,y,z,entity) + local count = {value=0} + get_entity_neighbour_cells(entity, counter, count) + if count.value < 2 then return end -- this was the last +-- log(string.format("try to split network %s at at x=%s y=%s z=%s %s ", cell.chunk.network.id, x,y,z, entity.name)) +-- log(dbgHeatNetwork(cell.chunk.network)) +-- local area + local others,positions = {},{} + local outside,inside = get_linked_entities(cell) + local chunk_neighbours = chunk_nodes(cell.entities, outside) + local cell_neighbours = cell_nodes(cell.entities) + chunk_neighbours(entity, insert_table, others, positions) + local n,c = #others,table_size(count)-1 +-- log(string.format("found %s entities in %s cells inside of %s total", n,c,count.value)) + if n == 0 then return elseif n == 1 then + if not is_border(cell.chunk.area, Vector(x,y)) then return end +-- log(string.format("split heat network alone %s", serpent.line{count=cell.count})) + local connected = false + get_entity_neighbour_cells(entity, function (neighbour) + if not connected and cell.chunk ~= neighbour.chunk then + connected = testpath(cell, neighbour, connected_cells) -- outside + end + end) + if connected then -- outside connected +-- log(" ... but nothing happens, cuz cells are connected on the outside") + else + local p,other = positions[1],others[1] + local new = create_heat_network(p.x,p.y,z) + move_entities(other, p, new,cell, cell_neighbours, inside) +-- log("moved into new network " .. tostring(new.chunk.network.id)) +-- log(dbgHeatNetwork(new.chunk.network)) + end + return -- weird, lol + end + if c > 1 then + get_entity_neighbour_cells(entity, function (other,p) + if other ~= cell then + if not testpath(cell, other, connected_cells) then -- outside + local new = create_heat_network(p.x,p.y,z) + move_cells(other, p, Vector(0,0), new.chunk.network, cell_neighbours,new,cell) +-- log("moved neighbour into new network " .. tostring(new.chunk.network.id)) + end + end + end) + end + local before = others[n] +-- log(string.format("split heat network %s", serpent.line{count=cell.count})) + for i,other in ipairs(others) do + local p = positions[i] + if get2d(cell.entities, p.x,p.y) then + if before and before ~= other then + local path = getpath(before, other, chunk_neighbours) + if path and #path == 2 and path[1] == entity then path = nil --[[log"WTF"]] end + local outside = is_connected_outside(path) + if path then +-- log(string.format("found path of length %s %sside", #path, outside and "out" or "in")) + for _,node in ipairs(path) do +-- log(node.name .. ": " .. dbgVector(node.position)) + end + end + if not path or outside then + -- TODO determine which half is now smaller then the other and fill-move the smaller one. -- maybe impossibru ? -- maybe with area? + local network + if path then -- outside + if not testpath(before, other, cell_neighbours) then -- inside +-- log("splitting cell inside network") + local new = create_heat_network_cell(p.x,p.y,z, cell.chunk.network) + move_entities(other, p, new,cell, cell_neighbours, inside) + network = new.chunk.network + end + else -- inside + local new = create_heat_network(p.x,p.y,z) + move_entities(other, p, new,cell, cell_neighbours, inside) + network = new.chunk.network + end +-- area = growarea(area, new.area) + other = nil -- skip this before + if not path then +-- log("moved into new network " .. tostring(network.id)) +-- log(dbgHeatNetwork(network)) + end + end + end + before = other or before + end + end +-- cell.area = subarea(area, cell.area) +end + + +local function network_score(network) + local count = network.count + return count.chunk + count.cell + count.reactor +end +local function chunk_score(chunk) + local sum = 0 for _,count in pairs(chunk.count) do sum = sum + count end + return sum + network_score(chunk.network) +end +local function cell_score(cell) + local count = cell.count + return 2 * count.entity + count.reactor + count.outlet + chunk_score(cell.chunk) +end + +local function merge_heat_chunk(a,b) + if chunk_score(a) < chunk_score(b) then a,b = b,a end -- spare some work +-- log(string.format("merge heat chunk at x=%s y=%s z=%s", a.position.x,a.position.y,a.position.z)) + for k,count in pairs(b.count) do + a.count[k] = a.count[k] + count + end + for cell in pairs(b.cells) do + a.cells[cell] = cell + cell.chunk = a + end + for d,border in pairs(b.border) do + for c,cell in pairs(border) do + a.border[d][c] = cell + end + end + return a +end + +local function merge_heat_network(a,b) + local an,bn = a.chunk.network, b.chunk.network + if an ~= bn then + if cell_score(a) < cell_score(b) then an,bn = bn,an end -- spare some work +-- log(string.format("merge heat network %s into %s %s", bn.id, an.id, serpent.line{from=bn.count,to=an.count})) + for k,count in pairs(bn.count) do + an.count[k] = an.count[k] + count + end + for id,reactor in pairs(bn.reactors) do + an.reactors[id] = reactor + end + for x,xs in pairs(bn.chunks) do + for y,chunk in pairs(xs) do + local current = get2d(an.chunks, x,y) + chunk.network = an + if current then + an.count.chunk = an.count.chunk - 1 + chunk = merge_heat_chunk(current, chunk) + end + set2d(an.chunks, x,y, chunk) + end + end + cleanup_heat_network(bn.id) + end + return a +end + +local function merge_heat_network_cells(x,y,z, a,b) + if not b or b == a then return a end + if sameposition(a.chunk.position, b.chunk.position) then + if cell_score(a) < cell_score(b) then a,b = b,a end -- spare some work +-- log(string.format("merge heat network cells %s into %s %s", b.chunk.network.id, a.chunk.network.id, serpent.line{from=b.count,to=a.count})) +-- a.area = growarea(a.area, b.area) + for k,count in pairs(b.count) do + a.count[k] = a.count[k] + count + end + local z = a.chunk.position.z + for x,xs in pairs(b.entities) do + for y,entity in pairs(xs) do + set2d(a.entities, x,y, entity) + set_heat_network_cell(x,y,z, a) + set_chunk_borders(b.chunk, x,y, nil) + set_chunk_borders(a.chunk, x,y, a) + end + end + for _,reactor in pairs(b.reactors) do + local p = reactor.position + reactor.cell = a + a.reactors[reactor.id] = reactor + end + for d,border in pairs(b.chunk.border) do + for c,cell in pairs(border) do + if cell == b then + a.chunk.border[d][c] = a + end + end + end + b.chunk.cells[b] = nil + b.chunk.count.cell = b.chunk.count.cell - 1 + b.chunk.network.count.cell = b.chunk.network.count.cell - 1 + end + return merge_heat_network(a,b) +end + + +local function add_entity_to_network_cell(x,y,z,entity) + local cell = create_heat_network(x,y,z) + local is_reactor = entity.type == 'reactor' + for i,p,o in heat_buffer_transitions(entity) do + -- add heat pipe neighbours +-- log(string.format("looking at x=%s y=%s for heat network cell", o.x,o.y)) + cell = merge_heat_network_cells(x,y,z, cell, get_heat_network_cell(o.x,o.y,z)) + -- add reactor neighbours + if is_reactor or i == 1 then -- assuming heat pipe is just one tile in size (all connection positions are the same) + local reactors = get_heat_outlet(p.x,p.y,z, {}) +-- log(string.format("test x=%s y=%s for outlet with %s reactors", p.x,p.y, table_size(reactors))) + for _,reactor in pairs(reactors) do + local q = heat_buffer_transition_position(reactor, reactor.position,p) + cell = merge_heat_network_cells(x,y,z, cell, get_heat_network_cell(q.x,q.y,z)) + end + end + end + set_heat_network_cell(x,y,z, cell) + set_chunk_borders(cell.chunk, x,y, cell) + set2d(cell.entities, x,y, entity) + if is_reactor then + cell.chunk.network.count.reactor = cell.chunk.network.count.reactor + 1 + cell.count.reactor = cell.count.reactor + 1 + else + cell.count.entity = cell.count.entity + 1 + end +-- log(string.format("added cell to network %s at x=%s y=%s z=%s %s", cell.chunk.network.id, x,y,z, serpent.line{count=cell.count})) + return cell +end + +local function remove_entity_from_network_cell(x,y,z,entity,cell) + local network = cell.chunk.network + if entity.type ~= "reactor" then + cell.count.entity = cell.count.entity - 1 + else + cell.chunk.network.count.reactor = cell.chunk.network.count.reactor - 1 + cell.count.reactor = cell.count.reactor - 1 + end + rm2d(cell.entities, x,y) + remove_heat_network(x,y,z, cell) + if cell.count.entity + cell.count.reactor + cell.count.outlet > 0 then + split_heat_network_cell(cell, x,y,z, entity) + end +-- shrinkarea(cell.area,cell.entities) -- shrink area by entity on border +-- log(string.format("removed cell from network %s at x=%s y=%s z=%s %s", cell.chunk.network.id, x,y,z, serpent.line{count=cell.count})) +-- log(dbgHeatNetwork(network)) +end + + +local function get_heat_network(entity) + local cell = get_heat_network_cell(Coordinates(entity)) + return cell and cell.chunk.network +end + +local function get_connected_reactors(entity) + local network = get_heat_network(entity) + return network and network.reactors or {} +end + + +local function calculate_corner_index(area, x,y) + local i = 1 + if x < area.left_top.x or area.right_bottom.x < x then i = i + 1 end + if y < area.left_top.y or area.right_bottom.y < y then i = i + 2 end + return i -- {1=(x,y inside area), 2=(x outside area), 3=(y outside area), 4=(x,y outside area)} +end +local function add_reactor_to_network(entity) + if get_reactor_core_power(entity) then return end -- ignore reactor cores + local x,y,z = Coordinates(entity) +-- log(string.format("add reactor to network x=%s y=%s z=%s", x,y,z)) + local cell = add_entity_to_network_cell(x,y,z,entity) + local reactor = ReactorPosition(entity) + cell.chunk.network.reactors[reactor.id] = reactor + cell.reactors[reactor.id] = reactor + reactor.cell = cell + -- merge outlets + local corner = {cell} -- up to 4 cells + for _,p,o in heat_buffer_transitions(entity) do + local reactors = get_heat_outlet(o.x,o.y,z, {}) +-- log(string.format("outlet at x=%s y=%s z=%s with %s others", o.x,o.y,z, table_size(reactors))) + reactors[reactor.id] = reactor + set_heat_outlet(o.x,o.y,z, reactors) + local i = calculate_corner_index(cell.chunk.area, p.x,p.y) + local other = corner[i] or create_heat_network_cell(p.x,p.y,z, cell.chunk.network) + if not get2d(other.entities, p.x,p.y) then + other.count.outlet = other.count.outlet + 1 + set2d(other.entities, p.x,p.y, entity) + set_chunk_borders(other.chunk, p.x,p.y, other) + set_heat_network_cell(p.x,p.y,z, other) + end + other = merge_heat_network_cells(p.x,p.y,z, other, get_heat_network_cell(o.x,o.y,z)) + corner[i] = other + end + debug.update() +end + + +local function remove_reactor_from_network(entity) + if get_reactor_core_power(entity) then return end -- ignore reactor cores + local x,y,z = Coordinates(entity) + local cell = get_heat_network_cell(x,y,z) + local reactor = cell and cell.reactors[entity.unit_number] + if not reactor then return end +-- log(string.format("remove reactor from network cell %s at x=%s y=%s z=%s %s", cell.chunk.network.id, x,y,z, serpent.line{count=cell.count})) + -- cleanup outlets + local remove = {} + for _,p,o in heat_buffer_transitions(entity) do + local reactors = get_heat_outlet(o.x,o.y,z, {}) + reactors[reactor.id] = nil + if isempty(reactors) then reactors = nil end + set_heat_outlet(o.x,o.y,z, reactors) + set2d(remove, p.x,p.y, get_heat_network_cell(p.x,p.y,z)) + end + -- remove from cells now cuz of duplicate positions + for x,xs in pairs(remove) do + for y,other in pairs(xs) do + other.count.outlet = other.count.outlet - 1 + remove_heat_network(x,y,z, other) + set_chunk_borders(other.chunk, x,y) + rm2d(other.entities, x,y) + end + end + remove_entity_from_network_cell(x,y,z, entity, cell) + cell.chunk.network.reactors[reactor.id] = nil + cell.reactors[reactor.id] = nil + reactor.cell = nil + debug.update() +end + + +local function add_heat_pipe_to_network(entity) + local x,y,z = Coordinates(entity) +-- log(string.format("add heat pipe to network cell at x=%s y=%s z=%s", x,y,z)) + add_entity_to_network_cell(x,y,z,entity) + debug.update() +end + +local function remove_heat_pipe_from_network(entity) + local x,y,z = Coordinates(entity) + local cell = get_heat_network_cell(x,y,z) + if not cell then return end +-- log(string.format("remove heat pipe from network cell %s at x=%s y=%s z=%s %s", cell.chunk.network.id, x,y,z, serpent.line{count=cell.count})) + remove_entity_from_network_cell(x,y,z, entity, cell) + debug.update() +end + + +local function remove_heat_network_from_surface(z) +-- log("clear heat networks on surface " .. tostring(z)) + clear_heat_network_for_surface(z) + debug.update() +end + +local function remove_heat_network_from_chunk(x,y,z) -- chunk coords +-- log(string.format("clear heat networks from chunk at x=%s y=%s z=%s", x,y,z)) + local area = ChunkArea(ChunkCoordinates(x,y,z)) + for x = area.left_top.x, area.right_bottom.x do + for y = area.left_top.y, area.right_bottom.y do + local cell = get_heat_network_cell(x,y,z) + if cell then remove_heat_network(x,y,z, cell) end + end + end + debug.update() +end + + +-- circular dependency +mod.add_reactor = add_reactor_to_network +mod.add_heat_pipe = add_heat_pipe_to_network +return { -- exports + init=mod.init, load=mod.load, + get = get_heat_network, + get_connected_reactors = get_connected_reactors, + add_reactor = add_reactor_to_network, + remove_reactor = remove_reactor_from_network, + add_heat_pipe = add_heat_pipe_to_network, + remove_heat_pipe = remove_heat_pipe_from_network, + remove_from_surface = remove_heat_network_from_surface, + remove_from_chunk = remove_heat_network_from_chunk, + debug = debug, +} diff --git a/scripts/heat/util.lua b/scripts/heat/util.lua new file mode 100644 index 0000000..e6bc87e --- /dev/null +++ b/scripts/heat/util.lua @@ -0,0 +1,124 @@ +local rpath = (...):match("(.-)[^%.]+$") +local rroot = rpath:match("^([^%.]+%.)") +local isempty = require(rroot .. "util").isempty + + +local function split(s,d,f) + local r = {} for x in s:gmatch("[^" .. d .. "]+") do table.insert(r,f(x)) end return r +end + +local function imerge(a,b) + if #a < #b then a,b = b,a end -- spare some work + for _,v in ipairs(b) do table.insert(a,v) end + return a +end + +local function isemptyline(t2d, y) + for _,xs in pairs(t2d) do + if xs[y] then return false end + end + return true +end + +local function get(t,k,d) return t[k] or d end +local function set(t,k,v) t[k] = v return t end + +local function get2d(t,x,y,d) return get(get(t, x, {}), y, d) end +local function set2d(t,x,y,v) return set(t, x, set(get(t, x, {}), y, v)) end + +local function get3d(t,x,y,z,d) return get2d(get(t, x, {}), y, z, d) end +local function set3d(t,x,y,z,v) return set(t, x, set2d(get(t, x, {}), y, z, v)) end + +local function rm2d(t,x,y) + if t[x] then + set2d(t,x,y,nil) + if isempty(t[x]) then + t[x] = nil + end + end + return t +end + +local function sameposition(a,b) + return a.x == b.x and a.y == b.y +end + +local function moveposition(position, direction, distance) + local offset = O[direction] + if offset then + return { + x = position.x + offset.x * distance, + y = position.y + offset.y * distance, + } + end +end + +--[[ not used atm +local function growarea(a,b) + if not a then return b elseif not b then return a end + a.left_top.x = math.min(a.left_top.x,b.left_top.x) + a.left_top.y = math.min(a.left_top.y,b.left_top.y) + a.right_bottom.x = math.max(a.right_bottom.x,b.right_bottom.x) + a.right_bottom.y = math.max(a.right_bottom.y,b.right_bottom.y) + return a +end + +local function subarea(sub,area) -- sub is sub area of area + if not sub then return area end + if sameposition(sub.left_top, area.left_top) then + if sub.right_bottom.x == area.right_bottom.x then + area.left_top.y = sub.right_bottom.y + end -- no else here to contract area to a point + if sub.right_bottom.y == area.right_bottom.y then + area.left_top.x = sub.right_bottom.x + end + elseif sameposition(sub.right_bottom, area.right_bottom) then + if sub.left_top.x == area.left_top.x then + area.right_bottom.y = sub.left_top.y + elseif sub.left_top.y == area.left_top.y then -- but else here is ok -- sit in front of a black screen and let it fill up your mind. + area.right_bottom.x = sub.left_top.x + end + end + return area -- nothing changed, lol +end + +local function shrinkarea(area,entities) + if area.left_top.x == x and not entities[x] then + area.left_top.x = area.left_top.x + 1 + end + if area.right_bottom.x == x and not entities[x] then + area.right_bottom.x = area.right_bottom.x - 1 + end + if area.left_top.y == y and isemptyline(entities,y) then + area.left_top.y = area.left_top.y + 1 + end + if area.right_bottom.y == y and isemptyline(entities,y) then + area.right_bottom.y = area.right_bottom.y - 1 + end + return area +end +--]] + + +local MEMO = {} -- memoize +local function memo(f,...) -- doens't support arguments + return MEMO[f] or set(MEMO,f,f(...))[f] +end + + +return { -- exports + memo = memo, + split = split, + imerge = imerge, + isemptyline = isemptyline, + get = get , set = set , + get2d = get2d, set2d = set2d, + get3d = get3d, set3d = set3d, + rm2d = rm2d, + sameposition = sameposition, + moveposition = moveposition, +-- growarea = growarea, +-- subarea = subarea, +-- shrinkarea = shrinkarea, +} + diff --git a/scripts/init.lua b/scripts/init.lua new file mode 100644 index 0000000..d7124fc --- /dev/null +++ b/scripts/init.lua @@ -0,0 +1,65 @@ +local rpath = (...):match("(.-)[^%.]+$") +local Setting = require(rpath .. "setting") +local migration = require(rpath .. "migration") +local technology = require(rpath .. "technology") +local forces = require(rpath .. "forces") +local network = require(rpath .. "heat.network") +local interface = require(rpath .. "entity.interface") +local reactor = require(rpath .. "entity.reactor") +local fallout = require(rpath .. "entity.fallout") +local tower = require(rpath .. "entity.tower") +local ruin = require(rpath .. "entity.ruin") +local gui = require(rpath .. "gui.init") + + + + + +local function load() + network.load() +end + + +local function init() + global.version = 12 -- important for migrations + + global.random = game.create_random_generator() + global.lightEffects = {} + + global.sarcophagus = {} + global.interfaces = {} -- global.interfaces stores the interface ghost circuit connections at x,y,z + global.reactors = {} -- global.reactors stores the reactor and its parts(core, circuit interface, eccs) + global.towers = {} -- global.towers stores the cooling tower and the steam maker entity + global.ruins = {} -- global.ruins stores reactor ruins + + global.geigers = {} + global.fallout = {} + global.delayed_fallout = {} + + gui.init() + forces.init() + technology.init() + network.init() + + load() + --game.write_file("RealisticReactors.log"," ") -- this line cleans the log file on game start +end + + +local function on_tick(tick) + --global.dbg = 1 + interface.tick(tick) + fallout.tick(tick) + reactor.tick(tick) + tower.tick(tick) + ruin.tick(tick) + gui.tick(tick) +end + + +return { -- exports + init = init, + load = load, + tick = on_tick, + migration = migration, +} diff --git a/scripts/interface.lua b/scripts/interface.lua new file mode 100644 index 0000000..6e785f9 --- /dev/null +++ b/scripts/interface.lua @@ -0,0 +1,40 @@ +local rpath = (...):match("(.-)[^%.]+$") +local network = require(rpath .. "heat.network") + +-- for legacy + +-- local function add_heatpipe(entity) +-- if not network.get(entity) then +-- network.add_heat_pipe(entity) +-- end +-- end +-- +-- +-- local function remove_heatpipe(entity) +-- if network.get(entity) then +-- network.remove_heat_pipe(entity) +-- end +-- end + + +local function debug_heat_network(enabled) + if enabled then + network.debug.enable() + else + network.debug.disable() + end +end + +local function log_heat_network(enabled) + network.debug.logging(enabled) +end + + +return { -- interface +-- add_heatpipe = add_heatpipe, +-- remove_heatpipe = remove_heatpipe, + debug_heat_network = debug_heat_network, + debug_log_heat_network = log_heat_network, +} + +-- remote.call("rr-interface", "debug_heat_network", true) diff --git a/scripts/migration.lua b/scripts/migration.lua new file mode 100644 index 0000000..b5f752c --- /dev/null +++ b/scripts/migration.lua @@ -0,0 +1,378 @@ +local rpath = (...):match("(.-)[^%.]+$") +local Setting = require(rpath .. "setting") +local network = require(rpath .. "heat.network") +local technology = require(rpath .. "technology") +local union_tables = require(rpath .. "util").union_tables +local splitty = require(rpath .. "gui.util").splitty +local Setting = require(rpath .. "setting") + + +local function on_settings_changed() + for _,reactor in pairs(global.reactors) do --updating signals for removed fuel cell mods + if reactor.entity.valid == true then + if reactor.entity.get_fuel_inventory().is_empty() then + reactor.signals.parameters["uranium-fuel-cells"].signal = {type="item", name="uranium-fuel-cell"} + reactor.signals.parameters["uranium-fuel-cells"].count = 0 + end + end + reactor.signals.parameters["used-uranium-fuel-cells"] = nil + --if reactor.entity.get_burnt_result_inventory().is_empty() then + -- reactor.signals.parameters["used-uranium-fuel-cells"].signal = {type="item", name="used-up-uranium-fuel-cell"} + -- reactor.signals.parameters["used-uranium-fuel-cells"].count = 0 + --end + reactor.signals.parameters["neighbour-bonus"] = {signal=SIGNAL_NEIGHBOUR_BONUS, count=0, index=13} + end + technology.init() +end + + + +local function migration() + --msg("RealisticReactors - current mod version: "..global.version) + --game.players[1].print("on_configuration_changed. global.version: "..global.version) + if not global.version or global.version < 1 then + for i,reactor in pairs(global.reactors) do + reactor.signals.parameters["coolant-temperature"] = nil + reactor.signals.parameters["neighbour-bonus"] = nil + --fluid signal removal + --reactor.signals.parameters["coolant-amount"] = nil + + if not reactor.efficiency then + reactor.efficiency = 100 + end + if not reactor.signals.parameters["power-output"] then + reactor.signals.parameters["power-output"] = {signal=SIGNAL_REACTOR_POWER_OUTPUT,count=0,index=7} + end + if not reactor.signals.parameters["efficiency"] then + reactor.signals.parameters["efficiency"] = {signal=SIGNAL_REACTOR_EFFICIENCY,count=0,index=10} + end + + --2 + reactor.displayer = reactor.core.surface.create_entity{name = "realistic-reactor-normal", position = {reactor.core.position.x, reactor.core.position.y}, force = reactor.core.force.name, create_build_effect_smoke = false} + if not reactor.core.get_fuel_inventory().is_empty() then + reactor.displayer.get_fuel_inventory().insert(reactor.core.get_fuel_inventory()[1]) + end + + --3 + reactor.fuel_last_tick = reactor.core.burner.remaining_burning_fuel + if reactor.core.get_fuel_inventory().is_empty() then + reactor.cells_last_tick = 0 + else + reactor.cells_last_tick = reactor.core.get_fuel_inventory()[1].count + end + reactor.displayer.active = reactor.core.active + reactor.displayer.minable = reactor.core.minable + + --4 + reactor.power = reactor.core.surface.create_entity{ + name = REACTOR_POWER_NAME, + position = reactor.core.position, + force = reactor.core.force, + create_build_effect_smoke = false, + } + reactor.power.destructible=false + reactor.power.energy = 17000000 + reactor.signals.parameters["coolant-amount"] = {signal=SIGNAL_COOLANT_AMOUNT, count=0, index=6} + + --5 (removed, was the old implementation of bonus cells) + --6 + reactor.signals.parameters["cell-bonus"] = {signal=SIGNAL_REACTOR_CELL_BONUS, count=0, index=11} + + --7 + reactor.interface.operable = true + + --8 + reactor.cooling_history = 0 + local color = "black" + if reactor.state == "starting" then + color = "yellow" + elseif reactor.state == "running" then + color = "green" + elseif reactor.state == "scramed" then + color = "red" + end + reactor.lamp = reactor.core.surface.create_entity{name = "rr-"..color.."-lamp", position = {reactor.core.position.x-0.62,reactor.core.position.y+0.6}, force = reactor.core.force.name, create_build_effect_smoke = false} + reactor.lamp.destructible = false + reactor.light = reactor.core.surface.create_entity{name = "rr-"..color.."-light", position = {reactor.core.position.x-0.62,reactor.core.position.y+0.6}, force = reactor.core.force.name, create_build_effect_smoke = false} + reactor.light.destructible = false + reactor.interface_warning_tick = 0 + reactor.interface_warning = nil + reactor.cooling_warning_tick = 0 + reactor.cooling_warning = nil + reactor.power_usage = {starting = 0,cooling = 0, interface = 0} + reactor.neighbours = 0 + reactor.bonus_cells = {} + reactor.core.destructible = false + + --9 + reactor.signals.parameters["electric-power"] = {signal=SIGNAL_REACTOR_ELECTRIC_POWER, count=0, index=12} + reactor.fluid_amount_last_tick = 0 + reactor.power_output_last_tick = 0 + if reactor.core.get_burnt_result_inventory().is_empty() then + reactor.used_cells_last_tick = 0 + else + reactor.used_cells_last_tick = reactor.core.get_burnt_result_inventory()[1].count + end + + end + + --7 + gui.init() + + --9 + global.fallout = {} + global.falloutclouds = {} + global.geigers = {} + + + + global.version=1 + end + + if global.version < 2 then + + --10 + global.ruins = {} + global.sarcophagus = {} + game.create_force("radioactivity") + game.create_force("radioactivity-strong") + + --11 + local reactor_state = {} + reactor_state["stopped"] = 0 + reactor_state["starting"] = 1 + reactor_state["running"] = 2 + reactor_state["scramed"] = 3 + + --12 + global.delayed_fallout={} + global.tick_delayer = 0 + for _, stats in pairs (global.stats) do + stats.max = 999 + end + + for i,reactor in pairs(global.reactors) do + --11 + reactor.state = reactor_state[reactor.state] + reactor.last_temp_update = game.tick + reactor.signals.parameters["uranium-fuel-cells"].signal = {type="item", name="uranium-fuel-cell"} + --reactor.signals.parameters["used-uranium-fuel-cells"].signal = {type="item", name="used-up-uranium-fuel-cell"} + reactor.signals.parameters["used-uranium-fuel-cells"] = nil + reactor.core.destructible = false + reactor.max_power = 155 + + reactor.interface.destructible = false + reactor.interface.minable = false + end + global.version = 2 + end + + if global.version <3 then + + --for _, stats in pairs (global.stats) do + -- stats.max = 999 + --end + global.all_heat_pipes = {} + global.underground_heat_pipes = {} + for _, surface in pairs(game.surfaces) do + global.all_heat_pipes[surface.name] = {} + local heat_pipes = surface.find_entities_filtered{type='heat-pipe'} + + for _, hp in pairs (heat_pipes) do + if hp.name == "rr-underground-heat-pipe" then + table.insert(global.underground_heat_pipes, hp) + end + --function get_connected_heat_pipes(heat_pipe) + local result = {} + + for i,heatpipe in pairs(heat_pipes) do + + if hp.position.x == heatpipe.position.x then + if hp.position.y == heatpipe.position.y + 1 or hp.position.y == heatpipe.position.y - 1 then + ----logging("--> connected to heat pipe, ID: " .. heatpipe.unit_number) + table.insert(result, heatpipe) + end + end + + if hp.position.y == heatpipe.position.y then + if hp.position.x == heatpipe.position.x + 1 or hp.position.x == heatpipe.position.x - 1 then + ----logging("--> connected to heat pipe, ID: " .. heatpipe.unit_number) + table.insert(result, heatpipe) + end + end + + end + global.all_heat_pipes[surface.name][hp.unit_number] = {hp,result,{}} + end + end + + for i,reactor in pairs(global.reactors) do + reactor.max_efficiency = 210 + local hp_neighbour_entities_ew = reactor.core.surface.find_entities_filtered{area = {{reactor.position.x-2,reactor.position.y-1},{reactor.position.x+2,reactor.position.y}}, type='heat-pipe'} --east and west + local hp_neighbour_entities_n = reactor.core.surface.find_entities_filtered{area = {{reactor.position.x-1,reactor.position.y-2},{reactor.position.x+1,reactor.position.y}}, type='heat-pipe'} -- north + local table_of_heat_pipes_to_check = union_tables(hp_neighbour_entities_ew,hp_neighbour_entities_n) + for _, hp in pairs(table_of_heat_pipes_to_check) do + table.insert(global.all_heat_pipes[reactor.core.surface.name][hp.unit_number][3],reactor) + end + + end + global.version = 3 + end + + if global.version <4 then + for i,reactor in pairs(global.reactors) do + reactor.last_states_update = game.tick + end + global.version = 4 + end + + if global.version <5 then + for key, gui in pairs (global.guis) do + if gui.graph then gui.graph.destroy() end + end + global.version = 5 + end + + if global.version <6 then + for key, force in pairs (game.forces) do + if force.technologies["nuclear-power"].researched then + force.recipes["nuclear-reactor"].enabled = true + end + end + global.version = 6 + end + if global.version <7 then + for i,reactor in pairs(global.reactors) do + reactor.light.teleport{reactor.core.position.x+0.017,reactor.core.position.y+0.88} + reactor.lamp.teleport{reactor.core.position.x+0.017,reactor.core.position.y+0.88} + end + global.version = 7 + end + if global.version <10 then + for i,reactor in pairs(global.reactors) do + reactor.core.get_fuel_inventory().clear() + reactor.core.get_burnt_result_inventory().clear() + reactor.core.get_fuel_inventory().insert{name="rr-dummy-fuel-cell", count = 50} + reactor.core.burner.currently_burning = "rr-dummy-fuel-cell" + reactor.core.burner.remaining_burning_fuel = 9223372035000000000 + end + global.version = 10 + end + if global.version <11 then + global.random = game.create_random_generator() + global.lightEffects = global.lightEffects or {} + global.interfaces = {} + network.init() + + local towers = {} + global.iterate_cooling_towers = nil -- not needed anymore + for _,tower in pairs(global.towers) do + if tower.entity.valid then + towers[tower.id] = tower -- use unit_number as index + end + end + global.towers = towers + + local ruins = {} + for _,ruin in pairs(global.ruins) do + if ruin.entity.valid then + ruins[ruin.id] = ruin -- use unit_number as index + ruin.entity.destructible = false + ruin.entity.minable = true + end + end + global.ruins = ruins + + local fallouts = {} + for _,fallout in pairs(global.fallout) do + if fallout.surface.valid then + fallout.id = fallout.surface.index + fallout.positions = {[fallout.tick] = fallout.position} + fallout.position = nil + fallout.tick = nil + fallout.clouds = {} + fallouts[fallout.id] = fallout -- use surface.index as index + end + end + for _,cloud in pairs(global.falloutclouds) do + if cloud.valid then + local fallout = fallouts[cloud.surface.index] or { + id = cloud.surface.index, + surface = cloud.surface, + positions = {}, + clouds = {}, + } + fallouts[fallout.id] = fallout + table.insert(fallout.clouds, cloud) + end + end + global.falloutclouds = nil + global.fallout = fallouts + + local sarcophagus = {} + for _,entity in pairs(global.sarcophagus) do + if entity.valid then + sarcophagus[entity.unit_number] = entity + end + end + global.sarcophagus = sarcophagus + + local lookup = {} + for key,reactor in pairs(global.reactors) do + lookup[key] = reactor.displayer.unit_number + end + + local reactors = {} + global.iterate_reactor_temp_signals = nil + global.iterate_reactor_states = nil + global.iterate_reactor_temp = nil + global.skip_temp_iteration = nil + global.tick_delayer = nil + for _,reactor in pairs(global.reactors) do + if reactor.displayer.valid then + reactor.connected_neighbours_IDs = nil + reactor.entity = reactor.displayer + reactor.displayer = nil + reactor.id = reactor.entity.unit_number + reactors[reactor.id] = reactor -- use unit_number as index + end + end + global.reactors = reactors + + global.all_heat_pipes = nil + global.connected_reactors = nil + global.underground_heat_pipes = nil + + local stats = {} + for key,stat in pairs(global.stats) do + stats[lookup[key]] = stat + end + global.stats = stats + + local guis = {} + global.worker_key = nil + for key, gui in pairs(global.guis) do + local reactor_key, playerid = splitty(key,"-") + if gui.valid then + local id = lookup[reactor_key] + guis[id .. "-" .. playerid] = gui + gui.name = "rr_gui_" .. id + end + end + global.guis = guis + + global.version = 11 + end + if global.version <12 then + network.init() -- fixed a bug there, see the π release + global.version = 12 + end + --msg("RealisticReactors - current mod version (after migration): "..global.version) +end + +local function on_configuration_changed() + migration() + on_settings_changed() +end + +return on_configuration_changed -- exports diff --git a/scripts/setting.lua b/scripts/setting.lua new file mode 100644 index 0000000..5273b76 --- /dev/null +++ b/scripts/setting.lua @@ -0,0 +1,53 @@ +local Setting = {} + +local function get_setting(type, key) + return settings[type]["realistic-reactors-" .. key].value +end + + +function Setting.startup(key) + return get_setting('startup', key) +end + +function Setting.map(key) + return get_setting('global', key) +end + +function Setting.appearance(key) + return Setting.startup(key .. "-appearance") +end + +function Setting.protoduration(key) + return Setting.startup(key .. "-duration") +end + +function Setting.duration(key) + return Setting.map("reactor-" .. key .. "-duration") +end + +function Setting.behavior(key) + return Setting.map(key .. "-behaviour") +end + +function Setting.formula() + return Setting.map("calculate-stats-function") +end + +function Setting.ingos_formula() + local option = Setting.formula() + return option == "ingo" +end + +function Setting.ownlys_formula() + local option = Setting.formula() + return option == "ownly" +end + +function Setting.meltdown() + local name = Setting.map("explosion-mode") + return name == "meltdown" + or not game.entity_prototypes[name] +end + + +return Setting -- exports diff --git a/scripts/technology.lua b/scripts/technology.lua new file mode 100644 index 0000000..7876d08 --- /dev/null +++ b/scripts/technology.lua @@ -0,0 +1,17 @@ + +local function init() + for _,force in pairs (game.forces) do --bugfix for reactor recipe not showing up + if force.technologies["nuclear-power"].researched then + force.recipes["realistic-reactor"].enabled = true + force.recipes["rr-cooling-tower"].enabled = true + force.recipes["reactor-sarcophagus"].enabled = true + end + --force.recipes["breeder-reactor"].enabled = true + end +end + + +return { -- exports + init = init, +} + diff --git a/scripts/util.lua b/scripts/util.lua new file mode 100644 index 0000000..36f0017 --- /dev/null +++ b/scripts/util.lua @@ -0,0 +1,63 @@ +local function dbg(str) + if not global.dbg then global.dbg = 1 end + if type(str) ~= "number" and type(str) ~= "string" then + if str == true then + str = "true" + elseif str == false then + str = "false" + elseif type(str) == "table" then + str = serpent.line(str) + else + str = type(str) + end + end + game.players[1].print(global.dbg.."/"..game.tick..": "..str) + global.dbg = global.dbg + 1 +end + +local function msg(s) + game.print(s) + --for _, player in pairs(game.players) do + -- if player.connected then + -- player.print(s) + -- end + --end +end + +-- function logging(message) + -- game.write_file("RealisticReactors.log","\r\n[" .. game.tick .. "] " .. message,true) +-- end + + +local function union_tables(t1, t2) + for i,v in ipairs(t2) do + table.insert(t1, v) + end + return t1 +end + +local function tablemax(tbl) + local ret = 1 --actually 0 or nil, but for this usecase i need at least 1 + for _, val in pairs(tbl) do + if val > ret then + ret = val + end + end + return ret +end + + + +local function isempty(t) return not next(t) end + + +local function noop() end + + +return { -- exports + dbg=dbg, msg=msg, + union_tables = union_tables, + tablemax = tablemax, + isempty = isempty, + noop = noop, +} diff --git a/settings.lua b/settings.lua new file mode 100644 index 0000000..a17a6d0 --- /dev/null +++ b/settings.lua @@ -0,0 +1,171 @@ +local function flatten(tables) + local result = {} for _,t in ipairs(tables) do + for _,v in ipairs(t) do table.insert(result, v) end + end return result +end + + + +local function Setting(setting) + setting.name = "realistic-reactors-" .. setting.name + setting.type = setting.type .. "-setting" + return setting +end + +local function StartupSetting(setting) + setting.setting_type = "startup" + return Setting(setting) +end + +local function MapSetting(setting) + setting.setting_type = "runtime-global" + return Setting(setting) +end + +local function RuntimeSetting(setting) + setting.setting_type = "runtime-per-user" + return Setting(setting) +end + + + + +if mods["base"] then + data:extend{ + StartupSetting{ + type = "bool", + name = "disable-vanilla-reactor", + default_value = true, + order="a1", + }, + } +end + + + + +data:extend{ + StartupSetting{ + type = "bool", + name = "disable-reactor-light", + default_value = false, + order="a2", + }, + StartupSetting{ + type = "string", + name = "fallout-appearance", + default_value = "half-transparent", + allowed_values = { + "invisible", + "half-transparent", + "less-transparent", + "green-veil", + }, + order="a3", + }, + StartupSetting{ + type = "int", + name = "clouds-generation", + default_value = 200, + maximum_value = 9999999, + minimum_value = 0, + order = "a4", + }, + StartupSetting{ + type = "int", + name = "clouds-duration", + default_value = 80, + minimum_value = 1, + maximum_value = 9999999, + order="a5", + }, + StartupSetting{ + type = "int", + name = "fallout-duration", + default_value = 600, + minimum_value = 1, + maximum_value = 9999999, + order="a6", + }, + StartupSetting{ + type = "int", + name = "sarcophagus-duration", + default_value = 1800, + minimum_value = 1, + maximum_value = 9999999, + order="a7", + }, + MapSetting{ + type = "string", + name = "calculate-stats-function", + default_value = "ingo", + allowed_values = { + "ownly", + "ingo", + }, + order="b1", + }, + MapSetting{ + type = "int", + name = "reactor-scram-duration", + default_value = 180, + minimum_value = 1, + order="b2", + }, + MapSetting{ + type = "int", + name = "reactor-starting-duration", + default_value = 30, + minimum_value = 1, + order="b3", + }, + MapSetting{ + type = "double", + name = "energy-consumption-multiplier", + default_value = 1, + maximum_value = 2.5, + minimum_value = 0, + order = "b4", + }, + MapSetting{ + type = "bool", + name = "static-cooling-power-consumption", + default_value = true, + order="b5", + }, + MapSetting{ + type = "string", + name = "scram-behaviour", + default_value = "limit-to-current-cell", + allowed_values = { + "limit-to-current-cell", + "stop-half-empty", + "consume-additional-cell", + "decay-heat-v1", + }, + order="b6", + }, + MapSetting{ + type = "string", + name = "explosion-mode", + default_value = "meltdown", + allowed_values = flatten{ + { + "meltdown", + }, + not mods["True-Nukes"] and {} or { + "really-very-small-atomic-bomb-projectile", + "very-small-atomic-bomb-projectile", + "small-atomic-bomb-projectile", + "atomic-rocket", + "big-atomic-bomb-projectile", + "very-big-atomic-bomb-projectile", + "thermobaric-rocket", + }, + not mods["PlutoniumEnergy"] and {} or { + "plutonium-atomic-rocket", + }, + }, + order="b7", + }, +} diff --git a/sound/geiger0.ogg b/sound/geiger0.ogg new file mode 100644 index 0000000..43d1412 Binary files /dev/null and b/sound/geiger0.ogg differ diff --git a/sound/geiger1.ogg b/sound/geiger1.ogg new file mode 100644 index 0000000..910d9e9 Binary files /dev/null and b/sound/geiger1.ogg differ diff --git a/sound/reactor-active.ogg b/sound/reactor-active.ogg new file mode 100644 index 0000000..bb0aada Binary files /dev/null and b/sound/reactor-active.ogg differ diff --git a/thumbnail.png b/thumbnail.png new file mode 100644 index 0000000..810a929 Binary files /dev/null and b/thumbnail.png differ