ShadowNinja
2016-03-21 26de2f7c88c1e5d4d4505e1aa01a96fbe537b3c0
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
85a984 368 --[[
S 369 Radioactivity
366fc3 370
85a984 371 Radiation resistance represents the extent to which a material
S 372 attenuates radiation passing through it; i.e., how good a radiation
373 shield it is.  This is identified per node type.  For materials that
374 exist in real life, the radiation resistance value that this system
375 uses for a node type consisting of a solid cube of that material is the
376 (approximate) number of halvings of ionising radiation that is achieved
377 by a meter of the material in real life.  This is approximately
378 proportional to density, which provides a good way to estimate it.
379 Homogeneous mixtures of materials have radiation resistance computed
380 by a simple weighted mean.  Note that the amount of attenuation that
381 a material achieves in-game is not required to be (and is not) the
382 same as the attenuation achieved in real life.
383
384 Radiation resistance for a node type may be specified in the node
385 definition, under the key "radiation_resistance".  As an interim
386 measure, until node definitions widely include this, this code
387 knows a bunch of values for particular node types in several mods,
388 and values for groups of node types.  The node definition takes
389 precedence if it specifies a value.  Nodes for which no value at
390 all is known are taken to provide no radiation resistance at all;
391 this is appropriate for the majority of node types.  Only node types
392 consisting of a fairly homogeneous mass of material should report
393 non-zero radiation resistance; anything with non-uniform geometry
394 or complex internal structure should show no radiation resistance.
395 Fractional resistance values are permitted.
396 --]]
397
26de2f 398 local rad_resistance_node = {
ec008d 399     ["default:brick"] = 13,
Z 400     ["default:bronzeblock"] = 45,
401     ["default:clay"] = 15,
402     ["default:coalblock"] = 9.6,
403     ["default:cobble"] = 15,
404     ["default:copperblock"] = 46,
405     ["default:desert_cobble"] = 15,
406     ["default:desert_sand"] = 10,
407     ["default:desert_stone"] = 17,
408     ["default:desert_stonebrick"] = 17,
409     ["default:diamondblock"] = 24,
410     ["default:dirt"] = 8.2,
411     ["default:dirt_with_grass"] = 8.2,
412     ["default:dirt_with_grass_footsteps"] = 8.2,
413     ["default:dirt_with_snow"] = 8.2,
414     ["default:glass"] = 17,
415     ["default:goldblock"] = 170,
416     ["default:gravel"] = 10,
417     ["default:ice"] = 5.6,
418     ["default:lava_flowing"] = 8.5,
419     ["default:lava_source"] = 17,
420     ["default:mese"] = 21,
421     ["default:mossycobble"] = 15,
422     ["default:nyancat"] = 1000,
423     ["default:nyancat_rainbow"] = 1000,
424     ["default:obsidian"] = 18,
425     ["default:obsidian_glass"] = 18,
426     ["default:sand"] = 10,
427     ["default:sandstone"] = 15,
428     ["default:sandstonebrick"] = 15,
429     ["default:snowblock"] = 1.7,
430     ["default:steelblock"] = 40,
431     ["default:stone"] = 17,
432     ["default:stone_with_coal"] = 16,
433     ["default:stone_with_copper"] = 20,
434     ["default:stone_with_diamond"] = 18,
435     ["default:stone_with_gold"] = 34,
436     ["default:stone_with_iron"] = 20,
437     ["default:stone_with_mese"] = 17,
438     ["default:stonebrick"] = 17,
439     ["default:water_flowing"] = 2.8,
440     ["default:water_source"] = 5.6,
441     ["farming:desert_sand_soil"] = 10,
442     ["farming:desert_sand_soil_wet"] = 10,
443     ["farming:soil"] = 8.2,
444     ["farming:soil_wet"] = 8.2,
445     ["glooptest:akalin_crystal_glass"] = 21,
446     ["glooptest:akalinblock"] = 40,
447     ["glooptest:alatro_crystal_glass"] = 21,
448     ["glooptest:alatroblock"] = 40,
449     ["glooptest:amethystblock"] = 18,
450     ["glooptest:arol_crystal_glass"] = 21,
451     ["glooptest:crystal_glass"] = 21,
452     ["glooptest:emeraldblock"] = 19,
453     ["glooptest:heavy_crystal_glass"] = 21,
454     ["glooptest:mineral_akalin"] = 20,
455     ["glooptest:mineral_alatro"] = 20,
456     ["glooptest:mineral_amethyst"] = 17,
457     ["glooptest:mineral_arol"] = 20,
458     ["glooptest:mineral_desert_coal"] = 16,
459     ["glooptest:mineral_desert_iron"] = 20,
460     ["glooptest:mineral_emerald"] = 17,
461     ["glooptest:mineral_kalite"] = 20,
462     ["glooptest:mineral_ruby"] = 18,
463     ["glooptest:mineral_sapphire"] = 18,
464     ["glooptest:mineral_talinite"] = 20,
465     ["glooptest:mineral_topaz"] = 18,
466     ["glooptest:reinforced_crystal_glass"] = 21,
467     ["glooptest:rubyblock"] = 27,
468     ["glooptest:sapphireblock"] = 27,
469     ["glooptest:talinite_crystal_glass"] = 21,
470     ["glooptest:taliniteblock"] = 40,
471     ["glooptest:topazblock"] = 24,
472     ["mesecons_extrawires:mese_powered"] = 21,
473     ["moreblocks:cactus_brick"] = 13,
474     ["moreblocks:cactus_checker"] = 8.5,
475     ["moreblocks:circle_stone_bricks"] = 17,
476     ["moreblocks:clean_glass"] = 17,
477     ["moreblocks:coal_checker"] = 9.0,
478     ["moreblocks:coal_glass"] = 17,
479     ["moreblocks:coal_stone"] = 17,
480     ["moreblocks:coal_stone_bricks"] = 17,
481     ["moreblocks:glow_glass"] = 17,
482     ["moreblocks:grey_bricks"] = 15,
483     ["moreblocks:iron_checker"] = 11,
484     ["moreblocks:iron_glass"] = 17,
485     ["moreblocks:iron_stone"] = 17,
486     ["moreblocks:iron_stone_bricks"] = 17,
487     ["moreblocks:plankstone"] = 9.3,
488     ["moreblocks:split_stone_tile"] = 15,
489     ["moreblocks:split_stone_tile_alt"] = 15,
490     ["moreblocks:stone_tile"] = 15,
491     ["moreblocks:super_glow_glass"] = 17,
492     ["moreblocks:tar"] = 7.0,
493     ["moreblocks:wood_tile"] = 1.7,
494     ["moreblocks:wood_tile_center"] = 1.7,
495     ["moreblocks:wood_tile_down"] = 1.7,
496     ["moreblocks:wood_tile_flipped"] = 1.7,
497     ["moreblocks:wood_tile_full"] = 1.7,
498     ["moreblocks:wood_tile_left"] = 1.7,
499     ["moreblocks:wood_tile_right"] = 1.7,
500     ["moreblocks:wood_tile_up"] = 1.7,
501     ["moreores:mineral_mithril"] = 18,
502     ["moreores:mineral_silver"] = 21,
503     ["moreores:mineral_tin"] = 19,
504     ["moreores:mithril_block"] = 26,
505     ["moreores:silver_block"] = 53,
506     ["moreores:tin_block"] = 37,
507     ["snow:snow_brick"] = 2.8,
508     ["technic:brass_block"] = 43,
509     ["technic:carbon_steel_block"] = 40,
510     ["technic:cast_iron_block"] = 40,
511     ["technic:chernobylite_block"] = 40,
512     ["technic:chromium_block"] = 37,
513     ["technic:corium_flowing"] = 40,
514     ["technic:corium_source"] = 80,
515     ["technic:granite"] = 18,
e7d06b 516     ["technic:lead_block"] = 80,
ec008d 517     ["technic:marble"] = 18,
Z 518     ["technic:marble_bricks"] = 18,
519     ["technic:mineral_chromium"] = 19,
520     ["technic:mineral_uranium"] = 71,
521     ["technic:mineral_zinc"] = 19,
522     ["technic:stainless_steel_block"] = 40,
523     ["technic:zinc_block"] = 36,
524     ["tnt:tnt"] = 11,
525     ["tnt:tnt_burning"] = 11,
526 }
26de2f 527 local rad_resistance_group = {
ec008d 528     concrete = 16,
Z 529     tree = 3.4,
b0faa7 530     uranium_block = 500,
ec008d 531     wood = 1.7,
Z 532 }
533 local cache_radiation_resistance = {}
85a984 534 local function node_radiation_resistance(node_name)
26de2f 535     local resistance = cache_radiation_resistance[node_name]
S 536     if resistance then
537         return resistance
538     end
85a984 539     local def = minetest.registered_nodes[node_name]
26de2f 540     if not def then
S 541         cache_radiation_resistance[node_name] = 0
542         return 0
543     end
544     resistance = def.radiation_resistance or
545             rad_resistance_node[node_name]
546     if not resistance then
547         resistance = 0
ec008d 548         for g, v in pairs(def.groups) do
26de2f 549             if v > 0 and rad_resistance_group[g] then
S 550                 resistance = resistance + rad_resistance_group[g]
ec008d 551             end
Z 552         end
553     end
26de2f 554     resistance = math.sqrt(resistance)
S 555     cache_radiation_resistance[node_name] = resistance
556     return resistance
ec008d 557 end
26de2f 558
ec008d 559
85a984 560 --[[
S 561 Radioactive nodes cause damage to nearby players.  The damage
562 effect depends on the intrinsic strength of the radiation source,
563 the distance between the source and the player, and the shielding
564 effect of the intervening material.  These determine a rate of damage;
565 total damage caused is the integral of this over time.
566
567 In the absence of effective shielding, for a specific source the
568 damage rate varies realistically in inverse proportion to the square
569 of the distance.  (Distance is measured to the player's abdomen,
570 not to the nominal player position which corresponds to the foot.)
571 However, if the player is inside a non-walkable (liquid or gaseous)
572 radioactive node, the nominal distance could go to zero, yielding
573 infinite damage.  In that case, the player's body is displacing the
574 radioactive material, so the effective distance should remain non-zero.
575 We therefore apply a lower distance bound of sqrt(0.75), which is
576 the maximum distance one can get from the node center within the node.
577
578 A radioactive node is identified by being in the "radioactive" group,
579 and the group value signifies the strength of the radiation source.
26de2f 580 The group value is the distance from a node at which an unshielded
S 581 player will be damaged by 1 HP/s.  Or, equivalently, it is the square
582 root of the damage rate in HP/s that an unshielded player one node
583 away will take.
85a984 584
S 585 Shielding is assessed by adding the shielding values of all nodes
586 between the source node and the player, ignoring the source node itself.
587 As in reality, shielding causes exponential attenuation of radiation.
588 However, the effect is scaled down relative to real life.  A node with
589 radiation resistance value R yields attenuation of sqrt(R) * 0.1 nepers.
590 (In real life it would be about R * 0.69 nepers, by the definition
591 of the radiation resistance values.)  The sqrt part of this formula
592 scales down the differences between shielding types, reflecting the
593 game's simplification of making expensive materials such as gold
594 readily available in cubes.  The multiplicative factor in the
595 formula scales down the difference between shielded and unshielded
596 safe distances, avoiding the latter becoming impractically large.
597
598 Damage is processed at rates down to 0.25 HP/s, which in the absence of
599 shielding is attained at the distance specified by the "radioactive"
600 group value.  Computed damage rates below 0.25 HP/s result in no
601 damage at all to the player.  This gives the player an opportunity
602 to be safe, and limits the range at which source/player interactions
603 need to be considered.
604 --]]
26de2f 605 local abdomen_offset = 1
2912e2 606 local cache_scaled_shielding = {}
26de2f 607 local rad_dmg_cutoff = 0.25
b00e94 608
097d03 609 local function dmg_player(pos, o, strength)
26de2f 610     local pl_pos = o:getpos()
S 611     pl_pos.y = pl_pos.y + abdomen_offset
85a984 612     local shielding = 0
097d03 613     local dist = vector.distance(pos, pl_pos)
85a984 614     for ray_pos in technic.trace_node_ray(pos,
097d03 615             vector.direction(pos, pl_pos), dist) do
26de2f 616         local shield_name = minetest.get_node(ray_pos).name
S 617         shielding = shielding + node_radiation_resistance(shield_name) * 0.1
85a984 618     end
26de2f 619     local dmg = (strength * strength) /
097d03 620         (math.max(0.75, dist * dist) * math.exp(shielding))
26de2f 621     if dmg < rad_dmg_cutoff then return end
S 622     local dmg_int = math.floor(dmg)
623     -- The closer you are to getting one more damage point,
624     -- the more likely it will be added.
625     if math.random() < dmg - dmg_int then
626         dmg_int = dmg_int + 1
627     end
628     if dmg_int > 0 then
629         o:set_hp(math.max(o:get_hp() - dmg_int, 0))
85a984 630     end
S 631 end
b00e94 632
26de2f 633 local rad_dmg_mult_sqrt = math.sqrt(1 / rad_dmg_cutoff)
85a984 634 local function dmg_abm(pos, node)
S 635     local strength = minetest.get_item_group(node.name, "radioactive")
26de2f 636     local max_dist = strength * rad_dmg_mult_sqrt
85a984 637     for _, o in pairs(minetest.get_objects_inside_radius(pos,
26de2f 638             max_dist + abdomen_offset)) do
85a984 639         if o:is_player() then
097d03 640             dmg_player(pos, o, strength)
85a984 641         end
S 642     end
643 end
644
645
646 if minetest.setting_getbool("enable_damage") then
b00e94 647     minetest.register_abm({
VE 648         nodenames = {"group:radioactive"},
649         interval = 1,
650         chance = 1,
85a984 651         action = dmg_abm,
b00e94 652     })
VE 653 end
ec008d 654
85a984 655 -- Radioactive materials that can result from destroying a reactor
S 656 local griefing = technic.config:get_bool("enable_corium_griefing")
366fc3 657
85a984 658 for _, state in pairs({"flowing", "source"}) do
366fc3 659     minetest.register_node("technic:corium_"..state, {
Z 660         description = S(state == "source" and "Corium Source" or "Flowing Corium"),
661         drawtype = (state == "source" and "liquid" or "flowingliquid"),
662         [state == "source" and "tiles" or "special_tiles"] = {{
663             name = "technic_corium_"..state.."_animated.png",
664             animation = {
665                 type = "vertical_frames",
666                 aspect_w = 16,
667                 aspect_h = 16,
668                 length = 3.0,
669             },
670         }},
671         paramtype = "light",
672         paramtype2 = (state == "flowing" and "flowingliquid" or nil),
e11f0f 673         light_source = (state == "source" and 8 or 5),
366fc3 674         walkable = false,
Z 675         pointable = false,
676         diggable = false,
677         buildable_to = true,
678         drop = "",
679         drowning = 1,
680         liquidtype = state,
681         liquid_alternative_flowing = "technic:corium_flowing",
682         liquid_alternative_source = "technic:corium_source",
683         liquid_viscosity = LAVA_VISC,
684         liquid_renewable = false,
685         damage_per_second = 6,
85a984 686         post_effect_color = {a=192, r=80, g=160, b=80},
366fc3 687         groups = {
Z 688             liquid = 2,
689             hot = 3,
85a984 690             igniter = (griefing and 1 or 0),
26de2f 691             radioactive = (state == "source" and 16 or 8),
366fc3 692             not_in_creative_inventory = (state == "flowing" and 1 or nil),
Z 693         },
694     })
695 end
696
85a984 697 if rawget(_G, "bucket") and bucket.register_liquid then
366fc3 698     bucket.register_liquid(
Z 699         "technic:corium_source",
700         "technic:corium_flowing",
701         "technic:bucket_corium",
702         "technic_bucket_corium.png",
703         "Corium Bucket"
704     )
705 end
706
707 minetest.register_node("technic:chernobylite_block", {
708         description = S("Chernobylite Block"),
85a984 709     tiles = {"technic_chernobylite_block.png"},
366fc3 710     is_ground_content = true,
26de2f 711     groups = {cracky=1, radioactive=6, level=2},
366fc3 712     sounds = default.node_sound_stone_defaults(),
Z 713     light_source = 2,
714 })
715
716 minetest.register_abm({
717     nodenames = {"group:water"},
718     neighbors = {"technic:corium_source"},
719     interval = 1,
720     chance = 1,
85a984 721     action = function(pos, node)
366fc3 722         minetest.remove_node(pos)
Z 723     end,
724 })
725
85a984 726 minetest.register_abm({
S 727     nodenames = {"technic:corium_flowing"},
728     neighbors = {"group:water"},
729     interval = 1,
730     chance = 1,
731     action = function(pos, node)
732         minetest.set_node(pos, {name="technic:chernobylite_block"})
733     end,
734 })
735
736 minetest.register_abm({
737     nodenames = {"technic:corium_flowing"},
738     interval = 5,
739     chance = (griefing and 10 or 1),
740     action = function(pos, node)
741         minetest.set_node(pos, {name="technic:chernobylite_block"})
742     end,
743 })
744
745 if griefing then
8ef83e 746     minetest.register_abm({
85a984 747         nodenames = {"technic:corium_source", "technic:corium_flowing"},
c5e948 748         interval = 4,
Z 749         chance = 4,
85a984 750         action = function(pos, node)
c5e948 751             for _, offset in ipairs({
Z 752                 vector.new(1,0,0),
753                 vector.new(-1,0,0),
754                 vector.new(0,0,1),
755                 vector.new(0,0,-1),
756                 vector.new(0,-1,0),
757             }) do
758                 if math.random(8) == 1 then
759                     minetest.dig_node(vector.add(pos, offset))
760                 end
366fc3 761             end
c5e948 762         end,
Z 763     })
764 end
85a984 765