tibia-client/modules/game_market/market.lua

1268 lines
35 KiB
Lua

--[[
Finalizing Market:
Note: Feel free to work on any area and submit
it as a pull request from your git fork.
BeniS's Skype: benjiz69
List:
* Add offer management:
- Current Offers
- Offer History
* Clean up the interface building
- Add a new market interface file to handle building?
* Extend information features
- Hover over offers for purchase information (balance after transaction, etc)
]]
Market = {}
local protocol = runinsandbox('marketprotocol')
marketWindow = nil
mainTabBar = nil
displaysTabBar = nil
offersTabBar = nil
selectionTabBar = nil
marketOffersPanel = nil
browsePanel = nil
overviewPanel = nil
itemOffersPanel = nil
itemDetailsPanel = nil
itemStatsPanel = nil
myOffersPanel = nil
currentOffersPanel = nil
offerHistoryPanel = nil
itemsPanel = nil
selectedOffer = {}
selectedMyOffer = {}
nameLabel = nil
feeLabel = nil
balanceLabel = nil
totalPriceEdit = nil
piecePriceEdit = nil
amountEdit = nil
searchEdit = nil
radioItemSet = nil
selectedItem = nil
offerTypeList = nil
categoryList = nil
subCategoryList = nil
slotFilterList = nil
createOfferButton = nil
buyButton = nil
sellButton = nil
anonymous = nil
filterButtons = {}
buyOfferTable = nil
sellOfferTable = nil
detailsTable = nil
buyStatsTable = nil
sellStatsTable = nil
buyCancelButton = nil
sellCancelButton = nil
buyMyOfferTable = nil
sellMyOfferTable = nil
offerExhaust = {}
marketOffers = {}
marketItems = {}
information = {}
currentItems = {}
lastCreatedOffer = 0
fee = 0
averagePrice = 0
loaded = false
local function isItemValid(item, category, searchFilter)
if not item or not item.marketData then
return false
end
if not category then
category = MarketCategory.All
end
if item.marketData.category ~= category and category ~= MarketCategory.All then
return false
end
-- filter item
local slotFilter = false
if slotFilterList:isEnabled() then
slotFilter = getMarketSlotFilterId(slotFilterList:getCurrentOption().text)
end
local marketData = item.marketData
local filterVocation = filterButtons[MarketFilters.Vocation]:isChecked()
local filterLevel = filterButtons[MarketFilters.Level]:isChecked()
local filterDepot = filterButtons[MarketFilters.Depot]:isChecked()
if slotFilter then
if slotFilter ~= 255 and item.thingType:getClothSlot() ~= slotFilter then
return false
end
end
local player = g_game.getLocalPlayer()
if filterLevel and marketData.requiredLevel and player:getLevel() < marketData.requiredLevel then
return false
end
if filterVocation and marketData.restrictVocation > 0 then
local voc = Bit.bit(information.vocation)
if not Bit.hasBit(marketData.restrictVocation, voc) then
return false
end
end
if filterDepot and Market.getDepotCount(item.marketData.tradeAs) <= 0 then
return false
end
if searchFilter then
return marketData.name:lower():find(searchFilter)
end
return true
end
local function clearItems()
currentItems = {}
Market.refreshItemsWidget()
end
local function clearOffers()
marketOffers[MarketAction.Buy] = {}
marketOffers[MarketAction.Sell] = {}
buyOfferTable:clearData()
sellOfferTable:clearData()
end
local function clearMyOffers()
marketOffers[MarketAction.Buy] = {}
marketOffers[MarketAction.Sell] = {}
buyMyOfferTable:clearData()
sellMyOfferTable:clearData()
end
local function clearFilters()
for _, filter in pairs(filterButtons) do
if filter and filter:isChecked() ~= filter.default then
filter:setChecked(filter.default)
end
end
end
local function clearFee()
feeLabel:setText('')
fee = 20
end
local function refreshTypeList()
offerTypeList:clearOptions()
offerTypeList:addOption('Buy')
if Market.isItemSelected() then
if Market.getDepotCount(selectedItem.item.marketData.tradeAs) > 0 then
offerTypeList:addOption('Sell')
end
end
end
local function addOffer(offer, offerType)
if not offer then
return false
end
local id = offer:getId()
local player = offer:getPlayer()
local amount = offer:getAmount()
local price = offer:getPrice()
local timestamp = offer:getTimeStamp()
local itemName = offer:getItem():getMarketData().name
buyOfferTable:toggleSorting(false)
sellOfferTable:toggleSorting(false)
buyMyOfferTable:toggleSorting(false)
sellMyOfferTable:toggleSorting(false)
if amount < 1 then return false end
if offerType == MarketAction.Buy then
if offer.warn then
buyOfferTable:setColumnStyle('OfferTableWarningColumn', true)
end
local row = nil
if offer.var == MarketRequest.MyOffers then
row = buyMyOfferTable:addRow({
{text = itemName},
{text = price*amount},
{text = price},
{text = amount},
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
})
else
row = buyOfferTable:addRow({
{text = player},
{text = amount},
{text = price*amount},
{text = price},
{text = string.gsub(os.date('%c', timestamp), " ", " ")}
})
end
row.ref = id
if offer.warn then
row:setTooltip(tr('This offer is 25%% below the average market price'))
buyOfferTable:setColumnStyle('OfferTableColumn', true)
end
else
if offer.warn then
sellOfferTable:setColumnStyle('OfferTableWarningColumn', true)
end
local row = nil
if offer.var == MarketRequest.MyOffers then
row = sellMyOfferTable:addRow({
{text = itemName},
{text = price*amount},
{text = price},
{text = amount},
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
})
else
row = sellOfferTable:addRow({
{text = player},
{text = amount},
{text = price*amount},
{text = price},
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
})
end
row.ref = id
if offer.warn then
row:setTooltip(tr('This offer is 25%% above the average market price'))
sellOfferTable:setColumnStyle('OfferTableColumn', true)
end
end
buyOfferTable:toggleSorting(false)
sellOfferTable:toggleSorting(false)
buyOfferTable:sort()
sellOfferTable:sort()
buyMyOfferTable:toggleSorting(false)
sellMyOfferTable:toggleSorting(false)
buyMyOfferTable:sort()
sellMyOfferTable:sort()
return true
end
local function mergeOffer(offer)
if not offer then
return false
end
local id = offer:getId()
local offerType = offer:getType()
local amount = offer:getAmount()
local replaced = false
if offerType == MarketAction.Buy then
if averagePrice > 0 then
offer.warn = offer:getPrice() <= averagePrice - math.floor(averagePrice / 4)
end
for i = 1, #marketOffers[MarketAction.Buy] do
local o = marketOffers[MarketAction.Buy][i]
-- replace existing offer
if o:isEqual(id) then
marketOffers[MarketAction.Buy][i] = offer
replaced = true
end
end
if not replaced then
table.insert(marketOffers[MarketAction.Buy], offer)
end
else
if averagePrice > 0 then
offer.warn = offer:getPrice() >= averagePrice + math.floor(averagePrice / 4)
end
for i = 1, #marketOffers[MarketAction.Sell] do
local o = marketOffers[MarketAction.Sell][i]
-- replace existing offer
if o:isEqual(id) then
marketOffers[MarketAction.Sell][i] = offer
replaced = true
end
end
if not replaced then
table.insert(marketOffers[MarketAction.Sell], offer)
end
end
return true
end
local function updateOffers(offers)
if not buyOfferTable or not sellOfferTable then
return
end
balanceLabel:setColor('#bbbbbb')
selectedOffer[MarketAction.Buy] = nil
selectedOffer[MarketAction.Sell] = nil
selectedMyOffer[MarketAction.Buy] = nil
selectedMyOffer[MarketAction.Sell] = nil
-- clear existing offer data
buyOfferTable:clearData()
buyOfferTable:setSorting(4, TABLE_SORTING_DESC)
sellOfferTable:clearData()
sellOfferTable:setSorting(4, TABLE_SORTING_ASC)
sellButton:setEnabled(false)
buyButton:setEnabled(false)
buyCancelButton:setEnabled(false)
sellCancelButton:setEnabled(false)
for _, offer in pairs(offers) do
mergeOffer(offer)
end
for type, offers in pairs(marketOffers) do
for i = 1, #offers do
addOffer(offers[i], type)
end
end
end
local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
if not selectedItem then
return
end
-- update item details
detailsTable:clearData()
for k, desc in pairs(descriptions) do
local columns = {
{text = getMarketDescriptionName(desc[1])..':'},
{text = desc[2]}
}
detailsTable:addRow(columns)
end
-- update sale item statistics
sellStatsTable:clearData()
if table.empty(saleStats) then
sellStatsTable:addRow({{text = 'No information'}})
else
local offerAmount = 0
local transactions, totalPrice, highestPrice, lowestPrice = 0, 0, 0, 0
for _, stat in pairs(saleStats) do
if not stat:isNull() then
offerAmount = offerAmount + 1
transactions = transactions + stat:getTransactions()
totalPrice = totalPrice + stat:getTotalPrice()
local newHigh = stat:getHighestPrice()
if newHigh > highestPrice then
highestPrice = newHigh
end
local newLow = stat:getLowestPrice()
-- ?? getting '0xffffffff' result from lowest price in 9.60 cipsoft
if (lowestPrice == 0 or newLow < lowestPrice) and newLow ~= 0xffffffff then
lowestPrice = newLow
end
end
end
if offerAmount >= 5 and transactions >= 10 then
averagePrice = math.round(totalPrice / transactions)
else
averagePrice = 0
end
sellStatsTable:addRow({{text = 'Total Transations:'}, {text = transactions}})
sellStatsTable:addRow({{text = 'Highest Price:'}, {text = highestPrice}})
if totalPrice > 0 and transactions > 0 then
sellStatsTable:addRow({{text = 'Average Price:'},
{text = math.floor(totalPrice/transactions)}})
else
sellStatsTable:addRow({{text = 'Average Price:'}, {text = 0}})
end
sellStatsTable:addRow({{text = 'Lowest Price:'}, {text = lowestPrice}})
end
-- update buy item statistics
buyStatsTable:clearData()
if table.empty(purchaseStats) then
buyStatsTable:addRow({{text = 'No information'}})
else
local transactions, totalPrice, highestPrice, lowestPrice = 0, 0, 0, 0
for _, stat in pairs(purchaseStats) do
if not stat:isNull() then
transactions = transactions + stat:getTransactions()
totalPrice = totalPrice + stat:getTotalPrice()
local newHigh = stat:getHighestPrice()
if newHigh > highestPrice then
highestPrice = newHigh
end
local newLow = stat:getLowestPrice()
-- ?? getting '0xffffffff' result from lowest price in 9.60 cipsoft
if (lowestPrice == 0 or newLow < lowestPrice) and newLow ~= 0xffffffff then
lowestPrice = newLow
end
end
end
buyStatsTable:addRow({{text = 'Total Transations:'},{text = transactions}})
buyStatsTable:addRow({{text = 'Highest Price:'}, {text = highestPrice}})
if totalPrice > 0 and transactions > 0 then
buyStatsTable:addRow({{text = 'Average Price:'},
{text = math.floor(totalPrice/transactions)}})
else
buyStatsTable:addRow({{text = 'Average Price:'}, {text = 0}})
end
buyStatsTable:addRow({{text = 'Lowest Price:'}, {text = lowestPrice}})
end
end
local function updateSelectedItem(widget)
selectedItem.item = widget.item
selectedItem.ref = widget
Market.resetCreateOffer()
if Market.isItemSelected() then
selectedItem:setItem(selectedItem.item.displayItem)
nameLabel:setText(selectedItem.item.marketData.name)
clearOffers()
Market.enableCreateOffer(true) -- update offer types
MarketProtocol.sendMarketBrowse(selectedItem.item.marketData.tradeAs) -- send browsed msg
else
Market.clearSelectedItem()
end
end
local function updateBalance(balance)
local balance = tonumber(balance)
if not balance then
return
end
if balance < 0 then balance = 0 end
information.balance = balance
balanceLabel:setText('Balance: '..balance..' gold')
balanceLabel:resizeToText()
end
local function updateFee(price, amount)
fee = math.ceil(price / 100 * amount)
if fee < 20 then
fee = 20
elseif fee > 1000 then
fee = 1000
end
feeLabel:setText('Fee: '..fee)
feeLabel:resizeToText()
end
local function destroyAmountWindow()
if amountWindow then
amountWindow:destroy()
amountWindow = nil
end
end
local function cancelMyOffer(actionType)
local offer = selectedMyOffer[actionType]
MarketProtocol.sendMarketCancelOffer(offer:getTimeStamp(), offer:getCounter())
Market.refreshMyOffers()
end
local function openAmountWindow(callback, actionType, actionText)
if not Market.isOfferSelected(actionType) then
return
end
amountWindow = g_ui.createWidget('AmountWindow', rootWidget)
amountWindow:lock()
local offer = selectedOffer[actionType]
local item = offer:getItem()
local maximum = offer:getAmount()
if actionType == MarketAction.Sell then
local depot = Market.getDepotCount(item:getId())
if maximum > depot then
maximum = depot
end
else
maximum = math.min(maximum, math.floor(information.balance / offer:getPrice()))
end
if item:isStackable() then
maximum = math.min(maximum, MarketMaxAmountStackable)
else
maximum = math.min(maximum, MarketMaxAmount)
end
local itembox = amountWindow:getChildById('item')
itembox:setItemId(item:getId())
local scrollbar = amountWindow:getChildById('amountScrollBar')
scrollbar:setText(offer:getPrice()..'gp')
scrollbar.onValueChange = function(widget, value)
widget:setText((value*offer:getPrice())..'gp')
itembox:setText(value)
end
scrollbar:setRange(1, maximum)
scrollbar:setValue(1)
local okButton = amountWindow:getChildById('buttonOk')
if actionText then
okButton:setText(actionText)
end
local okFunc = function()
local counter = offer:getCounter()
local timestamp = offer:getTimeStamp()
callback(scrollbar:getValue(), timestamp, counter)
destroyAmountWindow()
end
local cancelButton = amountWindow:getChildById('buttonCancel')
local cancelFunc = function()
destroyAmountWindow()
end
amountWindow.onEnter = okFunc
amountWindow.onEscape = cancelFunc
okButton.onClick = okFunc
cancelButton.onClick = cancelFunc
end
local function onSelectSellOffer(table, selectedRow, previousSelectedRow)
updateBalance()
for _, offer in pairs(marketOffers[MarketAction.Sell]) do
if offer:isEqual(selectedRow.ref) then
selectedOffer[MarketAction.Buy] = offer
end
end
local offer = selectedOffer[MarketAction.Buy]
if offer then
local price = offer:getPrice()
if price > information.balance then
balanceLabel:setColor('#b22222') -- red
buyButton:setEnabled(false)
else
local slice = (information.balance / 2)
if (price/slice) * 100 <= 40 then
color = '#008b00' -- green
elseif (price/slice) * 100 <= 70 then
color = '#eec900' -- yellow
else
color = '#ee9a00' -- orange
end
balanceLabel:setColor(color)
buyButton:setEnabled(true)
end
end
end
local function onSelectBuyOffer(table, selectedRow, previousSelectedRow)
updateBalance()
for _, offer in pairs(marketOffers[MarketAction.Buy]) do
if offer:isEqual(selectedRow.ref) then
selectedOffer[MarketAction.Sell] = offer
if Market.getDepotCount(offer:getItem():getId()) > 0 then
sellButton:setEnabled(true)
else
sellButton:setEnabled(false)
end
end
end
end
local function onSelectMyBuyOffer(table, selectedRow, previousSelectedRow)
for _, offer in pairs(marketOffers[MarketAction.Buy]) do
if offer:isEqual(selectedRow.ref) then
selectedMyOffer[MarketAction.Buy] = offer
buyCancelButton:setEnabled(true)
end
end
end
local function onSelectMySellOffer(table, selectedRow, previousSelectedRow)
for _, offer in pairs(marketOffers[MarketAction.Sell]) do
if offer:isEqual(selectedRow.ref) then
selectedMyOffer[MarketAction.Sell] = offer
sellCancelButton:setEnabled(true)
end
end
end
local function onChangeCategory(combobox, option)
local id = getMarketCategoryId(option)
if id == MarketCategory.MetaWeapons then
-- enable and load weapons filter/items
subCategoryList:setEnabled(true)
slotFilterList:setEnabled(true)
local subId = getMarketCategoryId(subCategoryList:getCurrentOption().text)
Market.loadMarketItems(subId)
else
subCategoryList:setEnabled(false)
slotFilterList:setEnabled(false)
Market.loadMarketItems(id) -- load standard filter
end
end
local function onChangeSubCategory(combobox, option)
Market.loadMarketItems(getMarketCategoryId(option))
slotFilterList:clearOptions()
local subId = getMarketCategoryId(subCategoryList:getCurrentOption().text)
local slots = MarketCategoryWeapons[subId].slots
for _, slot in pairs(slots) do
if table.haskey(MarketSlotFilters, slot) then
slotFilterList:addOption(MarketSlotFilters[slot])
end
end
slotFilterList:setEnabled(true)
end
local function onChangeSlotFilter(combobox, option)
Market.updateCurrentItems()
end
local function onChangeOfferType(combobox, option)
local item = selectedItem.item
local maximum = item.thingType:isStackable() and MarketMaxAmountStackable or MarketMaxAmount
if option == 'Sell' then
maximum = math.min(maximum, Market.getDepotCount(item.marketData.tradeAs))
amountEdit:setMaximum(maximum)
else
amountEdit:setMaximum(maximum)
end
end
local function onTotalPriceChange()
local amount = amountEdit:getValue()
local totalPrice = totalPriceEdit:getValue()
local piecePrice = math.floor(totalPrice/amount)
piecePriceEdit:setValue(piecePrice, true)
if Market.isItemSelected() then
updateFee(piecePrice, amount)
end
end
local function onPiecePriceChange()
local amount = amountEdit:getValue()
local totalPrice = totalPriceEdit:getValue()
local piecePrice = piecePriceEdit:getValue()
totalPriceEdit:setValue(piecePrice*amount, true)
if Market.isItemSelected() then
updateFee(piecePrice, amount)
end
end
local function onAmountChange()
local amount = amountEdit:getValue()
local piecePrice = piecePriceEdit:getValue()
local totalPrice = piecePrice * amount
totalPriceEdit:setValue(piecePrice*amount, true)
if Market.isItemSelected() then
updateFee(piecePrice, amount)
end
end
local function onMarketMessage(messageMode, message)
Market.displayMessage(message)
end
local function initMarketItems()
for c = MarketCategory.First, MarketCategory.Last do
marketItems[c] = {}
end
-- save a list of items which are already added
local itemSet = {}
-- populate all market items
local types = g_things.findThingTypeByAttr(ThingAttrMarket, 0)
for i = 1, #types do
local itemType = types[i]
local item = Item.create(itemType:getId())
if item then
local marketData = itemType:getMarketData()
if not table.empty(marketData) and not itemSet[marketData.tradeAs] then
-- Some items use a different sprite in Market
item:setId(marketData.showAs)
-- create new marketItem block
local marketItem = {
displayItem = item,
thingType = itemType,
marketData = marketData
}
-- add new market item
table.insert(marketItems[marketData.category], marketItem)
itemSet[marketData.tradeAs] = true
end
end
end
end
local function initInterface()
-- TODO: clean this up
-- setup main tabs
mainTabBar = marketWindow:getChildById('mainTabBar')
mainTabBar:setContentWidget(marketWindow:getChildById('mainTabContent'))
-- setup 'Market Offer' section tabs
marketOffersPanel = g_ui.loadUI('ui/marketoffers')
mainTabBar:addTab(tr('Market Offers'), marketOffersPanel)
selectionTabBar = marketOffersPanel:getChildById('leftTabBar')
selectionTabBar:setContentWidget(marketOffersPanel:getChildById('leftTabContent'))
browsePanel = g_ui.loadUI('ui/marketoffers/browse')
selectionTabBar:addTab(tr('Browse'), browsePanel)
-- Currently not used
-- "Reserved for more functionality later"
--overviewPanel = g_ui.loadUI('ui/marketoffers/overview')
--selectionTabBar:addTab(tr('Overview'), overviewPanel)
displaysTabBar = marketOffersPanel:getChildById('rightTabBar')
displaysTabBar:setContentWidget(marketOffersPanel:getChildById('rightTabContent'))
itemStatsPanel = g_ui.loadUI('ui/marketoffers/itemstats')
displaysTabBar:addTab(tr('Statistics'), itemStatsPanel)
itemDetailsPanel = g_ui.loadUI('ui/marketoffers/itemdetails')
displaysTabBar:addTab(tr('Details'), itemDetailsPanel)
itemOffersPanel = g_ui.loadUI('ui/marketoffers/itemoffers')
displaysTabBar:addTab(tr('Offers'), itemOffersPanel)
displaysTabBar:selectTab(displaysTabBar:getTab(tr('Offers')))
-- setup 'My Offer' section tabs
myOffersPanel = g_ui.loadUI('ui/myoffers')
mainTabBar:addTab(tr('My Offers'), myOffersPanel)
offersTabBar = myOffersPanel:getChildById('offersTabBar')
offersTabBar:setContentWidget(myOffersPanel:getChildById('offersTabContent'))
currentOffersPanel = g_ui.loadUI('ui/myoffers/currentoffers')
offersTabBar:addTab(tr('Current Offers'), currentOffersPanel)
offerHistoryPanel = g_ui.loadUI('ui/myoffers/offerhistory')
offersTabBar:addTab(tr('Offer History'), offerHistoryPanel)
balanceLabel = marketWindow:getChildById('balanceLabel')
-- setup offers
buyButton = itemOffersPanel:getChildById('buyButton')
buyButton.onClick = function() openAmountWindow(Market.acceptMarketOffer, MarketAction.Buy, 'Buy') end
sellButton = itemOffersPanel:getChildById('sellButton')
sellButton.onClick = function() openAmountWindow(Market.acceptMarketOffer, MarketAction.Sell, 'Sell') end
-- setup selected item
nameLabel = marketOffersPanel:getChildById('nameLabel')
selectedItem = marketOffersPanel:getChildById('selectedItem')
-- setup create new offer
totalPriceEdit = marketOffersPanel:getChildById('totalPriceEdit')
piecePriceEdit = marketOffersPanel:getChildById('piecePriceEdit')
amountEdit = marketOffersPanel:getChildById('amountEdit')
feeLabel = marketOffersPanel:getChildById('feeLabel')
totalPriceEdit.onValueChange = onTotalPriceChange
piecePriceEdit.onValueChange = onPiecePriceChange
amountEdit.onValueChange = onAmountChange
offerTypeList = marketOffersPanel:getChildById('offerTypeComboBox')
offerTypeList.onOptionChange = onChangeOfferType
anonymous = marketOffersPanel:getChildById('anonymousCheckBox')
createOfferButton = marketOffersPanel:getChildById('createOfferButton')
createOfferButton.onClick = Market.createNewOffer
Market.enableCreateOffer(false)
-- setup filters
filterButtons[MarketFilters.Vocation] = browsePanel:getChildById('filterVocation')
filterButtons[MarketFilters.Level] = browsePanel:getChildById('filterLevel')
filterButtons[MarketFilters.Depot] = browsePanel:getChildById('filterDepot')
filterButtons[MarketFilters.SearchAll] = browsePanel:getChildById('filterSearchAll')
-- set filter default values
clearFilters()
-- hook filters
for _, filter in pairs(filterButtons) do
filter.onCheckChange = Market.updateCurrentItems
end
searchEdit = browsePanel:getChildById('searchEdit')
categoryList = browsePanel:getChildById('categoryComboBox')
subCategoryList = browsePanel:getChildById('subCategoryComboBox')
slotFilterList = browsePanel:getChildById('slotComboBox')
slotFilterList:addOption(MarketSlotFilters[255])
slotFilterList:setEnabled(false)
for i = MarketCategory.First, MarketCategory.Last do
if i >= MarketCategory.Ammunition and i <= MarketCategory.WandsRods then
subCategoryList:addOption(getMarketCategoryName(i))
else
categoryList:addOption(getMarketCategoryName(i))
end
end
categoryList:addOption(getMarketCategoryName(255)) -- meta weapons
categoryList:setCurrentOption(getMarketCategoryName(MarketCategory.First))
subCategoryList:setEnabled(false)
-- hook item filters
categoryList.onOptionChange = onChangeCategory
subCategoryList.onOptionChange = onChangeSubCategory
slotFilterList.onOptionChange = onChangeSlotFilter
-- setup tables
buyOfferTable = itemOffersPanel:recursiveGetChildById('buyingTable')
sellOfferTable = itemOffersPanel:recursiveGetChildById('sellingTable')
detailsTable = itemDetailsPanel:recursiveGetChildById('detailsTable')
buyStatsTable = itemStatsPanel:recursiveGetChildById('buyStatsTable')
sellStatsTable = itemStatsPanel:recursiveGetChildById('sellStatsTable')
buyOfferTable.onSelectionChange = onSelectBuyOffer
sellOfferTable.onSelectionChange = onSelectSellOffer
-- setup my offers
buyMyOfferTable = currentOffersPanel:recursiveGetChildById('myBuyingTable')
sellMyOfferTable = currentOffersPanel:recursiveGetChildById('mySellingTable')
buyMyOfferTable.onSelectionChange = onSelectMyBuyOffer
sellMyOfferTable.onSelectionChange = onSelectMySellOffer
buyCancelButton = currentOffersPanel:getChildById('buyCancelButton')
buyCancelButton.onClick = function() cancelMyOffer(MarketAction.Buy) end
sellCancelButton = currentOffersPanel:getChildById('sellCancelButton')
sellCancelButton.onClick = function() cancelMyOffer(MarketAction.Sell) end
buyStatsTable:setColumnWidth({120, 270})
sellStatsTable:setColumnWidth({120, 270})
detailsTable:setColumnWidth({80, 330})
buyOfferTable:setSorting(4, TABLE_SORTING_DESC)
sellOfferTable:setSorting(4, TABLE_SORTING_ASC)
buyMyOfferTable:setSorting(3, TABLE_SORTING_DESC)
sellMyOfferTable:setSorting(3, TABLE_SORTING_DESC)
end
function init()
g_ui.importStyle('market')
g_ui.importStyle('ui/general/markettabs')
g_ui.importStyle('ui/general/marketbuttons')
g_ui.importStyle('ui/general/marketcombobox')
g_ui.importStyle('ui/general/amountwindow')
offerExhaust[MarketAction.Sell] = 10
offerExhaust[MarketAction.Buy] = 20
registerMessageMode(MessageModes.Market, onMarketMessage)
protocol.initProtocol()
connect(g_game, { onGameEnd = Market.reset })
connect(g_game, { onGameEnd = Market.close })
marketWindow = g_ui.createWidget('MarketWindow', rootWidget)
marketWindow:hide()
initInterface() -- build interface
end
function terminate()
Market.close()
unregisterMessageMode(MessageModes.Market, onMarketMessage)
protocol.terminateProtocol()
disconnect(g_game, { onGameEnd = Market.reset })
disconnect(g_game, { onGameEnd = Market.close })
destroyAmountWindow()
marketWindow:destroy()
Market = nil
end
function Market.reset()
balanceLabel:setColor('#bbbbbb')
categoryList:setCurrentOption(getMarketCategoryName(MarketCategory.First))
searchEdit:setText('')
clearFilters()
clearMyOffers()
if not table.empty(information) then
Market.updateCurrentItems()
end
end
function Market.displayMessage(message)
if marketWindow:isHidden() then return end
local infoBox = displayInfoBox(tr('Market Error'), message)
infoBox:lock()
end
function Market.clearSelectedItem()
if Market.isItemSelected() then
Market.resetCreateOffer(true)
offerTypeList:clearOptions()
offerTypeList:setText('Please Select')
offerTypeList:setEnabled(false)
clearOffers()
radioItemSet:selectWidget(nil)
nameLabel:setText('No item selected.')
selectedItem:setItem(nil)
selectedItem.item = nil
selectedItem.ref:setChecked(false)
selectedItem.ref = nil
detailsTable:clearData()
buyStatsTable:clearData()
sellStatsTable:clearData()
Market.enableCreateOffer(false)
end
end
function Market.isItemSelected()
return selectedItem and selectedItem.item
end
function Market.isOfferSelected(type)
return selectedOffer[type] and not selectedOffer[type]:isNull()
end
function Market.getDepotCount(itemId)
return information.depotItems[itemId] or 0
end
function Market.enableCreateOffer(enable)
offerTypeList:setEnabled(enable)
totalPriceEdit:setEnabled(enable)
piecePriceEdit:setEnabled(enable)
amountEdit:setEnabled(enable)
anonymous:setEnabled(enable)
createOfferButton:setEnabled(enable)
local prevAmountButton = marketOffersPanel:recursiveGetChildById('prevAmountButton')
local nextAmountButton = marketOffersPanel:recursiveGetChildById('nextAmountButton')
prevAmountButton:setEnabled(enable)
nextAmountButton:setEnabled(enable)
end
function Market.close(notify)
if notify == nil then notify = true end
if not marketWindow:isHidden() then
marketWindow:hide()
marketWindow:unlock()
modules.game_interface.getRootPanel():focus()
Market.clearSelectedItem()
Market.reset()
if notify then
MarketProtocol.sendMarketLeave()
end
end
end
function Market.incrementAmount()
amountEdit:setValue(amountEdit:getValue() + 1)
end
function Market.decrementAmount()
amountEdit:setValue(amountEdit:getValue() - 1)
end
function Market.updateCurrentItems()
local id = getMarketCategoryId(categoryList:getCurrentOption().text)
if id == MarketCategory.MetaWeapons then
id = getMarketCategoryId(subCategoryList:getCurrentOption().text)
end
Market.loadMarketItems(id)
end
function Market.resetCreateOffer(resetFee)
piecePriceEdit:setValue(1)
totalPriceEdit:setValue(1)
amountEdit:setValue(1)
refreshTypeList()
if resetFee then
clearFee()
else
updateFee(0, 0)
end
end
function Market.refreshItemsWidget(selectItem)
local selectItem = selectItem or 0
itemsPanel = browsePanel:recursiveGetChildById('itemsPanel')
local layout = itemsPanel:getLayout()
layout:disableUpdates()
Market.clearSelectedItem()
itemsPanel:destroyChildren()
if radioItemSet then
radioItemSet:destroy()
end
radioItemSet = UIRadioGroup.create()
local select = nil
for i = 1, #currentItems do
local item = currentItems[i]
local itemBox = g_ui.createWidget('MarketItemBox', itemsPanel)
itemBox.onCheckChange = Market.onItemBoxChecked
itemBox.item = item
if selectItem > 0 and item.marketData.tradeAs == selectItem then
select = itemBox
selectItem = 0
end
local itemWidget = itemBox:getChildById('item')
itemWidget:setItem(item.displayItem)
local amount = Market.getDepotCount(item.marketData.tradeAs)
if amount > 0 then
itemWidget:setText(amount)
itemBox:setTooltip('You have '.. amount ..' in your depot.')
end
radioItemSet:addWidget(itemBox)
end
if select then
radioItemSet:selectWidget(select, false)
end
layout:enableUpdates()
layout:update()
end
function Market.refreshOffers()
if Market.isItemSelected() then
Market.onItemBoxChecked(selectedItem.ref)
else
Market.refreshMyOffers()
end
end
function Market.refreshMyOffers()
clearMyOffers()
MarketProtocol.sendMarketBrowseMyOffers()
end
function Market.loadMarketItems(category)
clearItems()
-- check search filter
local searchFilter = searchEdit:getText()
if searchFilter and searchFilter:len() > 2 then
if filterButtons[MarketFilters.SearchAll]:isChecked() then
category = MarketCategory.All
end
end
if category == MarketCategory.All then
-- loop all categories
for category = MarketCategory.First, MarketCategory.Last do
for i = 1, #marketItems[category] do
local item = marketItems[category][i]
if isItemValid(item, category, searchFilter) then
table.insert(currentItems, item)
end
end
end
else
-- loop specific category
for i = 1, #marketItems[category] do
local item = marketItems[category][i]
if isItemValid(item, category, searchFilter) then
table.insert(currentItems, item)
end
end
end
Market.refreshItemsWidget()
end
function Market.createNewOffer()
local type = offerTypeList:getCurrentOption().text
if type == 'Sell' then
type = MarketAction.Sell
else
type = MarketAction.Buy
end
if not Market.isItemSelected() then
return
end
local spriteId = selectedItem.item.marketData.tradeAs
local piecePrice = piecePriceEdit:getValue()
local amount = amountEdit:getValue()
local anonymous = anonymous:isChecked() and 1 or 0
-- error checking
local errorMsg = ''
if type == MarketAction.Buy then
if information.balance < ((piecePrice * amount) + fee) then
errorMsg = errorMsg..'Not enough balance to create this offer.\n'
end
elseif type == MarketAction.Sell then
if information.balance < fee then
errorMsg = errorMsg..'Not enough balance to create this offer.\n'
end
if Market.getDepotCount(spriteId) < amount then
errorMsg = errorMsg..'Not enough items in your depot to create this offer.\n'
end
end
if piecePrice > piecePriceEdit.maximum then
errorMsg = errorMsg..'Price is too high.\n'
elseif piecePrice < piecePriceEdit.minimum then
errorMsg = errorMsg..'Price is too low.\n'
end
if amount > amountEdit.maximum then
errorMsg = errorMsg..'Amount is too high.\n'
elseif amount < amountEdit.minimum then
errorMsg = errorMsg..'Amount is too low.\n'
end
if amount * piecePrice > MarketMaxPrice then
errorMsg = errorMsg..'Total price is too high.\n'
end
if information.totalOffers >= MarketMaxOffers then
errorMsg = errorMsg..'You cannot create more offers.\n'
end
local timeCheck = os.time() - lastCreatedOffer
if timeCheck < offerExhaust[type] then
local waitTime = math.ceil(offerExhaust[type] - timeCheck)
errorMsg = errorMsg..'You must wait '.. waitTime ..' seconds before creating a new offer.\n'
end
if errorMsg ~= '' then
Market.displayMessage(errorMsg)
return
end
MarketProtocol.sendMarketCreateOffer(type, spriteId, amount, piecePrice, anonymous)
lastCreatedOffer = os.time()
Market.resetCreateOffer()
end
function Market.acceptMarketOffer(amount, timestamp, counter)
if timestamp > 0 and amount > 0 then
MarketProtocol.sendMarketAcceptOffer(timestamp, counter, amount)
Market.refreshOffers()
end
end
function Market.onItemBoxChecked(widget)
if widget:isChecked() then
updateSelectedItem(widget)
end
end
-- protocol callback functions
function Market.onMarketEnter(depotItems, offers, balance, vocation)
if not loaded then
initMarketItems()
loaded = true
end
updateBalance(balance)
averagePrice = 0
information.totalOffers = offers
local player = g_game.getLocalPlayer()
if player then
information.player = player
end
if vocation == -1 then
if player then
information.vocation = player:getVocation()
end
else
-- vocation must be compatible with < 950
information.vocation = vocation
end
-- set list of depot items
information.depotItems = depotItems
-- update the items widget to match depot items
if Market.isItemSelected() then
local spriteId = selectedItem.item.marketData.tradeAs
MarketProtocol.silent(true) -- disable protocol messages
Market.refreshItemsWidget(spriteId)
MarketProtocol.silent(false) -- enable protocol messages
else
Market.refreshItemsWidget()
end
if table.empty(currentItems) then
Market.loadMarketItems(MarketCategory.First)
end
if g_game.isOnline() then
marketWindow:lock()
marketWindow:show()
end
end
function Market.onMarketLeave()
Market.close(false)
end
function Market.onMarketDetail(itemId, descriptions, purchaseStats, saleStats)
updateDetails(itemId, descriptions, purchaseStats, saleStats)
end
function Market.onMarketBrowse(offers)
updateOffers(offers)
end