Vanessa Ezekowitz
2017-04-13 1d20af1bcba7611289d194afcac0dc1ecec87a0c
commit | author | age
85a984 1 --[[
S 2  The enriched uranium rod driven EU generator.
3 A very large and advanced machine providing vast amounts of power.
4 Very efficient but also expensive to run as it needs uranium.
5 Provides 10000 HV EUs for one week (only counted when loaded).
ee0765 6
85a984 7 The nuclear reactor core requires a casing of water and a protective
S 8 shield to work.  This is checked now and then and if the casing is not
9 intact the reactor will melt down!
10 --]]
11
12 local burn_ticks = 7 * 24 * 60 * 60  -- Seconds
13 local power_supply = 100000  -- EUs
14 local fuel_type = "technic:uranium_fuel"  -- The reactor burns this
ee0765 15
be2f30 16 local S = technic.getter
ee0765 17
1d20af 18 local reactor_desc = S("@1 Nuclear Reactor Core", S("HV"))
VE 19 local cable_entry = "^technic_cable_connection_overlay.png"
85a984 20
S 21 -- FIXME: Recipe should make more sense like a rod recepticle, steam chamber, HV generator?
ee0765 22 minetest.register_craft({
S 23     output = 'technic:hv_nuclear_reactor_core',
24     recipe = {
5e4a87 25         {'technic:carbon_plate',          'default:obsidian_glass', 'technic:carbon_plate'},
Z 26         {'technic:composite_plate',       'technic:machine_casing', 'technic:composite_plate'},
83c649 27         {'technic:stainless_steel_ingot', 'technic:hv_cable',       'technic:stainless_steel_ingot'},
ee0765 28     }
S 29 })
30
85a984 31 local reactor_formspec =
ee0765 32     "invsize[8,9;]"..
be2f30 33     "label[0,0;"..S("Nuclear Reactor Rod Compartment").."]"..
ee0765 34     "list[current_name;src;2,1;3,2;]"..
d732c8 35     "list[current_player;main;0,5;8,4;]"..
E 36     "listring[]"
ee0765 37
85a984 38 local SS_OFF = 0
S 39 local SS_DANGER = 1
40 local SS_CLEAR = 2
41
d59055 42 local reactor_siren = {}
85a984 43 local function siren_set_state(pos, state)
d59055 44     local hpos = minetest.hash_node_position(pos)
Z 45     local siren = reactor_siren[hpos]
46     if not siren then
85a984 47         if state == SS_OFF then return end
S 48         siren = {state=SS_OFF}
d59055 49         reactor_siren[hpos] = siren
Z 50     end
85a984 51     if state == SS_DANGER and siren.state ~= SS_DANGER then
d59055 52         if siren.handle then minetest.sound_stop(siren.handle) end
85a984 53         siren.handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_danger_loop",
S 54                 {pos=pos, gain=1.5, loop=true, max_hear_distance=48})
55         siren.state = SS_DANGER
56     elseif state == SS_CLEAR then
d59055 57         if siren.handle then minetest.sound_stop(siren.handle) end
85a984 58         local clear_handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_clear",
S 59                 {pos=pos, gain=1.5, loop=false, max_hear_distance=48})
d59055 60         siren.handle = clear_handle
85a984 61         siren.state = SS_CLEAR
S 62         minetest.after(10, function()
63             if siren.handle ~= clear_handle then return end
64             minetest.sound_stop(clear_handle)
65             if reactor_siren[hpos] == siren then
66                 reactor_siren[hpos] = nil
d59055 67             end
Z 68         end)
85a984 69     elseif state == SS_OFF and siren.state ~= SS_OFF then
d59055 70         if siren.handle then minetest.sound_stop(siren.handle) end
Z 71         reactor_siren[hpos] = nil
72     end
73 end
85a984 74
d59055 75 local function siren_danger(pos, meta)
Z 76     meta:set_int("siren", 1)
85a984 77     siren_set_state(pos, SS_DANGER)
d59055 78 end
85a984 79
d59055 80 local function siren_clear(pos, meta)
Z 81     if meta:get_int("siren") ~= 0 then
85a984 82         siren_set_state(pos, SS_CLEAR)
d59055 83         meta:set_int("siren", 0)
Z 84     end
85 end
86
85a984 87 --[[
S 88 The standard reactor structure consists of a 9x9x9 cube.  A cross
89 section through the middle:
90
91     CCCC CCCC
92     CBBB BBBC
8ccb6d 93     CBLL LLBC
S 94     CBLWWWLBC
95     CBLW#WLBC
96     CBLW|WLBC
97     CBLL|LLBC
85a984 98     CBBB|BBBC
S 99     CCCC|CCCC
8ccb6d 100     C = Concrete, B = Blast-resistant concrete, L = Lead,
85a984 101     W = water node, # = reactor core, | = HV cable
S 102
8ccb6d 103 The man-hole is optional (but necessary for refueling).
85a984 104
S 105 For the reactor to operate and not melt down, it insists on the inner
106 7x7x7 portion (from the core out to the blast-resistant concrete)
107 being intact.  Intactness only depends on the number of nodes of the
108 right type in each layer.  The water layer must have water in all but
109 at most one node; the steel and blast-resistant concrete layers must
110 have the right material in all but at most two nodes.  The permitted
111 gaps are meant for the cable and man-hole, but can actually be anywhere
112 and contain anything.  For the reactor to be useful, a cable must
113 connect to the core, but it can go in any direction.
114
115 The outer concrete layer of the standard structure is not required
116 for the reactor to operate.  It is noted here because it used to
117 be mandatory, and for historical reasons (that it predates the
118 implementation of radiation) it needs to continue being adequate
119 shielding of legacy reactors.  If it ever ceases to be adequate
120 shielding for new reactors, legacy ones should be grandfathered.
8ccb6d 121
S 122 For legacy reasons, if the reactor has a stainless steel layer instead
123 of a lead layer it will be converted to a lead layer.
85a984 124 --]]
S 125 local function reactor_structure_badness(pos)
ee0765 126     local vm = VoxelManip()
84cf65 127     local pos1 = vector.subtract(pos, 3)
Z 128     local pos2 = vector.add(pos, 3)
ee0765 129     local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2)
S 130     local data = vm:get_data()
131     local area = VoxelArea:new({MinEdge=MinEdge, MaxEdge=MaxEdge})
132
133     local c_blast_concrete = minetest.get_content_id("technic:blast_resistant_concrete")
8ccb6d 134     local c_lead = minetest.get_content_id("technic:lead_block")
S 135     local c_steel = minetest.get_content_id("technic:stainless_steel_block")
ee0765 136     local c_water_source = minetest.get_content_id("default:water_source")
S 137     local c_water_flowing = minetest.get_content_id("default:water_flowing")
138
8ccb6d 139     local blast_layer, steel_layer, lead_layer, water_layer = 0, 0, 0, 0
ee0765 140
S 141     for z = pos1.z, pos2.z do
142     for y = pos1.y, pos2.y do
143     for x = pos1.x, pos2.x do
84cf65 144         local cid = data[area:index(x, y, z)]
ee0765 145         if x == pos1.x or x == pos2.x or
S 146            y == pos1.y or y == pos2.y or
147            z == pos1.z or z == pos2.z then
84cf65 148             if cid == c_blast_concrete then
8ccb6d 149                 blast_layer = blast_layer + 1
ee0765 150             end
S 151         elseif x == pos1.x+1 or x == pos2.x-1 or
8ccb6d 152                y == pos1.y+1 or y == pos2.y-1 or
S 153                z == pos1.z+1 or z == pos2.z-1 then
154             if cid == c_lead then
155                 lead_layer = lead_layer + 1
156             elseif cid == c_steel then
157                 steel_layer = steel_layer + 1
ee0765 158             end
S 159         elseif x == pos1.x+2 or x == pos2.x-2 or
8ccb6d 160                y == pos1.y+2 or y == pos2.y-2 or
S 161                z == pos1.z+2 or z == pos2.z-2 then
ee0765 162             if cid == c_water_source or cid == c_water_flowing then
8ccb6d 163                 water_layer = water_layer + 1
ee0765 164             end
S 165         end
166     end
167     end
168     end
8ccb6d 169
S 170     if steel_layer >= 96 then
171         for z = pos1.z+1, pos2.z-1 do
172         for y = pos1.y+1, pos2.y-1 do
173         for x = pos1.x+1, pos2.x-1 do
174             local vi = area:index(x, y, z)
175             if x == pos1.x+1 or x == pos2.x-1 or
176                y == pos1.y+1 or y == pos2.y-1 or
177                z == pos1.z+1 or z == pos2.z-1 then
178                 if data[vi] == c_steel then
179                     data[vi] = c_lead
180                 end
181             end
182         end
183         end
184         end
185         vm:set_data(data)
186         vm:write_to_map()
187         lead_layer = steel_layer
188     end
189
190     if water_layer > 25 then water_layer = 25 end
191     if lead_layer > 96 then lead_layer = 96 end
192     if blast_layer > 216 then blast_layer = 216 end
193     return (25 - water_layer) + (96 - lead_layer) + (216 - blast_layer)
ee0765 194 end
S 195
85a984 196
S 197 local function melt_down_reactor(pos)
198     minetest.log("action", "A reactor melted down at "..minetest.pos_to_string(pos))
366fc3 199     minetest.set_node(pos, {name="technic:corium_source"})
ee0765 200 end
67b90f 201
85a984 202
67b90f 203 minetest.register_abm({
78f16c 204     label = "Machines: reactor melt-down check",
67b90f 205     nodenames = {"technic:hv_nuclear_reactor_core_active"},
85a984 206     interval = 4,
67b90f 207     chance = 1,
Z 208     action = function (pos, node)
209         local meta = minetest.get_meta(pos)
210         local badness = reactor_structure_badness(pos)
211         local accum_badness = meta:get_int("structure_accumulated_badness")
212         if badness == 0 then
213             if accum_badness ~= 0 then
85a984 214                 meta:set_int("structure_accumulated_badness", accum_badness - 4)
d59055 215                 siren_clear(pos, meta)
67b90f 216             end
Z 217         else
d59055 218             siren_danger(pos, meta)
67b90f 219             accum_badness = accum_badness + badness
85a984 220             if accum_badness >= 25 then
S 221                 melt_down_reactor(pos)
67b90f 222             else
Z 223                 meta:set_int("structure_accumulated_badness", accum_badness)
224             end
225         end
226     end,
227 })
ee0765 228
85a984 229 local function run(pos, node)
563a4c 230     local meta = minetest.get_meta(pos)
N 231     local burn_time = meta:get_int("burn_time") or 0
ee0765 232
563a4c 233     if burn_time >= burn_ticks or burn_time == 0 then
N 234         local inv = meta:get_inventory()
235         if not inv:is_empty("src") then 
85a984 236             local src_list = inv:get_list("src")
563a4c 237             local correct_fuel_count = 0
85a984 238             for _, src_stack in pairs(src_list) do
S 239                 if src_stack and src_stack:get_name() == fuel_type then
240                     correct_fuel_count = correct_fuel_count + 1
ee0765 241                 end
563a4c 242             end
85a984 243             -- Check that the reactor is complete and has the correct fuel
563a4c 244             if correct_fuel_count == 6 and
85a984 245                     reactor_structure_badness(pos) == 0 then
563a4c 246                 meta:set_int("burn_time", 1)
N 247                 technic.swap_node(pos, "technic:hv_nuclear_reactor_core_active") 
248                 meta:set_int("HV_EU_supply", power_supply)
85a984 249                 for idx, src_stack in pairs(src_list) do
S 250                     src_stack:take_item()
251                     inv:set_stack("src", idx, src_stack)
ee0765 252                 end
563a4c 253                 return
ee0765 254             end
S 255         end
563a4c 256         meta:set_int("HV_EU_supply", 0)
N 257         meta:set_int("burn_time", 0)
85a984 258         meta:set_string("infotext", S("%s Idle"):format(reactor_desc))
563a4c 259         technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
67b90f 260         meta:set_int("structure_accumulated_badness", 0)
d59055 261         siren_clear(pos, meta)
563a4c 262     elseif burn_time > 0 then
N 263         burn_time = burn_time + 1
264         meta:set_int("burn_time", burn_time)
265         local percent = math.floor(burn_time / burn_ticks * 100)
85a984 266         meta:set_string("infotext", reactor_desc.." ("..percent.."%)")
563a4c 267         meta:set_int("HV_EU_supply", power_supply)
ee0765 268     end
563a4c 269 end
N 270
271 minetest.register_node("technic:hv_nuclear_reactor_core", {
85a984 272     description = reactor_desc,
1d20af 273     tiles = {
VE 274         "technic_hv_nuclear_reactor_core.png",
275         "technic_hv_nuclear_reactor_core.png"..cable_entry
276     },
277     drawtype = "mesh",
278     mesh = "technic_reactor.obj",
83c649 279     groups = {cracky=1, technic_machine=1, technic_hv=1},
563a4c 280     legacy_facedir_simple = true,
N 281     sounds = default.node_sound_wood_defaults(),
282     paramtype = "light",
1d20af 283     paramtype2 = "facedir",
563a4c 284     stack_max = 1,
N 285     on_construct = function(pos)
286         local meta = minetest.get_meta(pos)
85a984 287         meta:set_string("infotext", reactor_desc)
S 288         meta:set_string("formspec", reactor_formspec)
563a4c 289         local inv = meta:get_inventory()
N 290         inv:set_size("src", 6)
85a984 291     end,
563a4c 292     can_dig = technic.machine_can_dig,
85a984 293     on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
563a4c 294     allow_metadata_inventory_put = technic.machine_inventory_put,
N 295     allow_metadata_inventory_take = technic.machine_inventory_take,
296     allow_metadata_inventory_move = technic.machine_inventory_move,
297     technic_run = run,
298 })
299
300 minetest.register_node("technic:hv_nuclear_reactor_core_active", {
1d20af 301     tiles = {
VE 302         "technic_hv_nuclear_reactor_core.png",
303         "technic_hv_nuclear_reactor_core.png"..cable_entry
304     },
305     drawtype = "mesh",
306     mesh = "technic_reactor.obj",
83c649 307     groups = {cracky=1, technic_machine=1, technic_hv=1,
06dec2 308         radioactive=4, not_in_creative_inventory=1},
563a4c 309     legacy_facedir_simple = true,
N 310     sounds = default.node_sound_wood_defaults(),
85a984 311     drop = "technic:hv_nuclear_reactor_core",
S 312     light_source = 14,
563a4c 313     paramtype = "light",
1d20af 314     paramtype2 = "facedir",
563a4c 315     can_dig = technic.machine_can_dig,
85a984 316     after_dig_node = melt_down_reactor,
S 317     on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
563a4c 318     allow_metadata_inventory_put = technic.machine_inventory_put,
N 319     allow_metadata_inventory_take = technic.machine_inventory_take,
320     allow_metadata_inventory_move = technic.machine_inventory_move,
321     technic_run = run,
1c617f 322     technic_on_disable = function(pos, node)
N 323         local timer = minetest.get_node_timer(pos)
324             timer:start(1)
325         end,
326     on_timer = function(pos, node)
327         local meta = minetest.get_meta(pos)
85a984 328
1c617f 329         -- Connected back?
3252da 330         if meta:get_int("HV_EU_timeout") > 0 then return false end
85a984 331
1c617f 332         local burn_time = meta:get_int("burn_time") or 0
N 333
334         if burn_time >= burn_ticks or burn_time == 0 then
335             meta:set_int("HV_EU_supply", 0)
336             meta:set_int("burn_time", 0)
337             technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
67b90f 338             meta:set_int("structure_accumulated_badness", 0)
d59055 339             siren_clear(pos, meta)
3252da 340             return false
1c617f 341         end
85a984 342
1c617f 343         meta:set_int("burn_time", burn_time + 1)
3252da 344         return true
1c617f 345     end,
ee0765 346 })
S 347
348 technic.register_machine("HV", "technic:hv_nuclear_reactor_core",        technic.producer)
349 technic.register_machine("HV", "technic:hv_nuclear_reactor_core_active", technic.producer)
350