forked from SmallJoker/exchange_shop
-
Notifications
You must be signed in to change notification settings - Fork 2
/
shop_functions.lua
133 lines (116 loc) · 3.96 KB
/
shop_functions.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
local S = exchange_shop.S
local min = math.min
-- Tool wear aware replacement for contains_item.
local function list_contains_items(inv, listname, stacks)
local list = inv:get_list(listname)
-- Convert the stacks into {item = count} and {item = wear} tables
-- Note that this uses the "best" tool wear if there are multiple tools for
-- simplicity (and you can't set wear with the item picker anyway)
local counts = {}
local wears = {}
for _, stack in ipairs(stacks) do
local name = stack:get_name()
counts[name] = (counts[name] or 0) + stack:get_count()
wears[name] = min(stack:get_wear(), wears[name] or math.huge)
end
-- Decrease the stored counts for every item in the list
for _, list_stack in ipairs(list) do
local name = list_stack:get_name()
if counts[name] and list_stack:get_wear() <= wears[name] then
counts[name] = counts[name] - list_stack:get_count()
end
end
-- Return false if any count is above 0
for _, count in pairs(counts) do
if count > 0 then
return false
end
end
return true
end
-- Tool wear aware replacement for remove_item.
function exchange_shop.list_remove_item(inv, listname, stack)
local wanted_count = stack:get_count()
if wanted_count == 0 then
return stack
end
local list = inv:get_list(listname)
local name = stack:get_name()
local wear = stack:get_wear()
-- Information about the removed stack
-- this includes the metadata of the last taken stack
local taken_stack = ItemStack()
local remaining = wanted_count
local removed_wear = 0
for index, list_stack in ipairs(list) do
if list_stack:get_name() == name and
list_stack:get_wear() <= wear then
-- Only sell better tools (less worn out)
taken_stack = list_stack:take_item(remaining)
inv:set_stack(listname, index, list_stack)
removed_wear = math.max(removed_wear, taken_stack:get_wear())
remaining = remaining - taken_stack:get_count()
if remaining == 0 then
break
end
end
end
-- For oversized stacks, ItemStack:add_item returns a leftover
-- handle the stack count manually to avoid this issue
taken_stack:set_count(wanted_count - remaining)
taken_stack:set_wear(removed_wear)
return taken_stack
end
function exchange_shop.exchange_action(player_inv, shop_inv, pos)
if not shop_inv:is_empty("custm_ej") then
return S("One or multiple ejection fields are filled.") .. " " ..
S("Please empty them or contact the shop owner.")
end
local owner_wants = shop_inv:get_list("cust_ow")
local owner_gives = shop_inv:get_list("cust_og")
-- Check for space in the shop
for _, item in ipairs(owner_wants) do
if not shop_inv:room_for_item("custm", item) then
return S("The stock in this shop is full.") .. " " ..
S("Please contact the shop owner.")
end
end
-- Check availability of the shop's items
if not list_contains_items(shop_inv, "stock", owner_gives) then
return S("This shop is sold out.")
end
-- Check for space in the player's inventory
for _, item in ipairs(owner_gives) do
if not player_inv:room_for_item("main", item) then
return S("You do not have enough space in your inventory.")
end
end
-- Check availability of the player's items
if not list_contains_items(player_inv, "main", owner_wants) then
return S("You do not have the required items.")
end
local list_remove_item = exchange_shop.list_remove_item
-- Conditions are ok: (try to) exchange now
local fully_exchanged = true
for _, item in ipairs(owner_wants) do
local stack = list_remove_item(player_inv, "main", item)
if shop_inv:room_for_item("custm", stack) then
shop_inv:add_item("custm", stack)
else
-- Move to ejection field
shop_inv:add_item("custm_ej", stack)
fully_exchanged = false
end
end
for _, item in ipairs(owner_gives) do
local stack = list_remove_item(shop_inv, "stock", item)
if player_inv:room_for_item("main", stack) then
player_inv:add_item("main", stack)
else
minetest.item_drop(stack, nil, pos)
end
end
if not fully_exchanged then
return S("Warning! Stacks are overflowing somewhere!")
end
end