Sires
2018-03-15 e12220be79e25c1c5cb866e1e98091d35079cd3e
technic/machines/HV/nuclear_reactor.lua
@@ -1,257 +1,482 @@
-- The enriched uranium rod driven EU generator.
-- A very large and advanced machine providing vast amounts of power.
-- Very efficient but also expensive to run as it needs uranium. (10000EU 86400 ticks (24h))
-- Provides HV EUs that can be down converted as needed.
--
-- The nuclear reactor core needs water and a protective shield to work.
-- This is checked now and then and if the machine is tampered with... BOOM!
--[[
 The enriched uranium rod driven EU generator.
A very large and advanced machine providing vast amounts of power.
Very efficient but also expensive to run as it needs uranium.
Provides 10000 HV EUs for one week (only counted when loaded).
local burn_ticks   = 7 * 24 * 60 * 60       -- (seconds).
local power_supply = 100000                 -- EUs
local fuel_type    = "technic:uranium_fuel" -- The reactor burns this stuff
The nuclear reactor core requires a casing of water and a protective
shield to work.  This is checked now and then and if the casing is not
intact the reactor will melt down!
--]]
local burn_ticks = 7 * 24 * 60 * 60  -- Seconds
local power_supply = 100000  -- EUs
local fuel_type = "technic:uranium_fuel"  -- The reactor burns this
local digiline_meltdown = technic.config:get_bool("enable_nuclear_reactor_digiline_selfdestruct")
local digiline_remote_path = minetest.get_modpath("digiline_remote")
-- FIXME: recipe must make more sense like a rod recepticle, steam chamber, HV generator?
local S = technic.getter
local reactor_desc = S("@1 Nuclear Reactor Core", S("HV"))
local cable_entry = "^technic_cable_connection_overlay.png"
-- FIXME: Recipe should make more sense like a rod recepticle, steam chamber, HV generator?
minetest.register_craft({
   output = 'technic:hv_nuclear_reactor_core',
   recipe = {
      {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot'},
      {'technic:stainless_steel_ingot',                              '', 'technic:stainless_steel_ingot'},
      {'technic:stainless_steel_ingot',              'technic:hv_cable', 'technic:stainless_steel_ingot'},
      {'technic:carbon_plate',          'default:obsidian_glass', 'technic:carbon_plate'},
      {'technic:composite_plate',       'technic:machine_casing', 'technic:composite_plate'},
      {'technic:stainless_steel_ingot', 'technic:hv_cable',       'technic:stainless_steel_ingot'},
   }
})
local generator_formspec =
   "invsize[8,9;]"..
   "label[0,0;Nuclear Reactor Rod Compartment]"..
local function make_reactor_formspec(meta)
   local f = "size[8,9]"..
   "label[0,0;"..S("Nuclear Reactor Rod Compartment").."]"..
   "list[current_name;src;2,1;3,2;]"..
   "list[current_player;main;0,5;8,4;]"
   "list[current_player;main;0,5;8,4;]"..
   "listring[]"..
   "button[5.5,1.5;2,1;start;Start]"..
   "checkbox[5.5,2.5;autostart;automatic Start;"..meta:get_string("autostart").."]"
   if not digiline_remote_path then
      return f
   end
   local digiline_enabled = meta:get_string("enable_digiline")
   f = f.."checkbox[0.5,2.8;enable_digiline;Enable Digiline;"..digiline_enabled.."]"
   if digiline_enabled ~= "true" then
      return f
   end
   return f..
      "button_exit[4.6,3.69;2,1;save;Save]"..
      "field[1,4;4,1;remote_channel;Digiline Remote Channel;${remote_channel}]"
end
-- "Boxy sphere"
local nodebox = {
   { -0.353, -0.353, -0.353, 0.353, 0.353, 0.353 }, -- Box
   { -0.495, -0.064, -0.064, 0.495, 0.064, 0.064 }, -- Circle +-x
   { -0.483, -0.128, -0.128, 0.483, 0.128, 0.128 },
   { -0.462, -0.191, -0.191, 0.462, 0.191, 0.191 },
   { -0.433, -0.249, -0.249, 0.433, 0.249, 0.249 },
   { -0.397, -0.303, -0.303, 0.397, 0.303, 0.303 },
   { -0.305, -0.396, -0.305, 0.305, 0.396, 0.305 }, -- Circle +-y
   { -0.250, -0.432, -0.250, 0.250, 0.432, 0.250 },
   { -0.191, -0.461, -0.191, 0.191, 0.461, 0.191 },
   { -0.130, -0.482, -0.130, 0.130, 0.482, 0.130 },
   { -0.066, -0.495, -0.066, 0.066, 0.495, 0.066 },
   { -0.064, -0.064, -0.495, 0.064, 0.064, 0.495 }, -- Circle +-z
   { -0.128, -0.128, -0.483, 0.128, 0.128, 0.483 },
   { -0.191, -0.191, -0.462, 0.191, 0.191, 0.462 },
   { -0.249, -0.249, -0.433, 0.249, 0.249, 0.433 },
   { -0.303, -0.303, -0.397, 0.303, 0.303, 0.397 },
}
local SS_OFF = 0
local SS_DANGER = 1
local SS_CLEAR = 2
minetest.register_node("technic:hv_nuclear_reactor_core", {
   description = "Nuclear Reactor",
   tiles = {"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png",
            "technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png",
            "technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png"},
   groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2},
   legacy_facedir_simple = true,
   sounds = default.node_sound_wood_defaults(),
   drawtype="nodebox",
   paramtype = "light",
   stack_max = 1,
   node_box = {
      type = "fixed",
      fixed = nodebox
   },
   on_construct = function(pos)
      local meta = minetest.get_meta(pos)
      meta:set_string("infotext", "Nuclear Reactor Core")
      meta:set_int("HV_EU_supply", 0)
      -- Signal to the switching station that this device burns some
      -- sort of fuel and needs special handling
      meta:set_int("HV_EU_from_fuel", 1)
      meta:set_int("burn_time", 0)
      meta:set_string("formspec", generator_formspec)
      local inv = meta:get_inventory()
      inv:set_size("src", 6)
   end,
   can_dig = function(pos,player)
      local meta = minetest.get_meta(pos);
      local inv = meta:get_inventory()
      if not inv:is_empty("src") then
         minetest.chat_send_player(player:get_player_name(),
            "Machine cannot be removed because it is not empty");
         return false
      else
         return true
      end
   end,
})
local reactor_siren = {}
local function siren_set_state(pos, state)
   local hpos = minetest.hash_node_position(pos)
   local siren = reactor_siren[hpos]
   if not siren then
      if state == SS_OFF then return end
      siren = {state=SS_OFF}
      reactor_siren[hpos] = siren
   end
   if state == SS_DANGER and siren.state ~= SS_DANGER then
      if siren.handle then minetest.sound_stop(siren.handle) end
      siren.handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_danger_loop",
            {pos=pos, gain=1.5, loop=true, max_hear_distance=48})
      siren.state = SS_DANGER
   elseif state == SS_CLEAR then
      if siren.handle then minetest.sound_stop(siren.handle) end
      local clear_handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_clear",
            {pos=pos, gain=1.5, loop=false, max_hear_distance=48})
      siren.handle = clear_handle
      siren.state = SS_CLEAR
      minetest.after(10, function()
         if siren.handle ~= clear_handle then return end
         minetest.sound_stop(clear_handle)
         if reactor_siren[hpos] == siren then
            reactor_siren[hpos] = nil
         end
      end)
   elseif state == SS_OFF and siren.state ~= SS_OFF then
      if siren.handle then minetest.sound_stop(siren.handle) end
      reactor_siren[hpos] = nil
   end
