940 lines
36 KiB
Lua
940 lines
36 KiB
Lua
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,
|
|
}
|
|
|