commit | author | age
|
ee0765
|
1 |
-- The enriched uranium rod driven EU generator. |
S |
2 |
-- A very large and advanced machine providing vast amounts of power. |
30adc3
|
3 |
-- Very efficient but also expensive to run as it needs uranium. (10000EU 86400 ticks (one week)) |
ee0765
|
4 |
-- Provides HV EUs that can be down converted as needed. |
S |
5 |
-- |
|
6 |
-- The nuclear reactor core needs water and a protective shield to work. |
|
7 |
-- This is checked now and then and if the machine is tampered with... BOOM! |
|
8 |
|
|
9 |
local burn_ticks = 7 * 24 * 60 * 60 -- (seconds). |
|
10 |
local power_supply = 100000 -- EUs |
|
11 |
local fuel_type = "technic:uranium_fuel" -- The reactor burns this stuff |
|
12 |
|
be2f30
|
13 |
local S = technic.getter |
ee0765
|
14 |
|
ec008d
|
15 |
if not vector.length_square then |
Z |
16 |
vector.length_square = function (v) |
|
17 |
return v.x*v.x + v.y*v.y + v.z*v.z |
17c5b6
|
18 |
end |
Z |
19 |
end |
|
20 |
|
ee0765
|
21 |
-- FIXME: recipe must make more sense like a rod recepticle, steam chamber, HV generator? |
S |
22 |
minetest.register_craft({ |
|
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'}, |
|
27 |
{'technic:stainless_steel_ingot', 'technic:hv_cable0', 'technic:stainless_steel_ingot'}, |
ee0765
|
28 |
} |
S |
29 |
}) |
|
30 |
|
|
31 |
local generator_formspec = |
|
32 |
"invsize[8,9;]".. |
be2f30
|
33 |
"label[0,0;"..S("Nuclear Reactor Rod Compartment").."]".. |
ee0765
|
34 |
"list[current_name;src;2,1;3,2;]".. |
S |
35 |
"list[current_player;main;0,5;8,4;]" |
|
36 |
|
|
37 |
-- "Boxy sphere" |
|
38 |
local nodebox = { |
|
39 |
{ -0.353, -0.353, -0.353, 0.353, 0.353, 0.353 }, -- Box |
|
40 |
{ -0.495, -0.064, -0.064, 0.495, 0.064, 0.064 }, -- Circle +-x |
|
41 |
{ -0.483, -0.128, -0.128, 0.483, 0.128, 0.128 }, |
|
42 |
{ -0.462, -0.191, -0.191, 0.462, 0.191, 0.191 }, |
|
43 |
{ -0.433, -0.249, -0.249, 0.433, 0.249, 0.249 }, |
|
44 |
{ -0.397, -0.303, -0.303, 0.397, 0.303, 0.303 }, |
|
45 |
{ -0.305, -0.396, -0.305, 0.305, 0.396, 0.305 }, -- Circle +-y |
|
46 |
{ -0.250, -0.432, -0.250, 0.250, 0.432, 0.250 }, |
|
47 |
{ -0.191, -0.461, -0.191, 0.191, 0.461, 0.191 }, |
|
48 |
{ -0.130, -0.482, -0.130, 0.130, 0.482, 0.130 }, |
|
49 |
{ -0.066, -0.495, -0.066, 0.066, 0.495, 0.066 }, |
|
50 |
{ -0.064, -0.064, -0.495, 0.064, 0.064, 0.495 }, -- Circle +-z |
|
51 |
{ -0.128, -0.128, -0.483, 0.128, 0.128, 0.483 }, |
|
52 |
{ -0.191, -0.191, -0.462, 0.191, 0.191, 0.462 }, |
|
53 |
{ -0.249, -0.249, -0.433, 0.249, 0.249, 0.433 }, |
|
54 |
{ -0.303, -0.303, -0.397, 0.303, 0.303, 0.397 }, |
|
55 |
} |
|
56 |
|
d59055
|
57 |
local reactor_siren = {} |
Z |
58 |
local function siren_set_state(pos, newstate) |
|
59 |
local hpos = minetest.hash_node_position(pos) |
|
60 |
local siren = reactor_siren[hpos] |
|
61 |
if not siren then |
|
62 |
if newstate == "off" then return end |
|
63 |
siren = {state="off"} |
|
64 |
reactor_siren[hpos] = siren |
|
65 |
end |
|
66 |
if newstate == "danger" and siren.state ~= "danger" then |
|
67 |
if siren.handle then minetest.sound_stop(siren.handle) end |
|
68 |
siren.handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_danger_loop", {pos=pos, gain=1.5, loop=true, max_hear_distance=48}) |
|
69 |
siren.state = "danger" |
|
70 |
elseif newstate == "clear" then |
|
71 |
if siren.handle then minetest.sound_stop(siren.handle) end |
|
72 |
local clear_handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_clear", {pos=pos, gain=1.5, loop=false, max_hear_distance=48}) |
|
73 |
siren.handle = clear_handle |
|
74 |
siren.state = "clear" |
|
75 |
minetest.after(10, function () |
|
76 |
if siren.handle == clear_handle then |
|
77 |
minetest.sound_stop(clear_handle) |
|
78 |
if reactor_siren[hpos] == siren then |
|
79 |
reactor_siren[hpos] = nil |
|
80 |
end |
|
81 |
end |
|
82 |
end) |
|
83 |
elseif newstate == "off" and siren.state ~= "off" then |
|
84 |
if siren.handle then minetest.sound_stop(siren.handle) end |
|
85 |
siren.handle = nil |
|
86 |
reactor_siren[hpos] = nil |
|
87 |
end |
|
88 |
end |
|
89 |
local function siren_danger(pos, meta) |
|
90 |
meta:set_int("siren", 1) |
|
91 |
siren_set_state(pos, "danger") |
|
92 |
end |
|
93 |
local function siren_clear(pos, meta) |
|
94 |
if meta:get_int("siren") ~= 0 then |
|
95 |
siren_set_state(pos, "clear") |
|
96 |
meta:set_int("siren", 0) |
|
97 |
end |
|
98 |
end |
|
99 |
|
84cf65
|
100 |
-- The standard reactor structure consists of a 9x9x9 cube. A cross |
Z |
101 |
-- section through the middle: |
|
102 |
-- |
|
103 |
-- CCCC CCCC |
|
104 |
-- CBBB BBBC |
|
105 |
-- CBSS SSBC |
|
106 |
-- CBSWWWSBC |
|
107 |
-- CBSW#WSBC |
|
108 |
-- CBSW|WSBC |
|
109 |
-- CBSS|SSBC |
|
110 |
-- CBBB|BBBC |
|
111 |
-- CCCC|CCCC |
|
112 |
-- C = Concrete, B = Blast-resistant concrete, S = Stainless Steel, |
|
113 |
-- W = water node, # = reactor core, | = HV cable |
|
114 |
-- |
|
115 |
-- The man-hole and the HV cable are only in the middle, and the man-hole |
|
116 |
-- is optional. |
|
117 |
-- |
|
118 |
-- For the reactor to operate and not melt down, it insists on the inner |
|
119 |
-- 7x7x7 portion (from the core out to the blast-resistant concrete) |
|
120 |
-- being intact. Intactness only depends on the number of nodes of the |
|
121 |
-- right type in each layer. The water layer must have water in all but |
|
122 |
-- at most one node; the steel and blast-resistant concrete layers must |
|
123 |
-- have the right material in all but at most two nodes. The permitted |
|
124 |
-- gaps are meant for the cable and man-hole, but can actually be anywhere |
|
125 |
-- and contain anything. For the reactor to be useful, a cable must |
|
126 |
-- connect to the core, but it can go in any direction. |
|
127 |
-- |
|
128 |
-- The outer concrete layer of the standard structure is not required |
|
129 |
-- for the reactor to operate. It is noted here because it used to |
|
130 |
-- be mandatory, and for historical reasons (that it predates the |
|
131 |
-- implementation of radiation) it needs to continue being adequate |
|
132 |
-- shielding of legacy reactors. If it ever ceases to be adequate |
|
133 |
-- shielding for new reactors, legacy ones should be grandfathered. |
67b90f
|
134 |
local reactor_structure_badness = function(pos) |
ee0765
|
135 |
local vm = VoxelManip() |
84cf65
|
136 |
local pos1 = vector.subtract(pos, 3) |
Z |
137 |
local pos2 = vector.add(pos, 3) |
ee0765
|
138 |
local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2) |
S |
139 |
local data = vm:get_data() |
|
140 |
local area = VoxelArea:new({MinEdge=MinEdge, MaxEdge=MaxEdge}) |
|
141 |
|
|
142 |
local c_blast_concrete = minetest.get_content_id("technic:blast_resistant_concrete") |
|
143 |
local c_stainless_steel = minetest.get_content_id("technic:stainless_steel_block") |
|
144 |
local c_water_source = minetest.get_content_id("default:water_source") |
|
145 |
local c_water_flowing = minetest.get_content_id("default:water_flowing") |
|
146 |
|
84cf65
|
147 |
local blastlayer, steellayer, waterlayer = 0, 0, 0 |
ee0765
|
148 |
|
S |
149 |
for z = pos1.z, pos2.z do |
|
150 |
for y = pos1.y, pos2.y do |
|
151 |
for x = pos1.x, pos2.x do |
84cf65
|
152 |
local cid = data[area:index(x, y, z)] |
ee0765
|
153 |
if x == pos1.x or x == pos2.x or |
S |
154 |
y == pos1.y or y == pos2.y or |
|
155 |
z == pos1.z or z == pos2.z then |
84cf65
|
156 |
if cid == c_blast_concrete then |
Z |
157 |
blastlayer = blastlayer + 1 |
ee0765
|
158 |
end |
S |
159 |
elseif x == pos1.x+1 or x == pos2.x-1 or |
|
160 |
y == pos1.y+1 or y == pos2.y-1 or |
|
161 |
z == pos1.z+1 or z == pos2.z-1 then |
84cf65
|
162 |
if cid == c_stainless_steel then |
Z |
163 |
steellayer = steellayer + 1 |
ee0765
|
164 |
end |
S |
165 |
elseif x == pos1.x+2 or x == pos2.x-2 or |
|
166 |
y == pos1.y+2 or y == pos2.y-2 or |
|
167 |
z == pos1.z+2 or z == pos2.z-2 then |
|
168 |
if cid == c_water_source or cid == c_water_flowing then |
|
169 |
waterlayer = waterlayer + 1 |
|
170 |
end |
|
171 |
end |
|
172 |
end |
|
173 |
end |
|
174 |
end |
67b90f
|
175 |
if waterlayer > 25 then waterlayer = 25 end |
Z |
176 |
if steellayer > 96 then steellayer = 96 end |
|
177 |
if blastlayer > 216 then blastlayer = 216 end |
84cf65
|
178 |
return (25 - waterlayer) + (96 - steellayer) + (216 - blastlayer) |
ee0765
|
179 |
end |
S |
180 |
|
67b90f
|
181 |
local function meltdown_reactor(pos) |
Z |
182 |
print("A reactor melted down at "..minetest.pos_to_string(pos)) |
366fc3
|
183 |
minetest.set_node(pos, {name="technic:corium_source"}) |
ee0765
|
184 |
end |
67b90f
|
185 |
|
Z |
186 |
minetest.register_abm({ |
|
187 |
nodenames = {"technic:hv_nuclear_reactor_core_active"}, |
|
188 |
interval = 1, |
|
189 |
chance = 1, |
|
190 |
action = function (pos, node) |
|
191 |
local meta = minetest.get_meta(pos) |
|
192 |
local badness = reactor_structure_badness(pos) |
|
193 |
local accum_badness = meta:get_int("structure_accumulated_badness") |
|
194 |
if badness == 0 then |
|
195 |
if accum_badness ~= 0 then |
|
196 |
meta:set_int("structure_accumulated_badness", accum_badness - 1) |
d59055
|
197 |
siren_clear(pos, meta) |
67b90f
|
198 |
end |
Z |
199 |
else |
d59055
|
200 |
siren_danger(pos, meta) |
67b90f
|
201 |
accum_badness = accum_badness + badness |
Z |
202 |
if accum_badness >= 100 then |
|
203 |
meltdown_reactor(pos) |
|
204 |
else |
|
205 |
meta:set_int("structure_accumulated_badness", accum_badness) |
|
206 |
end |
|
207 |
end |
|
208 |
end, |
|
209 |
}) |
ee0765
|
210 |
|
563a4c
|
211 |
local run = function(pos, node) |
N |
212 |
local meta = minetest.get_meta(pos) |
|
213 |
local machine_name = S("Nuclear %s Generator Core"):format("HV") |
|
214 |
local burn_time = meta:get_int("burn_time") or 0 |
ee0765
|
215 |
|
563a4c
|
216 |
if burn_time >= burn_ticks or burn_time == 0 then |
N |
217 |
local inv = meta:get_inventory() |
|
218 |
if not inv:is_empty("src") then |
|
219 |
local srclist = inv:get_list("src") |
|
220 |
local correct_fuel_count = 0 |
|
221 |
for _, srcstack in pairs(srclist) do |
|
222 |
if srcstack then |
|
223 |
if srcstack:get_name() == fuel_type then |
|
224 |
correct_fuel_count = correct_fuel_count + 1 |
ee0765
|
225 |
end |
S |
226 |
end |
563a4c
|
227 |
end |
N |
228 |
-- Check that the reactor is complete as well |
|
229 |
-- as the correct number of correct fuel |
|
230 |
if correct_fuel_count == 6 and |
67b90f
|
231 |
reactor_structure_badness(pos) == 0 then |
563a4c
|
232 |
meta:set_int("burn_time", 1) |
N |
233 |
technic.swap_node(pos, "technic:hv_nuclear_reactor_core_active") |
|
234 |
meta:set_int("HV_EU_supply", power_supply) |
|
235 |
for idx, srcstack in pairs(srclist) do |
|
236 |
srcstack:take_item() |
|
237 |
inv:set_stack("src", idx, srcstack) |
ee0765
|
238 |
end |
563a4c
|
239 |
return |
ee0765
|
240 |
end |
S |
241 |
end |
563a4c
|
242 |
meta:set_int("HV_EU_supply", 0) |
N |
243 |
meta:set_int("burn_time", 0) |
|
244 |
meta:set_string("infotext", S("%s Idle"):format(machine_name)) |
|
245 |
technic.swap_node(pos, "technic:hv_nuclear_reactor_core") |
67b90f
|
246 |
meta:set_int("structure_accumulated_badness", 0) |
d59055
|
247 |
siren_clear(pos, meta) |
563a4c
|
248 |
elseif burn_time > 0 then |
N |
249 |
burn_time = burn_time + 1 |
|
250 |
meta:set_int("burn_time", burn_time) |
|
251 |
local percent = math.floor(burn_time / burn_ticks * 100) |
|
252 |
meta:set_string("infotext", machine_name.." ("..percent.."%)") |
|
253 |
meta:set_int("HV_EU_supply", power_supply) |
ee0765
|
254 |
end |
563a4c
|
255 |
end |
N |
256 |
|
|
257 |
minetest.register_node("technic:hv_nuclear_reactor_core", { |
|
258 |
description = S("Nuclear %s Generator Core"):format("HV"), |
|
259 |
tiles = {"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png", |
|
260 |
"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png", |
|
261 |
"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png"}, |
4996d1
|
262 |
groups = {cracky=1, technic_machine=1}, |
563a4c
|
263 |
legacy_facedir_simple = true, |
N |
264 |
sounds = default.node_sound_wood_defaults(), |
|
265 |
drawtype="nodebox", |
|
266 |
paramtype = "light", |
|
267 |
stack_max = 1, |
|
268 |
node_box = { |
|
269 |
type = "fixed", |
|
270 |
fixed = nodebox |
|
271 |
}, |
|
272 |
on_construct = function(pos) |
|
273 |
local meta = minetest.get_meta(pos) |
|
274 |
meta:set_string("infotext", S("Nuclear %s Generator Core"):format("HV")) |
|
275 |
meta:set_int("HV_EU_supply", 0) |
|
276 |
-- Signal to the switching station that this device burns some |
|
277 |
-- sort of fuel and needs special handling |
|
278 |
meta:set_int("HV_EU_from_fuel", 1) |
|
279 |
meta:set_int("burn_time", 0) |
|
280 |
meta:set_string("formspec", generator_formspec) |
|
281 |
local inv = meta:get_inventory() |
|
282 |
inv:set_size("src", 6) |
|
283 |
end, |
|
284 |
can_dig = technic.machine_can_dig, |
d59055
|
285 |
on_destruct = function(pos) siren_set_state(pos, "off") end, |
563a4c
|
286 |
allow_metadata_inventory_put = technic.machine_inventory_put, |
N |
287 |
allow_metadata_inventory_take = technic.machine_inventory_take, |
|
288 |
allow_metadata_inventory_move = technic.machine_inventory_move, |
|
289 |
technic_run = run, |
|
290 |
}) |
|
291 |
|
|
292 |
minetest.register_node("technic:hv_nuclear_reactor_core_active", { |
|
293 |
tiles = {"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png", |
|
294 |
"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png", |
|
295 |
"technic_hv_nuclear_reactor_core.png", "technic_hv_nuclear_reactor_core.png"}, |
2912e2
|
296 |
groups = {cracky=1, technic_machine=1, radioactive=11000, not_in_creative_inventory=1}, |
563a4c
|
297 |
legacy_facedir_simple = true, |
N |
298 |
sounds = default.node_sound_wood_defaults(), |
|
299 |
drop="technic:hv_nuclear_reactor_core", |
|
300 |
drawtype="nodebox", |
|
301 |
light_source = 15, |
|
302 |
paramtype = "light", |
|
303 |
node_box = { |
|
304 |
type = "fixed", |
|
305 |
fixed = nodebox |
|
306 |
}, |
|
307 |
can_dig = technic.machine_can_dig, |
67b90f
|
308 |
after_dig_node = meltdown_reactor, |
d59055
|
309 |
on_destruct = function(pos) siren_set_state(pos, "off") end, |
563a4c
|
310 |
allow_metadata_inventory_put = technic.machine_inventory_put, |
N |
311 |
allow_metadata_inventory_take = technic.machine_inventory_take, |
|
312 |
allow_metadata_inventory_move = technic.machine_inventory_move, |
|
313 |
technic_run = run, |
1c617f
|
314 |
technic_on_disable = function(pos, node) |
N |
315 |
local timer = minetest.get_node_timer(pos) |
|
316 |
timer:start(1) |
|
317 |
end, |
|
318 |
on_timer = function(pos, node) |
|
319 |
local meta = minetest.get_meta(pos) |
|
320 |
|
|
321 |
-- Connected back? |
|
322 |
if meta:get_int("HV_EU_timeout") > 0 then return end |
|
323 |
|
|
324 |
local burn_time = meta:get_int("burn_time") or 0 |
|
325 |
|
|
326 |
if burn_time >= burn_ticks or burn_time == 0 then |
|
327 |
meta:set_int("HV_EU_supply", 0) |
|
328 |
meta:set_int("burn_time", 0) |
|
329 |
technic.swap_node(pos, "technic:hv_nuclear_reactor_core") |
67b90f
|
330 |
meta:set_int("structure_accumulated_badness", 0) |
d59055
|
331 |
siren_clear(pos, meta) |
1c617f
|
332 |
return |
N |
333 |
end |
|
334 |
|
|
335 |
meta:set_int("burn_time", burn_time + 1) |
|
336 |
local timer = minetest.get_node_timer(pos) |
|
337 |
timer:start(1) |
|
338 |
end, |
ee0765
|
339 |
}) |
S |
340 |
|
|
341 |
technic.register_machine("HV", "technic:hv_nuclear_reactor_core", technic.producer) |
|
342 |
technic.register_machine("HV", "technic:hv_nuclear_reactor_core_active", technic.producer) |
|
343 |
|
ec008d
|
344 |
-- radioactivity |
366fc3
|
345 |
|
ec008d
|
346 |
-- Radiation resistance represents the extent to which a material |
Z |
347 |
-- attenuates radiation passing through it; i.e., how good a radiation |
|
348 |
-- shield it is. This is identified per node type. For materials that |
|
349 |
-- exist in real life, the radiation resistance value that this system |
|
350 |
-- uses for a node type consisting of a solid cube of that material is the |
|
351 |
-- (approximate) number of halvings of ionising radiation that is achieved |
|
352 |
-- by a metre of the material in real life. This is approximately |
|
353 |
-- proportional to density, which provides a good way to estimate it. |
|
354 |
-- Homogeneous mixtures of materials have radiation resistance computed |
|
355 |
-- by a simple weighted mean. Note that the amount of attenuation that |
|
356 |
-- a material achieves in-game is not required to be (and is not) the |
|
357 |
-- same as the attenuation achieved in real life. |
|
358 |
-- |
|
359 |
-- Radiation resistance for a node type may be specified in the node |
|
360 |
-- definition, under the key "radiation_resistance". As an interim |
|
361 |
-- measure, until node definitions widely include this, this code |
|
362 |
-- knows a bunch of values for particular node types in several mods, |
|
363 |
-- and values for groups of node types. The node definition takes |
|
364 |
-- precedence if it specifies a value. Nodes for which no value at |
|
365 |
-- all is known are taken to provide no radiation resistance at all; |
|
366 |
-- this is appropriate for the majority of node types. Only node types |
|
367 |
-- consisting of a fairly homogeneous mass of material should report |
|
368 |
-- non-zero radiation resistance; anything with non-uniform geometry |
|
369 |
-- or complex internal structure should show no radiation resistance. |
|
370 |
-- Fractional resistance values are permitted; two significant figures |
|
371 |
-- is the recommended precision. |
|
372 |
local default_radiation_resistance_per_node = { |
|
373 |
["default:brick"] = 13, |
|
374 |
["default:bronzeblock"] = 45, |
|
375 |
["default:clay"] = 15, |
|
376 |
["default:coalblock"] = 9.6, |
|
377 |
["default:cobble"] = 15, |
|
378 |
["default:copperblock"] = 46, |
|
379 |
["default:desert_cobble"] = 15, |
|
380 |
["default:desert_sand"] = 10, |
|
381 |
["default:desert_stone"] = 17, |
|
382 |
["default:desert_stonebrick"] = 17, |
|
383 |
["default:diamondblock"] = 24, |
|
384 |
["default:dirt"] = 8.2, |
|
385 |
["default:dirt_with_grass"] = 8.2, |
|
386 |
["default:dirt_with_grass_footsteps"] = 8.2, |
|
387 |
["default:dirt_with_snow"] = 8.2, |
|
388 |
["default:glass"] = 17, |
|
389 |
["default:goldblock"] = 170, |
|
390 |
["default:gravel"] = 10, |
|
391 |
["default:ice"] = 5.6, |
|
392 |
["default:lava_flowing"] = 8.5, |
|
393 |
["default:lava_source"] = 17, |
|
394 |
["default:mese"] = 21, |
|
395 |
["default:mossycobble"] = 15, |
|
396 |
["default:nyancat"] = 1000, |
|
397 |
["default:nyancat_rainbow"] = 1000, |
|
398 |
["default:obsidian"] = 18, |
|
399 |
["default:obsidian_glass"] = 18, |
|
400 |
["default:sand"] = 10, |
|
401 |
["default:sandstone"] = 15, |
|
402 |
["default:sandstonebrick"] = 15, |
|
403 |
["default:snowblock"] = 1.7, |
|
404 |
["default:steelblock"] = 40, |
|
405 |
["default:stone"] = 17, |
|
406 |
["default:stone_with_coal"] = 16, |
|
407 |
["default:stone_with_copper"] = 20, |
|
408 |
["default:stone_with_diamond"] = 18, |
|
409 |
["default:stone_with_gold"] = 34, |
|
410 |
["default:stone_with_iron"] = 20, |
|
411 |
["default:stone_with_mese"] = 17, |
|
412 |
["default:stonebrick"] = 17, |
|
413 |
["default:water_flowing"] = 2.8, |
|
414 |
["default:water_source"] = 5.6, |
|
415 |
["farming:desert_sand_soil"] = 10, |
|
416 |
["farming:desert_sand_soil_wet"] = 10, |
|
417 |
["farming:soil"] = 8.2, |
|
418 |
["farming:soil_wet"] = 8.2, |
|
419 |
["glooptest:akalin_crystal_glass"] = 21, |
|
420 |
["glooptest:akalinblock"] = 40, |
|
421 |
["glooptest:alatro_crystal_glass"] = 21, |
|
422 |
["glooptest:alatroblock"] = 40, |
|
423 |
["glooptest:amethystblock"] = 18, |
|
424 |
["glooptest:arol_crystal_glass"] = 21, |
|
425 |
["glooptest:crystal_glass"] = 21, |
|
426 |
["glooptest:emeraldblock"] = 19, |
|
427 |
["glooptest:heavy_crystal_glass"] = 21, |
|
428 |
["glooptest:mineral_akalin"] = 20, |
|
429 |
["glooptest:mineral_alatro"] = 20, |
|
430 |
["glooptest:mineral_amethyst"] = 17, |
|
431 |
["glooptest:mineral_arol"] = 20, |
|
432 |
["glooptest:mineral_desert_coal"] = 16, |
|
433 |
["glooptest:mineral_desert_iron"] = 20, |
|
434 |
["glooptest:mineral_emerald"] = 17, |
|
435 |
["glooptest:mineral_kalite"] = 20, |
|
436 |
["glooptest:mineral_ruby"] = 18, |
|
437 |
["glooptest:mineral_sapphire"] = 18, |
|
438 |
["glooptest:mineral_talinite"] = 20, |
|
439 |
["glooptest:mineral_topaz"] = 18, |
|
440 |
["glooptest:reinforced_crystal_glass"] = 21, |
|
441 |
["glooptest:rubyblock"] = 27, |
|
442 |
["glooptest:sapphireblock"] = 27, |
|
443 |
["glooptest:talinite_crystal_glass"] = 21, |
|
444 |
["glooptest:taliniteblock"] = 40, |
|
445 |
["glooptest:topazblock"] = 24, |
|
446 |
["mesecons_extrawires:mese_powered"] = 21, |
|
447 |
["moreblocks:cactus_brick"] = 13, |
|
448 |
["moreblocks:cactus_checker"] = 8.5, |
|
449 |
["moreblocks:circle_stone_bricks"] = 17, |
|
450 |
["moreblocks:clean_glass"] = 17, |
|
451 |
["moreblocks:coal_checker"] = 9.0, |
|
452 |
["moreblocks:coal_glass"] = 17, |
|
453 |
["moreblocks:coal_stone"] = 17, |
|
454 |
["moreblocks:coal_stone_bricks"] = 17, |
|
455 |
["moreblocks:glow_glass"] = 17, |
|
456 |
["moreblocks:grey_bricks"] = 15, |
|
457 |
["moreblocks:iron_checker"] = 11, |
|
458 |
["moreblocks:iron_glass"] = 17, |
|
459 |
["moreblocks:iron_stone"] = 17, |
|
460 |
["moreblocks:iron_stone_bricks"] = 17, |
|
461 |
["moreblocks:plankstone"] = 9.3, |
|
462 |
["moreblocks:split_stone_tile"] = 15, |
|
463 |
["moreblocks:split_stone_tile_alt"] = 15, |
|
464 |
["moreblocks:stone_tile"] = 15, |
|
465 |
["moreblocks:super_glow_glass"] = 17, |
|
466 |
["moreblocks:tar"] = 7.0, |
|
467 |
["moreblocks:wood_tile"] = 1.7, |
|
468 |
["moreblocks:wood_tile_center"] = 1.7, |
|
469 |
["moreblocks:wood_tile_down"] = 1.7, |
|
470 |
["moreblocks:wood_tile_flipped"] = 1.7, |
|
471 |
["moreblocks:wood_tile_full"] = 1.7, |
|
472 |
["moreblocks:wood_tile_left"] = 1.7, |
|
473 |
["moreblocks:wood_tile_right"] = 1.7, |
|
474 |
["moreblocks:wood_tile_up"] = 1.7, |
|
475 |
["moreores:mineral_mithril"] = 18, |
|
476 |
["moreores:mineral_silver"] = 21, |
|
477 |
["moreores:mineral_tin"] = 19, |
|
478 |
["moreores:mithril_block"] = 26, |
|
479 |
["moreores:silver_block"] = 53, |
|
480 |
["moreores:tin_block"] = 37, |
|
481 |
["snow:snow_brick"] = 2.8, |
|
482 |
["technic:brass_block"] = 43, |
|
483 |
["technic:carbon_steel_block"] = 40, |
|
484 |
["technic:cast_iron_block"] = 40, |
|
485 |
["technic:chernobylite_block"] = 40, |
|
486 |
["technic:chromium_block"] = 37, |
|
487 |
["technic:corium_flowing"] = 40, |
|
488 |
["technic:corium_source"] = 80, |
|
489 |
["technic:granite"] = 18, |
|
490 |
["technic:marble"] = 18, |
|
491 |
["technic:marble_bricks"] = 18, |
|
492 |
["technic:mineral_chromium"] = 19, |
|
493 |
["technic:mineral_uranium"] = 71, |
|
494 |
["technic:mineral_zinc"] = 19, |
|
495 |
["technic:stainless_steel_block"] = 40, |
|
496 |
["technic:zinc_block"] = 36, |
|
497 |
["tnt:tnt"] = 11, |
|
498 |
["tnt:tnt_burning"] = 11, |
|
499 |
} |
|
500 |
local default_radiation_resistance_per_group = { |
|
501 |
concrete = 16, |
|
502 |
tree = 3.4, |
b0faa7
|
503 |
uranium_block = 500, |
ec008d
|
504 |
wood = 1.7, |
Z |
505 |
} |
|
506 |
local cache_radiation_resistance = {} |
|
507 |
local function node_radiation_resistance(nodename) |
|
508 |
local eff = cache_radiation_resistance[nodename] |
|
509 |
if eff then return eff end |
|
510 |
local def = minetest.registered_nodes[nodename] or {groups={}} |
|
511 |
eff = def.radiation_resistance or default_radiation_resistance_per_node[nodename] |
|
512 |
if not eff then |
|
513 |
for g, v in pairs(def.groups) do |
|
514 |
if v > 0 and default_radiation_resistance_per_group[g] then |
|
515 |
eff = default_radiation_resistance_per_group[g] |
|
516 |
break |
|
517 |
end |
|
518 |
end |
|
519 |
end |
|
520 |
if not eff then eff = 0 end |
|
521 |
cache_radiation_resistance[nodename] = eff |
|
522 |
return eff |
|
523 |
end |
|
524 |
|
|
525 |
-- Radioactive nodes cause damage to nearby players. The damage |
|
526 |
-- effect depends on the intrinsic strength of the radiation source, |
|
527 |
-- the distance between the source and the player, and the shielding |
|
528 |
-- effect of the intervening material. These determine a rate of damage; |
|
529 |
-- total damage caused is the integral of this over time. |
|
530 |
-- |
|
531 |
-- In the absence of effective shielding, for a specific source the |
|
532 |
-- damage rate varies realistically in inverse proportion to the square |
|
533 |
-- of the distance. (Distance is measured to the player's abdomen, |
|
534 |
-- not to the nominal player position which corresponds to the foot.) |
|
535 |
-- However, if the player is inside a non-walkable (liquid or gaseous) |
|
536 |
-- radioactive node, the nominal distance could go to zero, yielding |
|
537 |
-- infinite damage. In that case, the player's body is displacing the |
|
538 |
-- radioactive material, so the effective distance should remain non-zero. |
|
539 |
-- We therefore apply a lower distance bound of sqrt(0.75) m, which is |
|
540 |
-- the maximum distance one can get from the node centre within the node. |
|
541 |
-- |
|
542 |
-- A radioactive node is identified by being in the "radioactive" group, |
|
543 |
-- and the group value signifies the strength of the radiation source. |
7a9d2f
|
544 |
-- The group value is the distance in millimetres from a node at which |
Z |
545 |
-- an unshielded player will be damaged by 0.25 HP/s. Or, equivalently, |
0de4fe
|
546 |
-- it is 2000 times the square root of the damage rate in HP/s that an |
7a9d2f
|
547 |
-- unshielded player 1 m away will take. |
ec008d
|
548 |
-- |
Z |
549 |
-- Shielding is assessed by sampling every 0.25 m along the path |
|
550 |
-- from the source to the player, ignoring the source node itself. |
2912e2
|
551 |
-- The summed shielding values from the sampled nodes yield a measure |
Z |
552 |
-- of the total amount of shielding on the path. As in reality, |
|
553 |
-- shielding causes exponential attenuation of radiation. However, the |
|
554 |
-- effect is scaled down relative to real life. A metre of a node with |
|
555 |
-- radiation resistance value R yields attenuation of sqrt(R)*0.1 nepers. |
|
556 |
-- (In real life it would be about R*0.69 nepers, by the definition |
|
557 |
-- of the radiation resistance values.) The sqrt part of this formula |
|
558 |
-- scales down the differences between shielding types, reflecting the |
|
559 |
-- game's simplification of making expensive materials such as gold |
|
560 |
-- readily available in cubic metres. The multiplicative factor in the |
|
561 |
-- formula scales down the difference between shielded and unshielded |
|
562 |
-- safe distances, avoiding the latter becoming impractically large. |
ec008d
|
563 |
-- |
Z |
564 |
-- Damage is processed at rates down to 0.25 HP/s, which in the absence of |
|
565 |
-- shielding is attained at the distance specified by the "radioactive" |
|
566 |
-- group value. Computed damage rates below 0.25 HP/s result in no |
|
567 |
-- damage at all to the player. This gives the player an opportunity |
|
568 |
-- to be safe, and limits the range at which source/player interactions |
|
569 |
-- need to be considered. |
17c5b6
|
570 |
local assumed_abdomen_offset = vector.new(0, 1, 0) |
Z |
571 |
local assumed_abdomen_offset_length = vector.length(assumed_abdomen_offset) |
2912e2
|
572 |
local cache_scaled_shielding = {} |
366fc3
|
573 |
minetest.register_abm({ |
Z |
574 |
nodenames = {"group:radioactive"}, |
|
575 |
interval = 1, |
|
576 |
chance = 1, |
|
577 |
action = function (pos, node) |
17c5b6
|
578 |
local strength = minetest.registered_nodes[node.name].groups.radioactive |
7a9d2f
|
579 |
for _, o in ipairs(minetest.get_objects_inside_radius(pos, strength*0.001 + assumed_abdomen_offset_length)) do |
17c5b6
|
580 |
if o:is_player() then |
ec008d
|
581 |
local rel = vector.subtract(vector.add(o:getpos(), assumed_abdomen_offset), pos) |
Z |
582 |
local dist_sq = vector.length_square(rel) |
|
583 |
local dist = math.sqrt(dist_sq) |
|
584 |
local dirstep = dist == 0 and vector.new(0,0,0) or vector.divide(rel, dist*4) |
|
585 |
local intpos = pos |
2912e2
|
586 |
local shielding = 0 |
ec008d
|
587 |
for intdist = 0.25, dist, 0.25 do |
Z |
588 |
intpos = vector.add(intpos, dirstep) |
|
589 |
local intnodepos = vector.round(intpos) |
|
590 |
if not vector.equals(intnodepos, pos) then |
2912e2
|
591 |
local sname = minetest.get_node(intnodepos).name |
Z |
592 |
local sval = cache_scaled_shielding[sname] |
|
593 |
if not sval then |
|
594 |
sval = math.sqrt(node_radiation_resistance(sname)) * -0.025 |
|
595 |
cache_scaled_shielding[sname] = sval |
|
596 |
end |
|
597 |
shielding = shielding + sval |
ec008d
|
598 |
end |
Z |
599 |
end |
2912e2
|
600 |
local dmg_rate = 0.25e-6 * strength*strength * math.exp(shielding) / math.max(0.75, dist_sq) |
17c5b6
|
601 |
if dmg_rate >= 0.25 then |
Z |
602 |
local dmg_int = math.floor(dmg_rate) |
|
603 |
if math.random() < dmg_rate-dmg_int then |
|
604 |
dmg_int = dmg_int + 1 |
|
605 |
end |
|
606 |
if dmg_int > 0 then |
|
607 |
o:set_hp(math.max(o:get_hp() - dmg_int, 0)) |
|
608 |
end |
366fc3
|
609 |
end |
Z |
610 |
end |
|
611 |
end |
|
612 |
end, |
|
613 |
}) |
ec008d
|
614 |
|
Z |
615 |
-- radioactive materials that can result from destroying a reactor |
366fc3
|
616 |
|
Z |
617 |
for _, state in ipairs({ "flowing", "source" }) do |
|
618 |
minetest.register_node("technic:corium_"..state, { |
|
619 |
description = S(state == "source" and "Corium Source" or "Flowing Corium"), |
|
620 |
drawtype = (state == "source" and "liquid" or "flowingliquid"), |
|
621 |
[state == "source" and "tiles" or "special_tiles"] = {{ |
|
622 |
name = "technic_corium_"..state.."_animated.png", |
|
623 |
animation = { |
|
624 |
type = "vertical_frames", |
|
625 |
aspect_w = 16, |
|
626 |
aspect_h = 16, |
|
627 |
length = 3.0, |
|
628 |
}, |
|
629 |
}}, |
|
630 |
paramtype = "light", |
|
631 |
paramtype2 = (state == "flowing" and "flowingliquid" or nil), |
e11f0f
|
632 |
light_source = (state == "source" and 8 or 5), |
366fc3
|
633 |
walkable = false, |
Z |
634 |
pointable = false, |
|
635 |
diggable = false, |
|
636 |
buildable_to = true, |
|
637 |
drop = "", |
|
638 |
drowning = 1, |
|
639 |
liquidtype = state, |
|
640 |
liquid_alternative_flowing = "technic:corium_flowing", |
|
641 |
liquid_alternative_source = "technic:corium_source", |
|
642 |
liquid_viscosity = LAVA_VISC, |
|
643 |
liquid_renewable = false, |
|
644 |
damage_per_second = 6, |
|
645 |
post_effect_color = { a=192, r=80, g=160, b=80 }, |
|
646 |
groups = { |
|
647 |
liquid = 2, |
|
648 |
hot = 3, |
|
649 |
igniter = 1, |
7a9d2f
|
650 |
radioactive = (state == "source" and 32000 or 16000), |
366fc3
|
651 |
not_in_creative_inventory = (state == "flowing" and 1 or nil), |
Z |
652 |
}, |
|
653 |
}) |
|
654 |
end |
|
655 |
|
|
656 |
if bucket and bucket.register_liquid then |
|
657 |
bucket.register_liquid( |
|
658 |
"technic:corium_source", |
|
659 |
"technic:corium_flowing", |
|
660 |
"technic:bucket_corium", |
|
661 |
"technic_bucket_corium.png", |
|
662 |
"Corium Bucket" |
|
663 |
) |
|
664 |
end |
|
665 |
|
|
666 |
minetest.register_node("technic:chernobylite_block", { |
|
667 |
description = S("Chernobylite Block"), |
|
668 |
tiles = { "technic_chernobylite_block.png" }, |
|
669 |
is_ground_content = true, |
7a9d2f
|
670 |
groups = { cracky=1, radioactive=5000, level=2 }, |
366fc3
|
671 |
sounds = default.node_sound_stone_defaults(), |
Z |
672 |
light_source = 2, |
|
673 |
|
|
674 |
}) |
|
675 |
|
|
676 |
minetest.register_abm({ |
|
677 |
nodenames = {"group:water"}, |
|
678 |
neighbors = {"technic:corium_source"}, |
|
679 |
interval = 1, |
|
680 |
chance = 1, |
|
681 |
action = function (pos, node) |
|
682 |
minetest.remove_node(pos) |
|
683 |
end, |
|
684 |
}) |
|
685 |
|
|
686 |
minetest.register_abm({ |
|
687 |
nodenames = {"technic:corium_flowing"}, |
|
688 |
neighbors = {"group:water"}, |
|
689 |
interval = 1, |
|
690 |
chance = 1, |
|
691 |
action = function (pos, node) |
|
692 |
minetest.set_node(pos, {name="technic:chernobylite_block"}) |
|
693 |
end, |
|
694 |
}) |
|
695 |
|
c5e948
|
696 |
local griefing = technic.config:get_bool("enable_corium_griefing") |
Z |
697 |
|
366fc3
|
698 |
minetest.register_abm({ |
Z |
699 |
nodenames = {"technic:corium_flowing"}, |
|
700 |
interval = 5, |
c5e948
|
701 |
chance = (griefing and 10 or 1), |
366fc3
|
702 |
action = function (pos, node) |
Z |
703 |
minetest.set_node(pos, {name="technic:chernobylite_block"}) |
|
704 |
end, |
|
705 |
}) |
|
706 |
|
c5e948
|
707 |
if griefing then |
Z |
708 |
minetest.register_abm({ |
|
709 |
nodenames = { "technic:corium_source", "technic:corium_flowing" }, |
|
710 |
interval = 4, |
|
711 |
chance = 4, |
|
712 |
action = function (pos, node) |
|
713 |
for _, offset in ipairs({ |
|
714 |
vector.new(1,0,0), |
|
715 |
vector.new(-1,0,0), |
|
716 |
vector.new(0,0,1), |
|
717 |
vector.new(0,0,-1), |
|
718 |
vector.new(0,-1,0), |
|
719 |
}) do |
|
720 |
if math.random(8) == 1 then |
|
721 |
minetest.dig_node(vector.add(pos, offset)) |
|
722 |
end |
366fc3
|
723 |
end |
c5e948
|
724 |
end, |
Z |
725 |
}) |
|
726 |
end |