end
minetest.register_node("technic:hv_nuclear_reactor_core_active", {
   description = "HV Uranium Reactor",
   tiles = {"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png",
            "technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png",
       "technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png"},
   groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, not_in_creative_inventory=1},
   legacy_facedir_simple = true,
   sounds = default.node_sound_wood_defaults(),
   drop="technic:hv_nuclear_reactor_core",
   drawtype="nodebox",
   light_source = 15,
   paramtype = "light",
   node_box = {
      type = "fixed",
      fixed = nodebox
   },
   can_dig = function(pos,player)
      local meta = minetest.get_meta(pos);
      local inv = meta:get_inventory()
      if not inv:is_empty("src") then
         minetest.chat_send_player(player:get_player_name(),
            "Machine cannot be removed because it is not empty");
         return false
      else
         return true
      end
   end,
})
local function siren_danger(pos, meta)
   meta:set_int("siren", 1)
   siren_set_state(pos, SS_DANGER)
end
local check_reactor_structure = function(pos)
   -- The reactor consists of a 9x9x9 cube structure
   -- A cross section through the middle:
   --  CCCC CCCC
   --  CBBB BBBC
   --  CBSS SSBC
   --  CBSWWWSBC
   --  CBSW#WSBC
   --  CBSW|WSBC
   --  CBSS|SSBC
   --  CBBB|BBBC
   --  CCCC|CCCC
   --  C = Concrete, B = Blast resistant concrete, S = Stainless Steel,
   --  W = water node, # = reactor core, | = HV cable
   --  The man-hole and the HV cable is only in the middle
   --  The man-hole is optional
