Add purchasing from shops

master
rubenwardy 2018-11-25 22:15:12 +00:00
parent 3830040ad6
commit 8411dfd3ff
4 changed files with 251 additions and 2 deletions

View File

@ -9,4 +9,5 @@ company.permissions = {
SHOP_CREATE = "Can create a shop on commercial areas",
SHOP_ADMIN = "Can change the settings of a shop, including prices",
SHOP_CHEST = "Can place, modify shop chests",
BUY_ITEMS = "Can buy from shops",
}

View File

@ -92,6 +92,107 @@ function shop.unassign_chest(s, pos, inv)
chest.count = 0
end
function shop.can_buy(pos, pname, itemname, count, price)
assert(type(pos) == "table")
assert(type(pname) == "string")
assert(type(itemname) == "string" and minetest.registered_items[itemname])
assert(type(count) == "number" and count >= 0)
assert(type(price) == "number" and price >= 0)
local comp = company.get_active(pname)
if comp and not comp:check_perm(pname, "BUY_ITEMS",
{ itemname = itemname, count = count, price = price }) then
return false, "Missing permission: BUY_ITEMS"
end
local acc = banking.get_by_owner(comp and comp.name or pname)
if not acc then
return false, "You don't have a bank account"
end
if acc.balance < price then
return false, "Insufficient funds"
end
return true
end
function shop.buy(pos, pname, item, count)
assert(type(pos) == "table")
assert(type(pname) == "string")
assert(type(item) == "table")
assert(type(count) == "number" and count >= 0)
if count > item.stock then
return false, "Not enough stock"
end
local price = count * item.price
local suc, msg = shop.can_buy(pos, pname, item.name, count, price)
if not suc then
return false, msg
end
local to_give = ItemStack({ name = item.name, count = count })
local pinv = minetest.get_inventory({ type = "player", name = pname })
if not pinv:room_for_item("main", to_give) then
return false, "Not enough room in inv"
end
local area = land.get_by_pos(pos)
assert(area.owner:sub(1, 2) == "c:")
local s = shop.get_by_area(area.id)
local comp = company.get_active(pname)
local account = banking.get_by_owner(comp and comp.name or pname)
local owner_account = banking.get_by_owner(area.owner)
assert(account)
assert(owner_account)
-- Locate chest
local chests = s:get_chests_for_item(item.name, count, function(chest)
return minetest.get_node(chest.pos) ~= "ignore"
end)
if not chests then
return false, "Map unloaded"
-- chests = s:get_chests_for_item(item.name, count)
--
-- if not chests then
-- return false, "Error: unexpected out of stock. This should never happen."
-- end
end
local took = 0
for i=1, #chests do
local inv = minetest.get_inventory({ type = "node", pos = chests[i].pos })
local stack = inv:remove_item("main", { name = item.name, count = count - took })
if not stack:is_empty() then
took = took + stack:get_count()
s:chest_remove_item(chests[i].pos, stack)
if took == count then
break
end
end
end
assert(took == count)
if not banking.transfer(pname, account.owner, owner_account.owner, price,
"Purchase of item name=" .. item.name .. ", count=" .. count) then
return false, "Card payment error"
end
pinv:add_item("main", to_give)
shop.dirty = true
return true
end
-- Minetest won't be available in tests
if minetest then
local storage = minetest.get_mod_storage()

View File

