578 lines
16 KiB
Lua
578 lines
16 KiB
Lua
|
|
--[[
|
|
Reactor fitness check:
|
|
8x8x8 area surrounding the core must either contain..
|
|
Water source nodes
|
|
Neutron Absorber (with active medium)
|
|
Fluid Port (with COLD coolant available)
|
|
Also acceptable nodes:
|
|
Any fluid transfer conduit
|
|
Any reactor component
|
|
Unacceptable nodes (These raise heat INSTANTLY!):
|
|
Lava source
|
|
Hot coolant
|
|
..in order to keep the heat below critical. Any other detected node will either be MOLTEN or ACTIVATED (TODO) (you don't want this!)
|
|
Reactor core will be replaced by a molten core when the heat reaches 100%. All components and fuel will be lost!
|
|
]]
|
|
|
|
local AREA_SIZE = 8
|
|
|
|
local function calculate_fitness(pos)
|
|
-- Calculate the heat sink percentage
|
|
-- Amount of nodes we shall count down from
|
|
local add = {x = (AREA_SIZE) / 2, y = (AREA_SIZE) / 2, z = (AREA_SIZE) / 2}
|
|
local minp = vector.subtract(pos, add)
|
|
local maxp = vector.add(pos, add)
|
|
|
|
-- Get the vmanip mapgen object and the nodes and VoxelArea
|
|
local manip = minetest.get_voxel_manip()
|
|
local e1, e2 = manip:read_from_map(minp, maxp)
|
|
local area = VoxelArea:new{MinEdge=e1, MaxEdge=e2}
|
|
local data = manip:get_data()
|
|
|
|
local ids = {
|
|
c_water = minetest.get_content_id("default:water_source"),
|
|
c_lava = minetest.get_content_id("default:lava_source"),
|
|
}
|
|
|
|
local excession = 0
|
|
local hu = 0
|
|
local nodes = 0
|
|
for i in area:iter(
|
|
minp.x, minp.y, minp.z,
|
|
maxp.x, maxp.y, maxp.z
|
|
) do
|
|
nodes = nodes + 1
|
|
if data[i] == ids["c_water"] then
|
|
hu = hu - 1
|
|
elseif data[i] == ids["c_lava"] then
|
|
hu = hu + 1
|
|
else
|
|
local dp = minetest.get_name_from_content_id(data[i])
|
|
if excession <= 16 and (ele.helpers.get_item_group(dp, "ele_reactor_component") or
|
|
ele.helpers.get_item_group(dp, "ele_neutron_absorbant") or
|
|
ele.helpers.get_item_group(dp, "fluid_transport_source") or
|
|
ele.helpers.get_item_group(dp, "fluid_transport") or
|
|
ele.helpers.get_item_group(dp, "tube") or
|
|
ele.helpers.get_item_group(dp, "tubedevice")) then
|
|
hu = hu - 1
|
|
excession = excession + 1
|
|
elseif ele.helpers.get_item_group(dp, "hot") then
|
|
hu = hu + 1
|
|
else
|
|
hu = hu + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
hu = nodes + hu
|
|
|
|
return 100 - math.floor(100 * hu / nodes), hu
|
|
end
|
|
|
|
local function fuel_after_depletion(inv, power)
|
|
local fuel_count = 0
|
|
local change = false
|
|
|
|
local list = inv:get_list("fuel")
|
|
for i,stack in pairs(list) do
|
|
local sname = stack:get_name()
|
|
if ele.helpers.get_item_group(sname, "fissile_fuel") then
|
|
local stdef = minetest.registered_items[sname]
|
|
if stdef.fissile_count then
|
|
local meta = stack:get_meta()
|
|
local fscount = meta:get_int("fission_count")
|
|
if fscount < stdef.fissile_count then
|
|
fscount = fscount + 1
|
|
fuel_count = fuel_count + 1
|
|
|
|
meta:set_int("fission_count", fscount)
|
|
meta:set_string("description", ("%s\nDepleted: %d "):format(stdef.description,
|
|
math.floor(100 * fscount / stdef.fissile_count)).." %")
|
|
else
|
|
stack = ItemStack("elepower_nuclear:fuel_rod_depleted")
|
|
end
|
|
list[i] = stack
|
|
change = true
|
|
end
|
|
end
|
|
end
|
|
|
|
if change then
|
|
inv:set_list("fuel", list)
|
|
end
|
|
|
|
return fuel_count
|
|
end
|
|
|
|
local function can_dig(pos, player)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
return inv:is_empty("fuel")
|
|
end
|
|
|
|
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
|
if minetest.is_protected(pos, player:get_player_name()) then
|
|
return 0
|
|
end
|
|
|
|
if not ele.helpers.get_item_group(stack:get_name(), "fissile_fuel") then
|
|
return 0
|
|
end
|
|
|
|
return stack:get_count()
|
|
end
|
|
|
|
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local stack = inv:get_stack(from_list, from_index)
|
|
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
|
end
|
|
|
|
local function get_core_formspec(heat, power)
|
|
local status = "Activate by extracting the control rods"
|
|
|
|
if heat > 80 then
|
|
status = "!!! TEMPERATURE CRITICAL !!!"
|
|
elseif heat > 90 then
|
|
status = "!!! REACTOR CRITICAL !!!"
|
|
elseif heat > 95 then
|
|
status = "!!! REACTOR MELTDOWN IMMINENT !!!"
|
|
elseif power > 0 then
|
|
status = "Active reaction chain"
|
|
end
|
|
|
|
return "size[8,8.5]"..
|
|
default.gui_bg..
|
|
default.gui_bg_img..
|
|
default.gui_slots..
|
|
"list[context;fuel;2.5,0;3,3;]"..
|
|
"list[current_player;main;0,4.25;8,1;]"..
|
|
"list[current_player;main;0,5.5;8,3;8]"..
|
|
ele.formspec.create_bar(0, 0, power, "#ff0000", true)..
|
|
ele.formspec.create_bar(0.5, 0, heat, "#ffdd11", true)..
|
|
"label[0,3;Power: \t"..power.."%]"..
|
|
"label[0,3.25;Heat: \t"..heat.."%]"..
|
|
"label[0,3.75;".. status .."]"..
|
|
"listring[current_player;main]"..
|
|
"listring[context;fuel]"..
|
|
"listring[current_player;main]"..
|
|
default.get_hotbar_bg(0, 4.25)
|
|
end
|
|
|
|
local function get_controller_formspec(rod_pos, selected)
|
|
-- TODO: Reactor-dependent rod count
|
|
local rods = 4
|
|
local ctrls = {}
|
|
|
|
for num, depth in pairs(rod_pos) do
|
|
local xoffset = (num / rods) * 8
|
|
local sel = ""
|
|
|
|
if num == selected then
|
|
sel = " <- "
|
|
end
|
|
|
|
local fspc = ("label[%d,0;%s]"):format(xoffset - 0.25, depth .. " %" .. sel)
|
|
|
|
fspc = fspc .. ele.formspec.create_bar(xoffset - 1, 0.5, 100 - depth, "#252625", true)
|
|
|
|
table.insert(ctrls, fspc)
|
|
end
|
|
|
|
return "size[8,8.5]"..
|
|
default.gui_bg..
|
|
default.gui_bg_img..
|
|
default.gui_slots..
|
|
table.concat( ctrls, "" )..
|
|
"button[0,3.5;1.5,0.5;next;Next]"..
|
|
"button[1.5,3.5;1.5,0.5;prev;Previous]"..
|
|
"button[3.25,3.5;1.5,0.5;stop;SCRAM]"..
|
|
"button[5,3.5;1.5,0.5;up;Raise]"..
|
|
"button[6.5,3.5;1.5,0.5;down;Lower]"..
|
|
"tooltip[next;Select the next control rod]"..
|
|
"tooltip[prev;Select the previous control rod]"..
|
|
"tooltip[stop;Drops all the rods into the reactor core, instantly stopping it]"..
|
|
"tooltip[up;Raise selected control rod]"..
|
|
"tooltip[down;Lower selected control rod]"..
|
|
"list[current_player;main;0,4.25;8,1;]"..
|
|
"list[current_player;main;0,5.5;8,3;8]"..
|
|
"listring[current_player;main]"..
|
|
default.get_hotbar_bg(0, 4.25)
|
|
end
|
|
|
|
local function get_port_formspec(cool, hot)
|
|
return "size[8,8.5]"..
|
|
default.gui_bg..
|
|
default.gui_bg_img..
|
|
default.gui_slots..
|
|
ele.formspec.fluid_bar(0, 0, cool)..
|
|
ele.formspec.fluid_bar(7, 0, hot)..
|
|
"list[current_player;main;0,4.25;8,1;]"..
|
|
"list[current_player;main;0,5.5;8,3;8]"..
|
|
"listring[current_player;main]"..
|
|
default.get_hotbar_bg(0, 4.25)
|
|
end
|
|
|
|
local function reactor_core_timer(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local headless = false
|
|
local fuel_reactor = 0
|
|
|
|
-- SAFEGUARD: Expect a controller to be above the core
|
|
local controller_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
|
|
local controller_node = minetest.get_node_or_nil(controller_pos)
|
|
if not controller_node or controller_node.name ~= "elepower_nuclear:fission_controller" then
|
|
-- Don't do anything without a head
|
|
headless = true
|
|
end
|
|
|
|
local controller = minetest.get_meta(controller_pos)
|
|
|
|
-- Only read area ever so often
|
|
-- Calculate the absorbance of heat around the core
|
|
local hp = meta:get_int("absorbance")
|
|
local absorb_tick = meta:get_int("absorb_tick")
|
|
if absorb_tick > 10 then
|
|
hp = calculate_fitness(pos)
|
|
absorb_tick = 0
|
|
else
|
|
absorb_tick = absorb_tick + 1
|
|
end
|
|
|
|
meta:set_int("absorb_tick", absorb_tick)
|
|
meta:set_int("absorbance", hp)
|
|
|
|
-- Get reactor power setting
|
|
local power_setting_target = controller:get_int("setting")
|
|
local power_setting = meta:get_int("setting")
|
|
|
|
-- Do nothing
|
|
if headless then
|
|
power_setting = 0
|
|
else
|
|
if not (power_setting_target == 0 and power_setting == 0) then
|
|
-- Decrease or increase power
|
|
if power_setting_target > power_setting then
|
|
power_setting = power_setting + 1
|
|
elseif power_setting_target < power_setting then
|
|
power_setting = power_setting - 5
|
|
end
|
|
|
|
if power_setting < 0 then
|
|
power_setting = 0
|
|
elseif power_setting > 100 then
|
|
power_setting = 100
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Deplete fuel
|
|
if power_setting > 0 then
|
|
fuel_reactor = fuel_after_depletion(inv, power_setting)
|
|
if fuel_reactor == 0 then
|
|
-- Enforce zero power setting when no fuel present
|
|
power_setting = 0
|
|
end
|
|
end
|
|
|
|
-- Set power setting
|
|
meta:set_int("setting", power_setting)
|
|
|
|
-- Get reactor heat
|
|
local heat = meta:get_int("heat")
|
|
|
|
-- Calculate heat
|
|
-- I dont really know what I'm doing here, just playing around with the numbers
|
|
-- to get something workable
|
|
if power_setting > 5 then
|
|
local ceiling = math.floor(power_setting / 2)
|
|
if heat > ceiling and hp > 75 then
|
|
-- Remove heat when the heat goes above power setting and there's sufficient coolant
|
|
heat = heat + math.floor(((74 - hp)/2)/ceiling)
|
|
else
|
|
-- Heat up the reactor by the amount of fuel
|
|
-- If the reactor coolant is insufficient, add that factor to play
|
|
heat = heat + fuel_reactor + math.floor(80 * (1-(hp/100)))
|
|
|
|
-- Catch up to the power setting
|
|
if heat < ceiling then
|
|
heat = heat + math.floor((ceiling - heat) / 2)
|
|
end
|
|
end
|
|
elseif heat > 0 then
|
|
heat = heat + math.floor((-hp)/4)
|
|
end
|
|
|
|
if heat >= 100 then
|
|
minetest.set_node(pos, {name = "elepower_nuclear:corium_source"})
|
|
return false
|
|
end
|
|
|
|
if heat < 0 then
|
|
heat = 0
|
|
end
|
|
|
|
-- Nothing left to do in this timer, exit
|
|
if power_setting == 0 and heat == 0 then
|
|
meta:set_int("heat", heat)
|
|
meta:set_string("formspec", get_core_formspec(heat, power_setting))
|
|
return false
|
|
end
|
|
|
|
-- Expect a fluid port below the core
|
|
-- TODO: Allow multiple fluid ports in the core's affected area
|
|
local fluid_port_pos = {x = pos.x, y = pos.y - 1, z = pos.z}
|
|
local fluid_port_node = minetest.get_node_or_nil(fluid_port_pos)
|
|
if fluid_port_node ~= nil and fluid_port_node.name == "elepower_nuclear:reactor_fluid_port" then
|
|
local fpmeta = minetest.get_meta(fluid_port_pos)
|
|
|
|
-- Calculate how much heat is given to the fluid port
|
|
local burst_strength = math.max(math.floor((heat / 100) * 64), 1)
|
|
|
|
if fpmeta:get_int("burst") == 0 and heat > 0 then
|
|
fpmeta:set_int("burst", burst_strength)
|
|
minetest.get_node_timer(fluid_port_pos):start(1.0)
|
|
heat = heat - burst_strength
|
|
end
|
|
end
|
|
|
|
meta:set_int("heat", heat)
|
|
meta:set_string("formspec", get_core_formspec(heat, power_setting))
|
|
|
|
return true
|
|
end
|
|
|
|
local function reactor_controller_timer(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local settings = {}
|
|
local averg = 0
|
|
|
|
for i = 1, 4 do
|
|
table.insert(settings, meta:get_int("c" .. i))
|
|
averg = averg + settings[i]
|
|
end
|
|
|
|
meta:set_int("setting", 100 - (averg / 4))
|
|
meta:set_string("formspec", get_controller_formspec(settings, meta:get_int("selected")))
|
|
|
|
-- Ping the core
|
|
local core_pos = {x = pos.x, y = pos.y - 1, z = pos.z}
|
|
local core_node = minetest.get_node_or_nil(core_pos)
|
|
if core_node and core_node.name == "elepower_nuclear:fission_core" then
|
|
local timer = minetest.get_node_timer(core_pos)
|
|
if not timer:is_started() then
|
|
timer:start(1.0)
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function reactor_controller_manage(pos, formname, fields, sender)
|
|
if sender and sender ~= "" and minetest.is_protected(pos, sender:get_player_name()) then
|
|
return
|
|
end
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
local selected = meta:get_int("selected")
|
|
local change = false
|
|
|
|
if fields["next"] then
|
|
selected = selected + 1
|
|
if selected > 4 then
|
|
selected = 1
|
|
end
|
|
|
|
meta:set_int("selected", selected)
|
|
change = true
|
|
elseif fields["prev"] then
|
|
selected = selected - 1
|
|
if selected == 0 then
|
|
selected = 4
|
|
end
|
|
|
|
meta:set_int("selected", selected)
|
|
change = true
|
|
elseif fields["stop"] then
|
|
for i = 1, 4 do
|
|
meta:set_int("c" .. i, 100)
|
|
end
|
|
change = true
|
|
elseif fields["up"] then
|
|
local sl = meta:get_int("c"..selected)
|
|
sl = sl - 10
|
|
|
|
if sl <= 0 then
|
|
sl = 0
|
|
end
|
|
|
|
meta:set_int("c"..selected, sl)
|
|
change = true
|
|
elseif fields["down"] then
|
|
local sl = meta:get_int("c"..selected)
|
|
sl = sl + 10
|
|
|
|
if sl >= 100 then
|
|
sl = 100
|
|
end
|
|
|
|
meta:set_int("c"..selected, sl)
|
|
change = true
|
|
end
|
|
|
|
if change then
|
|
minetest.get_node_timer(pos):start(0.2)
|
|
end
|
|
end
|
|
|
|
local function reactor_port_timer(pos)
|
|
local refresh = false
|
|
local meta = minetest.get_meta(pos)
|
|
local cool = fluid_lib.get_buffer_data(pos, "cool")
|
|
local hot = fluid_lib.get_buffer_data(pos, "hot")
|
|
|
|
local heat_burst = meta:get_int("burst")
|
|
if heat_burst > 0 then
|
|
-- Convert a bucket of cold coolant into hot coolant
|
|
|
|
local heat_take = math.floor(cool.capacity * (heat_burst/100))
|
|
local coolant = heat_take
|
|
if coolant > cool.amount then
|
|
coolant = cool.amount
|
|
end
|
|
|
|
if hot.amount + coolant > hot.capacity and hot.amount < hot.capacity then
|
|
coolant = hot.capacity - hot.amount
|
|
end
|
|
|
|
if coolant > 0 and hot.amount + coolant < hot.capacity then
|
|
meta:set_int("burst", 0)
|
|
|
|
cool.amount = cool.amount - coolant
|
|
hot.amount = hot.amount + coolant
|
|
|
|
refresh = true
|
|
|
|
meta:set_string("cool_fluid", "elepower_nuclear:coolant_source")
|
|
meta:set_string("hot_fluid", "elepower_nuclear:hot_coolant_source")
|
|
|
|
meta:set_int("cool_fluid_storage", cool.amount)
|
|
meta:set_int("hot_fluid_storage", hot.amount)
|
|
end
|
|
end
|
|
|
|
meta:set_string("formspec", get_port_formspec(cool, hot))
|
|
|
|
return refresh
|
|
end
|
|
|
|
-- Reactor Core
|
|
ele.register_base_device("elepower_nuclear:fission_core", {
|
|
description = "Fission Reactor Core",
|
|
groups = {
|
|
cracky = 3,
|
|
ele_reactor_core = 1,
|
|
ele_reactor_component = 1,
|
|
},
|
|
tiles = {
|
|
"elenuclear_fission_core_top.png", "elepower_lead_block.png", "elenuclear_fission_core_side.png",
|
|
"elenuclear_fission_core_side.png", "elenuclear_fission_core_side.png", "elenuclear_fission_core_side.png",
|
|
},
|
|
on_timer = reactor_core_timer,
|
|
on_construct = function (pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
|
|
inv:set_size("fuel", 9)
|
|
meta:set_int("absorb_tick", 11)
|
|
|
|
meta:set_string("formspec", get_core_formspec(0,0,false))
|
|
end,
|
|
can_dig = can_dig,
|
|
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
|
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
|
allow_metadata_inventory_take = ele.default.allow_metadata_inventory_take,
|
|
|
|
on_metadata_inventory_move = ele.default.metadata_inventory_changed,
|
|
on_metadata_inventory_put = ele.default.metadata_inventory_changed,
|
|
on_metadata_inventory_take = ele.default.metadata_inventory_changed,
|
|
})
|
|
|
|
-- Reactor Control
|
|
ele.register_base_device("elepower_nuclear:fission_controller", {
|
|
description = "Fission Control Module\nPlace me on top of a Fission Reactor Core",
|
|
groups = {
|
|
cracky = 3,
|
|
ele_reactor_component = 1,
|
|
},
|
|
tiles = {
|
|
"elenuclear_fission_core_top.png", "elepower_lead_block.png", "elenuclear_fission_controller_side.png",
|
|
"elenuclear_fission_controller_side.png", "elenuclear_fission_controller_side.png", "elenuclear_fission_controller_side.png",
|
|
},
|
|
on_timer = reactor_controller_timer,
|
|
on_receive_fields = reactor_controller_manage,
|
|
on_construct = function (pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
|
|
meta:set_int("c1", 100)
|
|
meta:set_int("c2", 100)
|
|
meta:set_int("c3", 100)
|
|
meta:set_int("c4", 100)
|
|
|
|
meta:set_int("setting", 0)
|
|
meta:set_int("selected", 1)
|
|
|
|
meta:set_string("formspec", get_controller_formspec({100, 100, 100, 100}, 1))
|
|
end
|
|
})
|
|
|
|
-- Reactor Fluid Port
|
|
ele.register_base_device("elepower_nuclear:reactor_fluid_port", {
|
|
description = "Reactor Fluid Port\nPlace me on the bottom of a Fission Reactor Core",
|
|
groups = {
|
|
cracky = 3,
|
|
ele_reactor_component = 1,
|
|
ele_port = 1,
|
|
fluid_container = 1,
|
|
},
|
|
tiles = {
|
|
"elenuclear_machine_top.png", "elepower_lead_block.png", "elenuclear_machine_side.png^elepower_power_port.png",
|
|
"elenuclear_machine_side.png^elepower_power_port.png", "elenuclear_machine_side.png^elepower_power_port.png",
|
|
"elenuclear_machine_side.png^elepower_power_port.png",
|
|
},
|
|
on_timer = reactor_port_timer,
|
|
on_construct = function (pos)
|
|
local meta = minetest.get_meta(pos)
|
|
|
|
meta:set_string("cool_fluid", "elepower_nuclear:coolant_source")
|
|
meta:set_string("hot_fluid", "elepower_nuclear:hot_coolant_source")
|
|
|
|
meta:set_string("formspec", get_port_formspec())
|
|
end,
|
|
fluid_buffers = {
|
|
cool = {
|
|
capacity = 16000,
|
|
accepts = {"default:water_source", "elepower_nuclear:coolant_source"},
|
|
drainable = false,
|
|
},
|
|
hot = {
|
|
capacity = 16000,
|
|
accepts = {"elepower_nuclear:hot_coolant_source"},
|
|
drainable = true,
|
|
}
|
|
},
|
|
})
|
|
|
|
-- Load reactor cores
|
|
minetest.register_lbm({
|
|
label = "Refresh Reactors on load",
|
|
name = "elepower_nuclear:fission_core",
|
|
nodenames = {"elepower_nuclear:fission_core"},
|
|
run_at_every_load = true,
|
|
action = ele.helpers.start_timer,
|
|
})
|