easyvend/easyvend.lua

1105 lines
43 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
--vendor
--Copyright (C) 2012 Bad_Command
--Rewrited by Andrej
--
--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
---
-- TODO: Improve mod compability
local slots_max = 31
local registered_chests = {}
local cost_stack_max = minetest.registered_items[easyvend.currency].stack_max
local maxcost = cost_stack_max * slots_max
local joketimer_start = 3
-- Allow for other mods to register custom chests
easyvend.register_chest = function(node_name, inv_list, meta_owner)
registered_chests[node_name] = { inv_list = inv_list, meta_owner = meta_owner }
end
-- Partly a wrapper around contains_item, but does special treatment if the item
-- is a tool. Basically checks whether the items exist in the supplied inventory
-- list. If check_wear is true, only counts items without wear.
easyvend.check_and_get_items = function(inventory, listname, itemtable, check_wear)
local itemstring = itemtable.name
local minimum = itemtable.count
if check_wear == nil then check_wear = false end
local get_items = {}
-- Tool workaround
if minetest.registered_tools[itemstring] ~= nil then
local count = 0
for i=1,inventory:get_size(listname) do
local stack = inventory:get_stack(listname, i)
if stack:get_name() == itemstring then
if not check_wear or stack:get_wear() == 0 then
count = count + 1
table.insert(get_items, {id=i, item=stack})
if count >= minimum then
return true, get_items
end
end
end
end
return false
else
-- Normal Minetest check
return inventory:contains_item(listname, ItemStack(itemtable))
end
end
if minetest.get_modpath("default") ~= nil then
easyvend.register_chest("default:chest_locked", "main", "owner")
end
easyvend.free_slots= function(inv, listname)
local size = inv:get_size(listname)
local free = 0
for i=1,size do
local stack = inv:get_stack(listname, i)
if stack:is_empty() then
free = free + 1
end
end
return free
end
easyvend.buysell = function(nodename)
local buysell = nil
if ( nodename == "easyvend:depositor" or nodename == "easyvend:depositor_on" ) then
buysell = "buy"
elseif ( nodename == "easyvend:vendor" or nodename == "easyvend:vendor_on" ) then
buysell = "sell"
end
return buysell
end
easyvend.is_active = function(nodename)
if ( nodename == "easyvend:depositor_on" or nodename == "easyvend:vendor_on" ) then
return true
elseif ( nodename == "easyvend:depositor" or nodename == "easyvend:vendor" ) then
return false
else
return nil
end
end
easyvend.set_formspec = function(pos, player)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local description = minetest.registered_nodes[node.name].description;
local number = meta:get_int("number")
local cost = meta:get_int("cost")
local itemname = meta:get_string("itemname")
local bg = ""
local configmode = meta:get_int("configmode") == 1
if minetest.get_modpath("default") then
bg = default.gui_bg .. default.gui_bg_img .. default.gui_slots
end
local numbertext, costtext, buysellbuttontext
local itemcounttooltip = "Item count (append “s” to multiply with maximum stack size)"
local buysell = easyvend.buysell(node.name)
if buysell == "sell" then
numbertext = "Offered item"
costtext = "Price"
buysellbuttontext = "Buy"
elseif buysell == "buy" then
numbertext = "Requested item"
costtext = "Payment"
buysellbuttontext = "Sell"
else
return
end
local status = meta:get_string("status")
if status == "" then status = "Unknown." end
local message = meta:get_string("message")
if message == "" then message = "No message." end
local status_image
if node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on" then
status_image = "easyvend_status_on.png"
else
status_image = "easyvend_status_off.png"
end
-- TODO: Expose number of items in stock
local formspec = "size[8,7.3;]"
.. bg
.."label[3,-0.2;" .. minetest.formspec_escape(description) .. "]"
.."image[7.5,0.2;0.5,1;" .. status_image .. "]"
.."textarea[2.8,0.2;5.1,2;;Status: " .. minetest.formspec_escape(status) .. ";]"
.."textarea[2.8,1.3;5.6,2;;Message: " .. minetest.formspec_escape(message) .. ";]"
.."label[0,-0.15;"..numbertext.."]"
.."label[0,1.2;"..costtext.."]"
.."list[current_player;main;0,3.5;8,4;]"
if configmode then
local wear = "false"
if meta:get_int("wear") == 1 then wear = "true" end
formspec = formspec
.."item_image_button[0,1.65;1,1;"..easyvend.currency..";easyvend.currency_image;]"
.."list[current_name;item;0,0.35;1,1;]"
.."listring[current_player;main]"
.."listring[current_name;item]"
.."field[1.3,0.65;1.5,1;number;;" .. number .. "]"
.."tooltip[number;"..itemcounttooltip.."]"
.."field[1.3,1.95;1.5,1;cost;;" .. cost .. "]"
.."tooltip[cost;"..itemcounttooltip.."]"
.."button[6,2.8;2,0.5;save;Confirm]"
.."tooltip[save;Confirm configuration and activate machine (only for owner)]"
local weartext, weartooltip
if buysell == "buy" then
weartext = "Accept worn tools"
weartooltip = "If disabled, only tools in perfect condition will be bought from sellers (only settable by owner)"
else
weartext = "Sell worn tools"
weartooltip = "If disabled, only tools in perfect condition will be sold (only settable by owner)"
end
if minetest.registered_tools[itemname] ~= nil then
formspec = formspec .."checkbox[2,2.4;wear;"..minetest.formspec_escape(weartext)..";"..wear.."]"
.."tooltip[wear;"..minetest.formspec_escape(weartooltip).."]"
end
else
formspec = formspec
.."item_image_button[0,1.65;1,1;"..easyvend.currency..";easyvend.currency_image;]"
.."item_image_button[0,0.35;1,1;"..itemname..";item_image;]"
.."label[1,1.85;×" .. cost .. "]"
.."label[1,0.55;×" .. number .. "]"
.."button[6,2.8;2,0.5;config;Configure]"
if buysell == "sell" then
formspec = formspec .. "tooltip[config;Configure offered items and price (only for owner)]"
else
formspec = formspec .. "tooltip[config;Configure requested items and payment (only for owner)]"
end
formspec = formspec .."button[0,2.8;2,0.5;buysell;"..buysellbuttontext.."]"
if minetest.registered_tools[itemname] ~= nil then
local weartext
if meta:get_int("wear") == 0 then
if buysell == "buy" then
weartext = "Only intact tools are bought."
else
weartext = "Only intact tools are sold."
end
elseif buysell == "sell" then
weartext = "Warning: Might sell worn tools."
end
if weartext ~= nil then
formspec = formspec .."textarea[2.3,2.6;3,1;;"..minetest.formspec_escape(weartext)..";]"
end
end
end
meta:set_string("formspec", formspec)
end
easyvend.machine_disable = function(pos, node, playername)
if node.name == "easyvend:vendor_on" then
easyvend.sound_disable(pos)
minetest.swap_node(pos, {name="easyvend:vendor", param2 = node.param2})
return true
elseif node.name == "easyvend:depositor_on" then
easyvend.sound_disable(pos)
minetest.swap_node(pos, {name="easyvend:depositor", param2 = node.param2})
return true
else
if playername ~= nil then
easyvend.sound_error(playername)
end
return false
end
end
easyvend.machine_enable = function(pos, node)
if node.name == "easyvend:vendor" then
easyvend.sound_setup(pos)
minetest.swap_node(pos, {name="easyvend:vendor_on", param2 = node.param2})
return true
elseif node.name == "easyvend:depositor" then
easyvend.sound_setup(pos)
minetest.swap_node(pos, {name="easyvend:depositor_on", param2 = node.param2})
return true
else
return false
end
end
easyvend.machine_check = function(pos, node)
local active = true
local status = "Ready."
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
local meta = minetest.get_meta(pos)
local number = meta:get_int("number")
local cost = meta:get_int("cost")
local inv = meta:get_inventory()
local itemstack = inv:get_stack("item",1)
local itemname=meta:get_string("itemname")
local machine_owner = meta:get_string("owner")
local check_wear = meta:get_int("wear") == 0
local chestdef = registered_chests[chest.name]
local chest_meta, chest_inv
if chestdef then
chest_meta = minetest.get_meta({x=pos.x,y=pos.y-1,z=pos.z})
chest_inv = chest_meta:get_inventory()
if ( chest_meta:get_string(chestdef.meta_owner) == machine_owner and chest_inv ~= nil ) then
local buysell = easyvend.buysell(node.name)
local checkstack, checkitem
if buysell == "buy" then
checkitem = easyvend.currency
else
checkitem = itemname
end
local stock = 0
-- Count stock
-- FIXME: Ignore tools with bad wear level
for i=1,chest_inv:get_size(chestdef.inv_list) do
checkstack = chest_inv:get_stack(chestdef.inv_list, i)
if checkstack:get_name() == checkitem then
stock = stock + checkstack:get_count()
end
end
meta:set_int("stock", stock)
if not itemstack:is_empty() then
local number_stack_max = itemstack:get_stack_max()
if number >= 1 and number < number_stack_max and cost >= 1 and cost <= cost_stack_max then
local stack = {name=itemname, count=number, wear=0, metadata=""}
local price = {name=easyvend.currency, count=cost, wear=0, metadata=""}
local chest_has, chest_free
local coststacks = math.modf(cost / cost_stack_max)
local costremainder = math.fmod(cost, cost_stack_max)
local numberstacks = math.modf(number / number_stack_max)
local numberremainder = math.fmod(number, number_stack_max)
local numberfree = numberstacks
local costfree = coststacks
if numberremainder > 0 then numberfree = numberfree + 1 end
if costremainder > 0 then costfree = costfree + 1 end
if buysell == "sell" then
chest_has = easyvend.check_and_get_items(chest_inv, chestdef.inv_list, stack, check_wear)
chest_free = chest_inv:room_for_item(chestdef.inv_list, price)
if chest_has and chest_free then
if cost <= cost_stack_max and number <= number_stack_max then
active = true
elseif easyvend.free_slots(chest_inv, chestdef.inv_list) < costfree then
active = false
status = "No room in the machines storage!"
end
elseif not chest_has then
active = false
status = "The vending machine has insufficient materials!"
elseif not chest_free then
active = false
status = "No room in the machines storage!"
end
elseif buysell == "buy" then
chest_has = easyvend.check_and_get_items(chest_inv, chestdef.inv_list, price, check_wear)
chest_free = chest_inv:room_for_item(chestdef.inv_list, stack)
if chest_has and chest_free then
if cost <= cost_stack_max and number <= number_stack_max then
active = true
elseif easyvend.free_slots(chest_inv, chestdef.inv_list) < numberfree then
active = false
status = "No room in the machines storage!"
end
elseif not chest_has then
active = false
status = "The depositing machine is out of money!"
elseif not chest_free then
active = false
status = "No room in the machines storage!"
end
end
else
active = false
if buysell == "sell" then
status = "Invalid item count or price."
else
status = "Invalid item count or payment."
end
end
else
active = false
status = "Awaiting configuration by owner."
end
else
meta:set_int("stock", 0)
active = false
status = "Storage cant be accessed because it is owned by a different person!"
end
else
meta:set_int("stock", 0)
active = false
status = "No storage; machine needs a locked chest below it."
end
if meta:get_int("configmode") == 1 then
active = false
status = "Awaiting configuration by owner."
end
if itemname == easyvend.currency and number == cost and active then
local jt = meta:get_int("joketimer")
if jt > 0 then
jt = jt - 1
end
if jt == 0 then
if buysell == "sell" then
meta:set_string("message", "Item bought.")
else
meta:set_string("message", "Item sold.")
end
jt = -1
end
meta:set_int("joketimer", jt)
end
meta:set_string("status", status)
meta:set_string("infotext", easyvend.make_infotext(node.name, machine_owner, cost, number, itemname))
itemname=itemstack:get_name()
meta:set_string("itemname", itemname)
local change
if node.name == "easyvend:vendor" or node.name == "easyvend:depositor" then
if active then change = easyvend.machine_enable(pos, node) end
elseif node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on" then
if not active then change = easyvend.machine_disable(pos, node) end
end
easyvend.set_formspec(pos)
return change
end
easyvend.on_receive_fields_config = function(pos, formname, fields, sender)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local inv_self = meta:get_inventory()
local itemstack = inv_self:get_stack("item",1)
local buysell = easyvend.buysell(node.name)
if fields.config then
meta:set_int("configmode", 1)
local was_active = easyvend.is_active(node.name)
if was_active then
meta:set_string("message", "Configuration mode activated; machine disabled.")
else
meta:set_string("message", "Configuration mode activated.")
end
easyvend.machine_check(pos, node)
return
end
if not fields.save then
return
end
local number = fields.number
local cost = fields.cost
--[[ Convenience function:
When appending “s” or “S” to the number, it is multiplied
by the maximum stack size.
TODO: Expose this in user documentation ]]
local number_stack_max = itemstack:get_stack_max()
local ss = string.sub(number, #number, #number)
if ss == "s" or ss == "S" then
local n = tonumber(string.sub(number, 1, #number-1))
if string.len(number) == 1 then n = 1 end
if n ~= nil then
number = n * number_stack_max
end
end
ss = string.sub(cost, #cost, #cost)
if ss == "s" or ss == "S" then
local n = tonumber(string.sub(cost, 1, #cost-1))
if string.len(cost) == 1 then n = 1 end
if n ~= nil then
cost = n * cost_stack_max
end
end
number = tonumber(number)
cost = tonumber(cost)
local itemname=""
local oldnumber = meta:get_int("number")
local oldcost = meta:get_int("cost")
local maxnumber = number_stack_max * slots_max
if ( itemstack == nil or itemstack:is_empty() ) then
meta:set_string("status", "Awaiting configuration by owner.")
meta:set_string("message", "No item specified.")
easyvend.sound_error(sender:get_player_name())
easyvend.set_formspec(pos, sender)
return
elseif ( number == nil or number < 1 or number > maxnumber ) then
if maxnumber > 1 then
meta:set_string("message", string.format("Invalid item count; must be between 1 and %d!", maxnumber))
else
meta:set_string("message", "Invalid item count; must be exactly 1!")
end
meta:set_int("number", oldnumber)
easyvend.sound_error(sender:get_player_name())
easyvend.set_formspec(pos, sender)
return
elseif ( cost == nil or cost < 1 or cost > maxcost ) then
if maxcost > 1 then
meta:set_string("message", string.format("Invalid cost; must be between 1 and %d!", maxcost))
else
meta:set_string("message", "Invalid cost; must be exactly 1!")
end
meta:set_int("cost", oldcost)
easyvend.sound_error(sender:get_player_name())
easyvend.set_formspec(pos, sender)
return
end
meta:set_int("number", number)
meta:set_int("cost", cost)
itemname=itemstack:get_name()
meta:set_string("itemname", itemname)
meta:set_int("configmode", 0)
if itemname == easyvend.currency and number == cost and cost <= cost_stack_max then
meta:set_string("message", "Configuration successful. I am feeling funny.")
meta:set_int("joketimer", joketimer_start)
meta:set_int("joke_id", easyvend.assign_joke(buysell))
else
meta:set_string("message", "Configuration successful.")
end
local change = easyvend.machine_check(pos, node)
if not change then
if (node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on") then
easyvend.sound_setup(pos)
else
easyvend.sound_disable(pos)
end
end
end
easyvend.make_infotext = function(nodename, owner, cost, number, itemstring)
local d = ""
if itemstring == nil or itemstring == "" or number == 0 or cost == 0 then
if easyvend.buysell(nodename) == "sell" then
d = string.format("Inactive vending machine (owned by %s)", owner)
else
d = string.format("Inactive depositing machine (owned by %s)", owner)
end
return d
end
local iname = minetest.registered_items[itemstring].description
if iname == nil then iname = itemstring end
local printitem, printcost
if number == 1 then
printitem = iname
else
printitem = string.format("%d×%s", number, iname)
end
if cost == 1 then
printcost = easyvend.currency_desc
else
printcost = string.format("%d×%s", cost, easyvend.currency_desc)
end
if nodename == "easyvend:vendor_on" then
d = string.format("Vending machine (owned by %s)\nSelling: %s\nPrice: %s", owner, printitem, printcost)
elseif nodename == "easyvend:vendor" then
d = string.format("Inactive vending machine (owned by %s)\nSelling: %s\nPrice: %s", owner, printitem, printcost)
elseif nodename == "easyvend:depositor_on" then
d = string.format("Depositing machine (owned by %s)\nBuying: %s\nPayment: %s", owner, printitem, printcost)
elseif nodename == "easyvend:depositor" then
d = string.format("Inactive depositing machine (owned by %s)\nBuying: %s\nPayment: %s", owner, printitem, printcost)
end
return d
end
easyvend.on_receive_fields_buysell = function(pos, formname, fields, sender)
local sendername = sender:get_player_name()
local meta = minetest.get_meta(pos)
if not fields.buysell then
return
end
local node = minetest.get_node(pos)
local number = meta:get_int("number")
local cost = meta:get_int("cost")
local itemname=meta:get_string("itemname")
local item=meta:get_inventory():get_stack("item", 1)
local check_wear = meta:get_int("wear") == 0 and minetest.registered_tools[itemname] ~= nil
local buysell = easyvend.buysell(node.name)
local number_stack_max = item:get_stack_max()
local maxnumber = number_stack_max * slots_max
if ( number == nil or number < 1 or number > maxnumber ) or
( cost == nil or cost < 1 or cost > maxcost ) or
( itemname == nil or itemname=="") then
meta:set_string("status", "Invalid item count or price!")
easyvend.machine_disable(pos, node, sendername)
return
end
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
local chestdef = registered_chests[chest.name]
if chestdef and sender and sender:is_player() then
local chest_meta = minetest.get_meta({x=pos.x,y=pos.y-1,z=pos.z})
local chest_inv = chest_meta:get_inventory()
local player_inv = sender:get_inventory()
if ( chest_meta:get_string(chestdef.meta_owner) == meta:get_string("owner") and chest_inv ~= nil and player_inv ~= nil ) then
local stack = {name=itemname, count=number, wear=0, metadata=""}
local price = {name=easyvend.currency, count=cost, wear=0, metadata=""}
local chest_has, player_has, chest_free, player_free, chest_out, player_out
local msg = ""
if buysell == "sell" then
chest_has, chest_out = easyvend.check_and_get_items(chest_inv, "main", stack, check_wear)
player_has, player_out = easyvend.check_and_get_items(player_inv, "main", price, check_wear)
chest_free = chest_inv:room_for_item("main", price)
player_free = player_inv:room_for_item("main", stack)
if chest_has and player_has and chest_free and player_free then
if cost <= cost_stack_max and number <= number_stack_max then
easyvend.machine_enable(pos, node)
player_inv:remove_item("main", price)
if check_wear then
chest_inv:set_stack("main", chest_out[1].id, "")
player_inv:add_item("main", chest_out[1].item)
else
stack = chest_inv:remove_item("main", stack)
player_inv:add_item("main", stack)
end
chest_inv:add_item("main", price)
if itemname == easyvend.currency and number == cost and cost <= cost_stack_max then
meta:set_string("message", easyvend.get_joke(buysell, meta:get_int("joke_id")))
meta:set_int("joketimer", joketimer_start)
else
meta:set_string("message", "Item bought.")
end
easyvend.sound_vend(pos)
easyvend.machine_check(pos, node)
else
-- Large item counts (multiple stacks)
local coststacks = math.modf(cost / cost_stack_max)
local costremainder = math.fmod(cost, cost_stack_max)
local numberstacks = math.modf(number / number_stack_max)
local numberremainder = math.fmod(number, number_stack_max)
local numberfree = numberstacks
local costfree = coststacks
if numberremainder > 0 then numberfree = numberfree + 1 end
if costremainder > 0 then costfree = costfree + 1 end
if easyvend.free_slots(player_inv, "main") < numberfree then
if numberfree > 1 then
msg = string.format("No room in your inventory (%d empty slots required)!", numberfree)
else
msg = "No room in your inventory!"
end
meta:set_string("message", msg)
elseif easyvend.free_slots(chest_inv, "main") < costfree then
meta:set_string("status", "No room in the machines storage!")
easyvend.machine_disable(pos, node, sendername)
else
-- Remember items for transfer
local cheststacks = {}
easyvend.machine_enable(pos, node)
for i=1, coststacks do
price.count = cost_stack_max
player_inv:remove_item("main", price)
end
if costremainder > 0 then
price.count = costremainder
player_inv:remove_item("main", price)
end
if check_wear then
for o=1,#chest_out do
chest_inv:set_stack("main", chest_out[o].id, "")
end
else
for i=1, numberstacks do
stack.count = number_stack_max
table.insert(cheststacks, chest_inv:remove_item("main", stack))
end
end
if numberremainder > 0 then
stack.count = numberremainder
table.insert(cheststacks, chest_inv:remove_item("main", stack))
end
for i=1, coststacks do
price.count = cost_stack_max
chest_inv:add_item("main", price)
end
if costremainder > 0 then
price.count = costremainder
chest_inv:add_item("main", price)
end
if check_wear then
for o=1,#chest_out do
player_inv:add_item("main", chest_out[o].item)
end
else
for i=1,#cheststacks do
player_inv:add_item("main", cheststacks[i])
end
end
meta:set_string("message", "Item bought.")
easyvend.sound_vend(pos)
easyvend.machine_check(pos, node)
end
end
elseif chest_has and player_has then
if not player_free then
msg = "No room in your inventory!"
meta:set_string("message", msg)
easyvend.sound_error(sendername)
elseif not chest_free then
msg = "No room in the machines storage!"
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
end
else
if not chest_has then
msg = "The vending machine has insufficient materials!"
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
elseif not player_has then
msg = "You cant afford this item!"
meta:set_string("message", msg)
easyvend.sound_error(sendername)
end
end
else
chest_has, chest_out = easyvend.check_and_get_items(chest_inv, "main", price, check_wear)
player_has, player_out = easyvend.check_and_get_items(player_inv, "main", stack, check_wear)
chest_free = chest_inv:room_for_item("main", stack)
player_free = player_inv:room_for_item("main", price)
if chest_has and player_has and chest_free and player_free then
if cost <= cost_stack_max and number <= number_stack_max then
easyvend.machine_enable(pos, node)
if check_wear then
player_inv:set_stack("main", player_out[1].id, "")
chest_inv:add_item("main", player_out[1].item)
else
stack = player_inv:remove_item("main", stack)
chest_inv:add_item("main", stack)
end
chest_inv:remove_item("main", price)
player_inv:add_item("main", price)
meta:set_string("status", "Ready.")
if itemname == easyvend.currency and number == cost and cost <= cost_stack_max then
meta:set_string("message", easyvend.get_joke(buysell, meta:get_int("joke_id")))
meta:set_int("joketimer", joketimer_start)
else
meta:set_string("message", "Item sold.")
end
easyvend.sound_deposit(pos)
easyvend.machine_check(pos, node)
else
-- Large item counts (multiple stacks)
local coststacks = math.modf(cost / cost_stack_max)
local costremainder = math.fmod(cost, cost_stack_max)
local numberstacks = math.modf(number / number_stack_max)
local numberremainder = math.fmod(number, number_stack_max)
local numberfree = numberstacks
local costfree = coststacks
if numberremainder > 0 then numberfree = numberfree + 1 end
if costremainder > 0 then costfree = costfree + 1 end
if easyvend.free_slots(player_inv, "main") < costfree then
if costfree > 1 then
msg = string.format("No room in your inventory (%d empty slots required)!", costfree)
else
msg = "No room in your inventory!"
end
meta:set_string("message", msg)
easyvend.sound_error(sendername)
elseif easyvend.free_slots(chest_inv, "main") < numberfree then
easyvend.machine_disable(pos, node, sendername)
else
easyvend.machine_enable(pos, node)
-- Remember removed items for transfer
local playerstacks = {}
for i=1, coststacks do
price.count = cost_stack_max
chest_inv:remove_item("main", price)
end
if costremainder > 0 then
price.count = costremainder
chest_inv:remove_item("main", price)
end
if check_wear then
for o=1,#player_out do
player_inv:set_stack("main", player_out[o].id, "")
end
else
for i=1, numberstacks do
stack.count = number_stack_max
table.insert(playerstacks, player_inv:remove_item("main", stack))
end
end
if numberremainder > 0 then
stack.count = numberremainder
table.insert(playerstacks, player_inv:remove_item("main", stack))
end
for i=1, coststacks do
price.count = cost_stack_max
player_inv:add_item("main", price)
end
if costremainder > 0 then
price.count = costremainder
player_inv:add_item("main", price)
end
if check_wear then
for o=1,#player_out do
chest_inv:add_item("main", player_out[o].item)
end
else
for i=1,#playerstacks do
chest_inv:add_item("main", playerstacks[i])
end
end
meta:set_string("message", "Item sold.")
easyvend.sound_deposit(pos)
easyvend.machine_check(pos, node)
end
end
elseif chest_has and player_has then
if not player_free then
msg = "No room in your inventory!"
meta:set_string("message", msg)
easyvend.sound_error(sendername)
elseif not chest_free then
msg = "No room in the machines storage!"
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
end
else
if not player_has then
msg = "You have insufficient materials!"
meta:set_string("message", msg)
easyvend.sound_error(sendername)
elseif not chest_has then
msg = "The depositing machine is out of money!"
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
end
end
end
else
meta:set_string("status", "Storage cant be accessed because it is owned by a different person!")
easyvend.machine_disable(pos, node, sendername)
end
else
if sender and sender:is_player() then
meta:set_string("status", "No storage; machine needs a locked chest below it.")
easyvend.machine_disable(pos, node, sendername)
end
end
easyvend.set_formspec(pos, sender)
end
easyvend.after_place_node = function(pos, placer)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local player_name = placer:get_player_name()
inv:set_size("item", 1)
inv:set_size("gold", 1)
inv:set_stack( "gold", 1, easyvend.currency )
local d = ""
if node.name == "easyvend:vendor" then
d = string.format("Inactive vending machine (owned by %s)", player_name)
meta:set_int("wear", 1)
elseif node.name == "easyvend:depositor" then
d = string.format("Inactive depositing machine (owned by %s)", player_name)
meta:set_int("wear", 0)
end
meta:set_string("infotext", d)
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
meta:set_string("status", "Awaiting configuration by owner.")
meta:set_string("message", "Welcome! Please prepare the machine.")
meta:set_int("number", 1)
meta:set_int("cost", 1)
meta:set_int("stock", -1)
meta:set_int("configmode", 1)
meta:set_int("joketimer", -1)
meta:set_int("joke_id", 1)
meta:set_string("itemname", "")
meta:set_string("owner", player_name or "")
easyvend.set_formspec(pos, placer)
end
easyvend.can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local name = player:get_player_name()
-- Owner can always dig shop
if meta:get_string("owner") == name then
return true
end
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
local meta_chest = minetest.get_meta({x=pos.x,y=pos.y-1,z=pos.z});
if registered_chests[chest.name] then
if player and player:is_player() then
local owner_chest = meta_chest:get_string(registered_chests[chest.name].meta_owner)
if name == owner_chest then
return true --chest owner can also dig shop
end
end
return false
else
return true --if no chest, enyone can dig this shop
end
end
easyvend.on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local owner = meta:get_string("owner")
local sendername = sender:get_player_name(sender)
if fields.config or fields.save or fields.usermode then
if sender:get_player_name() == owner then
easyvend.on_receive_fields_config(pos, formname, fields, sender)
else
meta:set_string("message", "Only the owner may change the configuration.")
easyvend.sound_error(sendername)
easyvend.set_formspec(pos, sender)
return
end
elseif fields.wear ~= nil then
if sender:get_player_name() == owner then
if fields.wear == "true" then
if easyvend.buysell(node.name) == "buy" then
meta:set_string("message", "Used tools are now accepted.")
else
meta:set_string("message", "Used tools are now for sale.")
end
meta:set_int("wear", 1)
elseif fields.wear == "false" then
if easyvend.buysell(node.name) == "buy" then
meta:set_string("message", "Used tools are now rejected.")
else
meta:set_string("message", "Used tools wont be sold anymore.")
end
meta:set_int("wear", 0)
end
easyvend.set_formspec(pos, sender)
return
else
meta:set_string("message", "Only the owner may change the configuration.")
easyvend.sound_error(sendername)
easyvend.set_formspec(pos, sender)
return
end
elseif fields.buysell then
easyvend.on_receive_fields_buysell(pos, formname, fields, sender)
end
end
-- Jokes: Appear when machine exchanges currency for currency at equal rate
-- Vendor
local jokes_vendor = {
"Thank you. You have made a vending machine very happy.",
"Humans have a strange sense of humor.",
"Lets get this over with …",
"Item “bought”.",
"Tit for tat.",
"Do you realize what youve just bought?",
}
-- Depositor
local jokes_depositor = {
"Thank you, the money started to smell inside.",
"Money doesnt grow on trees, you know?",
"Sanity sold.",
"Well, that was an awkward exchange.",
"Are you having fun?",
"Is this really trading?",
}
easyvend.assign_joke = function(buysell)
local jokes
if buysell == "sell" then
jokes = jokes_vendor
elseif buysell == "buy" then
jokes = jokes_depositor
end
local r = math.random(1,#jokes)
return r
end
easyvend.get_joke = function(buysell, id)
local joke
if buysell == nil or id == nil then
-- Fallback message (should never happen)
return "Items exchanged."
end
if buysell == "sell" then
joke = jokes_vendor[id]
if joke == nil then joke = jokes_vendor[1] end
elseif buysell == "buy" then
joke = jokes_depositor[id]
if joke == nil then joke = jokes_depositor[1] end
end
return joke
end
easyvend.sound_error = function(playername)
minetest.sound_play("easyvend_error", {to_player = playername, gain = 0.25})
end
easyvend.sound_setup = function(pos)
minetest.sound_play("easyvend_activate", {pos = pos, gain = 0.5, max_hear_distance = 12,})
end
easyvend.sound_disable = function(pos)
minetest.sound_play("easyvend_disable", {pos = pos, gain = 0.9, max_hear_distance = 12,})
end
easyvend.sound_vend = function(pos)
minetest.sound_play("easyvend_vend", {pos = pos, gain = 0.4, max_hear_distance = 5,})
end
easyvend.sound_deposit = function(pos)
minetest.sound_play("easyvend_deposit", {pos = pos, gain = 0.4, max_hear_distance = 5,})
end
easyvend.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if listname=="item" then
local meta = minetest.get_meta(pos);
local owner = meta:get_string("owner")
local name = player:get_player_name()
if name == owner then
local inv = meta:get_inventory()
if stack==nil then
inv:set_stack( "item", 1, nil )
else
inv:set_stack( "item", 1, stack:get_name() )
meta:set_string("itemname", stack:get_name())
easyvend.set_formspec(pos, player)
end
end
end
return 0
end
easyvend.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return 0
end
easyvend.allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0
end
minetest.register_abm({
nodenames = {"easyvend:vendor", "easyvend:vendor_on", "easyvend:depositor", "easyvend:depositor_on"},
interval = 5,
chance = 1,
catch_up = false,
action = function(pos, node, active_object_count, active_object_count_wider)
easyvend.machine_check(pos, node)
end
})
-- Legacy support for vendor mod:
-- Transform the world and items to use the easyvend nodes/items
-- For safety reasons, only do this when player requested so
if minetest.setting_getbool("easyvend_convert_vendor") == true then
-- Replace vendor nodes
minetest.register_lbm({
name = "easyvend:replace_vendor",
nodenames = { "vendor:vendor", "vendor:depositor" },
run_at_every_load = true,
action = function(pos, node)
local newnodename
if node.name == "vendor:vendor" then
newnodename = "easyvend:vendor"
elseif node.name == "vendor:depositor" then
newnodename = "easyvend:depositor"
end
minetest.swap_node(pos, { name = newnodename, param2 = node.param2 })
local meta = minetest.get_meta(pos)
meta:set_string("status", "Initializing …")
meta:set_string("message", "Upgrade successful.")
meta:set_int("stock", -1)
meta:set_int("joketimer", -1)
meta:set_int("joke_id", 1)
local inv = meta:get_inventory()
inv:set_size("item", 1)
inv:set_size("gold", 1)
inv:set_stack("gold", 1, easyvend.currency)
local itemname = meta:get_string("itemname")
local configmode = 1
if itemname == "" or itemname == nil then
-- If no itemname set, try to scan first item in chest below
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
if chest.name == "default:chest_locked" then
local chest_meta = minetest.get_meta({x=pos.x,y=pos.y-1,z=pos.z})
local chest_inv = chest_meta:get_inventory()
if ( chest_meta:get_string("owner") == machine_owner and chest_inv ~= nil ) then
end
for i=1,chest_inv:get_size("main") do
checkstack = chest_inv:get_stack("main", i)
if not checkstack:is_empty() then
itemname = checkstack:get_name()
break
end
end
end
if itemname ~= "" and itemname ~= nil then
inv:set_stack("item", 1, itemname)
meta:set_string("itemname", itemname)
end
end
if itemname ~= "" and itemname ~= nil then
local itemstack = inv:get_stack("item", 1)
local number_stack_max = itemstack:get_stack_max()
local maxnumber = number_stack_max * slots_max
local cost = meta:get_int("cost")
local number = meta:get_int("number")
if number >= 1 and number <= maxnumber and cost >= 1 and cost <= maxcost then
configmode = 0
end
end
local owner = meta:get_string("owner")
if easyvend.buysell(newnodename) == "sell" then
meta:set_string("infotext", string.format("Vending machine (owned by %s)", owner))
else
meta:set_string("infotext", string.format("Depositing machine (owned by %s)", owner))
end
meta:set_int("configmode", configmode)
easyvend.machine_check(pos, node)
end,
})
end