261 lines
9.0 KiB
Lua
261 lines
9.0 KiB
Lua
--------------------------------------------------------
|
|
-- Minetest :: Safety Deposit Mod (safety_deposit)
|
|
--
|
|
-- See README.txt for licensing and other information.
|
|
-- Copyright (c) 2020, Leslie E. Krause
|
|
--
|
|
-- ./games/minetest_game/mods/safety_deposit/init.lua
|
|
--------------------------------------------------------
|
|
|
|
local username = minetest.setting_get( "name" ) -- used as public key for encryption
|
|
|
|
-----------------------
|
|
|
|
safety_deposit = { }
|
|
|
|
safety_deposit.encrypt_metadata = function ( itemstack, data )
|
|
local raw_data = cipher.encrypt_to_base64( minetest.serialize( data ), username )
|
|
itemstack:set_metadata( raw_data )
|
|
end
|
|
|
|
safety_deposit.decrypt_metadata = function ( itemstack )
|
|
local raw_data = itemstack:get_metadata( )
|
|
if cipher.is_encrypted( raw_data ) then
|
|
return minetest.deserialize( cipher.decrypt_from_base64( raw_data, username ) )
|
|
else
|
|
return minetest.deserialize( raw_data )
|
|
end
|
|
end
|
|
|
|
-----------------------
|
|
|
|
local disabled_safes = { }
|
|
|
|
local function open_safe_viewer( pos, player_name, access_code, safe_inv )
|
|
|
|
local function get_formspec( )
|
|
local formspec =
|
|
"size[8,7]" ..
|
|
default.gui_bg ..
|
|
default.gui_bg_img ..
|
|
default.gui_slots ..
|
|
string.format( "list[detached:%s_safe;main;0,0;8,2;]", player_name ) ..
|
|
"button[0.0,2.3;2,0.3;get;Get Items]" ..
|
|
"button[6.0.,2.3;2,0.3;unlock;Unlock]" ..
|
|
"list[current_player;main;0,3;8,1;]" ..
|
|
"list[current_player;main;0,4.2;8,3;8]" ..
|
|
"listring[nodemeta:%s;books]"..
|
|
"listring[current_player;main]" ..
|
|
"hidden[context;true]" ..
|
|
default.get_hotbar_bg(0,3)
|
|
|
|
return formspec
|
|
end
|
|
|
|
local function on_close( fs, player, fields )
|
|
if fields.quit then
|
|
local meta = minetest.get_meta( pos )
|
|
local data = { }
|
|
|
|
for _, itemstack in ipairs( safe_inv:get_list( "main" ) ) do
|
|
table.insert( data, itemstack:to_string( ) )
|
|
end
|
|
|
|
local raw_data = cipher.encrypt_to_base64( minetest.serialize( data ), access_code )
|
|
|
|
meta:set_string( "storage", raw_data )
|
|
safe_inv:set_list( "main", { } ) -- reset player's detached inventory
|
|
|
|
minetest.sound_play( "safe_close", { pos = pos, gain = 1.0, max_hear_distance = 5, loop = false } )
|
|
|
|
elseif fields.get then
|
|
local player_inv = player:get_inventory( )
|
|
default.get_contents( safe_inv, player_inv )
|
|
|
|
elseif fields.unlock then
|
|
local meta = minetest.get_meta( pos )
|
|
|
|
if not safe_inv:is_empty( "main" ) then
|
|
minetest.sound_play( "safe_close", { pos = pos, gain = 1.0, max_hear_distance = 5, loop = false } )
|
|
minetest.chat_send_player( player_name, "This safe cannot be unlocked until it is empty." )
|
|
minetest.destroy_form( player_name, minetest.FORMSPEC_SIGSTOP )
|
|
|
|
return
|
|
end
|
|
|
|
meta:set_string( "storage", "" )
|
|
meta:set_string( "infotext", "Unlocked Safe" )
|
|
|
|
minetest.sound_play( "safe_unlock", { pos = pos, gain = 1.0, max_hear_distance = 5, loop = false } )
|
|
minetest.log_status( "%s unlocks digital safe at %s", player_name, minetest.pos_to_string( pos ) )
|
|
minetest.chat_send_player( player_name, "This safe is unlocked. Right-click to set an access code." )
|
|
minetest.destroy_form( player_name, minetest.FORMSPEC_SIGSTOP )
|
|
end
|
|
end
|
|
|
|
minetest.sound_play( "safe_open", { pos = pos, gain = 1.0, max_hear_distance = 5, loop = false } )
|
|
minetest.create_form( nil, player_name, get_formspec( ), on_close )
|
|
end
|
|
|
|
local function open_safe_signin( pos, player_name )
|
|
local digits = { }
|
|
|
|
local function get_formspec( )
|
|
local formspec =
|
|
string.format( "size[4.0,6.3]" ) ..
|
|
default.gui_bg ..
|
|
default.gui_bg_img ..
|
|
|
|
"box[0.0,1.1;3.8,4.5;#222222]" ..
|
|
|
|
"button[0.5,3.4;1.0,1.0;num7;7]" ..
|
|
"button[1.5,3.4;1.0,1.0;num8;8]" ..
|
|
"button[2.5,3.4;1.0,1.0;num9;9]" ..
|
|
"button[0.5,2.4;1.0,1.0;num4;4]" ..
|
|
"button[1.5,2.4;1.0,1.0;num5;5]" ..
|
|
"button[2.5,2.4;1.0,1.0;num6;6]" ..
|
|
"button[0.5,1.4;1.0,1.0;num1;1]" ..
|
|
"button[1.5,1.4;1.0,1.0;num2;2]" ..
|
|
"button[2.5,1.4;1.0,1.0;num3;3]" ..
|
|
"button[0.5,4.4;2.0,1.0;num0;0]" ..
|
|
"button[2.5,4.4;1.0,1.0;clear;C]" ..
|
|
"button[1.0,5.7;2.0,1.0;enter;Enter]" ..
|
|
|
|
string.format( "image[0.0,0.0;1.0,1.0;counter_%s.png]", digits[ 1 ] or "nil" ) ..
|
|
string.format( "image[1.0,0.0;1.0,1.0;counter_%s.png]", digits[ 2 ] or "nil" ) ..
|
|
string.format( "image[2.0,0.0;1.0,1.0;counter_%s.png]", digits[ 3 ] or "nil" ) ..
|
|
string.format( "image[3.0,0.0;1.0,1.0;counter_%s.png]", digits[ 4 ] or "nil" )
|
|
return formspec
|
|
end
|
|
|
|
local function on_close( form, player, fields )
|
|
if fields.quit then return end
|
|
|
|
if fields.enter then
|
|
local meta = minetest.get_meta( pos )
|
|
local access_code = table.concat( digits )
|
|
|
|
if #access_code < 4 then
|
|
minetest.sound_play( "safe_abort", { pos = pos, gain = 0.5, max_hear_distance = 5, loop = false } )
|
|
return
|
|
end
|
|
|
|
if meta:get_string( "storage" ) == "" then
|
|
-- this safe is unlocked, so set access code and create inventory
|
|
local data = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }
|
|
local raw_data = cipher.encrypt_to_base64( minetest.serialize( data ), access_code )
|
|
|
|
meta:set_string( "storage", raw_data )
|
|
meta:set_string( "infotext", "Locked Safe" )
|
|
|
|
minetest.sound_play( "safe_lock", { pos = pos, gain = 1.0, max_hear_distance = 5, loop = false } )
|
|
minetest.log_status( "%s locks digital safe at %s", player_name, minetest.pos_to_string( pos ) )
|
|
minetest.chat_send_player( player_name, "This safe is ready for use. Your access code is " .. access_code .. ". Do not forget this number!" )
|
|
minetest.destroy_form( player_name )
|
|
|
|
else
|
|
-- this safe is locked, so validate the access code
|
|
local raw_data = meta:get_string( "storage" )
|
|
|
|
if not cipher.is_encrypted( raw_data ) then
|
|
minetest.chat_send_player( player_name, "This safe cannot be opened." )
|
|
minetest.destroy_form( player_name )
|
|
|
|
return
|
|
end
|
|
|
|
local data = minetest.deserialize( cipher.decrypt_from_base64( raw_data, access_code ) )
|
|
if not data then
|
|
local safe_id = minetest.hash_node_position( pos )
|
|
|
|
disabled_safes[ safe_id ] = player_name
|
|
minetest.after( 5.0, function ( )
|
|
disabled_safes[ safe_id ] = nil
|
|
end )
|
|
|
|
minetest.sound_play( "safe_error", { pos = pos, gain = 0.5, max_hear_distance = 5, loop = false } )
|
|
minetest.log_status( "%s is denied access to digital safe at %s", player_name, minetest.pos_to_string( pos ) )
|
|
minetest.chat_send_player( player_name, "Incorrect access code." )
|
|
minetest.destroy_form( player_name )
|
|
|
|
return
|
|
end
|
|
|
|
local safe_inv = minetest.get_inventory( { type = "detached", name = player_name .. "_safe" } )
|
|
safe_inv:set_list( "main", data )
|
|
|
|
open_safe_viewer( pos, player_name, access_code, safe_inv )
|
|
end
|
|
|
|
elseif fields.clear then
|
|
digits = { }
|
|
minetest.sound_play( "safe_entry", { pos = pos, gain = 0.5, max_hear_distance = 5, loop = false } )
|
|
minetest.update_form( player_name, get_formspec( ) )
|
|
|
|
else
|
|
local field_name = next( fields, nil ) -- next, since we only care about the name of the first button
|
|
local key = string.match( field_name, "^num([0-9])$" )
|
|
|
|
if key and #digits < 4 then
|
|
table.insert( digits, key )
|
|
minetest.sound_play( "safe_entry", { pos = pos, gain = 0.5, max_hear_distance = 5, loop = false } )
|
|
minetest.update_form( player_name, get_formspec( ) )
|
|
else
|
|
minetest.sound_play( "safe_abort", { pos = pos, gain = 0.5, max_hear_distance = 5, loop = false } )
|
|
end
|
|
end
|
|
end
|
|
|
|
minetest.create_form( nil, player_name, get_formspec( ), on_close )
|
|
end
|
|
|
|
minetest.register_on_joinplayer( function( player )
|
|
local player_name = player:get_player_name( )
|
|
local safe_inv = minetest.create_detached_inventory( player_name .. "_safe", { }, player_name )
|
|
|
|
safe_inv:set_size( "main", 16 )
|
|
end )
|
|
|
|
minetest.register_node( "safety_deposit:digital_safe", {
|
|
-- https://gitlab.com/VanessaE/currency/
|
|
description = ( "Digital Safe" ),
|
|
inventory_image = "safe_front.png",
|
|
paramtype = "light",
|
|
paramtype2 = "facedir",
|
|
tiles = {
|
|
"safe_side.png",
|
|
"safe_side.png",
|
|
"safe_side.png",
|
|
"safe_side.png",
|
|
"safe_side.png",
|
|
"safe_front.png",
|
|
},
|
|
is_ground_content = false,
|
|
groups = { cracky = 1, level = 3 },
|
|
|
|
can_dig = function ( pos, player )
|
|
local meta = minetest.get_meta( pos )
|
|
return meta:get_string( "storage" ) == "" -- allow dig only if safe is empty (and thus unlocked)
|
|
end,
|
|
on_construct = function ( pos )
|
|
local meta = minetest.get_meta( pos )
|
|
meta:get_inventory( ):set_size( "main", 8 * 2 )
|
|
meta:set_string( "infotext", "Unlocked Safe" )
|
|
meta:set_string( "oldtime", os.time( ) )
|
|
end,
|
|
after_place_node = function ( pos, player )
|
|
local player_name = player:get_player_name( )
|
|
minetest.chat_send_player( player_name, "This safe is unlocked. Right-click to set an access code." )
|
|
end,
|
|
on_rightclick = function ( pos, node, clicker )
|
|
local player_name = clicker:get_player_name( )
|
|
local safe_id = minetest.hash_node_position( pos )
|
|
|
|
if disabled_safes[ safe_id ] == player_name then
|
|
minetest.chat_send_player( player_name, "This safe has been disabled. Please try again later." )
|
|
else
|
|
open_safe_signin( pos, player_name )
|
|
end
|
|
end,
|
|
} )
|