local function siren_clear(pos, meta)
   if meta:get_int("siren") ~= 0 then
      siren_set_state(pos, SS_CLEAR)
      meta:set_int("siren", 0)
   end
end
--[[
The standard reactor structure consists of a 9x9x9 cube.  A cross
section through the middle:
   CCCC CCCC
   CBBB BBBC
   CBLL LLBC
   CBLWWWLBC
   CBLW#WLBC
   CBLW|WLBC
   CBLL|LLBC
   CBBB|BBBC
   CCCC|CCCC
   C = Concrete, B = Blast-resistant concrete, L = Lead,
   W = water node, # = reactor core, | = HV cable
The man-hole is optional (but necessary for refueling).
For the reactor to operate and not melt down, it insists on the inner
7x7x7 portion (from the core out to the blast-resistant concrete)
being intact.  Intactness only depends on the number of nodes of the
right type in each layer.  The water layer must have water in all but
at most one node; the steel and blast-resistant concrete layers must
have the right material in all but at most two nodes.  The permitted
gaps are meant for the cable and man-hole, but can actually be anywhere
and contain anything.  For the reactor to be useful, a cable must
connect to the core, but it can go in any direction.
The outer concrete layer of the standard structure is not required
for the reactor to operate.  It is noted here because it used to
be mandatory, and for historical reasons (that it predates the
implementation of radiation) it needs to continue being adequate
shielding of legacy reactors.  If it ever ceases to be adequate
shielding for new reactors, legacy ones should be grandfathered.
For legacy reasons, if the reactor has a stainless steel layer instead
of a lead layer it will be converted to a lead layer.
--]]
local function reactor_structure_badness(pos)
   local vm = VoxelManip()
   local pos1 = vector.subtract(pos, 4)
   local pos2 = vector.add(pos, 4)
   local pos1 = vector.subtract(pos, 3)
   local pos2 = vector.add(pos, 3)
   local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2)
   local data = vm:get_data()
   local area = VoxelArea:new({MinEdge=MinEdge, MaxEdge=MaxEdge})
   local c_concrete = minetest.get_content_id("technic:concrete")
   local c_blast_concrete = minetest.get_content_id("technic:blast_resistant_concrete")
   local c_stainless_steel = minetest.get_content_id("technic:stainless_steel_block")
   local c_lead = minetest.get_content_id("technic:lead_block")
   local c_steel = minetest.get_content_id("technic:stainless_steel_block")
   local c_water_source = minetest.get_content_id("default:water_source")
   local c_water_flowing = minetest.get_content_id("default:water_flowing")
   local concretelayer, blastlayer, steellayer, waterlayer = 0, 0, 0, 0
   local blast_layer, steel_layer, lead_layer, water_layer = 0, 0, 0, 0
   for z = pos1.z, pos2.z do
   for y = pos1.y, pos2.y do
   for x = pos1.x, pos2.x do
      -- If the position is in the outer layer
      local cid = data[area:index(x, y, z)]
      if x == pos1.x or x == pos2.x or
         y == pos1.y or y == pos2.y or
         z == pos1.z or z == pos2.z then
         if data[area:index(x, y, z)] == c_concrete then
            concretelayer = concretelayer + 1
         if cid == c_blast_concrete then
            blast_layer = blast_layer + 1
         end
      elseif x == pos1.x+1 or x == pos2.x-1 or
         y == pos1.y+1 or y == pos2.y-1 or
         z == pos1.z+1 or z == pos2.z-1 then
         if data[area:index(x, y, z)] == c_blast_concrete then
            blastlayer = blastlayer + 1
             y == pos1.y+1 or y == pos2.y-1 or
             z == pos1.z+1 or z == pos2.z-1 then
         if cid == c_lead then
            lead_layer = lead_layer + 1
         elseif cid == c_steel then
            steel_layer = steel_layer + 1
         end
      elseif x == pos1.x+2 or x == pos2.x-2 or
         y == pos1.y+2 or y == pos2.y-2 or
         z == pos1.z+2 or z == pos2.z-2 then
         if data[area:index(x, y, z)] == c_stainless_steel then
            steellayer = steellayer + 1
         end
      elseif x == pos1.x+3 or x == pos2.x-3 or
         y == pos1.y+3 or y == pos2.y-3 or
         z == pos1.z+3 or z == pos2.z-3 then
            local cid = data[area:index(x, y, z)]
             y == pos1.y+2 or y == pos2.y-2 or
             z == pos1.z+2 or z == pos2.z-2 then
         if cid == c_water_source or cid == c_water_flowing then
            waterlayer = waterlayer + 1
            water_layer = water_layer + 1
         end
      end
   end
   end
   end
   if waterlayer >= 25 and
      steellayer >= 96 and
      blastlayer >= 216 and
      concretelayer >= 384 then
      return true
   if steel_layer >= 96 then
      for z = pos1.z+1, pos2.z-1 do
      for y = pos1.y+1, pos2.y-1 do
      for x = pos1.x+1, pos2.x-1 do
         local vi = area:index(x, y, z)
         if x == pos1.x+1 or x == pos2.x-1 or
            y == pos1.y+1 or y == pos2.y-1 or
            z == pos1.z+1 or z == pos2.z-1 then
            if data[vi] == c_steel then
               data[vi] = c_lead
            end
         end
      end
      end
      end
      vm:set_data(data)
      vm:write_to_map()
      lead_layer = steel_layer
   end
   if water_layer > 25 then water_layer = 25 end
   if lead_layer > 96 then lead_layer = 96 end
   if blast_layer > 216 then blast_layer = 216 end
   return (25 - water_layer) + (96 - lead_layer) + (216 - blast_layer)
