1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213 |
- easyvend = easyvend or {}
- easyvend.modpath = minetest.get_modpath("easyvend")
- -- Localize for performance.
- local math_floor = math.floor
- local traversable_node_types = easyvend.traversable_node_types
- local registered_chests = easyvend.registered_chests
- local currency_types = {}
- local initial_currency = 1
- for k, v in ipairs(currency.note_names) do
- table.insert(currency_types, v)
- end
- -- Maximum price that can be configured on a vendor or depositor.
- local maxcost = 1000000
- local slots_max = 30
- -- Allow for other mods to register custom chests
- easyvend.register_chest = function(node_name, inv_list, meta_owner)
- easyvend.registered_chests[node_name] = { inv_list = inv_list, meta_owner = meta_owner }
- easyvend.traversable_node_types[node_name] = true
- 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
- 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)
- local meta = minetest.get_meta(pos)
- local node = minetest.get_node(pos)
- local description = utility.get_short_desc(minetest.reg_ns_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.formspec.get_form_colors() .. default.formspec.get_form_image() .. default.formspec.get_slot_colors()
- end
- local numbertext, costtext, buysellbuttontext
- local itemcounttooltip = "Item count"
- 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;]"
-
- local machine_currency = meta:get_string("machine_currency")
- 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;" .. machine_currency .. ";easyvend_currency_image;]"
- .."list[context;item;0,0.35;1,1;]"
- .."listring[current_player;main]"
- .."listring[context;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 = "Buy 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;" .. machine_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
- else
- if buysell == "sell" then
- weartext = "Warning: Might sell worn tools."
- else
- weartext = "Worn tools are bought, too."
- end
- 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.upgrade_currency = function(pos, meta, old_currency, old_cost)
- if old_currency == "default:gold_ingot" then
- -- Upgrade gold to currency at 1 to 25. This is a fixed exchange rate.
- meta:set_string("machine_currency", "currency:minegeld_5")
- meta:set_int("cost", math_floor((old_cost * 25) / 5))
- return ("currency:minegeld_5"), math_floor((old_cost * 25) / 5)
- end
- return old_currency, old_cost
- end
- easyvend.machine_check = function(pos, node)
- local active = true
- local status = "Ready."
- local meta = minetest.get_meta(pos)
- local machine_owner = meta:get_string("owner")
- local itemname = meta:get_string("itemname")
- local number = meta:get_int("number")
- local check_wear = meta:get_int("wear") == 0
- local inv = meta:get_inventory()
- local itemstack = inv:get_stack("item", 1)
- local buysell = easyvend.buysell(node.name)
-
- local machine_currency = meta:get_string("machine_currency")
- local cost = meta:get_int("cost")
- -- If the machine uses a depreciated currency, this will upgrade it using a fixed exchange rate.
- machine_currency, cost = easyvend.upgrade_currency(pos, meta, machine_currency, cost)
- local chest_pos_remove, chest_error_remove, chest_pos_add, chest_error_add
- if buysell == "sell" then
- -- Vending machine.
- chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, true)
- chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, false)
- else
- -- Depositing machine.
- chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, true)
- chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, false)
- end
- if chest_pos_remove and chest_pos_add then
- local rchest, rchestdef, rchest_meta, rchest_inv
- rchest = minetest.get_node(chest_pos_remove)
- rchestdef = registered_chests[rchest.name]
- rchest_meta = minetest.get_meta(chest_pos_remove)
- rchest_inv = rchest_meta:get_inventory()
- local checkstack, checkitem
- if buysell == "buy" then
- checkitem = machine_currency
- else
- checkitem = itemname
- end
- local stock = 0
- -- Count stock
- -- FIXME: Ignore tools with bad wear level
- for i=1,rchest_inv:get_size(rchestdef.inv_list) do
- checkstack = rchest_inv:get_stack(rchestdef.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 not(number >= 1 and number <= maxnumber and cost >= 1 and cost <= maxcost) then
- 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
- active = false
- meta:set_int("stock", 0)
- if chest_error_remove == "no_chest" and chest_error_add == "no_chest" then
- status = "No storage; machine needs to be connected with a locked chest."
- elseif chest_error_remove == "not_owned" or chest_error_add == "not_owned" then
- status = "Storage can’t be accessed because it is owned by a different person!"
- elseif chest_error_remove == "no_stock" then
- if buysell == "sell" then
- status = "The vending machine has insufficient materials!"
- else
- status = "The depositing machine is out of money!"
- end
- elseif chest_error_add == "no_space" then
- status = "No room in the machine’s storage!"
- else
- status = "Unknown error!"
- end
- end
- if meta:get_int("configmode") == 1 then
- active = false
- status = "Awaiting configuration by owner."
- end
- if currency.is_currency(itemname) then
- status = "Cannot treat currency as a directly saleable item!"
- active = false
- end
- -- If the currency type is depreciated, then this warning overrides all others.
- if not currency.is_currency(machine_currency) then
- status = "Machine uses a depreciated currency standard!"
- active = false
- end
- meta:set_string("status", status)
- itemname=itemstack:get_name()
- meta:set_string("itemname", itemname)
- -- Inform remote market system of any changes.
- depositor.update_info(pos, machine_owner, itemname, number, cost, machine_currency, buysell, active)
- 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
- local current_node = minetest.get_node(pos)
- meta:set_string("infotext", easyvend.make_infotext(pos, current_node.name, machine_owner, cost, number, itemname))
- 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
- number = tonumber(number)
- cost = tonumber(cost)
- local itemname=""
- local number_stack_max = 0
- if itemstack and not itemstack:is_empty() then
- itemname = itemstack:get_name()
- number_stack_max = itemstack:get_stack_max()
- end
- 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)
- 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)
- 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)
- return
- end
- meta:set_int("number", number)
- meta:set_int("cost", cost)
- meta:set_string("itemname", itemname)
- meta:set_int("configmode", 0)
- meta:set_string("message", "Configuration successful.")
- 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(pos, nodename, owner, cost, number, itemstring)
- local dname = rename.gpn(owner)
- 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>!)", dname)
- else
- d = string.format("Inactive Depositing Machine (Owned by <%s>!)", dname)
- end
- return d
- end
- local iname = minetest.registered_items[itemstring].description
- if iname == nil then iname = itemstring end
- iname = utility.get_short_desc(iname)
- local printitem, printcost
- if number == 1 then
- printitem = iname
- else
- printitem = string.format("%d×%s", number, iname)
- end
-
- local meta = minetest.get_meta(pos)
- local machine_currency = meta:get_string("machine_currency")
- if currency.is_currency(machine_currency) then
- printcost = currency.get_stack_value(machine_currency, cost) .. " Minegeld"
- else
- printcost = "Depreciated Currency!"
- end
- if nodename == "easyvend:vendor_on" then
- d = string.format("Vending Machine (Owned by <%s>!)\nSelling: %s\nPrice: %s", dname, printitem, printcost)
- elseif nodename == "easyvend:vendor" then
- d = string.format("Inactive Vending Machine (Owned by <%s>!)\nSelling: %s\nPrice: %s", dname, printitem, printcost)
- elseif nodename == "easyvend:depositor_on" then
- d = string.format("Depositing Machine (Owned by <%s>!)\nBuying: %s\nPayment: %s", dname, printitem, printcost)
- elseif nodename == "easyvend:depositor" then
- d = string.format("Inactive Depositing Machine (Owned by <%s>!)\nBuying: %s\nPayment: %s", dname, printitem, printcost)
- end
- return d
- end
- easyvend.execute_trade = function(pos, sender, player_inv, pin, vendor_inv, iin, remote_tax)
- local sendername = sender:get_player_name()
- local meta = minetest.get_meta(pos)
- 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)
- easyvend.set_formspec(pos)
- return
- end
- local machine_currency = meta:get_string("machine_currency")
- local machine_owner = meta:get_string("owner")
- -- Check currency.
- if not currency.is_currency(machine_currency) then
- easyvend.sound_error(sendername)
- minetest.chat_send_player(sendername, "# Server: Shop at " .. rc.pos_to_namestr(pos) .. " uses a depreciated currency, attempting to upgrade!")
- minetest.chat_send_player(sendername, "# Server: If this happens, try to use the shop again and it may work if nothing else is wrong.")
- easyvend.machine_check(pos, node)
- --meta:set_string("status", "Machine uses a depreciated currency standard!")
- --easyvend.machine_disable(pos, node, sendername)
- --easyvend.set_formspec(pos)
- return
- end
- -- Cannot sell or buy currency directly.
- if currency.is_currency(itemname) then
- meta:set_string("status", "Cannot treat currency as a directly saleable item!")
- easyvend.machine_disable(pos, node, sendername)
- easyvend.set_formspec(pos)
- return
- end
- local chest_pos_remove, chest_error_remove, chest_pos_add, chest_error_add
- if buysell == "sell" then
- -- Vending.
- chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, true)
- chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, false)
- else
- -- Depositing.
- chest_pos_remove, chest_error_remove = easyvend.find_connected_chest(machine_owner, pos, machine_currency, check_wear, cost, true)
- chest_pos_add, chest_error_add = easyvend.find_connected_chest(machine_owner, pos, itemname, check_wear, number, false)
- end
- if chest_pos_remove ~= nil and chest_pos_add ~= nil and sender and sender:is_player() then
- local rchest = minetest.get_node(chest_pos_remove)
- local rchestdef = registered_chests[rchest.name]
- local rchest_meta = minetest.get_meta(chest_pos_remove)
- local rchest_inv = rchest_meta:get_inventory()
- local achest = minetest.get_node(chest_pos_add)
- local achestdef = registered_chests[achest.name]
- local achest_meta = minetest.get_meta(chest_pos_add)
- local achest_inv = achest_meta:get_inventory()
- -- If passing a target inventory, redirect operations to it.
- -- This also indicates whether this is a remote trade executed via market.
- local vchest_inv = achest_inv
- local vchest_name = achestdef.inv_list
- if vendor_inv and iin then
- vchest_inv = vendor_inv
- vchest_name = iin
- end
- local stack = {name=itemname, count=number, wear=0, metadata=""}
- local price = currency.get_stack_value(machine_currency, cost)
- local chest_has, player_has, chest_free, player_free, chest_out, player_out
- local msg = ""
- if buysell == "sell" then
- -- Vending.
- local pricewithtax = price
- if vendor_inv then
- pricewithtax = currency.calculate_tax(price, 1, remote_tax)
- end
- chest_has, chest_out = easyvend.check_and_get_items(rchest_inv, rchestdef.inv_list, stack, check_wear)
- player_has = currency.has_cash_amount(player_inv, pin, pricewithtax)
- chest_free = currency.room_for_cash(vchest_inv, vchest_name, price)
- player_free = player_inv:room_for_item(pin, stack)
- if chest_has and player_has and chest_free and player_free then
- if number <= number_stack_max then
- easyvend.machine_enable(pos, node)
- -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
- if check_wear then
- rchest_inv:set_stack(rchestdef.inv_list, chest_out[1].id, "")
- player_inv:add_item(pin, chest_out[1].item)
- else
- stack = rchest_inv:remove_item(rchestdef.inv_list, stack)
- player_inv:add_item(pin, stack)
- end
- -- Transfer cash.
- currency.remove_cash(player_inv, pin, pricewithtax)
- currency.add_cash(vchest_inv, vchest_name, price)
- -- Deliver tax to the colonial government.
- currency.record_tax_income(pricewithtax - price)
- meta:set_string("message", "Item bought.")
- easyvend.sound_vend(pos)
- easyvend.machine_check(pos, node)
- local remote_str = ""
- if vendor_inv then
- remote_str = " remotely"
- end
- minetest.log("action", sendername .. remote_str .. " bought " .. number .. " " ..
- itemname .. " for " .. price .. " minegeld from vending machine owned by " ..
- machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (pricewithtax - price))
- else
- -- Large item counts (multiple stacks)
- local numberstacks = math.modf(number / number_stack_max)
- local numberremainder = math.fmod(number, number_stack_max)
- local numberfree = numberstacks
- if numberremainder > 0 then numberfree = numberfree + 1 end
- if not player_free and easyvend.free_slots(player_inv, pin) < 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 not chest_free and not currency.room_for_cash(vchest_inv, vchest_name, price) 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)
- -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
- if check_wear then
- for o=1, #chest_out do
- rchest_inv:set_stack(rchestdef.inv_list, chest_out[o].id, "")
- end
- else
- for i=1, numberstacks do
- stack.count = number_stack_max
- table.insert(cheststacks, rchest_inv:remove_item(rchestdef.inv_list, stack))
- end
- end
- if numberremainder > 0 then
- stack.count = numberremainder
- table.insert(cheststacks, rchest_inv:remove_item(rchestdef.inv_list, stack))
- end
- if check_wear then
- for o=1, #chest_out do
- player_inv:add_item(pin, chest_out[o].item)
- end
- else
- for i=1, #cheststacks do
- player_inv:add_item(pin, cheststacks[i])
- end
- end
- -- Transfer money.
- currency.remove_cash(player_inv, pin, pricewithtax)
- currency.add_cash(vchest_inv, vchest_name, price)
- -- Deliver tax to the colonial government.
- currency.record_tax_income(pricewithtax - price)
- meta:set_string("message", "Item bought.")
- easyvend.sound_vend(pos)
- easyvend.machine_check(pos, node)
- local remote_str = ""
- if vendor_inv then
- remote_str = " remotely"
- end
- minetest.log("action", sendername .. remote_str .. " bought " .. number .. " " ..
- itemname .. " for " .. price .. " minegeld from vending machine owned by " ..
- machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (pricewithtax - price))
- 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
- -- Depositing.
- local pricewithtax = price
- if vendor_inv then
- pricewithtax = currency.calculate_tax(price, 2, remote_tax)
- end
- chest_has = currency.has_cash_amount(rchest_inv, rchestdef.inv_list, price)
- player_has, player_out = easyvend.check_and_get_items(player_inv, pin, stack, check_wear)
- chest_free = vchest_inv:room_for_item(vchest_name, stack)
- player_free = currency.room_for_cash(player_inv, pin, pricewithtax)
- if chest_has and player_has and chest_free and player_free then
- if number <= number_stack_max then
- easyvend.machine_enable(pos, node)
- -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
- if check_wear then
- player_inv:set_stack(pin, player_out[1].id, "")
- vchest_inv:add_item(vchest_name, player_out[1].item)
- else
- stack = player_inv:remove_item(pin, stack)
- vchest_inv:add_item(vchest_name, stack)
- end
- -- Transfer money.
- currency.remove_cash(rchest_inv, rchestdef.inv_list, price)
- currency.add_cash(player_inv, pin, pricewithtax)
- -- Deliver tax to the colonial government.
- currency.record_tax_income(price - pricewithtax)
- meta:set_string("status", "Ready.")
- meta:set_string("message", "Item sold.")
- easyvend.sound_deposit(pos)
- easyvend.machine_check(pos, node)
- local remote_str = ""
- if vendor_inv then
- remote_str = " remotely"
- end
- minetest.log("action", sendername .. remote_str .. " sold " .. number .. " " ..
- itemname .. " for " .. price .. " minegeld to depositing machine owned by " ..
- machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (price - pricewithtax))
- else
- -- Large item counts (multiple stacks)
- local numberstacks = math.modf(number / number_stack_max)
- local numberremainder = math.fmod(number, number_stack_max)
- local numberfree = numberstacks
- if numberremainder > 0 then numberfree = numberfree + 1 end
- if not player_free and not currency.room_for_cash(player_inv, pin, pricewithtax) then
- msg = "Not enough room in your inventory for payment!"
- meta:set_string("message", msg)
- easyvend.sound_error(sendername)
- elseif not chest_free and easyvend.free_slots(vchest_inv, vchest_name) < numberfree then
- meta:set_string("status", "No room in the machine’s storage!")
- easyvend.machine_disable(pos, node, sendername)
- else
- easyvend.machine_enable(pos, node)
- -- Remember removed items for transfer
- local playerstacks = {}
- -- Transfer items before transfering cash (this is because cash transfers can use up an unexpected number of free slots).
- if check_wear then
- for o=1, #player_out do
- player_inv:set_stack(pin, player_out[o].id, "")
- end
- else
- for i=1, numberstacks do
- stack.count = number_stack_max
- table.insert(playerstacks, player_inv:remove_item(pin, stack))
- end
- end
- if numberremainder > 0 then
- stack.count = numberremainder
- table.insert(playerstacks, player_inv:remove_item(pin, stack))
- end
- if check_wear then
- for o=1, #player_out do
- vchest_inv:add_item(vchest_name, player_out[o].item)
- end
- else
- for i=1, #playerstacks do
- vchest_inv:add_item(vchest_name, playerstacks[i])
- end
- end
- -- Transfer money.
- currency.remove_cash(rchest_inv, rchestdef.inv_list, price)
- currency.add_cash(player_inv, pin, pricewithtax)
- -- Deliver tax to the colonial government.
- currency.record_tax_income(price - pricewithtax)
- meta:set_string("message", "Item sold.")
- easyvend.sound_deposit(pos)
- easyvend.machine_check(pos, node)
- local remote_str = ""
- if vendor_inv then
- remote_str = " remotely"
- end
- minetest.log("action", sendername .. remote_str .. " sold " .. number .. " " ..
- itemname .. " for " .. price .. " minegeld to depositing machine owned by " ..
- machine_owner .. " at " .. minetest.pos_to_string(pos) .. ", tax was " .. (price - pricewithtax))
- 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
- local status
- meta:set_int("stock", 0)
- if chest_error_remove == "no_chest" and chest_error_add == "no_chest" then
- status = "No storage; machine needs to be connected with a locked chest."
- elseif chest_error_remove == "not_owned" or chest_error_add == "not_owned" then
- status = "Storage can’t be accessed because it is owned by a different person!"
- elseif chest_error_remove == "no_stock" then
- if buysell == "sell" then
- status = "The vending machine has insufficient materials!"
- else
- status = "The depositing machine is out of money!"
- end
- elseif chest_error_add == "no_space" then
- status = "No room in the machine’s storage!"
- else
- status = "Unknown error!"
- end
- meta:set_string("status", status)
- easyvend.sound_error(sendername)
- end
- easyvend.set_formspec(pos)
- end
- -- Executed when player uses formspec on actual vending machine.
- easyvend.on_receive_fields_buysell = function(pos, formname, fields, sender)
- if not fields.buysell then
- return
- end
- return easyvend.execute_trade(pos, sender, sender:get_inventory(), "main", nil, nil, nil)
- 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()
- local dname = rename.gpn(player_name)
- inv:set_size("item", 1)
- inv:set_size("gold", 1)
- local machine_currency = currency_types[initial_currency]
- meta:set_string("machine_currency", machine_currency)
- meta:set_int("machine_currency_idx", initial_currency)
- inv:set_stack( "gold", 1, machine_currency )
- local d = ""
- if node.name == "easyvend:vendor" then
- d = string.format("Inactive Vending Machine (Owned by <%s>!)", dname)
- meta:set_int("wear", 1)
- elseif node.name == "easyvend:depositor" then
- d = string.format("Inactive Depositing Machine (Owned by <%s>!)", dname)
- meta:set_int("wear", 0)
- end
- meta:set_string("infotext", d)
- 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_string("itemname", "")
- meta:set_string("owner", player_name or "")
- meta:set_string("rename", dname)
- easyvend.set_formspec(pos)
- end
- easyvend.can_dig = function(pos, player)
- local meta = minetest.get_meta(pos)
- local name = player:get_player_name()
- local owner = meta:get_string("owner")
- -- Owner can always dig shop
- if owner == name then
- return true
- end
- local chest_pos = easyvend.find_connected_chest(owner, pos)
- local chest, meta_chest
- if chest_pos then
- chest = minetest.get_node(chest_pos)
- meta_chest = minetest.get_meta(chest_pos)
- else
- return true --if no chest, enyone can dig this shop
- end
- 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.easyvend_currency_image then
- if meta:get_int("configmode") == 1 and sendername == owner then
- -- Toggle through possible banknote denominations.
- local idx = meta:get_int("machine_currency_idx") or initial_currency
- idx = idx + 1
- if idx > #currency_types then idx = 1 end
- meta:set_string("machine_currency", currency_types[idx])
- meta:set_int("machine_currency_idx", idx)
- easyvend.set_formspec(pos)
- end
- end
- 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)
- 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)
- return
- else
- meta:set_string("message", "Only the owner may change the configuration.")
- easyvend.sound_error(sendername)
- easyvend.set_formspec(pos)
- return
- end
- elseif fields.buysell then
- easyvend.on_receive_fields_buysell(pos, formname, fields, sender)
- end
- end
- easyvend.sound_error = function(playername)
- minetest.sound_play("easyvend_error", {to_player = playername, gain = 0.25}, true)
- end
- easyvend.sound_setup = function(pos)
- minetest.sound_play("easyvend_activate", {pos = pos, gain = 0.5, max_hear_distance = 12}, true)
- end
- easyvend.sound_disable = function(pos)
- minetest.sound_play("easyvend_disable", {pos = pos, gain = 0.9, max_hear_distance = 12}, true)
- end
- easyvend.sound_vend = function(pos)
- minetest.sound_play("easyvend_vend", {pos = pos, gain = 0.4, max_hear_distance = 5}, true)
- end
- easyvend.sound_deposit = function(pos)
- minetest.sound_play("easyvend_deposit", {pos = pos, gain = 0.4, max_hear_distance = 5}, true)
- end
- --[[ Tower building ]]
- easyvend.is_traversable = function(pos)
- local node = minetest.get_node_or_nil(pos)
- if (node == nil) then
- return false
- end
- return traversable_node_types[node.name] == true
- end
- easyvend.neighboring_nodes = function(pos)
- local check = {
- {x=pos.x, y=pos.y-1, z=pos.z},
- {x=pos.x, y=pos.y+1, z=pos.z},
- }
- local trav = {}
- for i=1,#check do
- if easyvend.is_traversable(check[i]) then
- table.insert(trav, check[i])
- end
- end
- return trav
- end
- easyvend.find_connected_chest = function(owner, pos, nodename, check_wear, amount, removing)
- local nodes = easyvend.neighboring_nodes(pos)
-
- if (#nodes < 1 or #nodes > 2) then
- return nil, "no_chest"
- end
-
- -- Find the stack direction
- local first = nil
- local second = nil
- for i=1,#nodes do
- if ( first == nil ) then
- first = nodes[i]
- else
- second = nodes[i]
- end
- end
- local chest_pos, chest_internal
- if (first ~= nil and second ~= nil) then
- local dy = (first.y - second.y)/2
- chest_pos, chest_internal = easyvend.find_chest(owner, pos, dy, nodename, check_wear, amount, removing)
- if ( chest_pos == nil ) then
- chest_pos, chest_internal = easyvend.find_chest(owner, pos, -dy, nodename, check_wear, amount, removing, chest_internal)
- end
- else
- local dy = first.y - pos.y
- chest_pos, chest_internal = easyvend.find_chest(owner, pos, dy, nodename, check_wear, amount, removing)
- end
- if chest_internal.chests == 0 then
- return nil, "no_chest"
- elseif chest_internal.chests == chest_internal.other_chests then
- return nil, "not_owned"
- elseif removing and chest_internal.stock < 1 then
- return nil, "no_stock"
- elseif not removing and chest_internal.space < 1 then
- return nil, "no_space"
- elseif chest_pos ~= nil then
- return chest_pos
- else
- return nil, "unknown"
- end
- end
- easyvend.find_chest = function(owner, pos, dy, itemname, check_wear, amount, removing, internal)
- pos = {x=pos.x, y=pos.y + dy, z=pos.z}
- if internal == nil then
- internal = {}
- internal.chests = 0
- internal.other_chests = 0
- internal.stock = 0
- internal.space = 0
- end
- local node = minetest.get_node_or_nil(pos)
- if ( node == nil ) then
- return nil, internal
- end
- local chestdef = registered_chests[node.name]
- if (chestdef ~= nil) then
- internal.chests = internal.chests + 1
- local meta = minetest.get_meta(pos)
- if (owner ~= meta:get_string(chestdef.meta_owner)) then
- internal.other_chests = internal.other_chests + 1
- return nil, internal
- end
- local inv = meta:get_inventory()
- if (inv ~= nil) then
- if (itemname ~= nil and amount ~= nil and removing ~= nil and check_wear ~= nil) then
- local chest_has, chest_free
- -- We're going to query the chest to answer two questions:
- -- Does the chest contain the item in the amount requested?
- -- Does the chest contain free slots suitable to store the amount requested?
- if currency.is_currency(itemname) then
- -- Item is a fungible currency, use currency-related functions.
- local value = currency.get_stack_value(itemname, amount)
- chest_free = currency.room_for_cash(inv, chestdef.inv_list, value)
- chest_has = currency.has_cash_amount(inv, chestdef.inv_list, value)
- -- If the chest doesn't have enough space to ADD currency,
- -- we can't safely remove currency, either (due to denomination splitting).
- if not chest_free then
- chest_has = false
- end
- else
- -- Do regular itemstack-style check. Note: as of the current Minetest version,
- -- the raw inv:room_for_item() check works with stacks over the stackmax limit.
- -- The old version of this check also checked for number of free slots,
- -- but that shouldn't be necessary.
- local stack = {name=itemname, count=amount, wear=0, metadata=""}
- chest_has = easyvend.check_and_get_items(inv, chestdef.inv_list, stack, check_wear)
- chest_free = inv:room_for_item(chestdef.inv_list, stack)
- end
- if chest_has then
- internal.stock = internal.stock + 1
- end
- if chest_free then
- internal.space = internal.space + 1
- end
- if (removing and internal.stock == 0) or (not removing and internal.space == 0) then
- return easyvend.find_chest(owner, pos, dy, itemname, check_wear, amount, removing, internal)
- else
- return pos, internal
- end
- end
- end
- elseif (node.name ~= "easyvend:vendor" and node.name~="easyvend:depositor" and node.name~="easyvend:vendor_on" and node.name~="easyvend:depositor_on") then
- return nil, internal
- end
- return easyvend.find_chest(owner, pos, dy, itemname, check_wear, amount, removing, internal)
- end
- -- Pseudo-inventory handling
- 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
- local sn = stack:get_name()
- -- Do not permit currency denominations to be placed in this slot.
- if currency.is_currency(sn) then
- return 0
- end
- inv:set_stack("item", 1, sn)
- meta:set_string("itemname", sn)
- easyvend.set_formspec(pos)
- 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
|