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