Implement shop chests

master
rubenwardy 2018-11-25 19:42:40 +00:00
parent eee68f4b7f
commit be618fdc91
20 changed files with 413 additions and 43 deletions

View File

@ -6,6 +6,7 @@ exclude_files = {"mods/mtg", "mods/libs/lib_chatcmdbuilder", "mods/areas"}
globals = {
"minetest", "company",
"areas", "sfinv",
"shop",
ChatCmdBuilder = {fields = {"types"}}
}

View File

@ -38,8 +38,39 @@ Permissions can be granted by the CEO of a company to other
players at any time. This is done as part of the player tab
in the inventory formspec.
## Common Variable Names
You can see a list of permissions and their meaning in
mods/capitalism/company/permissions.lua
## Data Persistence
There's a helper available to save and load arrays of classes:
```lua
if minetest then
local storage = minetest.get_mod_storage()
lib_utils.make_saveload(company, storage, "_companies", "add", company.Company)
company.load()
end
```
make_saveload parameters:
* **tab** - The mod's table.
* **storage** - ModStorageRef
* **itemarraykey** - Key for the table of object, `tab[itemarraykey]`.
* **regkey** - The add function, `tab[regkey]`. Returns true on success.
* **class** - a reference to the class. Must have:
* `:new()` with metatables and initialisation
* from_table(), returns true if successful
* to_table(), returns table
## Conventions
### Common Variable Names
* `name` - player name
* `comp` - company
* `cname` - company name
* `pname` - player name
* `area` - an *areas* area
* `acc` - bank account

View File

@ -8,4 +8,5 @@ company.permissions = {
OWNS_AREA = "Can perform general area admin actions",
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",
}

View File

@ -71,6 +71,27 @@ function shop.create_shop(pname, pos)
return true
end
function shop.unassign_chest(s, pos, inv)
local chest = s:get_chest(pos)
if chest.itemname then
local stack = ItemStack(chest.itemname)
stack:set_count(chest.count)
if inv and inv:room_for_item("main", stack) then
inv:add_item("main", stack)
end
local node_inv = minetest.get_inventory({ type = "node", pos = pos })
node_inv:set_list("main", {})
local new = s:get_item_or_make(chest.itemname)
new.stock = new.stock - chest.count
end
chest.itemname = nil
chest.count = 0
end
-- Minetest won't be available in tests
if minetest then
local storage = minetest.get_mod_storage()

View File

