diff --git a/README.txt b/README.txt index babee53..c8bba47 100644 --- a/README.txt +++ b/README.txt @@ -1,15 +1,97 @@ -========================================================== -Giftbox Mod v2.1 by sorcerykid +Giftbox Mod v2.2 +By Leslie E. Krause https://forum.minetest.net/viewtopic.php?f=9&t=19133 +Repository +---------------------- -License of source code +Browse source code: + https://bitbucket.org/sorcerykid/giftbox + +Download archive: + https://bitbucket.org/sorcerykid/giftbox/get/master.zip + https://bitbucket.org/sorcerykid/giftbox/get/master.tar.gz + +Revision History +---------------------- + +Version 1.0b (12-Jan-2018) + - initial version within doors mod + +Version 2.0 (31-Aug-2018) + - separated common routines into standalone mod + - included support files for public release + +Version 1.0a (Build 01) + - initial build + +Version 1.1a (Build 02) + - moved configuration settings into separate file + - made infotext of admin giftbox configurable + - made background of admin giftbox configurable + - changed debug log messages into tokenized strings + +Version 2.0b (Build 03) + - merged colored giftboxes from mt_seasons mod + - added aliases for original colored giftboxes + - improved end-user customizations of giftbox + +Version 2.0 (Build 04) + - renamed admin giftbox to "present" for distinction + - created textures for newly registered nodes + - renamed associated texture files of present + - replaced owner meta for present with sender instead + - added protection check to inventory take of present + - disabled formspec for recipient of empty present + - added sender/receiver checks when digging giftbox + - made default infotext of giftbox configurable + - changed owner of giftbox to placer for simplicity + - set default receiver of giftbox during placement + - switched all prior references of owner to receiver + - registered alias for original admin giftbox + +Version 2.1 (Build 05) + - escaped giftbox message in formspec string + - improved validation of giftbox message input + +Version 2.2 (Build 06) + - added dependency for formspecs and ownership mods + - introduced packages with specialized behavior + - created textures for newly registered items + - fixed regex during validation of recipient names + - fixed erroneous checks against unowned nodes + - general code refactoring and reformatting + +Dependencies +---------------------- + +Default Mod (required) + https://github.com/minetest/minetest_game/default + +ActiveFormspecs Mod (required) + https://bitbucket.org/sorcerykid/formspecs + +Basic Ownership Mod (required) + https://bitbucket.org/sorcerykid/ownership + +Compatability +---------------------- + +Minetest 0.4.15+ required + +Installation +---------------------- + + 1) Unzip the archive into the mods directory of your game + 2) Rename the giftbox-master directory to "giftbox" + +Source Code License ---------------------------------------------------------- GNU Lesser General Public License v3 (LGPL-3.0) -Copyright (c) 2016-2017, Leslie E. Krause +Copyright (c) 2016-2018, Leslie E. Krause Original source code by maikerumine: https://github.com/maikerumine/just_test_tribute/blob/master/mods/mt_seasons/nodes.lua @@ -24,9 +106,7 @@ See the GNU Lesser General Public License for more details. http://www.gnu.org/licenses/lgpl-2.1.html - - -License of media (textures, sounds, and models) +Multimedia License (textures, sounds, and models) ---------------------------------------------------------- Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) diff --git a/config.lua b/config.lua index 2a1f1b2..04e150d 100644 --- a/config.lua +++ b/config.lua @@ -2,6 +2,7 @@ giftbox.present_infotext = "Christmas Present" giftbox.present_greeting = "present_greeting.png" +giftbox.package_viewer = "package_viewer.png" giftbox.present_items = { "default:torch 60", @@ -35,8 +36,9 @@ giftbox.present_items = { "default:meselamp 10", } -giftbox.giftbox_message_length_min = 5 -giftbox.giftbox_message_length_max = 150 +giftbox.parcel_public_description = "Parcel" +giftbox.parcel_private_description = "Parcel for %s" + giftbox.giftbox_public_infotext1 = "Gift Box" giftbox.giftbox_public_infotext2 = "'%s'" giftbox.giftbox_private_infotext1 = "Gift Box for %s" @@ -44,27 +46,16 @@ giftbox.giftbox_private_infotext2 = "Dear %s: '%s'" giftbox.giftbox_drops = { -- digging gift box allows for a single drop of items with a given a rarity - { items = { "default:mese" }, rarity = 75 }, - { items = { "protector:protect" }, rarity = 75 }, + { items = { "default:sword_diamond" }, rarity = 50 }, + { items = { "default:sword_bronze" }, rarity = 25 }, - { items = { "default:diamondblock" }, rarity = 50 }, - { items = { "default:goldblock" }, rarity = 50 }, + { items = { "default:pick_diamond" }, rarity = 50 }, + { items = { "default:pick_bronze" }, rarity = 25 }, - { items = { "default:coalblock 10" }, rarity = 25 }, - { items = { "default:obsidian 20" }, rarity = 25 }, - - { items = { "default:sword_diamond" }, rarity = 20 }, - { items = { "default:sword_bronze" }, rarity = 10 }, - - { items = { "default:pick_diamond" }, rarity = 20 }, - { items = { "default:pick_bronze" }, rarity = 10 }, - - { items = { "default:gold_lump 10" }, rarity = 5 }, - { items = { "default:coal_lump 20" }, rarity = 5 }, - - { items = { "default:orange 20" }, rarity = 5 }, - { items = { "default:apple 30" }, rarity = 5 }, + { items = { "default:gold_lump 5" }, rarity = 10 }, + { items = { "default:coal_lump 10" }, rarity = 10 }, -- default drop must be placed last and have rarity of 0 to avoid empty drops { items = { "farming:gingerbread_cookie 5", "farming:candycane 10" }, rarity = 0 }, } + diff --git a/depends.txt b/depends.txt index 4ad96d5..3664608 100644 --- a/depends.txt +++ b/depends.txt @@ -1 +1,3 @@ default +formspecs +ownership diff --git a/init.lua b/init.lua index 0939318..4de4cd6 100644 --- a/init.lua +++ b/init.lua @@ -1,10 +1,10 @@ -------------------------------------------------------- --- Minetest :: Giftbox Mod v2.1 (giftbox) +-- Minetest :: Giftbox Mod v2.2 (giftbox) -- -- See README.txt for licensing and other information. --- Copyright (c) 2016-2017, Leslie Ellen Krause +-- Copyright (c) 2016-2018, Leslie E. Krause -- --- ./games/just_test_tribute/mods/giftbox/init.lua +-- ./games/minetest_game/mods/giftbox/init.lua -------------------------------------------------------- giftbox = { } @@ -30,18 +30,18 @@ local box_colors = { "black", "blue", "cyan", "green", "magenta", "red", "white" minetest.register_node( "giftbox:present", { description = "Gift Box", - tiles = { "present_top.png", "present_bottom.png", "present_side.png", - "present_side.png", "present_side.png", "present_side.png" }, - is_ground_content = false, - groups = { choppy = 2, oddly_breakable_by_hand = 2 }, - sounds = default.node_sound_wood_defaults( ), + tiles = { "present_top.png", "present_bottom.png", "present_side.png", + "present_side.png", "present_side.png", "present_side.png" }, + is_ground_content = false, + groups = { choppy = 2, oddly_breakable_by_hand = 2 }, + sounds = default.node_sound_wood_defaults( ), drop = { }, - after_place_node = function( pos, player ) + after_place_node = function ( pos, player ) minetest.get_meta( pos ):set_string( "infotext", giftbox.present_infotext .. " (placed by " .. player:get_player_name( ) .. ")" ) end, - on_construct = function ( pos ) + on_construct = function ( pos ) local meta = minetest.get_meta( pos ) meta:get_inventory( ):set_size( "main", 1 ) meta:set_string( "oldtime", os.time( ) ) @@ -50,7 +50,7 @@ minetest.register_node( "giftbox:present", { can_dig = function ( pos, player ) return not minetest.is_protected( pos, player:get_player_name( ) ) and default.is_empty( pos ) end, - allow_metadata_inventory_take = function( pos, listname, index, stack, player ) + allow_metadata_inventory_take = function ( pos, listname, index, stack, player ) -- only allow receiver to take item and remove node -- of course, placer can already bypass protection @@ -59,16 +59,16 @@ minetest.register_node( "giftbox:present", { end return stack:get_count( ) end, - allow_metadata_inventory_put = function( pos, listname, index, stack, player ) + allow_metadata_inventory_put = function ( pos, listname, index, stack, player ) return 0 end, - on_metadata_inventory_take = function( pos, listname, index, stack, player ) + on_metadata_inventory_take = function ( pos, listname, index, stack, player ) if default.is_empty( pos ) then minetest.remove_node( pos ) end minetest.log( "action", string.format( default.STATUS_CONTAINER_GET, player:get_player_name( ), "giftbox", minetest.pos_to_string( pos ) ) ) end, - on_open = function( pos, clicker ) + on_open = function ( pos, clicker ) local pname = clicker:get_player_name( ) local spos = pos.x .. "," .. pos.y .. "," .. pos.z @@ -83,10 +83,10 @@ minetest.register_node( "giftbox:present", { "list[nodemeta:%s;main;6,0.5;1,1;]" slot = 0 - for _, name in ipairs( giftbox.present_items ) do + for _, name in ipairs( giftbox.present_items ) do formspec = formspec .. "item_image_button[ " .. ( slot % 8 ) .. "," .. ( math.floor ( slot / 8 ) + 2 ) .. ";1,1;" .. name .. ";add " .. name .. ";]" slot = slot + 1 - end + end return string.format( formspec, spos ) elseif not minetest.is_protected( pos, pname ) and not default.is_empty( pos ) then -- only show formspec if placer remembered to select item @@ -100,11 +100,11 @@ minetest.register_node( "giftbox:present", { default.gui_slots .. "list[nodemeta:%s;main;7,4;1,1;]" .. "list[current_player;main;1,5.5;8,1;]" .. - default.get_hotbar_bg(1,5.5) + default.get_hotbar_bg( 1, 5.5 ) return string.format( formspec, spos ) end end, - on_close = function( pos, player, fields ) + on_close = function ( pos, player, fields ) local fname, item fname = next( fields, nil ) -- use next since we only care about the name of a single button @@ -116,7 +116,156 @@ minetest.register_node( "giftbox:present", { end end end, -}) +} ) + +minetest.register_craftitem( "giftbox:package_unsealed", { + description = "Package (To seal, point to an object and use)", + inventory_image = "package_unsealed.png", + wield_image = "package_unsealed2.png", + groups = { flammable = 3 }, + on_use = function( cur_stack, player, pointed_thing ) + if pointed_thing.type == "object" and not pointed_thing.ref:is_player( ) then + local player_name = player:get_player_name( ) + local player_inv = player:get_inventory( ) + + -- ignore objects that are not builtin-item (sanity check) + local entity = pointed_thing.ref:get_luaentity( ) + if entity.name ~= "__builtin:item" then return end + + local item_name = ItemStack( entity.itemstring ):get_name( ) + if item_name == "giftbox:package_unsealed" or item_name == "giftbox:package_sealed" then + minetest.chat_send_player( player_name, "This item cannot be stored inside a package." ) + return + end + + cur_stack:take_item( ) + pointed_thing.ref:remove( ) + + -- produce a sealed package containing pointed_thing + local new_stack = ItemStack( "giftbox:package_sealed" ) + local meta = new_stack:get_meta( ) + + meta:set_string( "description", giftbox.parcel_public_description .. + " (from " .. player_name .. " on " .. os.date( "%x" ) .. ")" ) + meta:set_string( "timestamp", os.time( ) ) + meta:set_string( "receiver", default.OWNER_NOBODY ) + meta:set_string( "owner", player_name ) + meta:set_string( "is_anonymous", "false" ) + meta:set_string( "itemstring", entity.itemstring ) + + if player_inv:room_for_item( "main", new_stack ) then + player_inv:add_item( "main", new_stack ) + else + minetest.add_item( player:getpos( ), new_stack ) + end + end + return cur_stack + end +} ) + +minetest.register_craftitem( "giftbox:package_sealed", { + inventory_image = "package_sealed.png", + wield_image = "package_sealed2.png", + groups = { flammable = 3, not_in_creative_inventory = 1 }, + on_use = function( itemstack, player, pointed_thing ) + local player_name = player:get_player_name( ) + local meta = itemstack:get_meta( ) + local receiver = meta:get_string( "receiver" ) + local owner = meta:get_string( "owner" ) + + if owner == player_name or minetest.check_player_privs( player_name, { give = true } ) then + local formspec = + "size[8,3.5]" .. + default.gui_bg .. + default.gui_bg_img .. + "checkbox[4.8,1.8;is_anonymous;Anonymous Sender;" .. meta:get_string( "is_anonymous" ) .. "]" .. + "label[0.1,2.0;Recipient:]" .. + "field[1.8,2.4;3.0,0.25;receiver;;" .. receiver .. "]" .. + "label[2.9,0.0;Package Contents:]" .. + "image[3.5,0.6;1,1;gui_furnace_arrow_bg.png^[transformR270]" .. + "item_image_button[2.5,0.6;1,1;giftbox:package_sealed;restore;]" .. + "item_image_button[4.5,0.6;1,1;" .. ItemStack( meta:get_string( "itemstring" ) ):get_name( ) .. ";revert;]" .. + "label[0.1,2.9;Stored on " .. os.date( "%x %X", tonumber( meta:get_string( "timestamp" ) ) ) .. "]" .. + "button_exit[6.0,3.0;2,0.3;save;Save]" + + minetest.create_form( nil, player_name, formspec, function ( _, player, fields ) + + if fields.is_anonymous then + meta:set_string( "is_anonymous", fields.is_anonymous ) + + elseif fields.revert then + local itemstring = meta:get_string( "itemstring" ) + player:set_wielded_item( ItemStack( itemstring ) ) + minetest.destroy_form( player_name ) + + elseif fields.restore then + player:set_wielded_item( "giftbox:package_unsealed" ) + minetest.destroy_form( player_name ) + + elseif fields.save and fields.receiver then + if fields.receiver == owner then + minetest.chat_send_player( owner, "You cannot send a parcel to yourself." ) + return + elseif fields.receiver ~= default.OWNER_NOBODY and not string.find( fields.receiver, "^[-_A-Za-z0-9]+$" ) then + minetest.chat_send_player( owner, "The specified recipient is invalid." ) + return + end + + -- public vs. private letters and parcels + local description = fields.receiver == default.OWNER_NOBODY and + giftbox.parcel_public_description or + string.format( giftbox.parcel_private_description, fields.receiver ) + + if meta:get_string( "is_anonymous" ) == "false" then + description = description .. " (from " .. owner .. " on " .. os.date( "%x", tonumber( meta:get_string( "timestamp" ) ) ) .. ")" + end + + meta:set_string( "description", description ) + meta:set_string( "receiver", fields.receiver ) + + player:set_wielded_item( itemstack ) + end + end ) + + elseif receiver == player_name or receiver == default.OWNER_NOBODY then + local formspec = + "size[9,7]" .. + default.gui_bg .. + default.gui_bg_img .. + "background[0,0;9,6;" .. giftbox.package_viewer .. "]" .. + "label[0.5,0.5;" .. minetest.colorize( "#000000", os.date( "%x %X", tonumber( meta:get_string( "timestamp" ) ) ) ) .. "]" .. + "button_exit[3.0,6.5;3.0,0.3;open;Open Package]" + + if meta:get_string( "is_anonymous" ) == "false" then + formspec = formspec .. "label[3.5,2.5;" .. minetest.colorize( "#000000", owner ) .. "]" + end + if receiver ~= default.OWNER_NOBODY then + formspec = formspec .. "label[3.5,3.0;" .. minetest.colorize( "#000000", receiver ) .. "]" + end + + minetest.create_form( nil, player_name, formspec, function ( _, player, fields ) + if fields.open then + local itemstring = meta:get_string( "itemstring" ) + player:set_wielded_item( ItemStack( itemstring ) ) + -- TODO: record deliveries of letters and parcels in debug log + end + end ) + else + minetest.chat_send_player( player_name, "Access denied. This parcel does not belong to you." ) + end + + return itemstack + end +} ) + +minetest.register_craft( { + output = "giftbox:package_opened", + recipe = { + { "default:paper", "default:paper", "default:paper" }, + { "default:paper", "", "default:paper" }, + { "default:paper", "default:paper", "default:paper" }, + } +} ) for i, color in ipairs( box_colors ) do minetest.register_node( "giftbox:giftbox_" .. color, { @@ -142,7 +291,7 @@ for i, color in ipairs( box_colors ) do drop = { max_items = 1, items = giftbox.giftbox_drops }, - on_dig = function( pos, node, player ) + on_dig = function ( pos, node, player ) local digger = player:get_player_name( ) local receiver = minetest.get_meta( pos ):get_string( "receiver" ) -- local is_protected = minetest.get_meta( pos ):get_string( "is_protected" ) == "true" @@ -161,25 +310,25 @@ for i, color in ipairs( box_colors ) do minetest.remove_node( pos ) end end, - after_place_node = function( pos, player ) + after_place_node = function ( pos, player ) local placer = player:get_player_name( ) or "singleplayer" local meta = minetest.get_meta( pos ) default.set_owner( pos, placer ) - meta:set_string( "receiver", "" ) + meta:set_string( "receiver", default.OWNER_NOBODY ) meta:set_string( "is_anonymous", "false" ) -- initial item string: Gift Box (placed by sorcerykid) meta:set_string( "infotext", giftbox.giftbox_public_infotext1 .. " (from " .. placer .. ")" ) end, - on_open = function( pos, player, fields ) + on_open = function ( pos, player, fields ) local meta = minetest.get_meta( pos ) local formspec = "size[8,3]" .. default.gui_bg .. default.gui_bg_img .. "button_exit[6,2.5;2,0.3;save;Save]" .. - "checkbox[4.5,1.3;is_anonymous;Anonymous Gift;" .. meta:get_string( "is_anonymous" ) .. "]" .. + "checkbox[4.5,1.3;is_anonymous;Anonymous Sender;" .. meta:get_string( "is_anonymous" ) .. "]" .. "label[0.1,0;Personalize your holiday greeting (or leave blank for the default):]" .. "field[0.4,1;7.8,0.25;message;;" .. minetest.formspec_escape( meta:get_string( "message" ) ) .. "]" .. "label[0.1,1.5;Recipient:]" .. @@ -190,7 +339,7 @@ for i, color in ipairs( box_colors ) do return formspec end end, - on_close = function( pos, player, fields ) + on_close = function ( pos, player, fields ) local owner = player:get_player_name( ) local meta = minetest.get_meta( pos ) @@ -214,7 +363,7 @@ for i, color in ipairs( box_colors ) do elseif fields.receiver == owner then minetest.chat_send_player( owner, "You cannot give a gift to yourself." ) return - elseif fields.receiver ~= "" and not string.find( fields.receiver, "^[_A-Za-z0-9]+$" ) then + elseif fields.receiver ~= default.OWNER_NOBODY and not string.find( fields.receiver, "^[-_A-Za-z0-9]+$" ) then minetest.chat_send_player( owner, "The specified recipient is invalid." ) return end diff --git a/textures/package_sealed.png b/textures/package_sealed.png new file mode 100644 index 0000000..67a9138 Binary files /dev/null and b/textures/package_sealed.png differ diff --git a/textures/package_sealed2.png b/textures/package_sealed2.png new file mode 100644 index 0000000..0a2cd16 Binary files /dev/null and b/textures/package_sealed2.png differ diff --git a/textures/package_unsealed.png b/textures/package_unsealed.png new file mode 100644 index 0000000..8fae75c Binary files /dev/null and b/textures/package_unsealed.png differ diff --git a/textures/package_unsealed2.png b/textures/package_unsealed2.png new file mode 100644 index 0000000..20947ca Binary files /dev/null and b/textures/package_unsealed2.png differ diff --git a/textures/package_viewer.png b/textures/package_viewer.png new file mode 100644 index 0000000..7160d6d Binary files /dev/null and b/textures/package_viewer.png differ