-
Notifications
You must be signed in to change notification settings - Fork 0
/
transaction.lua
273 lines (234 loc) · 7.01 KB
/
transaction.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
local ss = server_shop
local S = core.get_translator(ss.modname)
--- Formats a string from deposit identification.
--
-- @local
-- @function format_deposit_id
-- @tparam string id Shop identifier.
-- @tparam bool buyer Denotes whether shop type is seller or buyer (default: false).
-- @treturn string String formatted as "<modname>:<buy/sell>:<id>:deposited".
local function format_deposit_id(id, buyer)
local deposit_id = ss.modname .. ":sell:"
if buyer then
deposit_id = ss.modname .. ":buy:"
end
return deposit_id .. id .. ":deposited"
end
--- Sets deposited amount for shop.
--
-- @local
-- @function set_deposit
-- @tparam string id Shop id.
-- @param player Player for whom deposit is being set.
-- @tparam int amount The amount deposit should be set to.
-- @tparam[opt] bool buyer Denotes whether shop is a seller or buyer
local function set_deposit(id, player, amount, buyer)
local p_meta = player:get_meta()
local d_type = type(amount)
if not buyer then
if d_type ~= "number" then
ss.log("error", "set_deposit: \"amount\" must be number for seller shops")
return
end
p_meta:set_int(format_deposit_id(id, false), amount)
else
if d_type == "table" then
amount = amount.name .. "," .. amount.count
elseif d_type ~= "string" then
ss.log("error", "set_deposit: \"amount\" must be a string (\"item,num\") or"
.. "table ({item, num}) for buyer shops")
return
end
if not string.find(amount, ",") then
ss.log("error", "set_deposit: malformatted \"amount\" (" .. amount
.. "), must be \"item,num\"")
return
end
p_meta:set_string(format_deposit_id(id, true), amount)
end
end
--- Retrieves amount player has deposited at shop.
--
-- @local
-- @function get_deposit
-- @tparam string id Shop id.
-- @param player Player to check.
-- @tparam[opt] bool buyer Denotes whether shop is a seller or buyer
-- @treturn int Total amount currently deposited.
local function get_deposit(id, player, buyer)
local p_meta = player:get_meta()
local deposit
if not buyer then
deposit = p_meta:get_int(format_deposit_id(id, false))
else
deposit = p_meta:get_string(format_deposit_id(id, true)):split(",")
deposit = {
name = deposit[1],
count = tonumber(deposit[2]),
}
end
return deposit
end
--- Clears deposit info from player meta.
--
-- @local
-- @tparam string id Shop identifier.
-- @tparam ObjectRef player
-- @tparam bool buyer
-- @treturn bool
local clear_deposit = function(id, player, buyer)
local p_meta = player:get_meta()
local deposit_id = format_deposit_id(id, buyer)
p_meta:set_string(deposit_id, nil)
return p_meta:get_string(deposit_id) == ""
end
--- Add item(s) to player inventory or drops on ground.
--
-- @local
-- @function player The player who is receiving the item.
-- @param product String identifier of the item.
-- @param quantity Amount to give.
local function give_product(player, product, quantity)
local istack = ItemStack(product)
if quantity then
istack:set_count(quantity)
end
-- add to player inventory or drop on ground
local pinv = player:get_inventory()
if not pinv:room_for_item("main", istack) then
core.chat_send_player(player:get_player_name(),
S("WARNING: @1 @2 was dropped on the ground.",
istack:get_count(), istack:get_description()))
core.item_drop(istack, player, player:get_pos())
else
pinv:add_item("main", istack)
end
end
--- Calculates money to be returned to player.
--
-- @local
-- @function calculate_refund
-- @param total
-- @return ItemStack list & remainder
local function calculate_refund(total)
local currencies = ss.get_currencies()
local keys = {}
-- sort currencies by value
for k in pairs(currencies) do
table.insert(keys, k)
end
table.sort(keys, function(kL, kR) return currencies[kL] > currencies[kR] end)
local refund = {}
local remain = total
for _, k in ipairs(keys) do
local v = currencies[k]
local count = math.floor(remain / v)
if count > 0 then
local stack = ItemStack(k)
stack:set_count(count)
table.insert(refund, stack)
remain = remain - (count * v)
end
end
return refund, remain
end
--- Returns remaining deposited money to player.
--
-- @local
-- @function give_refund
-- @tparam string id Shop id.
-- @param player Player to whom refund is given.
local function give_refund(id, player, buyer)
if not ss.currency_is_registered() then
ss.log("error", "no currencies registered, cannot give refund")
return
end
local p_meta = player:get_meta()
local deposit_id = format_deposit_id(id, buyer)
local refund, remain = calculate_refund(p_meta:get_int(deposit_id))
for _, istack in ipairs(refund) do
give_product(player, istack)
end
if remain > 0 then
ss.log("warning", "refund left remaining balance: Shop ID: " .. id
.. ", Player: " .. player:get_player_name() .. ", Balance: " .. remain)
p_meta:set_int(deposit_id, remain)
return
end
if remain < 0 then
ss.log("warning", "refunded extra money: Shop ID: " .. id
.. ", Player: " .. player:get_player_name() .. ", Discrepancy: " .. remain)
end
-- reset deposited amount after refund
p_meta:set_string(deposit_id, nil)
end
--- Calculates the price of item being purchased.
--
-- @local
-- @function calculate_price
-- @param shop_id String identifier of shop.
-- @param item_id String identifier of item (e.g. default:dirt).
-- @param quantity Number of item being purchased.
-- @return Total value of purchase.
local function calculate_price(shop_id, item_id, quantity)
local shop = ss.get_shop(shop_id)
if not shop then
return 0
end
local price_per = 0
for _, i in ipairs(shop.products) do
if i[1] == item_id then
price_per = i[2]
break
end
end
return price_per * quantity
end
--- Calculates value of an item stack against registered currencies.
--
-- @local
-- @function calculate_currency_value
-- @tparam ItemStack stack Item stack.
-- @treturn int Total value of item stack.
local function calculate_currency_value(stack)
local value = 0
for c, v in pairs(ss.get_currencies()) do
if stack:get_name() == c then
value = stack:get_count() * v
break
end
end
return value
end
--- Calculates value of an item stack against a registered shop's product list.
--
-- @local
-- @function calculate_product_value
-- @tparam ItemStack stack Item stack.
-- @tparam string id Shop id.
-- @tparam bool buyer Determines whether to parse buyer shops or seller shops.
-- @treturn int Total value of item stack.
local function calculate_product_value(stack, id, buyer)
local shop = ss.get_shop(id, buyer)
if not shop then return 0 end
local item_name = stack:get_name()
local value_per = 0
for _, product in ipairs(shop.products) do
if item_name == product[1] then
value_per = product[2]
break
end
end
return value_per * stack:get_count()
end
return {
set_deposit = set_deposit,
get_deposit = get_deposit,
clear_deposit = clear_deposit,
give_product = give_product,
calculate_refund = calculate_refund,
give_refund = give_refund,
calculate_price = calculate_price,
calculate_currency_value = calculate_currency_value,
calculate_product_value = calculate_product_value,
}