end
local explode_reactor = function(pos)
   print("A reactor exploded at "..minetest.pos_to_string(pos))
local function melt_down_reactor(pos)
   minetest.log("action", "A reactor melted down at "..minetest.pos_to_string(pos))
   minetest.set_node(pos, {name = "technic:corium_source"})
end
local function damage_nearby_players(pos)
   local objs = minetest.get_objects_inside_radius(pos, 4)
   for _, o in pairs(objs) do
      if o:is_player() then
         o:set_hp(math.max(o:get_hp() - 2, 0))
local function start_reactor(pos, meta)
   if minetest.get_node(pos).name ~= "technic:hv_nuclear_reactor_core" then
      return false
   end
   local inv = meta:get_inventory()
   if inv:is_empty("src") then
      return false
   end
   local src_list = inv:get_list("src")
   local correct_fuel_count = 0
   for _, src_stack in pairs(src_list) do
      if src_stack and src_stack:get_name() == fuel_type then
         correct_fuel_count = correct_fuel_count + 1
      end
   end
   -- Check that the reactor is complete and has the correct fuel
   if correct_fuel_count ~= 6 or reactor_structure_badness(pos) ~= 0 then
      return false
   end
   meta:set_int("burn_time", 1)
   technic.swap_node(pos, "technic:hv_nuclear_reactor_core_active")
   meta:set_int("HV_EU_supply", power_supply)
   for idx, src_stack in pairs(src_list) do
      src_stack:take_item()
      inv:set_stack("src", idx, src_stack)
   end
   return true
end
minetest.register_abm({
   nodenames = {"technic:hv_nuclear_reactor_core", "technic:hv_nuclear_reactor_core_active"},
   interval = 1,
   chance   = 1,
   action = function(pos, node, active_object_count, active_object_count_wider)
   label = "Machines: reactor melt-down check",
   nodenames = {"technic:hv_nuclear_reactor_core_active"},
   interval = 4,
   chance = 1,
   action = function (pos, node)
      local meta = minetest.get_meta(pos)
      local badness = reactor_structure_badness(pos)
      local accum_badness = meta:get_int("structure_accumulated_badness")
      if badness == 0 then
         if accum_badness ~= 0 then
            meta:set_int("structure_accumulated_badness", math.max(accum_badness - 4, 0))
            siren_clear(pos, meta)
         end
      else
         siren_danger(pos, meta)
         accum_badness = accum_badness + badness
         if accum_badness >= 25 then
            melt_down_reactor(pos)
         else
            meta:set_int("structure_accumulated_badness", accum_badness)
         end
      end
   end,
})
local function run(pos, node)
   local meta = minetest.get_meta(pos)
   local burn_time = meta:get_int("burn_time") or 0
   if burn_time >= burn_ticks or burn_time == 0 then
      if digiline_remote_path and meta:get_int("HV_EU_supply") == power_supply then
         digiline_remote.send_to_node(pos, meta:get_string("remote_channel"),
               "fuel used", 6, true)
      end
      if meta:get_string("autostart") == "true" then
         if start_reactor(pos, meta) then
            return
         end
      end
      meta:set_int("HV_EU_supply", 0)
      meta:set_int("burn_time", 0)
      meta:set_string("infotext", S("%s Idle"):format(reactor_desc))
      technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
      meta:set_int("structure_accumulated_badness", 0)
      siren_clear(pos, meta)
   elseif burn_time > 0 then
      burn_time = burn_time + 1
      meta:set_int("burn_time", burn_time)
      local percent = math.floor(burn_time / burn_ticks * 100)
      meta:set_string("infotext", reactor_desc.." ("..percent.."%)")
      meta:set_int("HV_EU_supply", power_supply)
   end
