minetest_easyvend/easyvend.lua

1123 lines
43 KiB
Lua
Raw Normal View History

2012-09-12 14:58:46 -07:00
---
--vendor
--Copyright (C) 2012 Bad_Command
2014-11-22 15:19:44 -08:00
--Rewrited by Andrej
2012-09-12 14:58:46 -07:00
--
--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
---
2012-09-12 14:55:10 -07:00
-- TODO: Improve mod compability
local slots_max = 31
2016-09-03 08:31:11 -07:00
2016-09-02 11:38:32 -07:00
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
2016-09-02 11:38:32 -07:00
-- 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
2016-09-02 19:21:01 -07:00
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
2016-09-02 19:21:01 -07:00
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})
2016-09-02 19:21:01 -07:00
if count >= minimum then
return true, get_items
2016-09-02 19:21:01 -07:00
end
end
end
end
return false
else
-- Normal Minetest check
return inventory:contains_item(listname, ItemStack(itemtable))
end
end
2016-09-02 11:38:32 -07:00
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
2016-09-02 06:49:28 -07:00
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
2016-09-06 00:30:11 -07:00
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
2016-09-01 12:56:09 -07:00
easyvend.set_formspec = function(pos, player)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
2012-09-12 14:55:10 -07:00
local description = minetest.registered_nodes[node.name].description;
2012-09-12 14:55:10 -07:00
local number = meta:get_int("number")
local cost = meta:get_int("cost")
2016-09-06 02:50:42 -07:00
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
2012-09-12 14:55:10 -07:00
2016-09-05 23:50:05 -07:00
local numbertext, costtext, buysellbuttontext
local itemcounttooltip = "Item count (append “s” to multiply with maximum stack size)"
2016-09-02 06:49:28 -07:00
local buysell = easyvend.buysell(node.name)
if buysell == "sell" then
numbertext = "Offered item"
costtext = "Price"
buysellbuttontext = "Buy"
2016-09-02 06:49:28 -07:00
elseif buysell == "buy" then
numbertext = "Requested item"
costtext = "Payment"
buysellbuttontext = "Sell"
2016-09-02 06:49:28 -07:00
else
return
end
2016-09-02 08:37:03 -07:00
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
2016-09-02 06:49:28 -07:00
2016-09-06 02:50:42 -07:00
-- 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) .. ";]"
2016-09-02 13:28:47 -07:00
.."textarea[2.8,1.3;5.6,2;;Message: " .. minetest.formspec_escape(message) .. ";]"
2012-09-12 14:55:10 -07:00
.."label[0,-0.15;"..numbertext.."]"
.."label[0,1.2;"..costtext.."]"
.."list[current_player;main;0,3.5;8,4;]"
2016-09-06 02:50:42 -07:00
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;]"
2016-09-02 16:30:01 -07:00
.."listring[current_player;main]"
.."listring[current_name;item]"
.."field[1.3,0.65;1.5,1;number;;" .. number .. "]"
2016-09-05 23:50:05 -07:00
.."tooltip[number;"..itemcounttooltip.."]"
.."field[1.3,1.95;1.5,1;cost;;" .. cost .. "]"
2016-09-05 23:50:05 -07:00
.."tooltip[cost;"..itemcounttooltip.."]"
.."button[6,2.8;2,0.5;save;Confirm]"
2016-09-05 23:50:05 -07:00
.."tooltip[save;Confirm configuration and activate machine (only for owner)]"
2016-09-02 16:25:03 -07:00
local weartext, weartooltip
if buysell == "buy" then
2016-09-02 20:16:01 -07:00
weartext = "Accept worn tools"
2016-09-05 23:50:05 -07:00
weartooltip = "If disabled, only tools in perfect condition will be bought from sellers (only settable by owner)"
2016-09-02 16:25:03 -07:00
else
2016-09-02 20:16:01 -07:00
weartext = "Sell worn tools"
2016-09-05 23:50:05 -07:00
weartooltip = "If disabled, only tools in perfect condition will be sold (only settable by owner)"
2016-09-02 16:25:03 -07:00
end
2016-09-06 02:50:42 -07:00
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]"
2016-09-05 23:50:05 -07:00
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.."]"
2016-09-02 19:48:43 -07:00
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
2016-09-02 20:16:01 -07:00
weartext = "Warning: Might sell worn tools."
2016-09-02 19:48:43 -07:00
end
if weartext ~= nil then
formspec = formspec .."textarea[2.3,2.6;3,1;;"..minetest.formspec_escape(weartext)..";]"
end
end
end
2012-09-12 14:55:10 -07:00
meta:set_string("formspec", formspec)
2012-09-12 14:55:10 -07:00
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})
2016-09-01 21:42:43 -07:00
return true
elseif node.name == "easyvend:depositor_on" then
easyvend.sound_disable(pos)
minetest.swap_node(pos, {name="easyvend:depositor", param2 = node.param2})
2016-09-01 21:42:43 -07:00
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})
2016-09-01 21:42:43 -07:00
return true
elseif node.name == "easyvend:depositor" then
easyvend.sound_setup(pos)
minetest.swap_node(pos, {name="easyvend:depositor_on", param2 = node.param2})
2016-09-01 21:42:43 -07:00
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)
2016-09-02 05:52:33 -07:00
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")
2016-09-02 19:21:01 -07:00
local check_wear = meta:get_int("wear") == 0
2016-09-02 11:38:32 -07:00
local chestdef = registered_chests[chest.name]
2016-09-06 02:50:42 -07:00
local chest_meta, chest_inv
2016-09-02 05:52:33 -07:00
2016-09-02 11:38:32 -07:00
if chestdef then
2016-09-06 02:50:42 -07:00
chest_meta = minetest.get_meta({x=pos.x,y=pos.y-1,z=pos.z})
chest_inv = chest_meta:get_inventory()
2016-09-02 11:38:32 -07:00
if ( chest_meta:get_string(chestdef.meta_owner) == machine_owner and chest_inv ~= nil ) then
2016-09-02 06:49:28 -07:00
local buysell = easyvend.buysell(node.name)
2016-09-06 02:50:42 -07:00
local checkstack, checkitem
if buysell == "buy" then
checkitem = easyvend.currency
2016-09-06 02:50:42 -07:00
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()
local maxnumber = number_stack_max * slots_max
if number >= 1 and number <= maxnumber and cost >= 1 and cost <= maxcost then
2016-09-09 11:57:07 -07:00
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
2016-09-02 05:06:02 -07:00
active = false
2016-09-03 08:01:36 -07:00
status = "No room in the machines storage!"
end
2016-09-09 11:57:07 -07:00
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
2016-09-02 05:06:02 -07:00
active = false
2016-09-03 08:01:36 -07:00
status = "No room in the machines storage!"
end
2016-09-09 11:57:07 -07:00
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
2016-09-02 20:57:23 -07:00
status = "Awaiting configuration by owner."
end
else
2016-09-06 02:50:42 -07:00
meta:set_int("stock", 0)
active = false
2016-09-03 08:01:36 -07:00
status = "Storage cant be accessed because it is owned by a different person!"
end
else
2016-09-06 02:50:42 -07:00
meta:set_int("stock", 0)
active = false
2016-09-02 08:41:09 -07:00
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
2016-09-02 08:37:03 -07:00
meta:set_string("status", status)
meta:set_string("infotext", easyvend.make_infotext(node.name, machine_owner, cost, number, itemname))
2016-09-02 06:11:51 -07:00
itemname=itemstack:get_name()
meta:set_string("itemname", itemname)
2016-09-02 05:52:33 -07:00
2016-09-02 08:37:03 -07:00
local change
if node.name == "easyvend:vendor" or node.name == "easyvend:depositor" then
2016-09-02 08:37:03 -07:00
if active then change = easyvend.machine_enable(pos, node) end
elseif node.name == "easyvend:vendor_on" or node.name == "easyvend:depositor_on" then
2016-09-02 08:37:03 -07:00
if not active then change = easyvend.machine_disable(pos, node) end
end
easyvend.set_formspec(pos)
2016-09-02 08:37:03 -07:00
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)
2016-09-06 00:30:11 -07:00
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)
2016-09-02 21:55:43 -07:00
local itemname=""
local oldnumber = meta:get_int("number")
local oldcost = meta:get_int("cost")
local maxnumber = number_stack_max * slots_max
2012-09-12 14:55:10 -07:00
if ( itemstack == nil or itemstack:is_empty() ) then
2016-09-02 20:57:23 -07:00
meta:set_string("status", "Awaiting configuration by owner.")
2016-09-06 00:05:30 -07:00
meta:set_string("message", "No item specified.")
easyvend.sound_error(sender:get_player_name())
2016-09-02 08:37:03 -07:00
easyvend.set_formspec(pos, sender)
return
elseif ( number == nil or number < 1 or number > maxnumber ) then
if maxnumber > 1 then
2016-09-02 08:37:03 -07:00
meta:set_string("message", string.format("Invalid item count; must be between 1 and %d!", maxnumber))
else
2016-09-02 08:37:03 -07:00
meta:set_string("message", "Invalid item count; must be exactly 1!")
end
meta:set_int("number", oldnumber)
2016-09-02 08:37:03 -07:00
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
2016-09-02 08:37:03 -07:00
meta:set_string("message", string.format("Invalid cost; must be between 1 and %d!", maxcost))
else
2016-09-02 08:37:03 -07:00
meta:set_string("message", "Invalid cost; must be exactly 1!")
end
meta:set_int("cost", oldcost)
2016-09-02 08:37:03 -07:00
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
2016-09-02 05:52:33 -07:00
2016-09-06 00:57:55 -07:00
local change = easyvend.machine_check(pos, node)
2016-09-01 21:42:43 -07:00
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
2016-09-02 05:52:33 -07:00
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
2016-09-02 05:52:33 -07:00
local iname = minetest.registered_items[itemstring].description
if iname == nil then iname = itemstring end
2016-09-02 11:53:04 -07:00
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
2016-09-02 11:53:04 -07:00
else
printcost = string.format("%d×%s", cost, easyvend.currency_desc)
2016-09-02 11:53:04 -07:00
end
2016-09-06 00:04:17 -07:00
if nodename == "easyvend:vendor_on" then
2016-09-02 11:53:04 -07:00
d = string.format("Vending machine (owned by %s)\nSelling: %s\nPrice: %s", owner, printitem, printcost)
2016-09-06 00:04:17 -07:00
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
2016-09-02 11:53:04 -07:00
d = string.format("Depositing machine (owned by %s)\nBuying: %s\nPayment: %s", owner, printitem, printcost)
2016-09-06 00:04:17 -07:00
elseif nodename == "easyvend:depositor" then
d = string.format("Inactive depositing machine (owned by %s)\nBuying: %s\nPayment: %s", owner, printitem, printcost)
2016-09-02 05:52:33 -07:00
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
2016-09-02 06:49:28 -07:00
local buysell = easyvend.buysell(node.name)
2012-09-12 14:55:10 -07:00
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
2016-09-01 13:52:30 -07:00
( itemname == nil or itemname=="") then
2016-09-02 08:37:03 -07:00
meta:set_string("status", "Invalid item count or price!")
easyvend.machine_disable(pos, node, sendername)
return
2012-09-12 14:55:10 -07:00
end
2016-09-01 13:52:30 -07:00
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
2016-09-02 11:38:32 -07:00
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()
2016-09-02 11:38:32 -07:00
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=""}
2016-09-02 19:21:01 -07:00
local chest_has, player_has, chest_free, player_free, chest_out, player_out
2016-09-02 05:17:30 -07:00
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)
2016-09-01 13:52:30 -07:00
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
2016-09-02 19:21:01 -07:00
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)
2016-09-06 02:50:42 -07:00
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
2016-09-01 17:16:41 -07:00
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
2016-09-02 08:37:03 -07:00
meta:set_string("message", msg)
elseif easyvend.free_slots(chest_inv, "main") < costfree then
2016-09-03 08:01:36 -07:00
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
2016-09-02 08:37:03 -07:00
meta:set_string("message", "Item bought.")
easyvend.sound_vend(pos)
2016-09-06 02:50:42 -07:00
easyvend.machine_check(pos, node)
end
end
2016-09-01 13:52:30 -07:00
elseif chest_has and player_has then
2016-09-02 08:37:03 -07:00
if not player_free then
2016-09-01 13:52:30 -07:00
msg = "No room in your inventory!"
2016-09-02 08:37:03 -07:00
meta:set_string("message", msg)
easyvend.sound_error(sendername)
2016-09-01 13:52:30 -07:00
elseif not chest_free then
2016-09-03 08:01:36 -07:00
msg = "No room in the machines storage!"
2016-09-02 08:37:03 -07:00
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
2016-09-01 13:52:30 -07:00
end
else
2016-09-02 08:37:03 -07:00
if not chest_has then
2016-09-01 13:52:30 -07:00
msg = "The vending machine has insufficient materials!"
2016-09-02 08:37:03 -07:00
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
2016-09-01 13:52:30 -07:00
elseif not player_has then
2016-09-03 08:01:36 -07:00
msg = "You cant afford this item!"
2016-09-02 08:37:03 -07:00
meta:set_string("message", msg)
easyvend.sound_error(sendername)
2016-09-01 13:52:30 -07:00
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)
2016-09-01 13:52:30 -07:00
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
2016-09-02 19:21:01 -07:00
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)
2016-09-02 09:58:42 -07:00
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)
2016-09-06 02:50:42 -07:00
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)
2016-09-01 17:16:41 -07:00
else
msg = "No room in your inventory!"
end
2016-09-06 01:11:30 -07:00
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
2016-09-02 08:37:03 -07:00
meta:set_string("message", "Item sold.")
easyvend.sound_deposit(pos)
2016-09-06 02:50:42 -07:00
easyvend.machine_check(pos, node)
end
end
2016-09-01 13:52:30 -07:00
elseif chest_has and player_has then
2016-09-02 08:37:03 -07:00
if not player_free then
2016-09-01 13:52:30 -07:00
msg = "No room in your inventory!"
2016-09-02 08:37:03 -07:00
meta:set_string("message", msg)
easyvend.sound_error(sendername)
2016-09-01 13:52:30 -07:00
elseif not chest_free then
2016-09-03 08:01:36 -07:00
msg = "No room in the machines storage!"
2016-09-02 08:37:03 -07:00
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
2016-09-01 13:52:30 -07:00
end
else
2016-09-02 08:37:03 -07:00
if not player_has then
2016-09-01 13:52:30 -07:00
msg = "You have insufficient materials!"
2016-09-02 08:37:03 -07:00
meta:set_string("message", msg)
easyvend.sound_error(sendername)
2016-09-02 08:37:03 -07:00
elseif not chest_has then
msg = "The depositing machine is out of money!"
meta:set_string("status", msg)
easyvend.machine_disable(pos, node, sendername)
2016-09-01 13:52:30 -07:00
end
end
end
else
2016-09-03 08:01:36 -07:00
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
2016-09-02 08:41:09 -07:00
meta:set_string("status", "No storage; machine needs a locked chest below it.")
easyvend.machine_disable(pos, node, sendername)
end
end
2016-09-02 08:37:03 -07:00
easyvend.set_formspec(pos, sender)
2012-09-12 14:55:10 -07:00
end
2016-09-01 12:56:09 -07:00
easyvend.after_place_node = function(pos, placer)
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
2014-11-22 15:19:44 -08:00
local player_name = placer:get_player_name()
inv:set_size("item", 1)
inv:set_size("gold", 1)
inv:set_stack( "gold", 1, easyvend.currency )
2016-09-01 15:19:45 -07:00
local d = ""
if node.name == "easyvend:vendor" then
2016-09-06 00:04:17 -07:00
d = string.format("Inactive vending machine (owned by %s)", player_name)
2016-09-02 21:00:26 -07:00
meta:set_int("wear", 1)
2016-09-01 15:19:45 -07:00
elseif node.name == "easyvend:depositor" then
2016-09-06 00:04:17 -07:00
d = string.format("Inactive depositing machine (owned by %s)", player_name)
2016-09-02 21:00:26 -07:00
meta:set_int("wear", 0)
2016-09-01 15:19:45 -07:00
end
meta:set_string("infotext", d)
2016-09-02 08:37:03 -07:00
local chest = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z})
2016-09-06 01:32:39 -07:00
meta:set_string("status", "Awaiting configuration by owner.")
2016-09-02 08:37:03 -07:00
meta:set_string("message", "Welcome! Please prepare the machine.")
2014-11-22 15:19:44 -08:00
meta:set_int("number", 1)
meta:set_int("cost", 1)
2016-09-06 02:50:42 -07:00
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", "")
2012-09-12 14:55:10 -07:00
2016-09-01 15:19:45 -07:00
meta:set_string("owner", player_name or "")
2016-09-01 12:56:09 -07:00
easyvend.set_formspec(pos, placer)
2012-09-12 14:55:10 -07:00
end
2016-09-01 12:56:09 -07:00
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
2014-11-22 15:19:44 -08:00
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});
2016-09-02 11:38:32 -07:00
if registered_chests[chest.name] then
2014-11-22 15:19:44 -08:00
if player and player:is_player() then
local owner_chest = meta_chest:get_string(registered_chests[chest.name].meta_owner)
2014-11-22 15:19:44 -08:00
if name == owner_chest then
return true --chest owner can also dig shop
2014-11-22 15:19:44 -08:00
end
end
return false
else
return true --if no chest, enyone can dig this shop
end
2012-09-12 14:55:10 -07:00
end
2016-09-01 12:56:09 -07:00
easyvend.on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
2016-09-02 16:25:03 -07:00
local node = minetest.get_node(pos)
local owner = meta:get_string("owner")
2016-09-02 21:55:43 -07:00
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
2016-09-06 00:56:50 -07:00
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
2016-09-02 16:25:03 -07:00
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
2016-09-02 16:25:03 -07:00
if easyvend.buysell(node.name) == "buy" then
meta:set_string("message", "Used tools are now rejected.")
else
2016-09-03 08:01:36 -07:00
meta:set_string("message", "Used tools wont be sold anymore.")
2016-09-02 16:25:03 -07:00
end
meta:set_int("wear", 0)
end
easyvend.set_formspec(pos, sender)
return
else
2016-09-06 00:56:50 -07:00
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)
2012-09-12 14:55:10 -07:00
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.",
2016-09-03 08:01:36 -07:00
"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)
2016-09-06 06:13:46 -07:00
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
2016-09-01 13:01:39 -07:00
easyvend.sound_error = function(playername)
2016-09-02 11:11:03 -07:00
minetest.sound_play("easyvend_error", {to_player = playername, gain = 0.25})
2016-09-01 13:01:39 -07:00
end
2016-09-01 21:42:43 -07:00
easyvend.sound_setup = function(pos)
2016-09-02 11:11:03 -07:00
minetest.sound_play("easyvend_activate", {pos = pos, gain = 0.5, max_hear_distance = 12,})
end
easyvend.sound_disable = function(pos)
2016-09-02 11:11:03 -07:00
minetest.sound_play("easyvend_disable", {pos = pos, gain = 0.9, max_hear_distance = 12,})
end
2016-09-01 12:56:09 -07:00
easyvend.sound_vend = function(pos)
2016-09-02 11:11:03 -07:00
minetest.sound_play("easyvend_vend", {pos = pos, gain = 0.4, max_hear_distance = 5,})
2012-09-12 14:55:10 -07:00
end
2016-09-01 12:56:09 -07:00
easyvend.sound_deposit = function(pos)
2016-09-02 11:11:03 -07:00
minetest.sound_play("easyvend_deposit", {pos = pos, gain = 0.4, max_hear_distance = 5,})
2016-09-01 12:35:50 -07:00
end
2012-09-12 14:55:10 -07:00
2016-09-01 12:56:09 -07:00
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
2012-09-12 14:55:10 -07:00
2016-09-01 12:56:09 -07:00
easyvend.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return 0
2012-09-12 14:55:10 -07:00
end
2016-09-01 12:56:09 -07:00
easyvend.allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0
2014-11-22 15:19:44 -08:00
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
})
2016-09-09 12:05:25 -07:00
-- 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
2016-09-09 12:05:25 -07:00
-- Replace vendor nodes
minetest.register_lbm({
name = "easyvend:replace_vendor",
nodenames = { "vendor:vendor", "vendor:depositor" },
run_at_every_load = true,
action = function(pos, node)
-- Replace node
2016-09-09 12:05:25 -07:00
local newnodename
if node.name == "vendor:vendor" then
newnodename = "easyvend:vendor"
elseif node.name == "vendor:depositor" then
newnodename = "easyvend:depositor"
end
2016-09-09 13:42:15 -07:00
-- Remove axis rotation; only allow 4 facedirs
local p2 = math.fmod(node.param2, 4)
minetest.swap_node(pos, { name = newnodename, param2 = p2 })
-- Initialize metadata
local meta = minetest.get_meta(pos)
2016-09-09 12:05:25 -07:00
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)
-- In vendor, all machines accepted worn tools
meta:set_int("wear", 1)
-- Set item
2016-09-09 12:05:25 -07:00
local itemname = meta:get_string("itemname")
if itemname == "" or itemname == nil then
itemname = meta:get_string("itemtype")
end
2016-09-09 12:05:25 -07:00
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
end
if itemname ~= "" and itemname ~= nil then
inv:set_stack("item", 1, itemname)
meta:set_string("itemname", itemname)
end
-- Check for valid item, item count and price
2016-09-09 12:05:25 -07:00
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
-- Final initialization stuff
meta:set_int("configmode", configmode)
2016-09-09 12:05:25 -07:00
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_string("status", "Initializing …")
meta:set_string("message", "Upgrade successful.")
2016-09-09 12:05:25 -07:00
easyvend.machine_check(pos, node)
end,
})
end