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, }