--- --vendor --Copyright (C) 2012 Bad_Command -- --This library is free software; you can redistribute it and/or --modify it under the terms of the GNU Lesser General Public --License as published by the Free Software Foundation; either --version 2.1 of the License, or (at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public --License along with this library; if not, write to the Free Software --Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA --- vendor.traversable_node_types = { "default:chest", "default:chest_locked", "vendor:vendor", "vendor:depositor" } vendor.formspec = function(pos, player) local meta = minetest.env:get_meta(pos) local meta = minetest.env:get_meta(pos) local node = minetest.env:get_node(pos) local description = minetest.registered_nodes[node.name].description; local buysell = "sell" if ( node.name == "vendor:depositor" ) then buysell = "buy" end local number = meta:get_int("number") local cost = meta:get_int("cost") local limit = meta:get_int("limit") local formspec = "size[9,7;]" .."label[0,0;Configure " .. description .. "]" .."field[2,1.5;2,1;number;Count:;" .. number .. "]" .."label[4,1.25;How many to bundle together]" .."field[2,3.0;2,1;cost;Price:;" .. cost .. "]" .."label[4,2.75;Price of a bundle]" .."field[2,4.5;2,1;limit;Sale Limit:;" .. limit .. "]" .."label[4,4.15;Number of bundles to "..buysell.."]" .."label[4,4.45; (0 for no limit)]" .."button[1.5,6;3,0.5;save;Save]" .."button_exit[4.5.5,6;3,0.5;close;Save & Close]" return formspec end vendor.after_place_node = function(pos, placer) local meta = minetest.env:get_meta(pos) meta:set_string("itemtype", "") meta:set_int("number", 0) meta:set_int("cost", 0) meta:set_int("limit", 0) meta:set_string("owner", placer:get_player_name() or "") meta:set_string("formspec", vendor.formspec(pos, placer)) local description = minetest.registered_nodes[minetest.env:get_node(pos).name].description; vendor.disable(pos, "New " .. description) end vendor.can_dig = function(pos,player) local meta = minetest.env:get_meta(pos); local owner = meta:get_string("owner") local name = player:get_player_name() if name == owner then return true end return minetest.get_player_privs(name)["server"] end vendor.on_receive_fields = function(pos, formname, fields, sender) local node = minetest.env:get_node(pos) local description = minetest.registered_nodes[node.name].description; local meta = minetest.env:get_meta(pos) local owner = meta:get_string("owner") if sender:get_player_name() ~= owner then minetest.chat_send_player(sender:get_player_name(), "vendor: Cannot configure machine. The " .. description .. " belongs to " .. owner ..".") return end local number = tonumber(fields.number) local cost = tonumber(fields.cost) local limit = tonumber(fields.limit) if ( number == nil or number < 1 or number > 99) then minetest.chat_send_player(owner, "vendor: Invalid count. You must enter a count between 1 and 99.") vendor.disable(pos, "Misconfigured") return end if ( cost == nil or cost < 0 ) then minetest.chat_send_player(owner, "vendor: Invalid price. You must enter a positive number for the price.") vendor.disable(pos, "Misconfigured") return end if ( limit == nil or limit < 0 ) then minetest.chat_send_player(owner, "vendor: Invalid sales limit. You must enter a positive number (or zero) for the limit.") vendor.disable(pos, "Misconfigured") return end local inv = vendor.find_connected_chest_inv(owner, pos, nil, nil, nil) if ( inv == nil ) then minetest.chat_send_player(owner, "vendor: Inventory is misconfigured. It must be connected in a line to a locked chest that has items to sell") vendor.disable(pos, "No Inventory/Improper Attachments") return end local itemname = nil for i=1,32 do local stack = inv:get_stack("main", i) if stack ~= nil and not stack:is_empty() then itemname = stack:get_name() break end end meta:set_string("itemtype", itemname) meta:set_int("number", number) meta:set_int("cost", cost) meta:set_int("limit", limit) meta:set_int("enabled", 1) meta:set_string("formspec", vendor.formspec(pos, sender)) local buysell = "selling" if ( node.name == "vendor:depositor" ) then buysell = "buying" end minetest.chat_send_player(owner, "vendor: " .. description .. " is now " .. buysell .. " " .. number .. " " .. vendor.get_item_desc(itemname) .. " for " .. cost .. money.currency_name) vendor.sound_activate(pos) vendor.refresh(pos) end vendor.disable = function(pos, desc) vendor.sound_deactivate(pos) local meta = minetest.env:get_meta(pos) local owner = meta:get_string("owner") local description = minetest.registered_nodes[minetest.env:get_node(pos).name].description; if ( desc == nil ) then desc = "Disabled " .. description end meta:set_string("infotext", ""..desc..", Owned By: " .. owner .. "") meta:set_int("enabled", 0) end vendor.refresh = function(pos, err) local meta = minetest.env:get_meta(pos) local node = minetest.env:get_node_or_nil(pos) if ( node == nil ) then return end if ( meta:get_int("enabled") ~= 1 ) then return end local itemtype = meta:get_string("itemtype") local number = meta:get_int("number") local cost = meta:get_int("cost") local owner = meta:get_string("owner") local limit = meta:get_int("limit") local infotext = nil local limit_text = "" if ( limit > 0 ) then limit_text = " (".. limit .. " left)" end if ( err == nil ) then err = "" else err = err .. ": " end local per_text = "" if ( number > 1 ) then local per = math.floor((cost * 100)/number + 0.5) / 100 per_text = " ("..per..money.currency_name.." each)" end if ( node.name == "vendor:vendor" ) then infotext = err .. owner .. " Sells " .. number .. " " .. vendor.get_item_desc(itemtype) .. " for " .. cost .. money.currency_name .. limit_text .. per_text else infotext = err .. owner .. " Buys " .. number .. " " .. vendor.get_item_desc(itemtype) .. " for " .. cost .. money.currency_name .. limit_text .. per_text end if ( meta:get_string("infotext") ~= infotext ) then meta:set_string("infotext", infotext) end end vendor.sound_activate = function(pos) minetest.sound_play("vendor_activate", {pos = pos, gain = 1.0, max_hear_distance = 10,}) end vendor.sound_deactivate = function(pos) minetest.sound_play("vendor_disable", {pos = pos, gain = 1.0, max_hear_distance = 10,}) end vendor.sound_error = function (pos) minetest.sound_play("vendor_error", {pos = pos, gain = 1.0, max_hear_distance = 10,}) end vendor.sound_deposit = function(pos) minetest.sound_play("vendor_deposit", {pos = pos, gain = 1.0, max_hear_distance = 10,}) end vendor.sound_vend = function(pos) minetest.sound_play("vendor_vend", {pos = pos, gain = 1.0, max_hear_distance = 10,}) end vendor.on_punch = function(pos, node, player) local meta = minetest.env:get_meta(pos) local node = minetest.env:get_node_or_nil(pos) if ( node == nil ) then return end local vending = false if ( node.name == "vendor:vendor" ) then vending = true elseif ( node.name == "vendor:depositor" ) then vending = false else return end local player_name = player:get_player_name() local itemtype = meta:get_string("itemtype") local number = meta:get_int("number") local cost = meta:get_int("cost") local owner = meta:get_string("owner") local limit = meta:get_int("limit") local enabled = meta:get_int("enabled") if not money.has_credit(player_name) then minetest.chat_send_player(player_name, "vendor: You don't have credit ('money' privilege).") vendor.sound_error(pos) return end if not money.has_credit(owner) then vendor.refresh(pos, "Account Suspended") vendor.sound_error(pos) return end if ( enabled ~= 1 ) then vendor.sound_error(pos) return end local chest_inv = vendor.find_connected_chest_inv(owner, pos, itemtype, number, vending) if ( chest_inv == nil ) then if ( vending ) then vendor.refresh(pos, "Out of Inventory"); else vendor.refresh(pos, "Storage is Full"); end vendor.sound_error(pos) return end local to_inv = nil local from_inv = nil local to_account = nil local from_account = nil local player_inv = player:get_inventory() if ( vending ) then to_inv = player_inv from_inv = chest_inv to_account = owner from_account = player_name else to_inv = chest_inv from_inv = player_inv to_account = player_name from_account = owner end if not from_inv:contains_item("main", itemtype .. " " .. number ) then minetest.chat_send_player(player_name, "vendor: Not enough (or no) items found to sell") return end if not to_inv:room_for_item("main", itemtype .. " " .. number ) then minetest.chat_send_player(player_name, "vendor: Not enough room to purchase items") vendor.sound_error(pos) return end local err = money.transfer(from_account, to_account, cost) if ( err ~= nil ) then minetest.chat_send_player(player_name, "vendor: Credit transfer failed: " .. err) if ( not vending ) then vendor.refresh(pos, "Out of Credit"); vendor.sound_error(pos) end vendor.sound_error(pos) return end from_inv:remove_item("main", itemtype .. " " .. number) to_inv:add_item("main", itemtype .. " " .. number) if ( vending ) then minetest.chat_send_player(player_name, "vendor: You bought " .. number .." " .. vendor.get_item_desc(itemtype) .. " from " .. owner .. " for " .. cost .. money.currency_name) vendor.sound_vend(pos) else minetest.chat_send_player(player_name, "vendor: You sold " .. number .. " " .. vendor.get_item_desc(itemtype) .. " to " .. owner .. " for " .. cost .. money.currency_name) vendor.sound_deposit(pos) end if ( limit > 0 ) then limit = limit - 1 meta:set_int("limit", limit) if ( limit == 0 ) then vendor.disable(pos, "Sold Out") else vendor.refresh(pos) end end end vendor.get_item_desc = function(nodetype) local itemdef = minetest.registered_items[nodetype] if ( itemdef ~= nil ) then return itemdef["description"] or "Unknown" else return "Unknown" end end vendor.is_traversable = function(pos) local node = minetest.env:get_node_or_nil(pos) if ( node == nil ) then return false end for i=1,#vendor.traversable_node_types do if node.name == vendor.traversable_node_types[i] then return true end end return false end vendor.neighboring_nodes = function(pos) local check = {{x=pos.x+1, y=pos.y, z=pos.z}, {x=pos.x-1, y=pos.y, z=pos.z}, {x=pos.x, y=pos.y+1, z=pos.z}, {x=pos.x, y=pos.y-1, z=pos.z}, {x=pos.x, y=pos.y, z=pos.z+1}, {x=pos.x, y=pos.y, z=pos.z-1}} local trav = {} for i=1,#check do if vendor.is_traversable(check[i]) then trav[#trav+1] = check[i] end end return trav end vendor.find_connected_chest_inv = function(owner, pos, nodename, amount, removing) local nodes = vendor.neighboring_nodes(pos) if ( #nodes < 1 or #nodes > 2 ) then return nil end -- Find the stack direction local first = nil local second = nil for i=1,#nodes do if ( first == nil ) then first = nodes[i] else second = nodes[i] end end if ( first ~= nil and second ~= nil ) then local dx = (first.x - second.x)/2 local dy = (first.y - second.y)/2 local dz = (first.z - second.z)/2 -- make sure they are in a column/row if ( (dx * dx + dy * dy + dz * dz) ~= 1 ) then return nil end local chest_pos = vendor.find_chest_inv(owner, pos, dx, dy, dz, nodename, amount, removing) if ( chest_pos == nil ) then chest_pos = vendor.find_chest_inv(owner, pos, -dx, -dy, -dz, nodename, amount, removing) end return chest_pos else local dx = first.x - pos.x local dy = first.y - pos.y local dz = first.z - pos.z return vendor.find_chest_inv(owner, pos, dx, dy, dz, nodename, amount, removing) end end vendor.find_chest_inv = function(owner, pos, dx, dy, dz, nodename, amount, removing) pos = {x=pos.x + dx, y=pos.y + dy, z=pos.z + dz} local node = minetest.env:get_node_or_nil(pos) if ( node == nil ) then return nil end --node.name == "default:chest" or if ( node.name == "default:chest_locked") then local meta = minetest.env:get_meta(pos) if ( node.name == "default:chest_locked" and owner ~= meta:get_string("owner") ) then return nil end local inv = meta:get_inventory() if ( inv ~= nil ) then if ( nodename ~= nil and amount ~= nil and removing ~= nil) then if ( removing and inv:contains_item("main", nodename .. " " .. amount) ) then return inv elseif ( (not removing) and inv:room_for_item("main", nodename .. " " .. amount) ) then return inv end elseif ( not inv:is_empty("main") ) then return inv end end elseif ( node.name ~= "vendor:vendor" and node.name~="vendor:depositor") then return nil end return vendor.find_chest_inv(owner, pos, dx, dy, dz, nodename, amount, removing) end