minetest-teaching/init.lua

227 lines
7.9 KiB
Lua

-- check if node at pos is a digit
local function node_is_digit(pos)
local node = minetest.get_node(pos)
local nd = minetest.registered_nodes[node.name]
if nd == nil then
return false
end
if nd.groups == nil then
return false
end
if nd.groups.teaching_util == nil then
return false
end
return true
end
-- check if node at pos is the digit dg
local function node_is_spec_digit(pos, dg)
if not node_is_digit(pos) then
return false
end
local node = minetest.get_node(pos)
local nd = minetest.registered_nodes[node.name]
if type(nd.teaching_digit) == 'table' then
for _, digit in ipairs(nd.teaching_digit) do
if digit == dg then
return true
end
end
elseif type(nd.teaching_digit) == 'string' then
if nd.teaching_digit == dg then
return true
end
end
return false
end
-- check a solution placed by player (checker node at pos) and give prizes if correct
local function check_solution(pos, player)
local meta = minetest.get_meta(pos)
local sol = meta:get_string('solution')
if node_is_spec_digit({x=pos.x, y=pos.y+1, z=pos.z}, sol) then
if meta:get_string('b_saytext') == 'true' then
minetest.chat_send_player(player:get_player_name(), meta:get_string('s_saytext'))
end
if meta:get_string('b_dispense') == 'true' then
minetest.add_item({x=pos.x, y=pos.y+2, z=pos.z}, meta:get_inventory():get_list('dispense')[1])
end
if meta:get_string('b_lock') == 'true' then
-- Place a lab block (indestructible for students) where the solution was
minetest.set_node({x=pos.x, y=pos.y+1, z=pos.z}, {name="teaching:lab"})
end
end
end
-- can_dig callback that only allows teachers or freebuild to destroy the node
local function only_dig_teacher_or_freebuild(pos, player)
if minetest.check_player_privs(player:get_player_name(), {teacher=true}) then
return true
elseif minetest.check_player_privs(player:get_player_name(), {freebuild=true}) then
return true
else
return false
end
end
local function register_util_node(name, digit, humanname)
minetest.register_node('teaching:util_' .. name, {
drawtype = 'normal',
tiles = {'teaching_lab.png', 'teaching_lab.png', 'teaching_lab.png',
'teaching_lab.png', 'teaching_lab.png', 'teaching_lab_util_' .. name .. '.png'},
paramtype2 = 'facedir',
description = humanname,
groups = {teaching_util=1, snappy=3},
teaching_digit = digit,
can_dig = function(pos, player)
if minetest.check_player_privs(player:get_player_name(), {teacher=true}) then
return true
else
local node = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
if node.name == 'teaching:lab_checker' or node.name == 'teaching:lab_allowdig' then
return true
else
return false
end
end
end,
})
end
minetest.register_privilege('teacher', {
description = "Teacher privilege",
give_to_singleplayer = false,
})
minetest.register_privilege('freebuild', {
description = "Free-building privilege",
give_to_singleplayer = false,
})
minetest.register_node('teaching:lab', {
drawtype = 'normal',
tiles = {'teaching_lab.png'},
description = 'Lab block',
groups = {oddly_breakable_by_hand=2},
can_dig = only_dig_teacher_or_freebuild,
})
minetest.register_node('teaching:lab_allowdig', {
drawtype = 'normal',
tiles = {'teaching_lab_allowdig.png'},
description = 'Allow-dig block (allows students to break block above)',
groups = {oddly_breakable_by_hand=2},
can_dig = only_dig_teacher_or_freebuild,
})
local checker_formspec =
'size[8,9]'..
'field[0.5,0.5;2,1;solution;Correct solution:;${solution}]'..
'checkbox[2.5,0.2;b_lock;Lock once solved?;${b_lock}]'..
'label[0.25,1;Action if right:]'..
'checkbox[0.5,1.5;b_saytext;Say text:;${b_saytext}]'..
'field[2.4,1.9;3,0.75;s_saytext;;${s_saytext}]'..
'checkbox[0.5,2.25;b_dispense;Dispense item:;${b_dispense}]'..
'list[nodemeta:${x},${y},${z};dispense;1,2.9;1,1;]'..
'list[current_player;main;0,5;8,4;]'..
'button_exit[0.3,4;2,1;save;Save]'
minetest.register_on_player_receive_fields(function(sender, formname, fields)
if formname:find('teaching:lab_checker_') == 1 then
local x, y, z = formname:match('teaching:lab_checker_(.-)_(.-)_(.*)')
local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
--print("Checker at " .. minetest.pos_to_string(pos) .. " got " .. dump(fields))
local meta = minetest.get_meta(pos)
if fields.b_saytext ~= nil then -- If we get a checkbox value we need to save that immediately because they are not sent on clicking 'Save' (due to a bug in minetest)
meta:set_string('b_saytext', fields.b_saytext)
end
if fields.b_dispense ~= nil then -- ditto
meta:set_string('b_dispense', fields.b_dispense)
end
if fields.b_lock ~= nil then -- ditto
meta:set_string('b_lock', fields.b_lock)
end
if fields.save ~= nil then
meta:set_string('solution', fields.solution)
if meta:get_string('b_saytext') == 'true' then
meta:set_string('s_saytext', fields.s_saytext)
end
end
end
end)
minetest.register_node('teaching:lab_checker', {
drawtype = 'normal',
tiles = {'teaching_lab_checker.png'},
description = 'Checking block',
groups = {oddly_breakable_by_hand=1},
can_dig = only_dig_teacher_or_freebuild,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
inv:set_size("dispense", 1)
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if minetest.check_player_privs(clicker:get_player_name(), {teacher=true}) then
local meta = minetest.get_meta(pos)
local formspec = checker_formspec
formspec = formspec:gsub('${x}', pos.x)
formspec = formspec:gsub('${y}', pos.y)
formspec = formspec:gsub('${z}', pos.z)
formspec = formspec:gsub('${(.-)}', function(name) return meta:get_string(name) end)
minetest.show_formspec(clicker:get_player_name(), 'teaching:lab_checker_'..pos.x..'_'..pos.y..'_'..pos.z, formspec)
-- We need to ue this complicated way because MT does not allow us to deny showing the formspec to some people
else
if not itemstack:is_empty() then
if minetest.registered_nodes[itemstack:get_name()] ~= nil then
if minetest.registered_nodes[itemstack:get_name()].teaching_digit ~= nil then
-- Someone wants to place a utility node, we can do that
local newpos = {x=pos.x, y=pos.y+1, z=pos.z} -- FIXME: This assumes said person wants to place node on top
minetest.set_node(newpos, {name=itemstack:get_name(), param2=minetest.dir_to_facedir(clicker:get_look_dir())})
itemstack:take_item()
minetest.log('action', clicker:get_player_name() .. ' places ' .. node.name .. ' at ' .. minetest.pos_to_string(newpos))
check_solution(pos, clicker)
end -- We don't have way to pass on_rightclick along
end
end
return false
end
end,
})
minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
if not minetest.check_player_privs(placer:get_player_name(), {teacher=true}) then
if minetest.registered_nodes[newnode.name].teaching_digit ~= nil then
local below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
if below.name == 'teaching:lab_checker' then
if not placer:get_player_name() then
return minetest.log('warning', 'placenode event triggered without valid player')
end
check_solution(pos, placer)
else
if minetest.check_player_privs(placer:get_player_name(), {freebuild=true}) then
return false
else
minetest.set_node(pos, oldnode)
return true -- Don't take item
end
end
end
end
end)
local s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
for i in s:gmatch('.') do
register_util_node(i, i, i)
end
register_util_node('decimalpoint', '.', '. (Decimal point)')
register_util_node('divide', {':', '/'}, ': (Divide)')
register_util_node('equals', '=', '= (Equals)')
register_util_node('less', '<', '< (Less than)')
register_util_node('minus', '-', '- (Minus)')
register_util_node('more', '>', '> (More than)')
register_util_node('multiply', {'*', 'x'}, '* (Multiply)')
register_util_node('plus', '+', '+ (Plus)')