1105 lines
43 KiB
Lua
1105 lines
43 KiB
Lua
---
|
||
--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 machine’s 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 machine’s 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 machine’s 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 machine’s 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 can’t 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 machine’s 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 machine’s 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 can’t 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 machine’s 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 can’t 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 won’t 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.",
|
||
"Let’s get this over with …",
|
||
"Item “bought”.",
|
||
"Tit for tat.",
|
||
"Do you realize what you’ve just bought?",
|
||
}
|
||
-- Depositor
|
||
local jokes_depositor = {
|
||
"Thank you, the money started to smell inside.",
|
||
"Money doesn’t 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
|