363 lines
11 KiB
Lua
363 lines
11 KiB
Lua
local formspec =
|
|
"size[8,8.5]"..
|
|
default.gui_bg..
|
|
default.gui_bg_img..
|
|
default.gui_slots..
|
|
"list[current_name;reactant;0,0;2,3;]"..
|
|
"list[current_name;catalyst;3.5,0;1,1;]"..
|
|
"list[current_name;tool;3.5,2;1,1;]"..
|
|
"list[current_name;product;6,0;2,3;]"..
|
|
"list[current_player;main;0,4.25;8,1;]"..
|
|
"list[current_player;main;0,5.5;8,3;8]"..
|
|
"listring[current_name;reactant]"..
|
|
"listring[current_player;main]"..
|
|
"listring[current_name;catalyst]"..
|
|
"listring[current_player;main]"..
|
|
"listring[current_name;tool]"..
|
|
"listring[current_player;main]"..
|
|
"listring[current_name;product]"..
|
|
"listring[current_player;main]"..
|
|
default.get_hotbar_bg(0, 4.25)
|
|
|
|
EPSILON = 1e-7
|
|
|
|
reactions = {}
|
|
|
|
--[[
|
|
This is how a formula looks like:
|
|
{
|
|
tool: (itemstring of tool needed for the reaction, or nil)
|
|
catalyst: (--"--)
|
|
reactants: {
|
|
(ItemStack's of up to six reactants, indexes more than six are silently ignored; no duplicate itemstrings please)
|
|
}
|
|
possible_outputs: {
|
|
{
|
|
probability: (...)
|
|
products: {
|
|
(ItemStack's of up to six products)
|
|
}
|
|
}
|
|
}
|
|
sound: (description of a sound to be played, or nil)
|
|
}
|
|
--]]
|
|
|
|
function register_reaction(formula)
|
|
local err = false
|
|
-- Basic checks of formula
|
|
if formula.reactants == nil then
|
|
minetest.log("error", "No reactants supplied to register_reaction() (nil)")
|
|
err = true
|
|
end
|
|
if #formula.reactants == 0 then
|
|
minetest.log("error", "No reactants supplied to register_reaction() (empty)")
|
|
err = true
|
|
end
|
|
local reactant_count = 0
|
|
for i=1, 6 do
|
|
if not (formula.reactants[i] == nil) then
|
|
reactant_count = reactant_count + 1
|
|
end
|
|
end
|
|
if not (#formula.reactants == reactant_count) then
|
|
minetest.log("error", "Extra reactants supplied to register_reaction() ")
|
|
err = true
|
|
end
|
|
if formula.possible_outputs == nil then
|
|
minetest.log("error", "No possible outputs supplied to register_reaction() (nil)")
|
|
err = true
|
|
end
|
|
if #formula.possible_outputs == 0 then
|
|
minetest.log("error", "No possible outputs supplied to register_reaction() (empty)")
|
|
err = true
|
|
end
|
|
local total_probability = 0.0
|
|
for i=1, #formula.possible_outputs do
|
|
local output = formula.possible_outputs[i]
|
|
if output.probability <= 0 or output.probability > 1 then
|
|
minetest.log("error", "Invalid probability for output supplied to register_reaction()")
|
|
err = true
|
|
end
|
|
total_probability = total_probability + output.probability
|
|
end
|
|
if math.abs(total_probability - 1.0) > EPSILON then
|
|
minetest.log("error", "Probabilities which do not sum to one supplied to register_reaction()")
|
|
err = true
|
|
end
|
|
if not err then
|
|
reactions[#reactions + 1] = formula
|
|
end
|
|
end
|
|
|
|
local function can_dig(pos, player)
|
|
local meta = minetest.get_meta(pos);
|
|
local inv = meta:get_inventory()
|
|
return inv:is_empty("reactant") and inv:is_empty("catalyst") and inv:is_empty("tool") and inv:is_empty("product")
|
|
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
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
if listname == "catalyst" then
|
|
return 1 -- There can be only one catalyst
|
|
elseif listname == "tool" then
|
|
return 1 -- There can be only one tool
|
|
elseif listname == "reactant" then
|
|
return stack:get_count()
|
|
elseif listname == "product" then
|
|
return 0
|
|
end
|
|
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 allow_metadata_inventory_take(pos, listname, index, stack, player)
|
|
if minetest.is_protected(pos, player:get_player_name()) then
|
|
return 0
|
|
end
|
|
return stack:get_count()
|
|
end
|
|
|
|
local function reaction_possible(pos, products)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
-- Actually not quite, this checks whether the reaction would be possible if we needed an empty stack for every product
|
|
-- TODO do this properly
|
|
local needed_stacks = #products
|
|
local free_stacks = 0
|
|
for i=1, 6 do
|
|
local prod = inv:get_stack("product", i)
|
|
if prod:is_empty() then
|
|
free_stacks = free_stacks + 1
|
|
end
|
|
end
|
|
return free_stacks >= needed_stacks
|
|
end
|
|
|
|
local function get_node_group(name, group)
|
|
if not minetest.registered_nodes[name] or not minetest.registered_nodes[name].groups[group] then
|
|
return 0
|
|
end
|
|
return minetest.registered_nodes[name].groups[group]
|
|
end
|
|
|
|
local function bunsen_sound(pos)
|
|
minetest.sound_play("chemistry_bunsen", {
|
|
pos = pos,
|
|
max_hear_distance = 16,
|
|
gain = 1.0,
|
|
})
|
|
end
|
|
|
|
local function explosion_sound(pos)
|
|
minetest.sound_play("chemistry_explosion", {
|
|
pos = pos,
|
|
max_hear_distance = 64,
|
|
gain = 1.0,
|
|
})
|
|
end
|
|
|
|
local function mortar_sound(pos)
|
|
minetest.sound_play("chemistry_mortar", {
|
|
pos = pos,
|
|
max_hear_distance = 8,
|
|
gain = 1.0,
|
|
})
|
|
end
|
|
|
|
local function pouring_sound(pos)
|
|
minetest.sound_play("chemistry_pouring", {
|
|
pos = pos,
|
|
max_hear_distance = 8,
|
|
gain = 1.0,
|
|
})
|
|
end
|
|
|
|
local function stirring_sound(pos)
|
|
minetest.sound_play("chemistry_stirring", {
|
|
pos = pos,
|
|
max_hear_distance = 8,
|
|
gain = 1.0,
|
|
})
|
|
end
|
|
|
|
local function check_item_match(item, stack)
|
|
if item == nil then
|
|
return stack:is_empty()
|
|
else
|
|
return stack:get_name() == item
|
|
end
|
|
end
|
|
|
|
local function check_name_match(actual, needed)
|
|
if needed:sub(1, 6) == "group:" then
|
|
return get_node_group(actual, needed:sub(7)) ~= 0
|
|
else
|
|
return actual == needed
|
|
end
|
|
end
|
|
|
|
local function lab_node_timer(pos, elapsed)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
local result = false
|
|
local catalyst = inv:get_stack("catalyst", 1)
|
|
local tool = inv:get_stack("tool", 1)
|
|
local r = {}
|
|
r[1] = inv:get_stack("reactant", 1)
|
|
r[2] = inv:get_stack("reactant", 2)
|
|
r[3] = inv:get_stack("reactant", 3)
|
|
r[4] = inv:get_stack("reactant", 4)
|
|
r[5] = inv:get_stack("reactant", 5)
|
|
r[6] = inv:get_stack("reactant", 6)
|
|
|
|
local reaction = nil
|
|
for i=1, #reactions do
|
|
local formula = reactions[i]
|
|
local needed_tool = formula.tool
|
|
local needed_catalyst = formula.catalyst
|
|
local needed_reactants = formula.reactants
|
|
local reaction_works = true
|
|
for j=1, 6 do
|
|
local needed_reactant = needed_reactants[j]
|
|
local ok = false
|
|
if needed_reactant == nil then
|
|
ok = true
|
|
else
|
|
for k=1, 6 do
|
|
local supplied_reactant = r[k]
|
|
if check_name_match(supplied_reactant:get_name(), needed_reactant:get_name()) and supplied_reactant:get_count() >= needed_reactant:get_count() then
|
|
ok = true
|
|
end
|
|
end
|
|
end
|
|
if not ok then
|
|
reaction_works = false
|
|
end
|
|
end
|
|
local reactant_count = 0
|
|
for k = 1, 6 do
|
|
local supplied_reactant = r[k]
|
|
if not r[k]:is_empty() then
|
|
reactant_count = reactant_count + 1
|
|
end
|
|
end
|
|
if reactant_count ~= #needed_reactants then
|
|
reaction_works = false
|
|
end
|
|
if not check_item_match(needed_tool, tool) then
|
|
reaction_works = false
|
|
end
|
|
if not check_item_match(needed_catalyst, catalyst) then
|
|
reaction_works = false
|
|
end
|
|
if reaction_works then
|
|
reaction = formula
|
|
end
|
|
end
|
|
|
|
if not (reaction == nil) then
|
|
local rand = math.random()
|
|
local total_probability = 0.0
|
|
local selected_output = nil
|
|
while selected_output == nil do
|
|
for i=1, #reaction.possible_outputs do
|
|
local output = reaction.possible_outputs[i]
|
|
total_probability = total_probability + output.probability
|
|
if total_probability > rand then
|
|
selected_output = output
|
|
break
|
|
end
|
|
end
|
|
if selected_output == nil then
|
|
minetest.log("info", "Hit epsilon, retry")
|
|
end
|
|
end
|
|
local products = selected_output.products
|
|
if reaction_possible(pos, products) then
|
|
local reactants = reaction.reactants
|
|
for j=1, 6 do
|
|
local reactant = reactants[j]
|
|
if not (reactant == nil) then
|
|
for k=1, 6 do
|
|
if check_name_match(r[k]:get_name(), reactant:get_name()) then
|
|
r[k]:set_count(r[k]:get_count() - reactant:get_count())
|
|
inv:set_stack("reactant", k, r[k])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for k=1, #products do
|
|
inv:add_item("product", products[k])
|
|
end
|
|
if reaction.sound then
|
|
local sound = reaction.sound
|
|
if sound == "bunsen" then
|
|
bunsen_sound()
|
|
elseif sound == "explosion" then
|
|
explosion_sound()
|
|
elseif sound == "mortar" then
|
|
mortar_sound()
|
|
elseif sound == "pouring" then
|
|
pouring_sound()
|
|
elseif sound == "stirring" then
|
|
stirring_sound()
|
|
end
|
|
end
|
|
result = true
|
|
end
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
minetest.register_node("chemistry:lab", {
|
|
description = "Laboratory",
|
|
tiles = {"chemistry_lab.png"},
|
|
groups = {cracky = 1},
|
|
drop = "chemistry:lab",
|
|
sounds = default.node_sound_stone_defaults(),
|
|
can_dig = can_dig,
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("formspec", formspec)
|
|
local inv = meta:get_inventory()
|
|
inv:set_size("reactant", 6)
|
|
inv:set_size("catalyst", 1)
|
|
inv:set_size("tool", 1)
|
|
inv:set_size("product", 6)
|
|
end,
|
|
on_blast = function(pos)
|
|
local drops = {}
|
|
default.get_inventory_drops(pos, "reactant", drops)
|
|
default.get_inventory_drops(pos, "catalyst", drops)
|
|
default.get_inventory_drops(pos, "tool", drops)
|
|
default.get_inventory_drops(pos, "product", drops)
|
|
drops[#drops+1] = "chemistry:lab"
|
|
minetest.remove_node(pos)
|
|
return drops
|
|
end,
|
|
|
|
on_timer = lab_node_timer,
|
|
-- The timer will check whether the reaction works
|
|
on_metadata_inventory_move = function(pos)
|
|
minetest.get_node_timer(pos):start(1.0)
|
|
end,
|
|
on_metadata_inventory_put = function(pos)
|
|
minetest.get_node_timer(pos):start(1.0)
|
|
end,
|
|
|
|
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
|
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
|
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
|
})
|