Minetest: Digilines Vending Machine
Last Updated: 2024 September 17 (2023 April 16)
A vending machine in Minetest constructed with the Digilines mod and programmed in Lua. This is what Digilines is: https://mesecons.net/digilines.html.
In Minetest, I am a green skeleton that lives deep beneath the surface of the world. The existence I lead is not very solitary.
In the market row of the town at spawn, I have a teleporter. It looks like a yellow phone booth, and people can use it to access my underground lair. One of the points of interests of my lair is a supermarket containing a vending machine.
When I invite visitors, most new players say they don’t have any
money, which isn’t a problem because food is extremely cheap (costs
default:grass
).
One may ask, “How can this be economical?”
Blueberries and wheat as far as Minetest is concerned can be grown in low light areas. Grass, however, requires sunlight. I can farm blueberries and wheat underground, but the game doesn’t let me farm grass. So an easy way to get started on the server I play on is to rip up grass from random areas and sell it to the vending machine.
The Vending Machine
The vending machine is in Subterra Market and to the left of the
point of arrival. You can find the teleporter to the vending machine
at juneland.fr:30000
(-961,
12.5, 1911).
How to Use
The vending machine works a lot like a real one; except, there is no item that represents a reptacle to slide in dollar bills. So the steps to use the vending machine are slightly different.
Each item listed for sale has a price of one
default:grass
Put grass into the Digiline Chest (the wooden chest pointed by the sign “DEPOSIT”).
Interact with the
digistuff:touchscreen
and click the button “Deposit”
After the item finishes traveling in the tube, the screen above the touchscreen should display the item deposited and the quantity.
Open the touchscreen again and place an order by entering a number in one of the fields.
Hit the Enter (or Return) key on your keyboard.
If the price does not surpass the balance, the product should travel in the tube to arrive in the silver chest.
- Open the touchscreen again and click the button “Refund” to return any unspent credits.
Source Code
This machine uses:
- 1 Luacontroller
- 2 Digiline Filter-Injectors
- 2 Digiline Chests
- 3 Digiline LCD
- 1 Digiline Touchscreen (from digistuff)
- however many digilines needed to connect devices
- however many pipes needed to connect chests
See device names starting at line #199
--{{GLOBAL}}--
STORE_CREDIT = "default:grass_1"
-- reports the contents of a list
-- channel intended for LCDs
local function showlist(channel, list, empty_msg)
empty_msg = empty_msg or "Empty List."
local size = table.maxn(list)
if size <= 0 then
digiline_send(channel, empty_msg)
return
end
local longstr= ""
for _, v in ipairs(list) do
longstr = longstr .. v.name .. ", ".. v.count .."; \n"
end
digiline_send(channel, longstr)
end
local function is_in_list(string, list)
for i, v in ipairs(list) do
if v.name == string then
return i;
end
end
return -1
end
local function additem(list, stack)
local index = is_in_list(stack.name, list)
-- if the item is not in the list or the list is empty
if (index < 0 or table.maxn(list) <= 0) and stack.count > 0 then
table.insert(list, stack)
return
else
-- if the item is in the list and it must be updated
list[index].count = list[index].count + stack.count
end
if list[index].count <= 0 then
table.remove(list, index)
end
end
-- use for both payment chest and stock chest
-- payment chest responds to uput or utake
-- stock chest responds to tput or ttake
local function detect(msg, list)
if msg == nil then
return
end
if msg.stack == nil then
return
end
local n = msg.stack.name
local c = msg.stack.count
if msg.action == "uput" then
-- player has put an item stack in the digichest
additem(list, {name=n, count= c})
elseif msg.action == "utake" then
-- player has taken an item
additem(list, {name=n, count= -c})
elseif msg.action == "tput" then
-- a tube has put an item
additem(list, {name=n, count= c})
elseif msg.action == "ttake" then
-- a tube has taken an item
additem(list, {name=n, count= -c})
end
end
-- only take specified item as currency
-- to either load funds or refund
local function process_payment(dfi, list, currency)
local size = table.maxn(list)
if size <= 0 then
return false
end
for i, v in ipairs(list) do
if v.name == currency then
digiline_send(dfi, v)
return true
end
end
end
local function process_sale(dfi, order, itemname)
if itemname == nil then
return "Programming error: var 'itemname' is nil"
end
if tonumber(order[itemname]) == nil then
return "Error: invalid input: not a number."
end
local quantity = math.floor(order[itemname])
if quantity <= 0 then
return "Error: invalid quantity (".. quantity ..")"
end
for i, v in ipairs(mem.stock) do
if v.name == itemname and quantity > v.count then
return "Order quantity exceeds stock!"
end
end
local cost = 1 * quantity
if table.maxn(mem.paid) <= 0 then
-- note: tried to check mem.paid == {} - doesn't work
return "Error: no funds loaded."
end
if mem.paid[1].count < cost then
return "Error: Insufficient funds."
end
-- sale is good to go
digiline_send(dfi, {name=itemname, count=quantity})
additem(mem.paid, {name=mem.paid[1].name, count=-cost})
return "Sale confirmed"
end
-- dfi_receive = customer payment
-- dfi_send = send to customer (refunds, wares)
local function touch_response(msg, dfi_receive, dfi_send)
-- ignore the event when a player closes menu
local lcd_status = "lcd3"
local credit = STORE_CREDIT
if msg["key_enter_field"] == nil and msg["quit"] ~= nil then
return "Hello! Use the touchscreen above to begin."
end
if msg["pay"] ~= nil then
if process_payment(dfi_receive, mem.queue, credit) then
return "Sent payment."
else
return "No payment items detected."
end
elseif msg["refund"] ~= nil then
if process_payment(dfi_send, mem.paid, credit) then
return "Sent refund."
else
return "No payment to return."
end
end
for i, itemstack in ipairs(mem.stock) do
if msg["key_enter_field"] == itemstack.name then
return process_sale(dfi_send, msg, itemstack.name)
end
end
end
local function spawnfield(channel, name, label, default, y)
local height = 1
local width = 8
local n = {}
n.command = "addfield"
n["X"] = 1
n["Y"] = 1 + y
n["W"] = width
n["H"] = height
n["name"] = name
n["label"] = label
n["default"] = default
digiline_send(channel, n)
end
local function touch_init(channel, wares)
local reset = {}
reset.command = "clear"
digiline_send(channel, reset)
local btn_pay = {}
btn_pay.command = "addbutton"
btn_pay["X"] = 1
btn_pay["Y"] = 0
btn_pay["W"] = 4
btn_pay["H"] = 1
btn_pay["name"] = "pay"
btn_pay["label"] = "Deposit"
digiline_send(channel, btn_pay)
local btn_refund = {}
btn_refund.command = "addbutton"
btn_refund["X"] = 5
btn_refund["Y"] = 0
btn_refund["W"] = 4
btn_refund["H"] = 1
btn_refund["name"] = "refund"
btn_refund["label"] = "Refund"
digiline_send(channel, btn_refund)
local guide = {}
guide.command = "addlabel"
guide["X"] = 2.5
guide["Y"] = 1
guide.label = "Press ENTER (or RETURN) key to place order."
digiline_send(channel, guide)
for i, v in ipairs(wares) do
local label = v.name .. " (stock: " .. v.count .. ")"
spawnfield(channel, v.name, label, "0", i)
end
end
local function main()
local ts = "ts" -- touchscreen, order form
local pay = "input" -- player pays w/ digichest
local dfi1 = "accept" -- DFI sends payment from 'pay'
local store = "store" -- digichest holds stock & payment
local dfi2 = "deploy" -- DFI sends wares from stock
local card = "swipe" -- experimental card reader
local lcd_queue = "lcd"
local lcd_paid = "lcd2"
if event.type == "program" then
mem.queue = {} -- items placed in chest
mem.paid = {} -- items paid by customers
mem.stock = {} -- items stocked by owner
touch_init(ts, mem.stock)
digiline_send("lcd", "Initialized!")
digiline_send("lcd2", "Initialized!")
digiline_send("lcd3", "Initialized!")
end
if event.type == "digiline" and event.channel == ts then
local status = touch_response(event.msg, dfi1, dfi2)
touch_init(ts, mem.stock)
digiline_send("lcd3", status)
showlist(lcd_paid, mem.paid, "No funds loaded.")
end
if event.type == "digiline" and event.channel == pay then
detect(event.msg, mem.queue)
showlist(lcd_queue, mem.queue, "Place payment in the chest below.")
end
if event.type == "digiline" and event.channel == store then
if event.msg.action == "uput"
or event.msg.action == "utake" then
-- store stock directly accessed by player
-- (as opposed to vending)
detect(event.msg, mem.stock)
elseif event.msg.action == "tput" then
-- vending, payment confirmation
-- update mem.paid to show current store credits
detect(event.msg, mem.paid)
showlist(lcd_paid, mem.paid, "No funds loaded.")
elseif event.msg.action == "ttake" then
-- vending, update stock after selling
if event.msg.stack.name == STORE_CREDIT then
detect(event.msg, mem.paid)
else
detect(event.msg, mem.stock)
end
showlist(lcd_paid, mem.paid, "No funds loaded.")
-- process_sale already deducts store creditss
-- note: upon refunds, the code will attempt to deduct
-- currency from the stock list
-- however additems() does allow negative quantities to be indexed.
end
touch_init(ts, mem.stock)
end
end
return main()
шкире фи лену меншти.и That’s Lojban for “thanks for reading!” You can leave a comment on my guestbook via e-mail or neocities.