Add buying support, and API
This commit is contained in:
parent
3492d921b4
commit
77a8d86dd9
16
README.md
16
README.md
@ -17,9 +17,9 @@ To see the contents of a barrel, simply point at it and the item, count and
|
||||
## Requirements
|
||||
|
||||
Optional dependencies:
|
||||
* currency (if you want to use shop barrels)
|
||||
* pipeworks
|
||||
* digilines
|
||||
* currency - my fork. Only needed if you want use shop barrels.
|
||||
* pipeworks - my fork
|
||||
* digilines - my fork
|
||||
|
||||
Optionally, if you want debug logging support, use:
|
||||
* moddebug - https://gitlab.com/CiaranG/moddebug
|
||||
@ -73,13 +73,19 @@ The message must be a table, with "action" and "item" fields. Action is one of:
|
||||
* "send" - instruct the barrel to send items. The "item" field should be one or
|
||||
more item/count pairs - examples: "default:cobble 4" or
|
||||
"default:stone 1 default:dirt 1". Any barrel that contains one of the items
|
||||
listed will send that many items down the pipeworks connection to the bottom
|
||||
or back of the barrel.
|
||||
listed will send that many items (or as many as it has, if it doesn't have
|
||||
enough) down the pipeworks connection to the bottom of the barrel.
|
||||
* "count" - the "item" field shoud be a single item name, e.g. "default:cobble".
|
||||
Any barrel containing that item will response with a message on the
|
||||
"barrelcount" channel, with the message being a table containing two fields,
|
||||
"item" (the item name) and "count" (the number of that item in the barrel.
|
||||
|
||||
## API
|
||||
|
||||
There are some API functions to allow use of barrels by other modules. A working
|
||||
example of this can be found in the people mod, where NPC entities can, for
|
||||
example, use shop barrels to buy and sell goods in the same way as a player.
|
||||
|
||||
## License
|
||||
|
||||
License: Code - LGPL, Textures - WTFPL
|
||||
|
319
init.lua
319
init.lua
@ -31,6 +31,7 @@ function barrel.update_obj(pos, node)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_string("item") == "" then return end
|
||||
local vec = barrel.dir2vec[node.param2]
|
||||
if not vec then return end
|
||||
local objpos = {x=pos.x + vec.x * 0.6,
|
||||
y=pos.y + vec.y * 0.6,
|
||||
z=pos.z + vec.z * 0.6}
|
||||
@ -41,26 +42,77 @@ function barrel.update_obj(pos, node)
|
||||
obj:get_luaentity().tex = {name}
|
||||
end
|
||||
|
||||
|
||||
-- Get the total amount of money the shop (represented by the given node
|
||||
-- meta) has.
|
||||
barrel.total_money = function(meta)
|
||||
local total = 0
|
||||
for i, stack in ipairs(meta:get_inventory():get_list("money")) do
|
||||
local denom = 0
|
||||
local nn = stack:get_name()
|
||||
if nn == "currency:minegeld_10" then
|
||||
denom = 10
|
||||
elseif nn == "currency:minegeld_5" then
|
||||
denom = 5
|
||||
elseif nn == "currency:minegeld" then
|
||||
denom = 1
|
||||
end
|
||||
if denom > 0 then
|
||||
total = total + denom * stack:get_count()
|
||||
end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
barrel.get_shop_customer_formspec = function(meta, pos, playername)
|
||||
local nm = "nodemeta:"..pos.x..","..pos.y..","..pos.z
|
||||
local price = meta:get_int("price")
|
||||
local buyprice = meta:get_int("buyprice")
|
||||
local saleunit = meta:get_int("saleunit")
|
||||
local item = meta:get_string("item")
|
||||
local formspec = "size[8,9.5]"
|
||||
if item == "" or price == 0 or saleunit == 0 then
|
||||
if item == "" or (price == 0 and buyprice ==0) or saleunit == 0 then
|
||||
formspec = formspec .. "label[0,0;Shop Closed]"
|
||||
else
|
||||
msg = "Put money here - $"..price.." for "..saleunit.." "..item
|
||||
local desc = minetest.registered_items[item].description
|
||||
local msg, msg2
|
||||
local buybtn = false
|
||||
if price ~= 0 then
|
||||
if meta:get_int("item_amount") >= saleunit then
|
||||
msg = "Put money here to buy "..saleunit.." "..desc.." for $"..price
|
||||
buybtn = true
|
||||
else
|
||||
msg = "Sorry - we ran out of "..desc
|
||||
end
|
||||
else
|
||||
msg = ""
|
||||
end
|
||||
|
||||
local sellbtn = false
|
||||
if buyprice ~= 0 then
|
||||
if barrel.total_money(meta) >= buyprice then
|
||||
msg2 = "Put "..saleunit.." "..desc.." here to get $"..buyprice
|
||||
sellbtn = true
|
||||
else
|
||||
msg2 = "Sorry, we can't afford to buy "..desc.." today"
|
||||
end
|
||||
else
|
||||
msg2 = ""
|
||||
end
|
||||
formspec = formspec..
|
||||
"label[0,0;"..msg.."]"..
|
||||
"list[current_player;payhere;0,0.5;3,2;]"..
|
||||
"label[5,2.5;Take your goods]"..
|
||||
"list["..nm..";paidfor;5,3;3,2;]"..
|
||||
"list[current_player;main;0,5.5;8,4;]"..
|
||||
"button[3,2;2,1;buy;Buy]"
|
||||
"label[0,1;"..msg2.."]"..
|
||||
"list["..nm..";exchange;0,2.5;6,2;]"..
|
||||
"list[current_player;main;0,5.5;8,4;]"
|
||||
if buybtn then
|
||||
formspec = formspec .."button[6,2;2,1;buy;Buy]"
|
||||
end
|
||||
if sellbtn then
|
||||
formspec = formspec .."button[6,3;2,1;sell;Sell]"
|
||||
end
|
||||
end
|
||||
if playername == meta:get_string("owner") then
|
||||
formspec = formspec.."button[3,3;2,1;manage;Manage]"
|
||||
formspec = formspec.."button[6,4;2,1;manage;Manage]"
|
||||
end
|
||||
return formspec
|
||||
end
|
||||
@ -71,8 +123,9 @@ barrel.get_shop_owner_formspec = function(meta, pos)
|
||||
"label[0,0;Money:]"..
|
||||
"list["..nm..";money;0,0.5;3,2;]"..
|
||||
"field[6,1;2,1;price;Price:;"..meta:get_int("price").."]"..
|
||||
"field[6,2;2,1;saleunit;Unit:;"..meta:get_int("saleunit").."]"..
|
||||
"button_exit[6,3;2,1;update;Update]"..
|
||||
"field[6,2;2,1;buyprice;Buy Price:;"..meta:get_int("buyprice").."]"..
|
||||
"field[6,3;2,1;saleunit;Unit:;"..meta:get_int("saleunit").."]"..
|
||||
"button_exit[5.5,4;2,1;update;Update]"..
|
||||
"list[current_player;main;0,5.5;8,4;]"
|
||||
return formspec
|
||||
end
|
||||
@ -90,6 +143,7 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
|
||||
return
|
||||
end
|
||||
meta:set_int("price", fields.price)
|
||||
meta:set_int("buyprice", fields.buyprice)
|
||||
meta:set_int("saleunit", fields.saleunit)
|
||||
barrel.set_infotext(minetest.get_node(pos).name, meta)
|
||||
dbg.v1(name.." set price "..fields.price..", unit "..fields.saleunit.." at "..minetest.pos_to_string(pos))
|
||||
@ -106,106 +160,118 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
|
||||
barrel.get_shop_owner_formspec(meta, pos))
|
||||
end
|
||||
elseif fields.buy then
|
||||
local amount = meta:get_int("item_amount")
|
||||
local saleunit = meta:get_int("saleunit")
|
||||
local price = meta:get_int("price")
|
||||
if price == 0 or saleunit == 0 or amount < saleunit then
|
||||
minetest.chat_send_player(name, "Out of stock!")
|
||||
return
|
||||
end
|
||||
local shopinv = meta:get_inventory()
|
||||
local playerinv = sender:get_inventory()
|
||||
|
||||
if playerinv:is_empty("payhere") then
|
||||
minetest.chat_send_player(name, "Put your money in first!")
|
||||
local status, msg = barrel.do_buy(pos)
|
||||
if not status then
|
||||
minetest.chat_send_player(name, msg)
|
||||
return
|
||||
end
|
||||
|
||||
-- Try and grab enough money...
|
||||
local gotmoney = 0
|
||||
local setstacks = {}
|
||||
for i, stack in ipairs(playerinv:get_list("payhere")) do
|
||||
local denom = 0
|
||||
local nn = stack:get_name()
|
||||
if nn == "currency:minegeld_10" then
|
||||
denom = 10
|
||||
elseif nn == "currency:minegeld_5" then
|
||||
denom = 5
|
||||
elseif nn == "currency:minegeld" then
|
||||
denom = 1
|
||||
end
|
||||
if denom > 0 then
|
||||
local left = stack:get_count()
|
||||
while left > 0 and gotmoney < price do
|
||||
left = left - 1
|
||||
gotmoney = gotmoney + denom
|
||||
end
|
||||
if left > 0 then
|
||||
setstacks[i] = ItemStack(nn.." "..left)
|
||||
else
|
||||
setstacks[i] = ItemStack(nil)
|
||||
end
|
||||
else
|
||||
setstacks[i] = stack
|
||||
end
|
||||
if gotmoney >= price then break end
|
||||
end
|
||||
|
||||
if gotmoney < price then
|
||||
minetest.chat_send_player(name, "Not enough money!")
|
||||
elseif fields.sell then
|
||||
local status, msg = barrel.do_sell(pos)
|
||||
if not status then
|
||||
minetest.chat_send_player(name, msg)
|
||||
return
|
||||
end
|
||||
|
||||
-- Remove money
|
||||
for i, stack in ipairs(setstacks) do
|
||||
playerinv:set_stack("payhere", i, stack)
|
||||
end
|
||||
|
||||
-- Hand over the goods and return change
|
||||
-- The notes are added back to the "playhere" area lowest
|
||||
-- denomination first. Highest first means if you pay $10 for
|
||||
-- a $1 item, you get 5+4*1 back in change. If you then buy again,
|
||||
-- it uses the 5, and now you have 8*1. Reversing the order means
|
||||
-- it use the 1 for the second purchase.
|
||||
shopinv:add_item("paidfor", ItemStack(meta:get_string("item").." "..saleunit))
|
||||
meta:set_int("item_amount", amount - saleunit)
|
||||
local change = gotmoney - price
|
||||
local putmoney = function(inv, listname, amount)
|
||||
local add10 = 0
|
||||
local add5 = 0
|
||||
local add1 = 0
|
||||
while amount >= 10 do
|
||||
add10 = add10 + 1
|
||||
amount = amount - 10
|
||||
end
|
||||
if amount >= 5 then
|
||||
add5 = add5 + 1
|
||||
amount = amount - 5
|
||||
end
|
||||
if amount > 0 then
|
||||
add1 = add1 + amount
|
||||
end
|
||||
|
||||
if add1 > 0 then
|
||||
inv:add_item(listname, ItemStack("currency:minegeld "..add1))
|
||||
end
|
||||
if add5 > 0 then
|
||||
inv:add_item(listname, ItemStack("currency:minegeld_5 "..add5))
|
||||
end
|
||||
if add10 > 0 then
|
||||
inv:add_item(listname, ItemStack("currency:minegeld_10 "..add10))
|
||||
end
|
||||
end
|
||||
putmoney(playerinv, "payhere", change)
|
||||
putmoney(shopinv, "money", price)
|
||||
barrel.set_infotext(minetest.get_node(pos).name, meta)
|
||||
dbg.v1(name.." bought "..saleunit.." of "..meta:get_string("item")..
|
||||
" for ".. price.." at "..minetest.pos_to_string(pos))
|
||||
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- API function
|
||||
-- Get shopinfo structure describing the shop at the given position.
|
||||
-- Returns nil if it isn't a valid shop.
|
||||
barrel.get_shopinfo = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local item = meta:get_string("item")
|
||||
if not item then return nil end
|
||||
local price = meta:get_int("price")
|
||||
local buyprice = meta:get_int("buyprice")
|
||||
local saleunit = meta:get_int("saleunit")
|
||||
local stock = meta:get_int("item_amount")
|
||||
return {pos = vector.new(pos),
|
||||
meta = meta,
|
||||
item=item,
|
||||
saleunit=saleunit,
|
||||
buyprice=buyprice,
|
||||
price=price,
|
||||
stock=stock}
|
||||
end
|
||||
|
||||
-- API function
|
||||
-- Do a 'sell' action. Goods must have been placed in the shop's "exchange"
|
||||
-- inventory. Returns true if the sell action succeeded (i.e. some goods were
|
||||
-- removed and money added), or false, message if it didn't happen for any
|
||||
-- reason.
|
||||
barrel.do_sell = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local amount = meta:get_int("item_amount")
|
||||
local saleunit = meta:get_int("saleunit")
|
||||
local price = meta:get_int("buyprice")
|
||||
if price == 0 or saleunit == 0 or barrel.total_money(meta) < price then
|
||||
return false, "Sale can't be done!"
|
||||
end
|
||||
local shopinv = meta:get_inventory()
|
||||
|
||||
if shopinv:is_empty("exchange") then
|
||||
return false, "Put your goods in first!"
|
||||
end
|
||||
|
||||
local toremove = ItemStack(meta:get_string("item").." "..saleunit)
|
||||
if not shopinv:contains_item("exchange", toremove) then
|
||||
return false, "Not enough!"
|
||||
end
|
||||
shopinv:remove_item("exchange", toremove)
|
||||
meta:set_int("item_amount", amount + saleunit)
|
||||
currency.put_to_inv(shopinv, "exchange", price)
|
||||
currency.remove_from_inv(shopinv, "money", price)
|
||||
barrel.set_infotext(minetest.get_node(pos).name, meta)
|
||||
dbg.v1("Shop bought "..saleunit.." of "..meta:get_string("item")..
|
||||
" for ".. price.." at "..minetest.pos_to_string(pos))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- API function
|
||||
-- Do a 'buy' action. Money must have been placed in the shop's "exchange"
|
||||
-- inventory. Returns true if the buy action succeeded (i.e. some money was
|
||||
-- removed and goods added), or false, message if it didn't happen for any
|
||||
-- reason.
|
||||
barrel.do_buy = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local item = meta:get_string("item")
|
||||
if not item then return nil end
|
||||
|
||||
local amount = meta:get_int("item_amount")
|
||||
local saleunit = meta:get_int("saleunit")
|
||||
local price = meta:get_int("price")
|
||||
if price == 0 or saleunit == 0 or amount < saleunit then
|
||||
return false, "Out of stock!"
|
||||
end
|
||||
local shopinv = meta:get_inventory()
|
||||
|
||||
if shopinv:is_empty("exchange") then
|
||||
return false, "Put your money in first!"
|
||||
end
|
||||
|
||||
local gotmoney = currency.remove_from_inv(shopinv, "exchange", price)
|
||||
if gotmoney == 0 then
|
||||
return false, "Not enough money!"
|
||||
end
|
||||
|
||||
-- Hand over the goods and return change
|
||||
shopinv:add_item("exchange", ItemStack(meta:get_string("item").." "..saleunit))
|
||||
meta:set_int("item_amount", amount - saleunit)
|
||||
local change = gotmoney - price
|
||||
currency.put_to_inv(shopinv, "exchange", change)
|
||||
currency.put_to_inv(shopinv, "money", price)
|
||||
barrel.set_infotext(minetest.get_node(pos).name, meta)
|
||||
dbg.v1("Shop sold "..saleunit.." of "..meta:get_string("item")..
|
||||
" for ".. price.." at "..minetest.pos_to_string(pos))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
minetest.register_entity("barrel:obj", {
|
||||
visual="wielditem",
|
||||
visual_size={x=0.2,y=0.2},
|
||||
@ -240,11 +306,31 @@ end
|
||||
function barrel.show_customer(player, pos, meta)
|
||||
local playername = player:get_player_name()
|
||||
local owner = meta:get_string("owner")
|
||||
player:get_inventory():set_size("payhere", 3*2)
|
||||
player:get_inventory():set_size("paidfor", 3*2)
|
||||
|
||||
-- Make sure the node's inventory has the 'exchange' list. We do
|
||||
-- it here for upgrade purposes.
|
||||
local inv = meta:get_inventory()
|
||||
local got_ex = false
|
||||
local got_mo = false
|
||||
for _, ln in ipairs(inv:get_lists()) do
|
||||
if ln == "exchange" then
|
||||
got_ex = true
|
||||
elseif ln == "money" then
|
||||
got_mo = true
|
||||
end
|
||||
end
|
||||
if not got_ex then
|
||||
inv:set_size("exchange", 6*2)
|
||||
end
|
||||
if not got_mo then
|
||||
inv:set_size("money", 3*2)
|
||||
end
|
||||
|
||||
if playername ~= owner then
|
||||
if meta:get_int("price") == 0 or meta:get_int("saleunit") == 0 or
|
||||
meta:get_int("item_amount") < meta:get_int("saleunit") then
|
||||
|
||||
if (meta:get_int("price") == 0 and meta:get_int("buyprice") == 0)
|
||||
or meta:get_int("saleunit") == 0 or meta:get_int("item") == ""
|
||||
then
|
||||
minetest.chat_send_player(playername,
|
||||
"Shop closed - contact "..owner ,true)
|
||||
return
|
||||
@ -349,9 +435,8 @@ function barrel.after_place_node_handler(pos, placer)
|
||||
meta:set_int("price", 0)
|
||||
meta:set_int("saleunit", 1)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("payhere", 3*2)
|
||||
inv:set_size("paidfor", 3*2)
|
||||
inv:set_size("money", 3*2)
|
||||
inv:set_size("exchange", 6*2)
|
||||
inv:set_size("money", 3*2)
|
||||
end
|
||||
if pipeworks then
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
@ -509,11 +594,27 @@ function barrel.set_infotext(nodename, meta)
|
||||
txt = "Empty shop"
|
||||
else
|
||||
local price = meta:get_int("price")
|
||||
local buyprice = meta:get_int("buyprice")
|
||||
local saleunit = meta:get_int("saleunit")
|
||||
if price == 0 or saleunit == 0 or amount < saleunit then
|
||||
|
||||
if (price == 0 and buyprice == 0) or saleunit == 0 then
|
||||
txt = "Closed shop (stock:"..amount..")"
|
||||
else
|
||||
txt = "Selling "..saleunit.." "..item.." for $"..price.." (stock:"..amount..")"
|
||||
local desc = minetest.registered_items[item].description
|
||||
if price > 0 and buyprice > 0 then
|
||||
txt = "Buying and selling "
|
||||
elseif price > 0 then
|
||||
txt = "Selling "
|
||||
else
|
||||
txt = "Buying "
|
||||
end
|
||||
if saleunit > 1 then
|
||||
txt = txt .. saleunit.." "
|
||||
end
|
||||
txt = txt .. desc
|
||||
if price > 0 then
|
||||
txt = txt .. " (stock:"..amount..")"
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user