end
local nuclear_reactor_receive_fields = function(pos, formname, fields, sender)
   local player_name = sender:get_player_name()
   if minetest.is_protected(pos, player_name) then
      minetest.chat_send_player(player_name, "You are not allowed to edit this!")
      minetest.record_protection_violation(pos, player_name)
      return
   end
   local meta = minetest.get_meta(pos)
   local update_formspec = false
   if fields.remote_channel then
      meta:set_string("remote_channel", fields.remote_channel)
   end
   if fields.start then
      local b = start_reactor(pos, meta)
      if b then
         minetest.chat_send_player(player_name, "Start successful")
      else
         minetest.chat_send_player(player_name, "Error")
      end
   end
   if fields.autostart then
      meta:set_string("autostart", fields.autostart)
      update_formspec = true
   end
   if fields.enable_digiline then
      meta:set_string("enable_digiline", fields.enable_digiline)
      update_formspec = true
   end
   if update_formspec then
      meta:set_string("formspec", make_reactor_formspec(meta))
   end
end
local digiline_remote_def = function(pos, channel, msg)
   local meta = minetest.get_meta(pos)
   if meta:get_string("enable_digiline") ~= "true" or
         channel ~= meta:get_string("remote_channel") then
      return
   end
   -- Convert string messages to tables:
   local msgt = type(msg)
   if msgt == "string" then
      local smsg = msg:lower()
      msg = {}
      if smsg == "get" then
         msg.command = "get"
      elseif smsg:sub(1, 13) == "self_destruct" then
         msg.command = "self_destruct"
         msg.timer = tonumber(smsg:sub(15)) or 0
      elseif smsg == "start" then
         msg.command = "start"
      end
   elseif msgt ~= "table" then
      return
   end
   if msg.command == "get" then
      local inv = meta:get_inventory()
      local invtable = {}
      for i = 1, 6 do
         local stack = inv:get_stack("src", i)
         if stack:is_empty() then
            invtable[i] = 0
         elseif stack:get_name() == fuel_type then
            invtable[i] = stack:get_count()
         else
            invtable[i] = -stack:get_count()
         end
      end
      digiline_remote.send_to_node(pos, channel, {
         burn_time = meta:get_int("burn_time"),
         enabled   = meta:get_int("HV_EU_supply") == power_supply,
         siren     = meta:get_int("siren") == 1,
         structure_accumulated_badness = meta:get_int("structure_accumulated_badness"),
         rods = invtable
      }, 6, true)
   elseif digiline_meltdown and msg.command == "self_destruct" and
         minetest.get_node(pos).name == "technic:hv_nuclear_reactor_core_active" then
      if msg.timer ~= 0 and type(msg.timer) == "number" then
         siren_danger(pos, meta)
         minetest.after(msg.timer, melt_down_reactor, pos)
      else
         melt_down_reactor(pos)
      end
   elseif msg.command == "start" then
      local b = start_reactor(pos, meta)
      if b then
         digiline_remote.send_to_node(pos, channel, "Start successful", 6, true)
      else
         digiline_remote.send_to_node(pos, channel, "Error", 6, true)
      end
   end
