2012-04-08 22:13:33 +02:00
|
|
|
NPCTrade = {}
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
-- private variables
|
|
|
|
local BUY = 1
|
|
|
|
local SELL = 2
|
|
|
|
local CURRENCY = 'gold'
|
|
|
|
local WEIGHT_UNIT = 'oz'
|
|
|
|
local LAST_INVENTORY = 10
|
|
|
|
|
2012-04-08 22:13:33 +02:00
|
|
|
local npcWindow
|
|
|
|
local itemsPanel
|
|
|
|
local radioTabs
|
|
|
|
local radioItems
|
|
|
|
local searchText
|
|
|
|
local setupPanel
|
|
|
|
local quantity
|
|
|
|
local quantityScroll
|
|
|
|
local nameLabel
|
|
|
|
local priceLabel
|
|
|
|
local moneyLabel
|
|
|
|
local weightLabel
|
|
|
|
local capacityLabel
|
2012-04-29 11:08:55 +02:00
|
|
|
local tradeButton
|
|
|
|
local buyTab
|
|
|
|
local sellTab
|
2012-04-08 22:13:33 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
local buyWithBackpack
|
|
|
|
local ignoreCapacity
|
|
|
|
local ignoreEquipped
|
|
|
|
local showAllItems
|
2012-04-08 22:13:33 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
local playerFreeCapacity
|
|
|
|
local playerMoney
|
|
|
|
local tradeItems = {}
|
|
|
|
local playerItems
|
|
|
|
|
|
|
|
local selectedItem
|
|
|
|
|
|
|
|
-- private functions
|
|
|
|
local function clearSelectedItem()
|
|
|
|
nameLabel:clearText()
|
|
|
|
weightLabel:clearText()
|
|
|
|
priceLabel:clearText()
|
|
|
|
quantityLabel:clearText()
|
|
|
|
tradeButton:disable()
|
|
|
|
quantityScroll:setMaximum(1)
|
|
|
|
if selectedItem then
|
|
|
|
radioItems:selectWidget(nil)
|
|
|
|
selectedItem = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function getCurrentTradeType()
|
|
|
|
if tradeButton:getText() == tr('Buy') then
|
|
|
|
return BUY
|
|
|
|
else
|
|
|
|
return SELL
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function getItemPrice(item)
|
|
|
|
if getCurrentTradeType() == BUY then
|
|
|
|
if buyWithBackpack:isChecked() then
|
|
|
|
if item.ptr:isStackable() then
|
|
|
|
return item.price*quantityScroll:getValue() + 20;
|
|
|
|
else
|
|
|
|
return item.price*quantityScroll:getValue() + math.ceil(quantityScroll:getValue()/20)*20
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return item.price*quantityScroll:getValue()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function getSellQuantity(item)
|
|
|
|
if not playerItems[item.ptr:getId()] then
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
|
|
|
local removeAmount = 0
|
|
|
|
if ignoreEquipped:isChecked() then
|
|
|
|
local localPlayer = g_game.getLocalPlayer()
|
|
|
|
for i=1,LAST_INVENTORY do
|
|
|
|
local inventoryItem = localPlayer:getInventoryItem(i)
|
|
|
|
if inventoryItem and inventoryItem:getId() == item.ptr:getId() then
|
|
|
|
removeAmount = removeAmount + inventoryItem:getCount()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return playerItems[item.ptr:getId()] - removeAmount
|
|
|
|
end
|
|
|
|
|
|
|
|
local function canTradeItem(item)
|
|
|
|
if getCurrentTradeType() == BUY then
|
|
|
|
return (ignoreCapacity:isChecked() or (not ignoreCapacity:isChecked() and playerFreeCapacity >= item.weight)) and playerMoney >= getItemPrice(item)
|
|
|
|
else
|
|
|
|
return getSellQuantity(item) > 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function refreshItem(item)
|
|
|
|
nameLabel:setText(item.name)
|
|
|
|
weightLabel:setText(string.format('%.2f', item.weight) .. ' ' .. WEIGHT_UNIT)
|
|
|
|
priceLabel:setText(getItemPrice(item) .. ' ' .. CURRENCY)
|
|
|
|
|
|
|
|
quantityLabel:setText(1)
|
|
|
|
quantityScroll:setValue(1)
|
|
|
|
|
|
|
|
if getCurrentTradeType() == BUY then
|
|
|
|
local capacityMaxCount = math.floor(playerFreeCapacity / item.weight)
|
|
|
|
if ignoreCapacity:isChecked() then
|
|
|
|
capacityMaxCount = 100
|
|
|
|
end
|
|
|
|
local priceMaxCount = math.floor(playerMoney / getItemPrice(item))
|
|
|
|
quantityScroll:setMaximum(math.max(0, math.min(100, math.min(priceMaxCount, capacityMaxCount))))
|
|
|
|
else
|
|
|
|
local removeAmount = 0
|
|
|
|
if ignoreEquipped:isChecked() then
|
|
|
|
local localPlayer = g_game.getLocalPlayer()
|
|
|
|
for i=1,LAST_INVENTORY do
|
|
|
|
local inventoryItem = localPlayer:getInventoryItem(i)
|
|
|
|
if inventoryItem and inventoryItem:getId() == item.ptr:getId() then
|
|
|
|
removeAmount = removeAmount + inventoryItem:getCount()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
quantityScroll:setMaximum(math.max(0, math.min(100, getSellQuantity(item))))
|
|
|
|
end
|
|
|
|
|
|
|
|
setupPanel:enable()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function refreshTradeItems()
|
|
|
|
local layout = itemsPanel:getLayout()
|
|
|
|
layout:disableUpdates()
|
|
|
|
|
|
|
|
clearSelectedItem()
|
|
|
|
|
|
|
|
searchText:clearText()
|
|
|
|
setupPanel:disable()
|
|
|
|
itemsPanel:destroyChildren()
|
|
|
|
|
|
|
|
if radioItems then
|
|
|
|
radioItems:destroy()
|
|
|
|
end
|
2012-06-26 00:13:30 +02:00
|
|
|
radioItems = UIRadioGroup.create()
|
2012-04-29 11:08:55 +02:00
|
|
|
|
|
|
|
local currentTradeItems = tradeItems[getCurrentTradeType()]
|
|
|
|
for key,item in pairs(currentTradeItems) do
|
2012-06-26 00:13:30 +02:00
|
|
|
local itemBox = g_ui.createWidget('NPCItemBox', itemsPanel)
|
2012-04-29 11:08:55 +02:00
|
|
|
itemBox.item = item
|
2012-04-30 08:34:29 +02:00
|
|
|
|
2012-04-30 00:43:23 +02:00
|
|
|
local name = item.name
|
|
|
|
local weight = string.format('%.2f', item.weight) .. ' ' .. WEIGHT_UNIT
|
|
|
|
local price = item.price .. ' ' .. CURRENCY
|
|
|
|
itemBox:setText(name .. '\n' .. weight .. '\n' .. price)
|
2012-04-29 11:08:55 +02:00
|
|
|
|
2012-04-30 00:43:23 +02:00
|
|
|
local itemWidget = itemBox:getChildById('item')
|
|
|
|
itemWidget:setItem(item.ptr)
|
|
|
|
itemWidget.onMouseRelease = NPCTrade.itemPopup
|
2012-04-29 11:08:55 +02:00
|
|
|
|
|
|
|
radioItems:addWidget(itemBox)
|
|
|
|
end
|
|
|
|
|
|
|
|
layout:enableUpdates()
|
|
|
|
layout:update()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function refreshPlayerGoods()
|
|
|
|
moneyLabel:setText(playerMoney .. ' ' .. CURRENCY)
|
|
|
|
capacityLabel:setText(string.format('%.2f', playerFreeCapacity) .. ' ' .. WEIGHT_UNIT)
|
|
|
|
|
|
|
|
local currentTradeType = getCurrentTradeType()
|
2012-04-30 23:57:17 +02:00
|
|
|
local searchFilter = searchText:getText():lower()
|
2012-04-29 11:08:55 +02:00
|
|
|
local foundSelectedItem = false
|
|
|
|
|
|
|
|
local items = itemsPanel:getChildCount()
|
|
|
|
for i=1,items do
|
|
|
|
local itemWidget = itemsPanel:getChildByIndex(i)
|
|
|
|
local item = itemWidget.item
|
|
|
|
|
|
|
|
local canTrade = canTradeItem(item)
|
|
|
|
itemWidget:setEnabled(canTrade)
|
|
|
|
|
2012-04-30 23:57:17 +02:00
|
|
|
local searchCondition = (searchFilter == '') or (searchFilter ~= '' and string.find(item.name:lower(), searchFilter) ~= nil)
|
2012-04-29 11:08:55 +02:00
|
|
|
local showAllItemsCondition = (currentTradeType == BUY) or (showAllItems:isChecked()) or (currentTradeType == SELL and not showAllItems:isChecked() and canTrade)
|
|
|
|
itemWidget:setVisible(searchCondition and showAllItemsCondition)
|
|
|
|
|
|
|
|
if selectedItem == item and itemWidget:isEnabled() and itemWidget:isVisible() then
|
|
|
|
foundSelectedItem = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if not foundSelectedItem then
|
|
|
|
clearSelectedItem()
|
|
|
|
end
|
|
|
|
|
|
|
|
if selectedItem then
|
|
|
|
refreshItem(selectedItem)
|
|
|
|
end
|
|
|
|
end
|
2012-04-25 01:04:37 +02:00
|
|
|
|
2012-04-30 02:53:46 +02:00
|
|
|
-- hooked functions
|
|
|
|
local function onOpenNpcTrade(items)
|
|
|
|
tradeItems[BUY] = {}
|
|
|
|
tradeItems[SELL] = {}
|
|
|
|
|
|
|
|
for key,item in pairs(items) do
|
|
|
|
local newItem = {}
|
|
|
|
newItem.ptr = item[1]
|
|
|
|
newItem.name = item[2]
|
|
|
|
newItem.weight = item[3] / 100
|
|
|
|
|
2012-05-13 17:05:17 +02:00
|
|
|
if item[4] > 0 then
|
2012-04-30 02:53:46 +02:00
|
|
|
newItem.price = item[4]
|
|
|
|
table.insert(tradeItems[BUY], newItem)
|
2012-05-13 17:05:17 +02:00
|
|
|
elseif item[5] > 0 then
|
2012-04-30 02:53:46 +02:00
|
|
|
newItem.price = item[5]
|
|
|
|
table.insert(tradeItems[SELL], newItem)
|
|
|
|
else
|
|
|
|
error("server error: item name " .. item[1] .. " has neither buy or sell price.")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
refreshTradeItems()
|
|
|
|
addEvent(NPCTrade.show) -- player goods has not been parsed yet
|
|
|
|
end
|
|
|
|
|
|
|
|
local function onCloseNpcTrade()
|
|
|
|
NPCTrade.hide()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function onPlayerGoods(money, items)
|
|
|
|
playerMoney = money
|
|
|
|
|
|
|
|
playerItems = {}
|
|
|
|
for key,item in pairs(items) do
|
|
|
|
local id = item[1]:getId()
|
|
|
|
if not playerItems[id] then
|
|
|
|
playerItems[id] = item[2]
|
|
|
|
else
|
|
|
|
playerItems[id] = playerItems[id] + item[2]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
refreshPlayerGoods()
|
|
|
|
end
|
|
|
|
|
|
|
|
local function onFreeCapacityChange(localPlayer, freeCapacity, oldFreeCapacity)
|
|
|
|
playerFreeCapacity = freeCapacity
|
|
|
|
|
|
|
|
if npcWindow:isVisible() then
|
|
|
|
refreshPlayerGoods()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function onInventoryChange(inventory, item, oldeItem)
|
|
|
|
if selectedItem then
|
|
|
|
refreshItem(selectedItem)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2012-04-08 22:13:33 +02:00
|
|
|
-- public functions
|
|
|
|
function NPCTrade.init()
|
2012-06-26 00:13:30 +02:00
|
|
|
npcWindow = g_ui.displayUI('npctrade.otui')
|
2012-04-08 22:13:33 +02:00
|
|
|
npcWindow:setVisible(false)
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-08 22:13:33 +02:00
|
|
|
itemsPanel = npcWindow:recursiveGetChildById('itemsPanel')
|
2012-04-29 11:08:55 +02:00
|
|
|
searchText = npcWindow:recursiveGetChildById('searchText')
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-08 22:13:33 +02:00
|
|
|
setupPanel = npcWindow:recursiveGetChildById('setupPanel')
|
|
|
|
quantityLabel = setupPanel:getChildById('quantity')
|
|
|
|
quantityScroll = setupPanel:getChildById('quantityScroll')
|
|
|
|
nameLabel = setupPanel:getChildById('name')
|
|
|
|
priceLabel = setupPanel:getChildById('price')
|
|
|
|
moneyLabel = setupPanel:getChildById('money')
|
|
|
|
weightLabel = setupPanel:getChildById('weight')
|
|
|
|
capacityLabel = setupPanel:getChildById('capacity')
|
2012-04-29 11:08:55 +02:00
|
|
|
tradeButton = npcWindow:recursiveGetChildById('tradeButton')
|
|
|
|
|
|
|
|
buyWithBackpack = npcWindow:recursiveGetChildById('buyWithBackpack')
|
|
|
|
ignoreCapacity = npcWindow:recursiveGetChildById('ignoreCapacity')
|
|
|
|
ignoreEquipped = npcWindow:recursiveGetChildById('ignoreEquipped')
|
|
|
|
showAllItems = npcWindow:recursiveGetChildById('showAllItems')
|
|
|
|
|
|
|
|
buyTab = npcWindow:getChildById('buyTab')
|
|
|
|
sellTab = npcWindow:getChildById('sellTab')
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-06-26 00:13:30 +02:00
|
|
|
radioTabs = UIRadioGroup.create()
|
2012-04-08 22:13:33 +02:00
|
|
|
radioTabs:addWidget(buyTab)
|
|
|
|
radioTabs:addWidget(sellTab)
|
|
|
|
radioTabs:selectWidget(buyTab)
|
2012-04-29 11:08:55 +02:00
|
|
|
radioTabs.onSelectionChange = NPCTrade.onTradeTypeChange
|
|
|
|
|
|
|
|
if g_game.isOnline() then -- event wont be sent again when reloading modules
|
|
|
|
playerFreeCapacity = g_game.getLocalPlayer():getFreeCapacity()
|
|
|
|
end
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-11 03:45:20 +02:00
|
|
|
connect(g_game, { onGameEnd = NPCTrade.hide,
|
2012-04-30 02:53:46 +02:00
|
|
|
onOpenNpcTrade = onOpenNpcTrade,
|
|
|
|
onCloseNpcTrade = onCloseNpcTrade,
|
|
|
|
onPlayerGoods = onPlayerGoods } )
|
2012-04-29 11:08:55 +02:00
|
|
|
|
2012-04-30 02:53:46 +02:00
|
|
|
connect(LocalPlayer, { onFreeCapacityChange = onFreeCapacityChange,
|
|
|
|
onInventoryChange = onInventoryChange } )
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function NPCTrade.terminate()
|
2012-04-09 22:52:39 +02:00
|
|
|
--radioTabs:destroy()
|
2012-04-08 22:13:33 +02:00
|
|
|
radioTabs = nil
|
|
|
|
npcWindow:destroy()
|
|
|
|
npcWindow = nil
|
|
|
|
itemsPanel = nil
|
|
|
|
buyButton = nil
|
|
|
|
sellButton = nil
|
|
|
|
searchText = nil
|
2012-04-29 11:08:55 +02:00
|
|
|
buyTab = nil
|
|
|
|
sellTab = nil
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-08 22:13:33 +02:00
|
|
|
setupPanel = nil
|
|
|
|
quantityLabel = nil
|
|
|
|
quantityScroll = nil
|
|
|
|
nameLabel = nil
|
|
|
|
priceLabel = nil
|
|
|
|
moneyLabel = nil
|
|
|
|
weightLabel = nil
|
|
|
|
capacityLabel = nil
|
|
|
|
offerSelected = nil
|
2012-04-29 11:08:55 +02:00
|
|
|
tradeButton = nil
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-25 01:04:37 +02:00
|
|
|
disconnect(g_game, { onGameEnd = NPCTrade.hide,
|
2012-04-30 02:53:46 +02:00
|
|
|
onOpenNpcTrade = onOpenNpcTrade,
|
|
|
|
onCloseNpcTrade = onCloseNpcTrade,
|
|
|
|
onPlayerGoods = onPlayerGoods } )
|
2012-04-29 11:08:55 +02:00
|
|
|
|
2012-04-30 02:53:46 +02:00
|
|
|
disconnect(LocalPlayer, { onFreeCapacityChange = onFreeCapacityChange,
|
|
|
|
onInventoryChange = onInventoryChange } )
|
2012-04-27 06:54:14 +02:00
|
|
|
|
|
|
|
NPCTrade = nil
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function NPCTrade.show()
|
|
|
|
if g_game.isOnline() then
|
2012-04-29 11:08:55 +02:00
|
|
|
if #tradeItems[BUY] > 0 then
|
|
|
|
radioTabs:selectWidget(buyTab)
|
|
|
|
else
|
|
|
|
radioTabs:selectWidget(sellTab)
|
|
|
|
end
|
|
|
|
|
2012-04-08 22:13:33 +02:00
|
|
|
npcWindow:show()
|
|
|
|
npcWindow:raise()
|
2012-04-30 01:06:23 +02:00
|
|
|
npcWindow:focus()
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function NPCTrade.hide()
|
|
|
|
npcWindow:hide()
|
|
|
|
end
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onItemBoxChecked(widget)
|
|
|
|
if widget:isChecked() then
|
|
|
|
local item = widget.item
|
|
|
|
selectedItem = item
|
|
|
|
refreshItem(item)
|
|
|
|
tradeButton:enable()
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onQuantityValueChange(quantity)
|
|
|
|
if quantityLabel and selectedItem then
|
|
|
|
quantityLabel:setText(quantity)
|
|
|
|
weightLabel:setText(string.format('%.2f', selectedItem.weight*quantity) .. ' ' .. WEIGHT_UNIT)
|
|
|
|
priceLabel:setText(getItemPrice(selectedItem) .. ' ' .. CURRENCY)
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onTradeTypeChange(radioTabs, selected, deselected)
|
|
|
|
tradeButton:setText(selected:getText())
|
|
|
|
selected:setOn(true)
|
|
|
|
deselected:setOn(false)
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
local currentTradeType = getCurrentTradeType()
|
|
|
|
buyWithBackpack:setVisible(currentTradeType == BUY)
|
|
|
|
ignoreCapacity:setVisible(currentTradeType == BUY)
|
|
|
|
ignoreEquipped:setVisible(currentTradeType == SELL)
|
|
|
|
showAllItems:setVisible(currentTradeType == SELL)
|
2012-04-08 22:13:33 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
refreshTradeItems()
|
|
|
|
refreshPlayerGoods()
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onTradeClick()
|
|
|
|
if getCurrentTradeType() == BUY then
|
2012-05-11 11:00:30 +02:00
|
|
|
g_game.buyItem(selectedItem.ptr, quantityScroll:getValue(), ignoreCapacity:isChecked(), buyWithBackpack:isChecked())
|
2012-04-08 22:13:33 +02:00
|
|
|
else
|
2012-04-29 11:08:55 +02:00
|
|
|
g_game.sellItem(selectedItem.ptr, quantityScroll:getValue(), ignoreEquipped:isChecked())
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onSearchTextChange()
|
|
|
|
refreshPlayerGoods()
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function NPCTrade.itemPopup(self, mousePosition, mouseButton)
|
|
|
|
if mouseButton == MouseRightButton then
|
2012-06-26 00:13:30 +02:00
|
|
|
local menu = g_ui.createWidget('PopupMenu')
|
2012-04-30 00:43:23 +02:00
|
|
|
menu:addOption(tr('Look'), function() return g_game.inspectNpcTrade(self:getItem()) end)
|
2012-04-08 22:13:33 +02:00
|
|
|
menu:display(mousePosition)
|
2012-04-30 00:43:23 +02:00
|
|
|
return true
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
2012-04-30 00:43:23 +02:00
|
|
|
return false
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onBuyWithBackpackChange()
|
|
|
|
if selectedItem then
|
|
|
|
refreshItem(selectedItem)
|
2012-04-09 22:52:39 +02:00
|
|
|
end
|
2012-04-29 11:08:55 +02:00
|
|
|
end
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onIgnoreCapacityChange()
|
|
|
|
refreshPlayerGoods()
|
|
|
|
end
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onIgnoreEquippedChange()
|
|
|
|
refreshPlayerGoods()
|
|
|
|
end
|
2012-04-27 06:54:14 +02:00
|
|
|
|
2012-04-29 11:08:55 +02:00
|
|
|
function NPCTrade.onShowAllItemsChange()
|
|
|
|
refreshPlayerGoods()
|
2012-04-08 22:13:33 +02:00
|
|
|
end
|