3 --Copyright (C) 2012 Bad_Command
6 --This library is free software; you can redistribute it and/or
7 --modify it under the terms of the GNU Lesser General Public
8 --License as published by the Free Software Foundation; either
9 --version 2.1 of the License, or (at your option) any later version.
11 --This program is distributed in the hope that it will be useful,
12 --but WITHOUT ANY WARRANTY; without even the implied warranty of
13 --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 --GNU General Public License for more details.
16 --You should have received a copy of the GNU Lesser General Public
17 --License along with this library; if not, write to the Free Software
18 --Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 -- TODO: Improve mod compability
24 local registered_chests
= {}
25 local cost_stack_max
= minetest
.registered_items
[easyvend
.currency
].stack_max
26 local maxcost
= cost_stack_max
* slots_max
28 local joketimer_start
= 3
30 -- Allow for other mods to register custom chests
31 easyvend
.register_chest
= function(node_name
, inv_list
, meta_owner
)
32 registered_chests
[node_name
] = { inv_list
= inv_list
, meta_owner
= meta_owner
}
35 -- Partly a wrapper around contains_item, but does special treatment if the item
36 -- is a tool. Basically checks whether the items exist in the supplied inventory
37 -- list. If check_wear is true, only counts items without wear.
38 easyvend
.check_and_get_items
= function(inventory
, listname
, itemtable
, check_wear
)
39 local itemstring
= itemtable
.name
40 local minimum
= itemtable
.count
41 if check_wear
== nil then check_wear
= false end
44 if minetest
.registered_tools
[itemstring
] ~= nil then
46 for i
=1,inventory
:get_size(listname
) do
47 local stack
= inventory
:get_stack(listname
, i
)
48 if stack
:get_name() == itemstring
then
49 if not check_wear
or stack
:get_wear() == 0 then
51 table.insert(get_items
, {id
=i
, item
=stack
})
52 if count
>= minimum
then
53 return true, get_items
60 -- Normal Minetest check
61 return inventory
:contains_item(listname
, ItemStack(itemtable
))
66 if minetest
.get_modpath("default") ~= nil then
67 easyvend
.register_chest("default:chest_locked", "main", "owner")
70 easyvend
.free_slots
= function(inv
, listname
)
71 local size
= inv
:get_size(listname
)
74 local stack
= inv
:get_stack(listname
, i
)
75 if stack
:is_empty() then
82 easyvend
.buysell
= function(nodename
)
84 if ( nodename
== "easyvend:depositor" or nodename
== "easyvend:depositor_on" ) then
86 elseif ( nodename
== "easyvend:vendor" or nodename
== "easyvend:vendor_on" ) then
92 easyvend
.is_active
= function(nodename
)
93 if ( nodename
== "easyvend:depositor_on" or nodename
== "easyvend:vendor_on" ) then
95 elseif ( nodename
== "easyvend:depositor" or nodename
== "easyvend:vendor" ) then
102 easyvend
.set_formspec
= function(pos
, player
)
103 local meta
= minetest
.get_meta(pos
)
104 local node
= minetest
.get_node(pos
)
106 local description
= minetest
.registered_nodes
[node
.name
].description
;
107 local number = meta
:get_int("number")
108 local cost
= meta
:get_int("cost")
109 local itemname
= meta
:get_string("itemname")
111 local configmode
= meta
:get_int("configmode") == 1
112 if minetest
.get_modpath("default") then
113 bg
= default
.gui_bg
.. default
.gui_bg_img
.. default
.gui_slots
116 local numbertext
, costtext
, buysellbuttontext
117 local itemcounttooltip
= "Item count (append “s” to multiply with maximum stack size)"
118 local buysell
= easyvend
.buysell(node
.name
)
119 if buysell
== "sell" then
120 numbertext
= "Offered item"
122 buysellbuttontext
= "Buy"
123 elseif buysell
== "buy" then
124 numbertext
= "Requested item"
126 buysellbuttontext
= "Sell"
130 local status
= meta
:get_string("status")
131 if status
== "" then status
= "Unknown." end
132 local message
= meta
:get_string("message")
133 if message
== "" then message
= "No message." end
135 if node
.name
== "easyvend:vendor_on" or node
.name
== "easyvend:depositor_on" then
136 status_image
= "easyvend_status_on.png"
138 status_image
= "easyvend_status_off.png"
141 -- TODO: Expose number of items in stock
143 local formspec
= "size[8,7.3;]"
145 .."label[3,-0.2;" .. minetest
.formspec_escape(description
) .. "]"
147 .."image[7.5,0.2;0.5,1;" .. status_image
.. "]"
148 .."textarea[2.8,0.2;5.1,2;;Status: " .. minetest
.formspec_escape(status
) .. ";]"
149 .."textarea[2.8,1.3;5.6,2;;Message: " .. minetest
.formspec_escape(message
) .. ";]"
151 .."label[0,-0.15;"..numbertext
.."]"
152 .."label[0,1.2;"..costtext
.."]"
153 .."list[current_player;main;0,3.5;8,4;]"
157 if meta
:get_int("wear") == 1 then wear
= "true" end
159 .."item_image_button[0,1.65;1,1;"..easyvend
.currency
..";easyvend.currency_image;]"
160 .."list[current_name;item;0,0.35;1,1;]"
161 .."listring[current_player;main]"
162 .."listring[current_name;item]"
163 .."field[1.3,0.65;1.5,1;number;;" .. number .. "]"
164 .."tooltip[number;"..itemcounttooltip
.."]"
165 .."field[1.3,1.95;1.5,1;cost;;" .. cost
.. "]"
166 .."tooltip[cost;"..itemcounttooltip
.."]"
167 .."button[6,2.8;2,0.5;save;Confirm]"
168 .."tooltip[save;Confirm configuration and activate machine (only for owner)]"
169 local weartext
, weartooltip
170 if buysell
== "buy" then
171 weartext
= "Accept worn tools"
172 weartooltip
= "If disabled, only tools in perfect condition will be bought from sellers (only settable by owner)"
174 weartext
= "Sell worn tools"
175 weartooltip
= "If disabled, only tools in perfect condition will be sold (only settable by owner)"
177 if minetest
.registered_tools
[itemname
] ~= nil then
178 formspec
= formspec
.."checkbox[2,2.4;wear;"..minetest
.formspec_escape(weartext
)..";"..wear
.."]"
179 .."tooltip[wear;"..minetest
.formspec_escape(weartooltip
).."]"
183 .."item_image_button[0,1.65;1,1;"..easyvend
.currency
..";easyvend.currency_image;]"
184 .."item_image_button[0,0.35;1,1;"..itemname
..";item_image;]"
185 .."label[1,1.85;×" .. cost
.. "]"
186 .."label[1,0.55;×" .. number .. "]"
187 .."button[6,2.8;2,0.5;config;Configure]"
188 if buysell
== "sell" then
189 formspec
= formspec
.. "tooltip[config;Configure offered items and price (only for owner)]"
191 formspec
= formspec
.. "tooltip[config;Configure requested items and payment (only for owner)]"
193 formspec
= formspec
.."button[0,2.8;2,0.5;buysell;"..buysellbuttontext
.."]"
194 if minetest
.registered_tools
[itemname
] ~= nil then
196 if meta
:get_int("wear") == 0 then
197 if buysell
== "buy" then
198 weartext
= "Only intact tools are bought."
200 weartext
= "Only intact tools are sold."
202 elseif buysell
== "sell" then
203 weartext
= "Warning: Might sell worn tools."
205 if weartext
~= nil then
206 formspec
= formspec
.."textarea[2.3,2.6;3,1;;"..minetest
.formspec_escape(weartext
)..";]"
211 meta
:set_string("formspec", formspec
)
214 easyvend
.machine_disable
= function(pos
, node
, playername
)
215 if node
.name
== "easyvend:vendor_on" then
216 easyvend
.sound_disable(pos
)
217 minetest
.swap_node(pos
, {name
="easyvend:vendor", param2
= node
.param2
})
219 elseif node
.name
== "easyvend:depositor_on" then
220 easyvend
.sound_disable(pos
)
221 minetest
.swap_node(pos
, {name
="easyvend:depositor", param2
= node
.param2
})
224 if playername
~= nil then
225 easyvend
.sound_error(playername
)
231 easyvend
.machine_enable
= function(pos
, node
)
232 if node
.name
== "easyvend:vendor" then
233 easyvend
.sound_setup(pos
)
234 minetest
.swap_node(pos
, {name
="easyvend:vendor_on", param2
= node
.param2
})
236 elseif node
.name
== "easyvend:depositor" then
237 easyvend
.sound_setup(pos
)
238 minetest
.swap_node(pos
, {name
="easyvend:depositor_on", param2
= node
.param2
})
245 easyvend
.machine_check
= function(pos
, node
)
247 local status
= "Ready."
249 local chest
= minetest
.get_node({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
250 local meta
= minetest
.get_meta(pos
)
251 local number = meta
:get_int("number")
252 local cost
= meta
:get_int("cost")
253 local inv
= meta
:get_inventory()
254 local itemstack
= inv
:get_stack("item",1)
255 local itemname
=meta
:get_string("itemname")
256 local machine_owner
= meta
:get_string("owner")
257 local check_wear
= meta
:get_int("wear") == 0
258 local chestdef
= registered_chests
[chest
.name
]
259 local chest_meta
, chest_inv
262 chest_meta
= minetest
.get_meta({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
263 chest_inv
= chest_meta
:get_inventory()
265 if ( chest_meta
:get_string(chestdef
.meta_owner
) == machine_owner
and chest_inv
~= nil ) then
266 local buysell
= easyvend
.buysell(node
.name
)
268 local checkstack
, checkitem
269 if buysell
== "buy" then
270 checkitem
= easyvend
.currency
276 -- FIXME: Ignore tools with bad wear level
277 for i
=1,chest_inv
:get_size(chestdef
.inv_list
) do
278 checkstack
= chest_inv
:get_stack(chestdef
.inv_list
, i
)
279 if checkstack
:get_name() == checkitem
then
280 stock
= stock
+ checkstack
:get_count()
283 meta
:set_int("stock", stock
)
285 if not itemstack
:is_empty() then
286 local number_stack_max
= itemstack
:get_stack_max()
287 local maxnumber
= number_stack_max
* slots_max
288 if number >= 1 and number <= maxnumber
and cost
>= 1 and cost
<= maxcost
then
289 local stack
= {name
=itemname
, count
=number, wear
=0, metadata
=""}
290 local price
= {name
=easyvend
.currency
, count
=cost
, wear
=0, metadata
=""}
292 local chest_has
, chest_free
294 local coststacks
= math
.modf(cost
/ cost_stack_max
)
295 local costremainder
= math
.fmod(cost
, cost_stack_max
)
296 local numberstacks
= math
.modf(number / number_stack_max
)
297 local numberremainder
= math
.fmod(number, number_stack_max
)
298 local numberfree
= numberstacks
299 local costfree
= coststacks
300 if numberremainder
> 0 then numberfree
= numberfree
+ 1 end
301 if costremainder
> 0 then costfree
= costfree
+ 1 end
303 if buysell
== "sell" then
304 chest_has
= easyvend
.check_and_get_items(chest_inv
, chestdef
.inv_list
, stack
, check_wear
)
305 chest_free
= chest_inv
:room_for_item(chestdef
.inv_list
, price
)
306 if chest_has
and chest_free
then
307 if cost
<= cost_stack_max
and number <= number_stack_max
then
309 elseif easyvend
.free_slots(chest_inv
, chestdef
.inv_list
) < costfree
then
311 status
= "No room in the machine’s storage!"
313 elseif not chest_has
then
315 status
= "The vending machine has insufficient materials!"
316 elseif not chest_free
then
318 status
= "No room in the machine’s storage!"
320 elseif buysell
== "buy" then
321 chest_has
= easyvend
.check_and_get_items(chest_inv
, chestdef
.inv_list
, price
, check_wear
)
322 chest_free
= chest_inv
:room_for_item(chestdef
.inv_list
, stack
)
323 if chest_has
and chest_free
then
324 if cost
<= cost_stack_max
and number <= number_stack_max
then
326 elseif easyvend
.free_slots(chest_inv
, chestdef
.inv_list
) < numberfree
then
328 status
= "No room in the machine’s storage!"
330 elseif not chest_has
then
332 status
= "The depositing machine is out of money!"
333 elseif not chest_free
then
335 status
= "No room in the machine’s storage!"
340 if buysell
== "sell" then
341 status
= "Invalid item count or price."
343 status
= "Invalid item count or payment."
348 status
= "Awaiting configuration by owner."
351 meta
:set_int("stock", 0)
353 status
= "Storage can’t be accessed because it is owned by a different person!"
356 meta
:set_int("stock", 0)
358 status
= "No storage; machine needs a locked chest below it."
360 if meta
:get_int("configmode") == 1 then
362 status
= "Awaiting configuration by owner."
365 if itemname
== easyvend
.currency
and number == cost
and active
then
366 local jt
= meta
:get_int("joketimer")
371 if buysell
== "sell" then
372 meta
:set_string("message", "Item bought.")
374 meta
:set_string("message", "Item sold.")
378 meta
:set_int("joketimer", jt
)
380 meta
:set_string("status", status
)
382 meta
:set_string("infotext", easyvend
.make_infotext(node
.name
, machine_owner
, cost
, number, itemname
))
383 itemname
=itemstack
:get_name()
384 meta
:set_string("itemname", itemname
)
387 if node
.name
== "easyvend:vendor" or node
.name
== "easyvend:depositor" then
388 if active
then change
= easyvend
.machine_enable(pos
, node
) end
389 elseif node
.name
== "easyvend:vendor_on" or node
.name
== "easyvend:depositor_on" then
390 if not active
then change
= easyvend
.machine_disable(pos
, node
) end
392 easyvend
.set_formspec(pos
)
396 easyvend
.on_receive_fields_config
= function(pos
, formname
, fields
, sender
)
397 local node
= minetest
.get_node(pos
)
398 local meta
= minetest
.get_meta(pos
)
399 local inv_self
= meta
:get_inventory()
400 local itemstack
= inv_self
:get_stack("item",1)
401 local buysell
= easyvend
.buysell(node
.name
)
403 if fields
.config
then
404 meta
:set_int("configmode", 1)
405 local was_active
= easyvend
.is_active(node
.name
)
407 meta
:set_string("message", "Configuration mode activated; machine disabled.")
409 meta
:set_string("message", "Configuration mode activated.")
411 easyvend
.machine_check(pos
, node
)
415 if not fields
.save
then
419 local number = fields
.number
420 local cost
= fields
.cost
422 --[[ Convenience function:
423 When appending “s” or “S” to the number, it is multiplied
424 by the maximum stack size.
425 TODO: Expose this in user documentation ]]
426 local number_stack_max
= itemstack
:get_stack_max()
427 local ss
= string.sub(number, #number, #number)
428 if ss
== "s" or ss
== "S" then
429 local n
= tonumber(string.sub(number, 1, #number-1))
430 if string.len(number) == 1 then n
= 1 end
432 number = n
* number_stack_max
435 ss
= string.sub(cost
, #cost
, #cost
)
436 if ss
== "s" or ss
== "S" then
437 local n
= tonumber(string.sub(cost
, 1, #cost
-1))
438 if string.len(cost
) == 1 then n
= 1 end
440 cost
= n
* cost_stack_max
443 number = tonumber(number)
444 cost
= tonumber(cost
)
448 local oldnumber
= meta
:get_int("number")
449 local oldcost
= meta
:get_int("cost")
450 local maxnumber
= number_stack_max
* slots_max
452 if ( itemstack
== nil or itemstack
:is_empty() ) then
453 meta
:set_string("status", "Awaiting configuration by owner.")
454 meta
:set_string("message", "No item specified.")
455 easyvend
.sound_error(sender
:get_player_name())
456 easyvend
.set_formspec(pos
, sender
)
458 elseif ( number == nil or number < 1 or number > maxnumber
) then
459 if maxnumber
> 1 then
460 meta
:set_string("message", string.format("Invalid item count; must be between 1 and %d!", maxnumber
))
462 meta
:set_string("message", "Invalid item count; must be exactly 1!")
464 meta
:set_int("number", oldnumber
)
465 easyvend
.sound_error(sender
:get_player_name())
466 easyvend
.set_formspec(pos
, sender
)
468 elseif ( cost
== nil or cost
< 1 or cost
> maxcost
) then
470 meta
:set_string("message", string.format("Invalid cost; must be between 1 and %d!", maxcost
))
472 meta
:set_string("message", "Invalid cost; must be exactly 1!")
474 meta
:set_int("cost", oldcost
)
475 easyvend
.sound_error(sender
:get_player_name())
476 easyvend
.set_formspec(pos
, sender
)
479 meta
:set_int("number", number)
480 meta
:set_int("cost", cost
)
481 itemname
=itemstack
:get_name()
482 meta
:set_string("itemname", itemname
)
483 meta
:set_int("configmode", 0)
485 if itemname
== easyvend
.currency
and number == cost
and cost
<= cost_stack_max
then
486 meta
:set_string("message", "Configuration successful. I am feeling funny.")
487 meta
:set_int("joketimer", joketimer_start
)
488 meta
:set_int("joke_id", easyvend
.assign_joke(buysell
))
490 meta
:set_string("message", "Configuration successful.")
493 local change
= easyvend
.machine_check(pos
, node
)
496 if (node
.name
== "easyvend:vendor_on" or node
.name
== "easyvend:depositor_on") then
497 easyvend
.sound_setup(pos
)
499 easyvend
.sound_disable(pos
)
504 easyvend
.make_infotext
= function(nodename
, owner
, cost
, number, itemstring
)
506 if itemstring
== nil or itemstring
== "" or number == 0 or cost
== 0 then
507 if easyvend
.buysell(nodename
) == "sell" then
508 d
= string.format("Inactive vending machine (owned by %s)", owner
)
510 d
= string.format("Inactive depositing machine (owned by %s)", owner
)
514 local iname
= minetest
.registered_items
[itemstring
].description
515 if iname
== nil then iname
= itemstring
end
516 local printitem
, printcost
520 printitem
= string.format("%d×%s", number, iname
)
523 printcost
= easyvend
.currency_desc
525 printcost
= string.format("%d×%s", cost
, easyvend
.currency_desc
)
527 if nodename
== "easyvend:vendor_on" then
528 d
= string.format("Vending machine (owned by %s)\nSelling: %s\nPrice: %s", owner
, printitem
, printcost
)
529 elseif nodename
== "easyvend:vendor" then
530 d
= string.format("Inactive vending machine (owned by %s)\nSelling: %s\nPrice: %s", owner
, printitem
, printcost
)
531 elseif nodename
== "easyvend:depositor_on" then
532 d
= string.format("Depositing machine (owned by %s)\nBuying: %s\nPayment: %s", owner
, printitem
, printcost
)
533 elseif nodename
== "easyvend:depositor" then
534 d
= string.format("Inactive depositing machine (owned by %s)\nBuying: %s\nPayment: %s", owner
, printitem
, printcost
)
539 easyvend
.on_receive_fields_buysell
= function(pos
, formname
, fields
, sender
)
540 local sendername
= sender
:get_player_name()
541 local meta
= minetest
.get_meta(pos
)
543 if not fields
.buysell
then
547 local node
= minetest
.get_node(pos
)
548 local number = meta
:get_int("number")
549 local cost
= meta
:get_int("cost")
550 local itemname
=meta
:get_string("itemname")
551 local item
=meta
:get_inventory():get_stack("item", 1)
552 local check_wear
= meta
:get_int("wear") == 0 and minetest
.registered_tools
[itemname
] ~= nil
554 local buysell
= easyvend
.buysell(node
.name
)
556 local number_stack_max
= item
:get_stack_max()
557 local maxnumber
= number_stack_max
* slots_max
558 if ( number == nil or number < 1 or number > maxnumber
) or
559 ( cost
== nil or cost
< 1 or cost
> maxcost
) or
560 ( itemname
== nil or itemname
=="") then
561 meta
:set_string("status", "Invalid item count or price!")
562 easyvend
.machine_disable(pos
, node
, sendername
)
567 local chest
= minetest
.get_node({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
568 local chestdef
= registered_chests
[chest
.name
]
569 if chestdef
and sender
and sender
:is_player() then
570 local chest_meta
= minetest
.get_meta({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
571 local chest_inv
= chest_meta
:get_inventory()
572 local player_inv
= sender
:get_inventory()
573 if ( chest_meta
:get_string(chestdef
.meta_owner
) == meta
:get_string("owner") and chest_inv
~= nil and player_inv
~= nil ) then
575 local stack
= {name
=itemname
, count
=number, wear
=0, metadata
=""}
576 local price
= {name
=easyvend
.currency
, count
=cost
, wear
=0, metadata
=""}
577 local chest_has
, player_has
, chest_free
, player_free
, chest_out
, player_out
579 if buysell
== "sell" then
580 chest_has
, chest_out
= easyvend
.check_and_get_items(chest_inv
, "main", stack
, check_wear
)
581 player_has
, player_out
= easyvend
.check_and_get_items(player_inv
, "main", price
, check_wear
)
582 chest_free
= chest_inv
:room_for_item("main", price
)
583 player_free
= player_inv
:room_for_item("main", stack
)
584 if chest_has
and player_has
and chest_free
and player_free
then
585 if cost
<= cost_stack_max
and number <= number_stack_max
then
586 easyvend
.machine_enable(pos
, node
)
587 player_inv
:remove_item("main", price
)
589 chest_inv
:set_stack("main", chest_out
[1].id
, "")
590 player_inv
:add_item("main", chest_out
[1].item
)
592 stack
= chest_inv
:remove_item("main", stack
)
593 player_inv
:add_item("main", stack
)
595 chest_inv
:add_item("main", price
)
596 if itemname
== easyvend
.currency
and number == cost
and cost
<= cost_stack_max
then
597 meta
:set_string("message", easyvend
.get_joke(buysell
, meta
:get_int("joke_id")))
598 meta
:set_int("joketimer", joketimer_start
)
600 meta
:set_string("message", "Item bought.")
602 easyvend
.sound_vend(pos
)
603 easyvend
.machine_check(pos
, node
)
605 -- Large item counts (multiple stacks)
606 local coststacks
= math
.modf(cost
/ cost_stack_max
)
607 local costremainder
= math
.fmod(cost
, cost_stack_max
)
608 local numberstacks
= math
.modf(number / number_stack_max
)
609 local numberremainder
= math
.fmod(number, number_stack_max
)
610 local numberfree
= numberstacks
611 local costfree
= coststacks
612 if numberremainder
> 0 then numberfree
= numberfree
+ 1 end
613 if costremainder
> 0 then costfree
= costfree
+ 1 end
614 if easyvend
.free_slots(player_inv
, "main") < numberfree
then
615 if numberfree
> 1 then
616 msg
= string.format("No room in your inventory (%d empty slots required)!", numberfree
)
618 msg
= "No room in your inventory!"
620 meta
:set_string("message", msg
)
621 elseif easyvend
.free_slots(chest_inv
, "main") < costfree
then
622 meta
:set_string("status", "No room in the machine’s storage!")
623 easyvend
.machine_disable(pos
, node
, sendername
)
625 -- Remember items for transfer
626 local cheststacks
= {}
627 easyvend
.machine_enable(pos
, node
)
628 for i
=1, coststacks
do
629 price
.count
= cost_stack_max
630 player_inv
:remove_item("main", price
)
632 if costremainder
> 0 then
633 price
.count
= costremainder
634 player_inv
:remove_item("main", price
)
637 for o
=1,#chest_out
do
638 chest_inv
:set_stack("main", chest_out
[o
].id
, "")
641 for i
=1, numberstacks
do
642 stack
.count
= number_stack_max
643 table.insert(cheststacks
, chest_inv
:remove_item("main", stack
))
646 if numberremainder
> 0 then
647 stack
.count
= numberremainder
648 table.insert(cheststacks
, chest_inv
:remove_item("main", stack
))
650 for i
=1, coststacks
do
651 price
.count
= cost_stack_max
652 chest_inv
:add_item("main", price
)
654 if costremainder
> 0 then
655 price
.count
= costremainder
656 chest_inv
:add_item("main", price
)
659 for o
=1,#chest_out
do
660 player_inv
:add_item("main", chest_out
[o
].item
)
663 for i
=1,#cheststacks
do
664 player_inv
:add_item("main", cheststacks
[i
])
667 meta
:set_string("message", "Item bought.")
668 easyvend
.sound_vend(pos
)
669 easyvend
.machine_check(pos
, node
)
672 elseif chest_has
and player_has
then
673 if not player_free
then
674 msg
= "No room in your inventory!"
675 meta
:set_string("message", msg
)
676 easyvend
.sound_error(sendername
)
677 elseif not chest_free
then
678 msg
= "No room in the machine’s storage!"
679 meta
:set_string("status", msg
)
680 easyvend
.machine_disable(pos
, node
, sendername
)
683 if not chest_has
then
684 msg
= "The vending machine has insufficient materials!"
685 meta
:set_string("status", msg
)
686 easyvend
.machine_disable(pos
, node
, sendername
)
687 elseif not player_has
then
688 msg
= "You can’t afford this item!"
689 meta
:set_string("message", msg
)
690 easyvend
.sound_error(sendername
)
694 chest_has
, chest_out
= easyvend
.check_and_get_items(chest_inv
, "main", price
, check_wear
)
695 player_has
, player_out
= easyvend
.check_and_get_items(player_inv
, "main", stack
, check_wear
)
696 chest_free
= chest_inv
:room_for_item("main", stack
)
697 player_free
= player_inv
:room_for_item("main", price
)
698 if chest_has
and player_has
and chest_free
and player_free
then
699 if cost
<= cost_stack_max
and number <= number_stack_max
then
700 easyvend
.machine_enable(pos
, node
)
702 player_inv
:set_stack("main", player_out
[1].id
, "")
703 chest_inv
:add_item("main", player_out
[1].item
)
705 stack
= player_inv
:remove_item("main", stack
)
706 chest_inv
:add_item("main", stack
)
708 chest_inv
:remove_item("main", price
)
709 player_inv
:add_item("main", price
)
710 meta
:set_string("status", "Ready.")
711 if itemname
== easyvend
.currency
and number == cost
and cost
<= cost_stack_max
then
712 meta
:set_string("message", easyvend
.get_joke(buysell
, meta
:get_int("joke_id")))
713 meta
:set_int("joketimer", joketimer_start
)
715 meta
:set_string("message", "Item sold.")
717 easyvend
.sound_deposit(pos
)
718 easyvend
.machine_check(pos
, node
)
720 -- Large item counts (multiple stacks)
721 local coststacks
= math
.modf(cost
/ cost_stack_max
)
722 local costremainder
= math
.fmod(cost
, cost_stack_max
)
723 local numberstacks
= math
.modf(number / number_stack_max
)
724 local numberremainder
= math
.fmod(number, number_stack_max
)
725 local numberfree
= numberstacks
726 local costfree
= coststacks
727 if numberremainder
> 0 then numberfree
= numberfree
+ 1 end
728 if costremainder
> 0 then costfree
= costfree
+ 1 end
729 if easyvend
.free_slots(player_inv
, "main") < costfree
then
731 msg
= string.format("No room in your inventory (%d empty slots required)!", costfree
)
733 msg
= "No room in your inventory!"
735 meta
:set_string("message", msg
)
736 easyvend
.sound_error(sendername
)
737 elseif easyvend
.free_slots(chest_inv
, "main") < numberfree
then
738 easyvend
.machine_disable(pos
, node
, sendername
)
740 easyvend
.machine_enable(pos
, node
)
741 -- Remember removed items for transfer
742 local playerstacks
= {}
743 for i
=1, coststacks
do
744 price
.count
= cost_stack_max
745 chest_inv
:remove_item("main", price
)
747 if costremainder
> 0 then
748 price
.count
= costremainder
749 chest_inv
:remove_item("main", price
)
752 for o
=1,#player_out
do
753 player_inv
:set_stack("main", player_out
[o
].id
, "")
756 for i
=1, numberstacks
do
757 stack
.count
= number_stack_max
758 table.insert(playerstacks
, player_inv
:remove_item("main", stack
))
761 if numberremainder
> 0 then
762 stack
.count
= numberremainder
763 table.insert(playerstacks
, player_inv
:remove_item("main", stack
))
765 for i
=1, coststacks
do
766 price
.count
= cost_stack_max
767 player_inv
:add_item("main", price
)
769 if costremainder
> 0 then
770 price
.count
= costremainder
771 player_inv
:add_item("main", price
)
774 for o
=1,#player_out
do
775 chest_inv
:add_item("main", player_out
[o
].item
)
778 for i
=1,#playerstacks
do
779 chest_inv
:add_item("main", playerstacks
[i
])
782 meta
:set_string("message", "Item sold.")
783 easyvend
.sound_deposit(pos
)
784 easyvend
.machine_check(pos
, node
)
787 elseif chest_has
and player_has
then
788 if not player_free
then
789 msg
= "No room in your inventory!"
790 meta
:set_string("message", msg
)
791 easyvend
.sound_error(sendername
)
792 elseif not chest_free
then
793 msg
= "No room in the machine’s storage!"
794 meta
:set_string("status", msg
)
795 easyvend
.machine_disable(pos
, node
, sendername
)
798 if not player_has
then
799 msg
= "You have insufficient materials!"
800 meta
:set_string("message", msg
)
801 easyvend
.sound_error(sendername
)
802 elseif not chest_has
then
803 msg
= "The depositing machine is out of money!"
804 meta
:set_string("status", msg
)
805 easyvend
.machine_disable(pos
, node
, sendername
)
810 meta
:set_string("status", "Storage can’t be accessed because it is owned by a different person!")
811 easyvend
.machine_disable(pos
, node
, sendername
)
814 if sender
and sender
:is_player() then
815 meta
:set_string("status", "No storage; machine needs a locked chest below it.")
816 easyvend
.machine_disable(pos
, node
, sendername
)
820 easyvend
.set_formspec(pos
, sender
)
824 easyvend
.after_place_node
= function(pos
, placer
)
825 local node
= minetest
.get_node(pos
)
826 local meta
= minetest
.get_meta(pos
)
827 local inv
= meta
:get_inventory()
828 local player_name
= placer
:get_player_name()
829 inv
:set_size("item", 1)
830 inv
:set_size("gold", 1)
832 inv
:set_stack( "gold", 1, easyvend
.currency
)
835 if node
.name
== "easyvend:vendor" then
836 d
= string.format("Inactive vending machine (owned by %s)", player_name
)
837 meta
:set_int("wear", 1)
838 elseif node
.name
== "easyvend:depositor" then
839 d
= string.format("Inactive depositing machine (owned by %s)", player_name
)
840 meta
:set_int("wear", 0)
842 meta
:set_string("infotext", d
)
843 local chest
= minetest
.get_node({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
844 meta
:set_string("status", "Awaiting configuration by owner.")
845 meta
:set_string("message", "Welcome! Please prepare the machine.")
846 meta
:set_int("number", 1)
847 meta
:set_int("cost", 1)
848 meta
:set_int("stock", -1)
849 meta
:set_int("configmode", 1)
850 meta
:set_int("joketimer", -1)
851 meta
:set_int("joke_id", 1)
852 meta
:set_string("itemname", "")
854 meta
:set_string("owner", player_name
or "")
856 easyvend
.set_formspec(pos
, placer
)
859 easyvend
.can_dig
= function(pos
, player
)
860 local meta
= minetest
.get_meta(pos
)
861 local name
= player
:get_player_name()
862 -- Owner can always dig shop
863 if meta
:get_string("owner") == name
then
866 local chest
= minetest
.get_node({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
867 local meta_chest
= minetest
.get_meta({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
});
868 if registered_chests
[chest
.name
] then
869 if player
and player
:is_player() then
870 local owner_chest
= meta_chest
:get_string(registered_chests
[chest
.name
].meta_owner
)
871 if name
== owner_chest
then
872 return true --chest owner can also dig shop
877 return true --if no chest, enyone can dig this shop
881 easyvend
.on_receive_fields
= function(pos
, formname
, fields
, sender
)
882 local meta
= minetest
.get_meta(pos
)
883 local node
= minetest
.get_node(pos
)
884 local owner
= meta
:get_string("owner")
885 local sendername
= sender
:get_player_name(sender
)
887 if fields
.config
or fields
.save
or fields
.usermode
then
888 if sender
:get_player_name() == owner
then
889 easyvend
.on_receive_fields_config(pos
, formname
, fields
, sender
)
891 meta
:set_string("message", "Only the owner may change the configuration.")
892 easyvend
.sound_error(sendername
)
893 easyvend
.set_formspec(pos
, sender
)
896 elseif fields
.wear
~= nil then
897 if sender
:get_player_name() == owner
then
898 if fields
.wear
== "true" then
899 if easyvend
.buysell(node
.name
) == "buy" then
900 meta
:set_string("message", "Used tools are now accepted.")
902 meta
:set_string("message", "Used tools are now for sale.")
904 meta
:set_int("wear", 1)
905 elseif fields
.wear
== "false" then
906 if easyvend
.buysell(node
.name
) == "buy" then
907 meta
:set_string("message", "Used tools are now rejected.")
909 meta
:set_string("message", "Used tools won’t be sold anymore.")
911 meta
:set_int("wear", 0)
913 easyvend
.set_formspec(pos
, sender
)
916 meta
:set_string("message", "Only the owner may change the configuration.")
917 easyvend
.sound_error(sendername
)
918 easyvend
.set_formspec(pos
, sender
)
921 elseif fields
.buysell
then
922 easyvend
.on_receive_fields_buysell(pos
, formname
, fields
, sender
)
926 -- Jokes: Appear when machine exchanges currency for currency at equal rate
929 local jokes_vendor
= {
930 "Thank you. You have made a vending machine very happy.",
931 "Humans have a strange sense of humor.",
932 "Let’s get this over with …",
935 "Do you realize what you’ve just bought?",
938 local jokes_depositor
= {
939 "Thank you, the money started to smell inside.",
940 "Money doesn’t grow on trees, you know?",
942 "Well, that was an awkward exchange.",
943 "Are you having fun?",
944 "Is this really trading?",
947 easyvend
.assign_joke
= function(buysell
)
949 if buysell
== "sell" then
951 elseif buysell
== "buy" then
952 jokes
= jokes_depositor
954 local r
= math
.random(1,#jokes
)
958 easyvend
.get_joke
= function(buysell
, id
)
960 if buysell
== nil or id
== nil then
961 -- Fallback message (should never happen)
962 return "Items exchanged."
964 if buysell
== "sell" then
965 joke
= jokes_vendor
[id
]
966 if joke
== nil then joke
= jokes_vendor
[1] end
967 elseif buysell
== "buy" then
968 joke
= jokes_depositor
[id
]
969 if joke
== nil then joke
= jokes_depositor
[1] end
974 easyvend
.sound_error
= function(playername
)
975 minetest
.sound_play("easyvend_error", {to_player
= playername
, gain
= 0.25})
978 easyvend
.sound_setup
= function(pos
)
979 minetest
.sound_play("easyvend_activate", {pos
= pos
, gain
= 0.5, max_hear_distance
= 12,})
982 easyvend
.sound_disable
= function(pos
)
983 minetest
.sound_play("easyvend_disable", {pos
= pos
, gain
= 0.9, max_hear_distance
= 12,})
986 easyvend
.sound_vend
= function(pos
)
987 minetest
.sound_play("easyvend_vend", {pos
= pos
, gain
= 0.4, max_hear_distance
= 5,})
990 easyvend
.sound_deposit
= function(pos
)
991 minetest
.sound_play("easyvend_deposit", {pos
= pos
, gain
= 0.4, max_hear_distance
= 5,})
994 easyvend
.allow_metadata_inventory_put
= function(pos
, listname
, index
, stack
, player
)
995 if listname
=="item" then
996 local meta
= minetest
.get_meta(pos
);
997 local owner
= meta
:get_string("owner")
998 local name
= player
:get_player_name()
999 if name
== owner
then
1000 local inv
= meta
:get_inventory()
1002 inv
:set_stack( "item", 1, nil )
1004 inv
:set_stack( "item", 1, stack
:get_name() )
1005 meta
:set_string("itemname", stack
:get_name())
1006 easyvend
.set_formspec(pos
, player
)
1013 easyvend
.allow_metadata_inventory_take
= function(pos
, listname
, index
, stack
, player
)
1017 easyvend
.allow_metadata_inventory_move
= function(pos
, from_list
, from_index
, to_list
, to_index
, count
, player
)
1021 minetest
.register_abm({
1022 nodenames
= {"easyvend:vendor", "easyvend:vendor_on", "easyvend:depositor", "easyvend:depositor_on"},
1026 action
= function(pos
, node
, active_object_count
, active_object_count_wider
)
1027 easyvend
.machine_check(pos
, node
)
1031 -- Legacy support for vendor mod:
1032 -- Transform the world and items to use the easyvend nodes/items
1034 -- For safety reasons, only do this when player requested so
1035 if minetest
.setting_getbool("easyvend_convert_vendor") == true then
1036 -- Replace vendor nodes
1037 minetest
.register_lbm({
1038 name
= "easyvend:replace_vendor",
1039 nodenames
= { "vendor:vendor", "vendor:depositor" },
1040 run_at_every_load
= true,
1041 action
= function(pos
, node
)
1044 if node
.name
== "vendor:vendor" then
1045 newnodename
= "easyvend:vendor"
1046 elseif node
.name
== "vendor:depositor" then
1047 newnodename
= "easyvend:depositor"
1049 minetest
.swap_node(pos
, { name
= newnodename
, param2
= node
.param2
})
1051 -- Initialize metadata
1052 local meta
= minetest
.get_meta(pos
)
1053 meta
:set_int("stock", -1)
1054 meta
:set_int("joketimer", -1)
1055 meta
:set_int("joke_id", 1)
1056 local inv
= meta
:get_inventory()
1057 inv
:set_size("item", 1)
1058 inv
:set_size("gold", 1)
1059 inv
:set_stack("gold", 1, easyvend
.currency
)
1061 -- In vendor, all machines accepted worn tools
1062 meta
:set_int("wear", 1)
1065 local itemname
= meta
:get_string("itemname")
1066 if itemname
== "" or itemname
== nil then
1067 itemname
= meta
:get_string("itemtype")
1069 local configmode
= 1
1070 if itemname
== "" or itemname
== nil then
1071 -- If no itemname set, try to scan first item in chest below
1072 local chest
= minetest
.get_node({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
1073 if chest
.name
== "default:chest_locked" then
1074 local chest_meta
= minetest
.get_meta({x
=pos
.x
,y
=pos
.y
-1,z
=pos
.z
})
1075 local chest_inv
= chest_meta
:get_inventory()
1076 if ( chest_meta
:get_string("owner") == machine_owner
and chest_inv
~= nil ) then
1078 for i
=1,chest_inv
:get_size("main") do
1079 checkstack
= chest_inv
:get_stack("main", i
)
1080 if not checkstack
:is_empty() then
1081 itemname
= checkstack
:get_name()
1087 if itemname
~= "" and itemname
~= nil then
1088 inv
:set_stack("item", 1, itemname
)
1089 meta
:set_string("itemname", itemname
)
1092 -- Check for valid item, item count and price
1093 if itemname
~= "" and itemname
~= nil then
1094 local itemstack
= inv
:get_stack("item", 1)
1095 local number_stack_max
= itemstack
:get_stack_max()
1096 local maxnumber
= number_stack_max
* slots_max
1097 local cost
= meta
:get_int("cost")
1098 local number = meta
:get_int("number")
1099 if number >= 1 and number <= maxnumber
and cost
>= 1 and cost
<= maxcost
then
1104 -- Final initialization stuff
1105 meta
:set_int("configmode", configmode
)
1107 local owner
= meta
:get_string("owner")
1108 if easyvend
.buysell(newnodename
) == "sell" then
1109 meta
:set_string("infotext", string.format("Vending machine (owned by %s)", owner
))
1111 meta
:set_string("infotext", string.format("Depositing machine (owned by %s)", owner
))
1115 meta
:set_string("status", "Initializing …")
1116 meta
:set_string("message", "Upgrade successful.")
1117 easyvend
.machine_check(pos
, node
)