ShadowNinja
2016-03-21 8ccb6d97ec88176b8006da685e54806e6e0c0b25
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,
S 321         radioactive=11000, 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
ec008d 398 local default_radiation_resistance_per_node = {
Z 399     ["default:brick"] = 13,
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 }
527 local default_radiation_resistance_per_group = {
528     concrete = 16,
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)
S 535     local eff = cache_radiation_resistance[node_name]
ec008d 536     if eff then return eff end
85a984 537     local def = minetest.registered_nodes[node_name]
S 538     eff = def and def.radiation_resistance or
539             default_radiation_resistance_per_node[node_name]
540     if def and not eff then
ec008d 541         for g, v in pairs(def.groups) do
Z 542             if v > 0 and default_radiation_resistance_per_group[g] then
543                 eff = default_radiation_resistance_per_group[g]
544                 break
545             end
546         end
547     end
548     if not eff then eff = 0 end
85a984 549     cache_radiation_resistance[node_name] = eff
ec008d 550     return eff
Z 551 end
552
85a984 553 --[[
S 554 Radioactive nodes cause damage to nearby players.  The damage
555 effect depends on the intrinsic strength of the radiation source,
556 the distance between the source and the player, and the shielding
557 effect of the intervening material.  These determine a rate of damage;
558 total damage caused is the integral of this over time.
559
560 In the absence of effective shielding, for a specific source the
561 damage rate varies realistically in inverse proportion to the square
562 of the distance.  (Distance is measured to the player's abdomen,
563 not to the nominal player position which corresponds to the foot.)
564 However, if the player is inside a non-walkable (liquid or gaseous)
565 radioactive node, the nominal distance could go to zero, yielding
566 infinite damage.  In that case, the player's body is displacing the
567 radioactive material, so the effective distance should remain non-zero.
568 We therefore apply a lower distance bound of sqrt(0.75), which is
569 the maximum distance one can get from the node center within the node.
570
571 A radioactive node is identified by being in the "radioactive" group,
572 and the group value signifies the strength of the radiation source.
573 The group value is 1000 times the distance from a node at which
574 an unshielded player will be damaged by 0.25 HP/s.  Or, equivalently,
575 it is 2000 times the square root of the damage rate in HP/s that an
576 unshielded player 1 node away will take.
577
578 Shielding is assessed by adding the shielding values of all nodes
579 between the source node and the player, ignoring the source node itself.
580 As in reality, shielding causes exponential attenuation of radiation.
581 However, the effect is scaled down relative to real life.  A node with
582 radiation resistance value R yields attenuation of sqrt(R) * 0.1 nepers.
583 (In real life it would be about R * 0.69 nepers, by the definition
584 of the radiation resistance values.)  The sqrt part of this formula
585 scales down the differences between shielding types, reflecting the
586 game's simplification of making expensive materials such as gold
587 readily available in cubes.  The multiplicative factor in the
588 formula scales down the difference between shielded and unshielded
589 safe distances, avoiding the latter becoming impractically large.
590
591 Damage is processed at rates down to 0.25 HP/s, which in the absence of
592 shielding is attained at the distance specified by the "radioactive"
593 group value.  Computed damage rates below 0.25 HP/s result in no
594 damage at all to the player.  This gives the player an opportunity
595 to be safe, and limits the range at which source/player interactions
596 need to be considered.
597 --]]
598 local abdomen_offset = vector.new(0, 1, 0)
599 local abdomen_offset_length = vector.length(abdomen_offset)
2912e2 600 local cache_scaled_shielding = {}
b00e94 601
097d03 602 local function dmg_player(pos, o, strength)
85a984 603     local pl_pos = vector.add(o:getpos(), abdomen_offset)
S 604     local shielding = 0
097d03 605     local dist = vector.distance(pos, pl_pos)
85a984 606     for ray_pos in technic.trace_node_ray(pos,
097d03 607             vector.direction(pos, pl_pos), dist) do
85a984 608         if not vector.equals(ray_pos, pos) then
S 609             local shield_name = minetest.get_node(ray_pos).name
610             local shield_val = cache_scaled_shielding[sname]
611             if not shield_val then
097d03 612                 shield_val = math.sqrt(node_radiation_resistance(shield_name)) * 0.025
85a984 613                 cache_scaled_shielding[shield_name] = shield_val
S 614             end
097d03 615             shielding = shielding + shield_val
85a984 616         end
S 617     end
097d03 618     local dmg = (0.25e-6 * strength * strength) /
S 619         (math.max(0.75, dist * dist) * math.exp(shielding))
85a984 620     if dmg >= 0.25 then
S 621         local dmg_int = math.floor(dmg)
622         -- The closer you are to getting one more damage point,
623         -- the more likely it will be added.
624         if math.random() < dmg - dmg_int then
625             dmg_int = dmg_int + 1
626         end
627         if dmg_int > 0 then
628             o:set_hp(math.max(o:get_hp() - dmg_int, 0))
629         end
630     end
631 end
b00e94 632
85a984 633 local function dmg_abm(pos, node)
S 634     local strength = minetest.get_item_group(node.name, "radioactive")
635     for _, o in pairs(minetest.get_objects_inside_radius(pos,
636             strength * 0.001 + abdomen_offset_length)) do
637         if o:is_player() then
097d03 638             dmg_player(pos, o, strength)
85a984 639         end
S 640     end
641 end
642
643
644 if minetest.setting_getbool("enable_damage") then
b00e94 645     minetest.register_abm({
VE 646         nodenames = {"group:radioactive"},
647         interval = 1,
648         chance = 1,
85a984 649         action = dmg_abm,
b00e94 650     })
VE 651 end
ec008d 652
85a984 653 -- Radioactive materials that can result from destroying a reactor
S 654 local griefing = technic.config:get_bool("enable_corium_griefing")
366fc3 655
85a984 656 for _, state in pairs({"flowing", "source"}) do
366fc3 657     minetest.register_node("technic:corium_"..state, {
Z 658         description = S(state == "source" and "Corium Source" or "Flowing Corium"),
659         drawtype = (state == "source" and "liquid" or "flowingliquid"),
660         [state == "source" and "tiles" or "special_tiles"] = {{
661             name = "technic_corium_"..state.."_animated.png",
662             animation = {
663                 type = "vertical_frames",
664                 aspect_w = 16,
665                 aspect_h = 16,
666                 length = 3.0,
667             },
668         }},
669         paramtype = "light",
670         paramtype2 = (state == "flowing" and "flowingliquid" or nil),
e11f0f 671         light_source = (state == "source" and 8 or 5),
366fc3 672         walkable = false,
Z 673         pointable = false,
674         diggable = false,
675         buildable_to = true,
676         drop = "",
677         drowning = 1,
678         liquidtype = state,
679         liquid_alternative_flowing = "technic:corium_flowing",
680         liquid_alternative_source = "technic:corium_source",
681         liquid_viscosity = LAVA_VISC,
682         liquid_renewable = false,
683         damage_per_second = 6,
85a984 684         post_effect_color = {a=192, r=80, g=160, b=80},
366fc3 685         groups = {
Z 686             liquid = 2,
687             hot = 3,
85a984 688             igniter = (griefing and 1 or 0),
7a9d2f 689             radioactive = (state == "source" and 32000 or 16000),
366fc3 690             not_in_creative_inventory = (state == "flowing" and 1 or nil),
Z 691         },
692     })
693 end
694
85a984 695 if rawget(_G, "bucket") and bucket.register_liquid then
366fc3 696     bucket.register_liquid(
Z 697         "technic:corium_source",
698         "technic:corium_flowing",
699         "technic:bucket_corium",
700         "technic_bucket_corium.png",
701         "Corium Bucket"
702     )
703 end
704
705 minetest.register_node("technic:chernobylite_block", {
706         description = S("Chernobylite Block"),
85a984 707     tiles = {"technic_chernobylite_block.png"},
366fc3 708     is_ground_content = true,
85a984 709     groups = {cracky=1, radioactive=5000, level=2},
366fc3 710     sounds = default.node_sound_stone_defaults(),
Z 711     light_source = 2,
712 })
713
714 minetest.register_abm({
715     nodenames = {"group:water"},
716     neighbors = {"technic:corium_source"},
717     interval = 1,
718     chance = 1,
85a984 719     action = function(pos, node)
366fc3 720         minetest.remove_node(pos)
Z 721     end,
722 })
723
85a984 724 minetest.register_abm({
S 725     nodenames = {"technic:corium_flowing"},
726     neighbors = {"group:water"},
727     interval = 1,
728     chance = 1,
729     action = function(pos, node)
730         minetest.set_node(pos, {name="technic:chernobylite_block"})
731     end,
732 })
733
734 minetest.register_abm({
735     nodenames = {"technic:corium_flowing"},
736     interval = 5,
737     chance = (griefing and 10 or 1),
738     action = function(pos, node)
739         minetest.set_node(pos, {name="technic:chernobylite_block"})
740     end,
741 })
742
743 if griefing then
8ef83e 744     minetest.register_abm({
85a984 745         nodenames = {"technic:corium_source", "technic:corium_flowing"},
c5e948 746         interval = 4,
Z 747         chance = 4,
85a984 748         action = function(pos, node)
c5e948 749             for _, offset in ipairs({
Z 750                 vector.new(1,0,0),
751                 vector.new(-1,0,0),
752                 vector.new(0,0,1),
753                 vector.new(0,0,-1),
754                 vector.new(0,-1,0),
755             }) do
756                 if math.random(8) == 1 then
757                     minetest.dig_node(vector.add(pos, offset))
758                 end
366fc3 759             end
c5e948 760         end,
Z 761     })
762 end
85a984 763