@ -7,37 +7,153 @@ function shop.show_shop_form(pname, pos)
end
end
shop.show_admin_form = lib_quickfs.register("shop:counter_admin", function(context, pname, pos)
assert(shop.can_admin(pname, pos))
local fs = {
"size[4,6]",
"tablecolumns[color;text;text;text;text]",
-- "tableoptions[background=#00000000;border=false]",
"table[0,0;4,6;list_items;",
"#999,Description,Stock,Price,Sales",
}
-- Description Stock PricePI Sold
local s = shop.get_by_pos(pos)
assert(s)
for _, item in pairs(s:get_items()) do
local fs = {
"size[7,7]",
"label[0,0;",
minetest.formspec_escape(s.name),
"]",
"dropdown[3,0;4;a;Open;1]",
"tablecolumns[color;text;text;text;text]",
-- "tableoptions[background=#00000000;border=false]",
"table[0,1;4.8,6;list_items;",
"#999,Description,Stock,Price,Sales",
}
-- Description Stock PricePI Sold
local items_kv = s:get_items()
local items = {}
context.items = items
for _, item in pairs(items_kv) do
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] = item.description
fs[#fs + 1] = desc
fs[#fs + 1] = ","
fs[#fs + 1] = item.stock
fs[#fs + 1] = ","
fs[#fs + 1] = item.price
fs[#fs + 1] = (item.price >= 0) and item.price or "-"
fs[#fs + 1] = ","
fs[#fs + 1] = item.sold
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]
fs[#fs + 1] = "field[5.3,1.3;2,1;price;;"
fs[#fs + 1] = item.price
fs[#fs + 1] = "]"
fs[#fs + 1] = "button[5,2;2,1;set_price;Set Price]"
fs[#fs + 1] = "box[5,3;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,4;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,5;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,6;1.8,0.8;#222]"
else
fs[#fs + 1] = "box[5,1;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,2;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,3;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,4;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,5;1.8,0.8;#222]"
fs[#fs + 1] = "box[5,6;1.8,0.8;#222]"
end
return table.concat(fs, "")
end,
function(context, player, formname, fields)
if fields.list_items then
local evt = minetest.explode_table_event(fields.list_items)
context.selected = evt.row - 1
return true
end
if fields.set_price then
local item = context.items[context.selected]
if item then
item.price = tonumber(fields.price) or -1
end
shop.dirty = true
return true
end
end)
shop.show_chest_form = lib_quickfs.register("shop:chest", function(context, pname, s, pos)
local inv = minetest.get_inventory({ type = "node", pos = pos })
s:chest_poll(pos, inv)
local title = "Unassigned chest"
local chest = s:get_chest(pos)
if chest.itemname then
local def = minetest.registered_items[chest.itemname] or {}
title = def.description or chest.itemname
local item = s:get_item(chest.itemname)
if item.price >= 0 then
title = title .. " ($" .. item.price .. ")"
else
title = title .. " (not for sale)"
end
end
local spos = pos.x .. "," .. pos.y .. "," .. pos.z
local fs = {
"size[8,7.3]",
default.gui_slots,
"label[0,0;", minetest.formspec_escape(title), "]",
"button[6,0.3;2,0;overview;Shop Overview]",
"list[nodemeta:", spos, ";main;0,0.9;8,2;]",
"list[current_player;main;0,3.25;8,1;]",
"list[current_player;main;0,4.48;8,3;8]",
"listring[nodemeta:", spos, ";main]",
"listring[current_player;main]",
default.get_hotbar_bg(0, 3.25)
}
if chest.itemname then
fs[#fs + 1] = "button[4,0.3;2,0;unassign;Unassign]"
end
return table.concat(fs, "")
end,
function(context, player, formname, fields)
if fields.unassign then
local s = context.args[1]
local pos = context.args[2]
shop.unassign_chest(s, pos, player:get_inventory())
return true
end
if fields.overview then
shop.show_admin_form(player:get_player_name(), context.args[2])
return false
end
end)

View File

@ -27,4 +27,115 @@ minetest.register_node("shop:counter", {
end,
})
local function can_interact_with_chest(player, pos)
local area = land.get_by_pos(pos)
return area and company.check_perm(player:get_player_name(), area.owner,
"SHOP_CHEST", { area = area })
end
local function can_add_item_to_chest(player, pos, stack)
if not can_interact_with_chest(player, pos) then
return false
end
local s = shop.get_by_pos(pos)
local chest = s:get_chest(pos)
return not chest.itemname or chest.itemname == stack:get_name()
end
local chest_template
chest_template = {
description = "Shop Chest",
sounds = default.node_sound_wood_defaults(),
groups = {choppy = 2, oddly_breakable_by_hand = 2},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Locked Chest")
meta:set_string("owner", "")
local inv = meta:get_inventory()
inv:set_size("main", 8*2)
end,
after_place_node = function(pos, player)
local pname = player:get_player_name()
if not can_interact_with_chest(player, pos) then
minetest.chat_send_player(pname,
"You don't have permission to do that! (Are you acting as the right company?)")
minetest.set_node(pos, { name = "air" })
return false
end
local s = shop.get_by_pos(pos)
if not s then
minetest.chat_send_player(pname, "Unable to find a shop")
minetest.set_node(pos, { name = "air" })
return false
end
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Shop chest (" .. s.name .. ")")
s:add_chest(pos)
shop.dirty = true
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main") and
can_interact_with_chest(player, pos)
end,
allow_metadata_inventory_move = function(pos, from_list, from_index,
to_list, to_index, count, player)
return can_interact_with_chest(player, pos) and count or 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return can_add_item_to_chest(player, pos, stack) and stack:get_count() or 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return can_interact_with_chest(player, pos) and stack:get_count() or 0
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local s = shop.get_by_pos(pos)
s:chest_add_item(pos, stack)
shop.dirty = true
-- HACK: player must have the inventory open
shop.show_chest_form(player:get_player_name(), s, pos)
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local s = shop.get_by_pos(pos)
s:chest_remove_item(pos, stack)
shop.dirty = true
end,
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local pname = clicker:get_player_name()
if not can_interact_with_chest(clicker, pos) then
minetest.chat_send_player(pname,
"You don't have permission to do that! (Are you acting as the right company?)")
return itemstack
end
local s = shop.get_by_pos(pos)
if not s then
minetest.chat_send_player(pname, "Unable to find a shop")
minetest.set_node(pos, { name = "air" })
return false
end
shop.show_chest_form(pname, s, pos)
return itemstack
end,
}
minetest.register_node("shop:chest", _.extend(chest_template, {
tiles = {
"shop_chest_top.png",
"shop_chest_top.png",
"shop_chest_side.png",
"shop_chest_side.png",
"shop_chest_front.png",
"shop_chest_side.png"
},
}))
print("[shop] loaded")

View File

@ -5,29 +5,118 @@ function Shop:new(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
obj.name = nil
obj.a_id = nil
obj.name = nil
obj.a_id = nil
obj.chests = {}
obj.items = {}
return obj
end
function Shop:to_table()
return {
name = self.name,
a_id = self.a_id
name = self.name,
a_id = self.a_id,
chests = self.chests,
items = self.items,
}
end
function Shop:from_table(tab)
self.name = tab.name
self.a_id = tab.a_id
self.name = tab.name
self.a_id = tab.a_id
self.chests = tab.chests or {}
self.items = tab.items or {}
return self.a_id ~= nil
end
function Shop:get_items()
-- TODO: Implement this
return {
{ description = "Chips", stock = 1000, price = 10, sold = 100 },
{ description = "Phones", stock = 10, price = 10000, sold = 2 },
{ description = "Silicon", stock = 100, price = 2, sold = 0 },
return self.items
end
function Shop:get_item(name)
return self.items[name]
end
function Shop:get_item_or_make(name)
local item = self.items[name] or {
name = name,
stock = 0,
price = -1,
sold = 0,
}
self.items[name] = item
return item
end
function Shop:add_chest(pos)
local posstr = minetest.pos_to_string(vector.floor(pos))
assert(not self.chests[posstr])
self.chests[posstr] = {
pos = vector.new(pos),
itemname = nil,
count = 0,
}
end
function Shop:get_chest(pos)
local posstr = minetest.pos_to_string(vector.floor(pos))
return self.chests[posstr]
end
function Shop:chest_poll(pos, inv)
local chest = self:get_chest(pos)
assert(chest)
local list = inv:get_list("main")
local count = 0
local iname = nil
for i=1, #list do
if not list[i]:is_empty() then
count = count + list[i]:get_count()
assert(not iname or iname == list[i]:get_name())
iname = list[i]:get_name()
end
end
if chest.itemname then
local old = self:get_item(chest.itemname)
if old then
old.stock = old.stock - chest.count
end
end
chest.itemname = iname or chest.itemname
chest.count = count
if chest.itemname then
local new = self:get_item_or_make(chest.itemname)
new.stock = new.stock + count
end
end
function Shop:chest_add_item(pos, stack)
local chest = self:get_chest(pos)
assert(chest)
chest.itemname = stack:get_name()
chest.count = chest.count + stack:get_count()
local new = self:get_item_or_make(chest.itemname)
new.stock = new.stock + stack:get_count()
end
function Shop:chest_remove_item(pos, stack)
local chest = self:get_chest(pos)
assert(chest)
chest.count = chest.count - stack:get_count()
local new = self:get_item_or_make(chest.itemname)
new.stock = new.stock - stack:get_count()
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

View File

@ -137,7 +137,7 @@ end
function beds.on_rightclick(pos, player)
local name = player:get_player_name()
local ppos = player:getpos()
local ppos = player:get_pos()
local tod = minetest.get_timeofday()
if tod > 0.2 and tod < 0.805 then

View File

@ -53,7 +53,7 @@ end
function beds.set_spawns()
for name,_ in pairs(beds.player) do
local player = minetest.get_player_by_name(name)
local p = player:getpos()
local p = player:get_pos()
-- but don't change spawn location if borrowing a bed
if not minetest.is_protected(p, name) then
beds.spawn[name] = p

View File

@ -58,7 +58,7 @@ function boat.on_rightclick(self, clicker)
clicker:set_detach()
player_api.player_attached[name] = false
player_api.set_animation(clicker, "stand" , 30)
local pos = clicker:getpos()
local pos = clicker:get_pos()
pos = {x = pos.x, y = pos.y + 0.2, z = pos.z}
minetest.after(0.1, function()
clicker:setpos(pos)
@ -116,7 +116,7 @@ function boat.on_punch(self, puncher)
local leftover = inv:add_item("main", "boats:boat")
-- if no room in inventory add a replacement boat to the world
if not leftover:is_empty() then
minetest.add_item(self.object:getpos(), leftover)
minetest.add_item(self.object:get_pos(), leftover)
end
end
-- delay remove to ensure player is detached
@ -153,7 +153,7 @@ function boat.on_step(self, dtime)
end
local velo = self.object:getvelocity()
if self.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
self.object:setpos(self.object:getpos())
self.object:setpos(self.object:get_pos())
return
end
local s = get_sign(self.v)
@ -167,7 +167,7 @@ function boat.on_step(self, dtime)
self.v = 5 * get_sign(self.v)
end
local p = self.object:getpos()
local p = self.object:get_pos()
p.y = p.y - 0.5
local new_velo
local new_acce = {x = 0, y = 0, z = 0}
@ -181,7 +181,7 @@ function boat.on_step(self, dtime)
end
new_velo = get_velocity(self.v, self.object:getyaw(),
self.object:getvelocity().y)
self.object:setpos(self.object:getpos())
self.object:setpos(self.object:get_pos())
else
p.y = p.y + 1
if is_water(p) then
@ -194,18 +194,18 @@ function boat.on_step(self, dtime)
new_acce = {x = 0, y = 5, z = 0}
end
new_velo = get_velocity(self.v, self.object:getyaw(), y)
self.object:setpos(self.object:getpos())
self.object:setpos(self.object:get_pos())
else
new_acce = {x = 0, y = 0, z = 0}
if math.abs(self.object:getvelocity().y) < 1 then
local pos = self.object:getpos()
local pos = self.object:get_pos()
pos.y = math.floor(pos.y) + 0.5
self.object:setpos(pos)
new_velo = get_velocity(self.v, self.object:getyaw(), 0)
else
new_velo = get_velocity(self.v, self.object:getyaw(),
self.object:getvelocity().y)
self.object:setpos(self.object:getpos())
self.object:setpos(self.object:get_pos())
end
end
end

View File

@ -197,7 +197,7 @@ minetest.register_on_dieplayer(function(player)
return
end
local pos = vector.round(player:getpos())
local pos = vector.round(player:get_pos())
local player_name = player:get_player_name()
-- check if it's possible to place bones, if not find space near player

View File

@ -148,7 +148,7 @@ minetest.register_craftitem("bucket:bucket_empty", {
if inv:room_for_item("main", {name=liquiddef.itemname}) then
inv:add_item("main", liquiddef.itemname)
else
local pos = user:getpos()
local pos = user:get_pos()
pos.y = math.floor(pos.y + 0.5)
minetest.add_item(pos, liquiddef.itemname)
end

View File

@ -121,7 +121,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if inv:room_for_item("main", new_stack) then
inv:add_item("main", new_stack)
else
minetest.add_item(player:getpos(), new_stack)
minetest.add_item(player:get_pos(), new_stack)
end
else
stack:get_meta():from_table({ fields = data })
@ -247,7 +247,7 @@ minetest.register_craftitem("default:skeleton_key", {
itemstack = new_stack
else
if inv:add_item("main", new_stack):get_count() > 0 then
minetest.add_item(user:getpos(), new_stack)
minetest.add_item(user:get_pos(), new_stack)
end -- else: added to inventory successfully
end

View File

@ -16,7 +16,7 @@ local item = {
burn_up = function(self)
-- disappear in a smoke puff
self.object:remove()
local p = self.object:getpos()
local p = self.object:get_pos()
minetest.sound_play("default_item_smoke", {
pos = p,
max_hear_distance = 8,
@ -48,7 +48,7 @@ local item = {
if self.ignite_timer > 10 then
self.ignite_timer = 0
local node = minetest.get_node_or_nil(self.object:getpos())
local node = minetest.get_node_or_nil(self.object:get_pos())
if not node then
return
end

View File

@ -169,7 +169,7 @@ if flame_sound then
function fire.update_player_sound(player)
local player_name = player:get_player_name()
-- Search for flame nodes in radius around player
local ppos = player:getpos()
local ppos = player:get_pos()
local areamin = vector.subtract(ppos, radius)
local areamax = vector.add(ppos, radius)
local fpos, num = minetest.find_nodes_in_area(

View File

@ -89,7 +89,7 @@ minetest.register_chatcommand("sethome", {
func = function(name)
name = name or "" -- fallback to blank name if nil
local player = minetest.get_player_by_name(name)
if player and sethome.set(name, player:getpos()) then
if player and sethome.set(name, player:get_pos()) then
return true, "Home set!"
end
return false, "Player not found!"

View File

@ -23,7 +23,7 @@ local function rotate_and_place(itemstack, placer, pointed_thing)
local param2 = 0
if placer then
local placer_pos = placer:getpos()
local placer_pos = placer:get_pos()
if placer_pos then
param2 = minetest.dir_to_facedir(vector.subtract(p1, placer_pos))
end