end
minetest.register_node("technic:hv_nuclear_reactor_core", {
   description = reactor_desc,
   tiles = {
      "technic_hv_nuclear_reactor_core.png",
      "technic_hv_nuclear_reactor_core.png"..cable_entry
   },
   drawtype = "mesh",
   mesh = "technic_reactor.obj",
   groups = {cracky = 1, technic_machine = 1, technic_hv = 1, digiline_remote_receive = 1},
   legacy_facedir_simple = true,
   sounds = default.node_sound_wood_defaults(),
   paramtype = "light",
   paramtype2 = "facedir",
   stack_max = 1,
   on_receive_fields = nuclear_reactor_receive_fields,
   on_construct = function(pos)
      local meta = minetest.get_meta(pos)
      meta:set_string("infotext", reactor_desc)
      meta:set_string("formspec", make_reactor_formspec(meta))
      if digiline_remote_path then
         meta:set_string("remote_channel",
               "nucelear_reactor"..minetest.pos_to_string(pos))
      end
      local inv = meta:get_inventory()
      inv:set_size("src", 6)
   end,
   _on_digiline_remote_receive = digiline_remote_def,
   can_dig = technic.machine_can_dig,
   on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
   allow_metadata_inventory_put = technic.machine_inventory_put,
   allow_metadata_inventory_take = technic.machine_inventory_take,
   allow_metadata_inventory_move = technic.machine_inventory_move,
   technic_run = run,
})
minetest.register_node("technic:hv_nuclear_reactor_core_active", {
   tiles = {
      "technic_hv_nuclear_reactor_core.png",
      "technic_hv_nuclear_reactor_core.png"..cable_entry
   },
   drawtype = "mesh",
   mesh = "technic_reactor.obj",
   groups = {cracky = 1, technic_machine = 1, technic_hv = 1, radioactive = 4,
      not_in_creative_inventory = 1, digiline_remote_receive = 1},
   legacy_facedir_simple = true,
   sounds = default.node_sound_wood_defaults(),
   drop = "technic:hv_nuclear_reactor_core",
   light_source = 14,
   paramtype = "light",
   paramtype2 = "facedir",
   on_receive_fields = nuclear_reactor_receive_fields,
   _on_digiline_remote_receive = digiline_remote_def,
   can_dig = technic.machine_can_dig,
   after_dig_node = melt_down_reactor,
   on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
   allow_metadata_inventory_put = technic.machine_inventory_put,
   allow_metadata_inventory_take = technic.machine_inventory_take,
   allow_metadata_inventory_move = technic.machine_inventory_move,
   technic_run = run,
   technic_on_disable = function(pos, node)
      local timer = minetest.get_node_timer(pos)
           timer:start(1)
        end,
   on_timer = function(pos, node)
      local meta = minetest.get_meta(pos)
      -- Connected back?
      if meta:get_int("HV_EU_timeout") > 0 then return false end
      local burn_time = meta:get_int("burn_time") or 0
      if burn_time >= burn_ticks or burn_time == 0 then
         local inv = meta:get_inventory()
         if not inv:is_empty("src") then
            local srclist = inv:get_list("src")
            local correct_fuel_count = 0
            for _, srcstack in pairs(srclist) do
               if srcstack then
                  if  srcstack:get_name() == fuel_type then
                     correct_fuel_count = correct_fuel_count + 1
                  end
               end
            end
            -- Check that the reactor is complete as well
            -- as the correct number of correct fuel
            if correct_fuel_count == 6 and
               check_reactor_structure(pos) then
               meta:set_int("burn_time", 1)
               hacky_swap_node(pos, "technic:hv_nuclear_reactor_core_active")
               meta:set_int("HV_EU_supply", power_supply)
               for idx, srcstack in pairs(srclist) do
                  srcstack:take_item()
                  inv:set_stack("src", idx, srcstack)
               end
               return
            end
         end
         meta:set_int("HV_EU_supply", 0)
         meta:set_int("burn_time", 0)
         meta:set_string("infotext", "Nuclear Reactor Core (idle)")
         hacky_swap_node(pos, "technic:hv_nuclear_reactor_core")
      elseif burn_time > 0 then
         damage_nearby_players(pos)
         if not check_reactor_structure(pos) then
            explode_reactor(pos)
         end
         burn_time = burn_time + 1
         meta:set_int("burn_time", burn_time)
         local percent = math.floor(burn_time / burn_ticks * 100)
         meta:set_string("infotext", "Nuclear Reactor Core ("..percent.."%)")
         meta:set_int("HV_EU_supply", power_supply)
         technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
         meta:set_int("structure_accumulated_badness", 0)
         siren_clear(pos, meta)
         return false
      end
   end
      meta:set_int("burn_time", burn_time + 1)
      return true
   end,
})
technic.register_machine("HV", "technic:hv_nuclear_reactor_core",        technic.producer)