@ -2,8 +2,7 @@ function shop.show_shop_form(pname, pos)
if shop.can_admin(pname, pos) then
shop.show_admin_form(pname, pos)
else
minetest.chat_send_player(pname, "Shop checkout unimplemented")
-- shop.show_shop_checkout_form(playername, pos)
shop.show_customer_form(pname, pos)
end
end
@ -170,3 +169,136 @@ shop.show_chest_form = lib_quickfs.register("shop:chest", {
end
end,
})
shop.show_customer_form = lib_quickfs.register("shop:customer", {
get = function(context, player, pos)
local s = shop.get_by_pos(pos)
assert(s)
local fs = {
"size[8,9.25]",
company.get_company_header(context.pname, 8, "balance"),
"label[4,1;",
minetest.formspec_escape(s.name),
"]",
"tablecolumns[color;text;text;text]",
"list[current_player;main;0,5.25;8,1;]",
"list[current_player;main;0,6.48;8,3;8]",
default.get_hotbar_bg(0, 5.25),
"table[0,1;5.8,4;list_items;",
"#999,Description,Stock,Price",
}
-- Description Stock PricePI Sold
local items_kv = s:get_items()
local items = {}
context.items = items
for _, item in pairs(items_kv) do
if item.price >= 0 then
local def = minetest.registered_items[item.name] or {}
local desc = def.description or item.name
items[#items + 1] = item
fs[#fs + 1] = ",,"
fs[#fs + 1] = desc
fs[#fs + 1] = ","
fs[#fs + 1] = item.stock
fs[#fs + 1] = ","
fs[#fs + 1] = item.price
end
end
if next(items) and not context.selected then
context.selected = 1
end
if context.selected then
if context.selected > #items then
context.selected = #items
end
if context.selected and context.selected > 0 then
fs[#fs + 1] = ";"
fs[#fs + 1] = tostring(context.selected + 1)
end
end
fs[#fs + 1] = "]"
if context.selected and context.selected > 0 then
local item = items[context.selected]
local suc, msg = shop.can_buy(pos, context.pname, item.name, 0, 0)
if suc then
fs[#fs + 1] = "field[6.3,1.3;2,1;num;;"
fs[#fs + 1] = tostring(context.num or 1)
fs[#fs + 1] = "]"
fs[#fs + 1] = "button[6,2;2,1;buyn;Buy]"
else
fs[#fs + 1] = "box[6,1;1.8,0.8;#f00]"
fs[#fs + 1] = "box[6,2;1.8,0.8;#222]"
fs[#fs + 1] = "label[6,1;"
fs[#fs + 1] = minetest.formspec_escape(msg)
fs[#fs + 1] = "]"
end
if context.error then
fs[#fs + 1] = "box[6,3;1.8,0.8;#f00]"
fs[#fs + 1] = "label[6,3;"
fs[#fs + 1] = minetest.formspec_escape(context.error)
fs[#fs + 1] = "]"
else
fs[#fs + 1] = "box[6,3;1.8,0.8;#222]"
end
fs[#fs + 1] = "box[6,4;1.8,0.8;#222]"
else
fs[#fs + 1] = "box[6,1;1.8,0.8;#222]"
fs[#fs + 1] = "box[6,2;1.8,0.8;#222]"
fs[#fs + 1] = "box[6,3;1.8,0.8;#222]"
fs[#fs + 1] = "box[6,4;1.8,0.8;#222]"
end
return table.concat(fs, "")
end,
on_receive_fields = function(context, player, fields, pos)
if fields.switch then
company.show_company_select_dialog(context.pname, function(player2)
shop.show_customer_form(player2:get_player_name(), unpack(context.args))
end)
return
end
if fields.list_items then
local evt = minetest.explode_table_event(fields.list_items)
context.selected = evt.row - 1
return true
end
if fields.num then
context.num = tonumber(fields.num) or 1
end
if fields.buyn then
local item = context.items[context.selected]
if item then
local count = tonumber(fields.num)
if not count then
context.error = "Not a number!"
return true
end
local _, msg = shop.buy(pos, context.pname, item, count)
if msg then
context.error = msg
else
context.error = nil
end
return true
end
return true
end
end,
})

View File

@ -67,6 +67,21 @@ function Shop:get_chest(pos)
return self.chests[posstr]
end
function Shop:get_chests_for_item(name, count, filter)
local ret = {}
for _, chest in pairs(self.chests) do
if chest.itemname == name and chest.count > 0 and (not filter or filter(chest)) then
count = count - chest.count
ret[#ret + 1] = chest
if count <= 0 then
return ret
end
end
end
return nil
end
function Shop:chest_poll(pos, inv)
local chest = self:get_chest(pos)
assert(chest)