Market item filtering improvements and other some minor improvements

* Can now filter market items by vocation, level, slot type, and depot items.
* Added new bitwise lib for handling flag operations.
* Can now get/set local player vocation/premium (TODO: spell list).
This commit is contained in:
BeniS 2012-07-24 03:11:53 +12:00
parent dc8ef845ab
commit 19dd96fd02
21 changed files with 369 additions and 116 deletions

View File

@ -14,17 +14,17 @@ Panel
anchors.top: parent.top anchors.top: parent.top
ButtonBox ButtonBox
id: opengl1
anchors.left: prev.right anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter anchors.verticalCenter: prev.verticalCenter
id: opengl1
text: OpenGL 1 text: OpenGL 1
size: 80 20 size: 80 20
margin-left: 6 margin-left: 6
ButtonBox ButtonBox
id: opengl2
anchors.left: prev.right anchors.left: prev.right
anchors.verticalCenter: prev.verticalCenter anchors.verticalCenter: prev.verticalCenter
id: opengl2
text: OpenGL 2 text: OpenGL 2
size: 80 20 size: 80 20
margin-left: 4 margin-left: 4

View File

@ -34,7 +34,20 @@ function debugContainersItems()
function UIItem:onHoverChange(hovered) function UIItem:onHoverChange(hovered)
if hovered then if hovered then
local item = self:getItem() local item = self:getItem()
if item then g_tooltip.display(item:getId()) end if item then
local texts = {
'id: '..item:getId(),
'\nstackable: '..tostring(item:isStackable()),
'\nmarketable: '..tostring(item:isMarketable()),
'\nvocation: '..(item:getMarketData() and item:getMarketData().restrictVocation or 'none'),
'\ncloth slot: '..item:getClothSlot()
}
local text = ''
for _, str in pairs(texts) do
text = text..str
end
g_tooltip.display(text)
end
else else
g_tooltip.hide() g_tooltip.hide()
end end

View File

@ -0,0 +1,17 @@
Bit = {}
function Bit.bit(p)
return 2 ^ p
end
function Bit.hasBit(x, p)
return x % (p + p) >= p
end
function Bit.setbit(x, p)
return Bit.hasBit(x, p) and x or x + p
end
function Bit.clearbit(x, p)
return Bit.hasBit(x, p) and x - p or x
end

View File

@ -9,6 +9,7 @@ Module
dofile 'math' dofile 'math'
dofile 'string' dofile 'string'
dofile 'table' dofile 'table'
dofile 'bitwise'
dofile 'const' dofile 'const'
dofile 'util' dofile 'util'

View File

@ -8,6 +8,12 @@ function UIComboBox.create()
return combobox return combobox
end end
function UIComboBox:clearOptions()
self.options = {}
self.currentIndex = -1
self:clearText()
end
function UIComboBox:setCurrentOption(text) function UIComboBox:setCurrentOption(text)
if not self.options then return end if not self.options then return end
for i,v in ipairs(self.options) do for i,v in ipairs(self.options) do
@ -29,6 +35,12 @@ function UIComboBox:setCurrentIndex(index)
end end
end end
function UIComboBox:getCurrentOption()
if table.hasKey(self.options, self.currentIndex) then
return self.options[self.currentIndex]
end
end
function UIComboBox:addOption(text, data) function UIComboBox:addOption(text, data)
table.insert(self.options, { text = text, data = data }) table.insert(self.options, { text = text, data = data })
local index = #self.options local index = #self.options

View File

@ -10,35 +10,38 @@ local mainTabBar
local marketOffersPanel local marketOffersPanel
local selectionTabBar local selectionTabBar
local displaysTabBar
local offersTabBar
local itemsPanel
local browsePanel local browsePanel
local searchPanel local searchPanel
local displaysTabBar
local itemOffersPanel local itemOffersPanel
local itemDetailsPanel local itemDetailsPanel
local itemStatsPanel local itemStatsPanel
local myOffersPanel local myOffersPanel
local offersTabBar
local currentOffersPanel local currentOffersPanel
local offerHistoryPanel local offerHistoryPanel
local marketOffers = {}
local marketItems = {}
local depot = {}
local information ={}
local selectedItem local selectedItem
local nameLabel local nameLabel
local radioItemSet
local categoryList
local subCategoryList
local slotFilterList
local filterButtons = {}
local buyOfferTable local buyOfferTable
local sellOfferTable local sellOfferTable
local detailsTable local detailsTable
local buyStatsTable local buyStatsTable
local sellStatsTable local sellStatsTable
local marketOffers = {}
local marketItems = {}
local depot = {}
local information = {}
local currentItems = {} local currentItems = {}
local itemsPanel
local radioItemSet
local filterBox
local offerTableHeader = { local offerTableHeader = {
{['text'] = 'Player Name', ['width'] = 100}, {['text'] = 'Player Name', ['width'] = 100},
@ -74,6 +77,56 @@ local function getMarketDescriptionId(name)
end end
end end
local function getMarketSlotFilterId(name)
local id = table.find(MarketSlotFilters, name)
if id then
return id
end
end
local function getMarketSlotFilterName(id)
if table.hasKey(MarketSlotFilters, id) then
return MarketSlotFilters[id]
end
end
local function isValidItem(item, category)
if item.marketData.category ~= category and category ~= MarketCategory[0] 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.ptr: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 not Market.depotContains(item.ptr:getId()) then
return false
end
return true
end
local function clearSelectedItem() local function clearSelectedItem()
if selectedItem and selectedItem.item.ptr then if selectedItem and selectedItem.item.ptr then
Market.updateOffers({}) Market.updateOffers({})
@ -97,20 +150,19 @@ local function initMarketItems()
local t = types[i] local t = types[i]
local newItem = Item.create(t:getId()) local newItem = Item.create(t:getId())
if newItem then if newItem then
local item = { local marketData = t:getMarketData()
ptr = newItem, if not table.empty(marketData) then
marketData = t:getMarketData() local item = {
} ptr = newItem,
marketItems[#marketItems+1] = item marketData = marketData
}
marketItems[#marketItems+1] = item
end
end end
end end
end end
local function updateItemsWidget() local function updateItemsWidget()
if table.empty(currentItems) then
return
end
itemsPanel = browsePanel:recursiveGetChildById('itemsPanel') itemsPanel = browsePanel:recursiveGetChildById('itemsPanel')
local layout = itemsPanel:getLayout() local layout = itemsPanel:getLayout()
layout:disableUpdates() layout:disableUpdates()
@ -138,25 +190,38 @@ local function updateItemsWidget()
layout:update() layout:update()
end end
local function loadDepotItems(depotItems) local function onUpdateCategory(combobox, option)
information.depotItems = {} local id = getMarketCategoryId(option)
for i = 1, #depotItems do if id == MarketCategory.MetaWeapons then
local data = depotItems[i] -- enable and load weapons filter/items
local item = Item.create(data[1]) subCategoryList:setEnabled(true)
if not item then slotFilterList:setEnabled(true)
break local subId = getMarketCategoryId(subCategoryList:getCurrentOption().text)
end Market.loadMarketItems(subId)
item:setCount(data[2]) else
local marketData = item:getMarketData() subCategoryList:setEnabled(false)
slotFilterList:setEnabled(false)
Market.loadMarketItems(id) -- load standard filter
end
end
if not table.empty(marketData) then local function onUpdateSubCategory(combobox, option)
local newItem = { local id = getMarketCategoryId(option)
ptr = item, Market.loadMarketItems(id)
marketData = marketData -- setup slot filter
} slotFilterList:clearOptions()
table.insert(information.depotItems, newItem) 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
end end
slotFilterList:setEnabled(true)
end
local function onUpdateSlotFilter(combobox, option)
Market.updateCurrentItems()
end end
local function initInterface() local function initInterface()
@ -208,16 +273,33 @@ local function initInterface()
selectedItem = marketOffersPanel:recursiveGetChildById('selectedItem') selectedItem = marketOffersPanel:recursiveGetChildById('selectedItem')
selectedItem.item = {} selectedItem.item = {}
-- populate filter combo box -- setup filters
filterBox = browsePanel:getChildById('filterComboBox') filterButtons[MarketFilters.Vocation] = browsePanel:getChildById('filterVocation')
for i = MarketCategory.First, MarketCategory.Last do filterButtons[MarketFilters.Level] = browsePanel:getChildById('filterLevel')
filterBox:addOption(getMarketCategoryName(i)) filterButtons[MarketFilters.Depot] = browsePanel:getChildById('filterDepot')
end
filterBox:setCurrentOption(getMarketCategoryName(MarketCategory.First))
filterBox.onOptionChange = function(combobox, option) categoryList = browsePanel:getChildById('categoryComboBox')
Market.loadMarketItems(getMarketCategoryId(option)) subCategoryList = browsePanel:getChildById('subCategoryComboBox')
slotFilterList = browsePanel:getChildById('typeComboBox')
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 end
categoryList:addOption(getMarketCategoryName(255)) -- meta weapons
categoryList:setCurrentOption(getMarketCategoryName(MarketCategory.First))
subCategoryList:setEnabled(false)
-- hook item filters
categoryList.onOptionChange = onUpdateCategory
subCategoryList.onOptionChange = onUpdateSubCategory
slotFilterList.onOptionChange = onUpdateSlotFilter
-- get tables -- get tables
buyOfferTable = itemOffersPanel:recursiveGetChildById('buyingTable') buyOfferTable = itemOffersPanel:recursiveGetChildById('buyingTable')
@ -242,48 +324,62 @@ function Market.terminate()
end end
mainTabBar = nil mainTabBar = nil
marketOffersPanel = nil displaysTabBar = nil
offersTabBar = nil
selectionTabBar = nil selectionTabBar = nil
marketOffersPanel = nil
browsePanel = nil browsePanel = nil
searchPanel = nil searchPanel = nil
displaysTabBar = nil
itemOffersPanel = nil itemOffersPanel = nil
itemDetailsPanel = nil itemDetailsPanel = nil
itemStatsPanel = nil itemStatsPanel = nil
myOffersPanel = nil myOffersPanel = nil
offersTabBar = nil
currentOffersPanel = nil currentOffersPanel = nil
offerHistoryPanel = nil offerHistoryPanel = nil
marketOffers = {}
marketItems = {}
depotItems = {}
information = {}
currentItems = {}
itemsPanel = nil itemsPanel = nil
nameLabel = nil nameLabel = nil
radioItemSet = nil
selectedItem = nil
categoryList = nil
subCategoryList = nil
slotFilterList = nil
filterButtons = {}
buyOfferTable = nil buyOfferTable = nil
sellOfferTable = nil sellOfferTable = nil
detailsTable = nil detailsTable = nil
buyStatsTable = nil buyStatsTable = nil
sellStatsTable = nil sellStatsTable = nil
radioItemSet = nil
selectedItem = nil
filterBox = nil
marketOffers = {}
marketItems = {}
information = {}
currentItems = {}
Market = nil Market = nil
end end
function Market.loadMarketItems(_category) function Market.depotContains(itemId)
for i = 1, #information.depotItems do
local item = information.depotItems[i]
if item.ptr:getId() == itemId then
return true
end
end
return false
end
function Market.loadMarketItems(category)
if table.empty(marketItems) then if table.empty(marketItems) then
initMarketItems() initMarketItems()
end end
currentItems = {} currentItems = {}
for i = 1, #marketItems do for i = 1, #marketItems do
-- filter items here
local item = marketItems[i] local item = marketItems[i]
local category = item.marketData.category if isValidItem(item, category) then
if category == _category or _category == MarketCategory[0] then
table.insert(currentItems, item) table.insert(currentItems, item)
end end
end end
@ -291,6 +387,36 @@ function Market.loadMarketItems(_category)
updateItemsWidget() updateItemsWidget()
end end
function Market.loadDepotItems(depotItems)
information.depotItems = {}
for i = 1, #depotItems do
local data = depotItems[i]
local newItem = Item.create(data[1])
if not newItem then
break
end
newItem:setCount(data[2])
local marketData = newItem:getMarketData()
if not table.empty(marketData) then
local item = {
ptr = newItem,
marketData = marketData
}
table.insert(information.depotItems, item)
end
end
end
function Market.updateCurrentItems()
-- get market category
local id = getMarketCategoryId(categoryList:getCurrentOption().text)
if id == MarketCategory.MetaWeapons then
id = getMarketCategoryId(subCategoryList:getCurrentOption().text)
end
Market.loadMarketItems(id)
end
function Market.updateOffers(offers) function Market.updateOffers(offers)
marketOffers[MarketAction.Buy] = {} marketOffers[MarketAction.Buy] = {}
marketOffers[MarketAction.Sell] = {} marketOffers[MarketAction.Sell] = {}
@ -330,7 +456,6 @@ function Market.updateDetails(itemId, descriptions, purchaseStats, saleStats)
return return
end end
selectedItem.item.details = { selectedItem.item.details = {
serverItemId = itemId,
descriptions = descriptions, descriptions = descriptions,
purchaseStats = purchaseStats, purchaseStats = purchaseStats,
saleStats = saleStats saleStats = saleStats
@ -340,7 +465,7 @@ function Market.updateDetails(itemId, descriptions, purchaseStats, saleStats)
detailsTable:clearData() detailsTable:clearData()
for k, desc in pairs(descriptions) do for k, desc in pairs(descriptions) do
local columns = { local columns = {
{['text'] = getMarketDescriptionName(desc[1])..':', ['width'] = 100}, {['text'] = getMarketDescriptionName(desc[1])..':'},
{['text'] = desc[2], ['width'] = 330} {['text'] = desc[2], ['width'] = 330}
} }
detailsTable:addRow(columns) detailsTable:addRow(columns)
@ -349,21 +474,20 @@ function Market.updateDetails(itemId, descriptions, purchaseStats, saleStats)
-- update sale item statistics -- update sale item statistics
sellStatsTable:clearData() sellStatsTable:clearData()
if table.empty(saleStats) then if table.empty(saleStats) then
sellStatsTable:addRow({{['text'] = 'No information', ['width'] = 110}}) sellStatsTable:addRow({{['text'] = 'No information'}})
else else
for k, stat in pairs(saleStats) do for k, stat in pairs(saleStats) do
if not table.empty(stat) then if not table.empty(stat) then
sellStatsTable:addRow({{['text'] = 'Total Transations:', ['width'] = 110}, sellStatsTable:addRow({{['text'] = 'Total Transations:'},
{['text'] = stat[1], ['width'] = 270}}) {['text'] = stat[1], ['width'] = 270}})
sellStatsTable:addRow({{['text'] = 'Highest Price:', ['width'] = 110}, sellStatsTable:addRow({{['text'] = 'Highest Price:'},
{['text'] = stat[3], ['width'] = 270}}) {['text'] = stat[3], ['width'] = 270}})
print(stat[2] .. '/' ..stat[1]) sellStatsTable:addRow({{['text'] = 'Average Price:'},
sellStatsTable:addRow({{['text'] = 'Average Price:', ['width'] = 110}, {['text'] = math.floor(stat[2]/stat[1])}})
{['text'] = math.floor(stat[2]/stat[1]), ['width'] = 270}})
sellStatsTable:addRow({{['text'] = 'Lowest Price:', ['width'] = 110}, sellStatsTable:addRow({{['text'] = 'Lowest Price:'},
{['text'] = stat[4], ['width'] = 270}}) {['text'] = stat[4], ['width'] = 270}})
end end
end end
@ -372,21 +496,20 @@ function Market.updateDetails(itemId, descriptions, purchaseStats, saleStats)
-- update buy item statistics -- update buy item statistics
buyStatsTable:clearData() buyStatsTable:clearData()
if table.empty(purchaseStats) then if table.empty(purchaseStats) then
buyStatsTable:addRow({{['text'] = 'No information', ['width'] = 110}}) buyStatsTable:addRow({{['text'] = 'No information'}})
else else
for k, stat in pairs(purchaseStats) do for k, stat in pairs(purchaseStats) do
if not table.empty(stat) then if not table.empty(stat) then
buyStatsTable:addRow({{['text'] = 'Total Transations:', ['width'] = 110}, buyStatsTable:addRow({{['text'] = 'Total Transations:'},
{['text'] = stat[1], ['width'] = 270}}) {['text'] = stat[1], ['width'] = 270}})
buyStatsTable:addRow({{['text'] = 'Highest Price:', ['width'] = 110}, buyStatsTable:addRow({{['text'] = 'Highest Price:'},
{['text'] = stat[3], ['width'] = 270}}) {['text'] = stat[3], ['width'] = 270}})
print(stat[2] .. '/' ..stat[1]) buyStatsTable:addRow({{['text'] = 'Average Price:'},
buyStatsTable:addRow({{['text'] = 'Average Price:', ['width'] = 110},
{['text'] = math.floor(stat[2]/stat[1]), ['width'] = 270}}) {['text'] = math.floor(stat[2]/stat[1]), ['width'] = 270}})
buyStatsTable:addRow({{['text'] = 'Lowest Price:', ['width'] = 110}, buyStatsTable:addRow({{['text'] = 'Lowest Price:'},
{['text'] = stat[4], ['width'] = 270}}) {['text'] = stat[4], ['width'] = 270}})
end end
end end
@ -399,7 +522,7 @@ function Market.updateSelectedItem(newItem)
if selectedItem.item.ptr then if selectedItem.item.ptr then
selectedItem:setItem(selectedItem.item.ptr) selectedItem:setItem(selectedItem.item.ptr)
nameLabel:setText(selectedItem.item.marketData.name) nameLabel:setText(selectedItem.item.marketData.name)
MarketProtocol.sendMarketBrowse(selectedItem.item.ptr:getId()) -- send sprite id browsed MarketProtocol.sendMarketBrowse(selectedItem.item.ptr:getId()) -- send browsed msg
end end
else else
selectedItem:setItem(nil) selectedItem:setItem(nil)
@ -407,21 +530,32 @@ function Market.updateSelectedItem(newItem)
end end
end end
function Market.onMarketEnter(depotItems, offers, balance) function Market.onMarketEnter(depotItems, offers, balance, vocation)
if marketWindow:isVisible() then if marketWindow:isVisible() then
return return
end end
marketWindow:lock()
marketOffers[MarketAction.Buy] = {} marketOffers[MarketAction.Buy] = {}
marketOffers[MarketAction.Sell] = {} marketOffers[MarketAction.Sell] = {}
--[[
TODO:
* clear filters on enter
* add market reset function
]]
information.balance = balance information.balance = balance
information.totalOffers = offers information.totalOffers = offers
if vocation < 0 then
local player = g_game.getLocalPlayer()
if player then information.vocation = player:getVocation() end
else
-- vocation must be compatible with < 950
information.vocation = vocation
end
if table.empty(currentItems) then if table.empty(currentItems) then
Market.loadMarketItems(MarketCategory.First) Market.loadMarketItems(MarketCategory.First)
end end
loadDepotItems(depotItems) Market.loadDepotItems(depotItems)
-- build offer table header -- build offer table header
if buyOfferTable and not buyOfferTable:hasHeader() then if buyOfferTable and not buyOfferTable:hasHeader() then
@ -436,6 +570,8 @@ function Market.onMarketEnter(depotItems, offers, balance)
-- Uncheck selected item, cannot make protocol calls to resend marketBrowsing -- Uncheck selected item, cannot make protocol calls to resend marketBrowsing
clearSelectedItem() clearSelectedItem()
end end
marketWindow:lock()
marketWindow:show() marketWindow:show()
end end

View File

@ -37,8 +37,9 @@ end
-- parsing protocols -- parsing protocols
local function parseMarketEnter(msg) local function parseMarketEnter(msg)
local balance = msg:getU32() local balance = msg:getU32()
local vocation = -1
if g_game.getProtocolVersion() < 950 then if g_game.getProtocolVersion() < 950 then
msg:getU8() -- get vocation id vocation = msg:getU8() -- get vocation id
end end
local offers = msg:getU8() local offers = msg:getU8()
local depotItems = {} local depotItems = {}
@ -51,7 +52,7 @@ local function parseMarketEnter(msg)
table.insert(depotItems, {itemId, itemCount}) table.insert(depotItems, {itemId, itemCount})
end end
signalcall(Market.onMarketEnter, depotItems, offers, balance) signalcall(Market.onMarketEnter, depotItems, offers, balance, vocation)
return true return true
end end

View File

@ -1,6 +1,6 @@
MarketButtonBox < UICheckBox MarketButtonBox < UICheckBox
font: verdana-11px-antialised font: verdana-11px-antialised
color: #f55e5ecc color: #f55e5ebb
size: 106 22 size: 106 22
text-offset: 0 0 text-offset: 0 0
text-align: center text-align: center

View File

@ -28,7 +28,7 @@ Panel
margin: 1 margin: 1
MarketComboBox MarketComboBox
id: filterComboBox id: categoryComboBox
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -37,7 +37,7 @@ Panel
margin-left: 3 margin-left: 3
MarketComboBox MarketComboBox
id: weaponComboBox id: subCategoryComboBox
anchors.top: prev.bottom anchors.top: prev.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -45,8 +45,11 @@ Panel
margin-right: 3 margin-right: 3
margin-left: 3 margin-left: 3
$disabled:
color: #aaaaaa44
MarketButtonBox MarketButtonBox
id: filterMatchLevel id: filterLevel
checked: false checked: false
!text: tr('Level') !text: tr('Level')
!tooltip: tr('Filter list to match your level') !tooltip: tr('Filter list to match your level')
@ -57,20 +60,20 @@ Panel
margin-left: 3 margin-left: 3
width: 40 width: 40
height: 20 height: 20
//@onClick: Market.filterMatchLevel() @onCheckChange: Market.updateCurrentItems()
MarketButtonBox MarketButtonBox
id: filterMatchVocation id: filterVocation
checked: false checked: false
!text: tr('Vocation') !text: tr('Voc.')
!tooltip: tr('Filter list to match your vocation') !tooltip: tr('Filter list to match your vocation')
anchors.top: prev.top anchors.top: prev.top
anchors.left: prev.right anchors.left: prev.right
margin-right: 3 margin-right: 3
margin-left: 3 margin-left: 3
width: 60 width: 34
height: 20 height: 20
//@onClick: Market.filterMatchVocation() @onCheckChange: Market.updateCurrentItems()
MarketComboBox MarketComboBox
id: typeComboBox id: typeComboBox
@ -80,8 +83,11 @@ Panel
margin-right: 3 margin-right: 3
margin-left: 3 margin-left: 3
$disabled:
color: #aaaaaa44
MarketButtonBox MarketButtonBox
id: showDepotOnly id: filterDepot
checked: false checked: false
!text: tr('Show Depot Only') !text: tr('Show Depot Only')
!tooltip: tr('Show your depot items only') !tooltip: tr('Show your depot items only')
@ -91,7 +97,7 @@ Panel
margin-top: 6 margin-top: 6
margin-right: 3 margin-right: 3
margin-left: 3 margin-left: 3
//@onClick: Market.setDisplayDepot() @onCheckChange: Market.updateCurrentItems()
Panel Panel
anchors.top: prev.bottom anchors.top: prev.bottom

View File

@ -12,7 +12,7 @@ DetailsTableColumn < TableColumn
background-color: alpha background-color: alpha
text-offset: 5 2 text-offset: 5 2
color: #cccccc color: #cccccc
width: 80 width: 100
focusable: false focusable: false
Panel Panel
@ -27,7 +27,7 @@ Panel
anchors.right: parent.right anchors.right: parent.right
margin-top: 55 margin-top: 55
margin-left: 6 margin-left: 6
margin-bottom: 45 margin-bottom: 75
margin-right: 6 margin-right: 6
padding: 1 padding: 1
focusable: false focusable: false

View File

@ -11,7 +11,7 @@ StatsTableColumn < TableColumn
background-color: alpha background-color: alpha
text-offset: 5 0 text-offset: 5 0
color: #cccccc color: #cccccc
width: 80 width: 110
focusable: false focusable: false
Panel Panel

View File

@ -28,6 +28,15 @@ MarketCategory = {
MarketCategory.First = MarketCategory.Armors MarketCategory.First = MarketCategory.Armors
MarketCategory.Last = MarketCategory.PremiumScrolls MarketCategory.Last = MarketCategory.PremiumScrolls
MarketCategoryWeapons = {
[MarketCategory.Ammunition] = { slots = {255} },
[MarketCategory.Axes] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
[MarketCategory.Clubs] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
[MarketCategory.DistanceWeapons] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
[MarketCategory.Swords] = { slots = {255, InventorySlotOther, InventorySlotLeft} },
[MarketCategory.WandsRods] = { slots = {255, InventorySlotOther, InventorySlotLeft} }
}
MarketCategoryStrings = { MarketCategoryStrings = {
[0] = 'All', [0] = 'All',
[1] = 'Armors', [1] = 'Armors',
@ -52,7 +61,7 @@ MarketCategoryStrings = {
[20] = 'Swords', [20] = 'Swords',
[21] = 'Wands and Rods', [21] = 'Wands and Rods',
[22] = 'Premium Scrolls', [22] = 'Premium Scrolls',
[255] = 'Meta Weapons' [255] = 'Weapons'
} }
MarketAction = { MarketAction = {
@ -90,6 +99,7 @@ MarketItemDescription = {
WeaponName = 14, WeaponName = 14,
Weight = 15 Weight = 15
} }
MarketItemDescription.First = MarketItemDescription.Armor MarketItemDescription.First = MarketItemDescription.Armor
MarketItemDescription.Last = MarketItemDescription.Weight MarketItemDescription.Last = MarketItemDescription.Weight
@ -110,3 +120,18 @@ MarketItemDescriptionStrings = {
[14] = 'Weapon Type', [14] = 'Weapon Type',
[15] = 'Weight' [15] = 'Weight'
} }
MarketSlotFilters = {
[InventorySlotOther] = "Two-Handed",
[InventorySlotLeft] = "One-Handed",
[255] = "Any"
}
MarketFilters = {
Vocation = 1,
Level = 2,
Depot = 3
}
MarketFilters.First = MarketFilters.vocation
MarketFilters.Last = MarketFilters.depot

View File

@ -1,5 +1,6 @@
-- @docclass Player -- @docclass Player
InventorySlotOther = 0
InventorySlotHead = 1 InventorySlotHead = 1
InventorySlotNeck = 2 InventorySlotNeck = 2
InventorySlotBack = 3 InventorySlotBack = 3

View File

@ -33,7 +33,10 @@ LocalPlayer::LocalPlayer()
m_lastPrewalkDone = true; m_lastPrewalkDone = true;
m_autoWalking = false; m_autoWalking = false;
m_known = false; m_known = false;
m_premium = false;
m_states = 0; m_states = 0;
m_vocation = 0;
m_skillsLevel.fill(-1); m_skillsLevel.fill(-1);
m_skillsLevelPercent.fill(-1); m_skillsLevelPercent.fill(-1);
@ -327,3 +330,22 @@ void LocalPlayer::setInventoryItem(Otc::InventorySlot inventory, const ItemPtr&
callLuaField("onInventoryChange", inventory, item, oldItem); callLuaField("onInventoryChange", inventory, item, oldItem);
} }
} }
void LocalPlayer::setVocation(int vocation)
{
if(m_vocation != vocation) {
int oldVocation = m_vocation;
m_vocation = vocation;
callLuaField("onVocationChange", vocation, oldVocation);
}
}
void LocalPlayer::setPremium(bool premium)
{
if(m_premium != premium) {
m_premium = premium;
callLuaField("onPremiumChange", premium);
}
}

View File

@ -52,10 +52,13 @@ public:
void setStamina(double stamina); void setStamina(double stamina);
void setKnown(bool known) { m_known = known; } void setKnown(bool known) { m_known = known; }
void setInventoryItem(Otc::InventorySlot inventory, const ItemPtr& item); void setInventoryItem(Otc::InventorySlot inventory, const ItemPtr& item);
void setVocation(int vocation);
void setPremium(bool premium);
int getStates() { return m_states; } int getStates() { return m_states; }
int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; } int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; }
int getSkillLevelPercent(Otc::Skill skill) { return m_skillsLevelPercent[skill]; } int getSkillLevelPercent(Otc::Skill skill) { return m_skillsLevelPercent[skill]; }
int getVocation() { return m_vocation; }
double getHealth() { return m_health; } double getHealth() { return m_health; }
double getMaxHealth() { return m_maxHealth; } double getMaxHealth() { return m_maxHealth; }
double getFreeCapacity() { return m_freeCapacity; } double getFreeCapacity() { return m_freeCapacity; }
@ -73,6 +76,7 @@ public:
bool isKnown() { return m_known; } bool isKnown() { return m_known; }
bool isPreWalking() { return m_preWalking; } bool isPreWalking() { return m_preWalking; }
bool isAutoWalking() { return m_autoWalking; } bool isAutoWalking() { return m_autoWalking; }
bool isPremium() { return m_premium; }
LocalPlayerPtr asLocalPlayer() { return std::static_pointer_cast<LocalPlayer>(shared_from_this()); } LocalPlayerPtr asLocalPlayer() { return std::static_pointer_cast<LocalPlayer>(shared_from_this()); }
bool isLocalPlayer() { return true; } bool isLocalPlayer() { return true; }
@ -96,6 +100,7 @@ private:
bool m_lastPrewalkDone; bool m_lastPrewalkDone;
bool m_walkLocked; bool m_walkLocked;
bool m_autoWalking; bool m_autoWalking;
bool m_premium;
Position m_lastPrewalkDestionation; Position m_lastPrewalkDestionation;
Timer m_walkLockTimer; Timer m_walkLockTimer;
ItemPtr m_inventoryItems[Otc::LastInventorySlot]; ItemPtr m_inventoryItems[Otc::LastInventorySlot];
@ -106,6 +111,7 @@ private:
bool m_known; bool m_known;
int m_states; int m_states;
int m_vocation;
double m_health; double m_health;
double m_maxHealth; double m_maxHealth;

View File

@ -350,7 +350,8 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<Item>("getId", &Item::getId); g_lua.bindClassMemberFunction<Item>("getId", &Item::getId);
g_lua.bindClassMemberFunction<Item>("isStackable", &Item::isStackable); g_lua.bindClassMemberFunction<Item>("isStackable", &Item::isStackable);
g_lua.bindClassMemberFunction<Item>("isMarketable", &Item::isMarketable); g_lua.bindClassMemberFunction<Item>("isMarketable", &Item::isMarketable);
g_lua.bindClassMemberFunction<Item>("getMarketData", &Item::isMarketable); g_lua.bindClassMemberFunction<Item>("getMarketData", &Item::getMarketData);
g_lua.bindClassMemberFunction<Item>("getClothSlot", &Item::getClothSlot);
g_lua.registerClass<Effect, Thing>(); g_lua.registerClass<Effect, Thing>();
g_lua.registerClass<Missile, Thing>(); g_lua.registerClass<Missile, Thing>();
@ -393,6 +394,8 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<LocalPlayer>("getSoul", &LocalPlayer::getSoul); g_lua.bindClassMemberFunction<LocalPlayer>("getSoul", &LocalPlayer::getSoul);
g_lua.bindClassMemberFunction<LocalPlayer>("getStamina", &LocalPlayer::getStamina); g_lua.bindClassMemberFunction<LocalPlayer>("getStamina", &LocalPlayer::getStamina);
g_lua.bindClassMemberFunction<LocalPlayer>("getInventoryItem", &LocalPlayer::getInventoryItem); g_lua.bindClassMemberFunction<LocalPlayer>("getInventoryItem", &LocalPlayer::getInventoryItem);
g_lua.bindClassMemberFunction<LocalPlayer>("getVocation", &LocalPlayer::getVocation);
g_lua.bindClassMemberFunction<LocalPlayer>("isPremium", &LocalPlayer::isPremium);
g_lua.bindClassMemberFunction<LocalPlayer>("isKnown", &LocalPlayer::isKnown); g_lua.bindClassMemberFunction<LocalPlayer>("isKnown", &LocalPlayer::isKnown);
g_lua.bindClassMemberFunction<LocalPlayer>("isPreWalking", &LocalPlayer::isPreWalking); g_lua.bindClassMemberFunction<LocalPlayer>("isPreWalking", &LocalPlayer::isPreWalking);
g_lua.bindClassMemberFunction<LocalPlayer>("asLocalPlayer", &LocalPlayer::asLocalPlayer); g_lua.bindClassMemberFunction<LocalPlayer>("asLocalPlayer", &LocalPlayer::asLocalPlayer);

View File

@ -107,8 +107,8 @@ int push_luavalue(const MarketData& data)
g_lua.setField("name"); g_lua.setField("name");
g_lua.pushInteger(data.requiredLevel); g_lua.pushInteger(data.requiredLevel);
g_lua.setField("requiredLevel"); g_lua.setField("requiredLevel");
g_lua.pushInteger(data.restrictProfession); g_lua.pushInteger(data.restrictVocation);
g_lua.setField("restrictProfession"); g_lua.setField("restrictVocation");
g_lua.pushInteger(data.showAs); g_lua.pushInteger(data.showAs);
g_lua.setField("showAs"); g_lua.setField("showAs");
g_lua.pushInteger(data.tradeAs); g_lua.pushInteger(data.tradeAs);
@ -125,8 +125,8 @@ bool luavalue_cast(int index, MarketData& data)
data.name = g_lua.popString(); data.name = g_lua.popString();
g_lua.getField("requiredLevel", index); g_lua.getField("requiredLevel", index);
data.requiredLevel = g_lua.popInteger(); data.requiredLevel = g_lua.popInteger();
g_lua.getField("restrictProfession", index); g_lua.getField("restrictVocation", index);
data.restrictProfession = g_lua.popInteger(); data.restrictVocation = g_lua.popInteger();
g_lua.getField("showAs", index); g_lua.getField("showAs", index);
data.showAs = g_lua.popInteger(); data.showAs = g_lua.popInteger();
g_lua.getField("tradeAs", index); g_lua.getField("tradeAs", index);

View File

@ -98,7 +98,7 @@ namespace Proto {
GameServerCreatureUnpass = 146, GameServerCreatureUnpass = 146,
GameServerEditText = 150, GameServerEditText = 150,
GameServerEditList = 151, GameServerEditList = 151,
GameServerPlayerDataBasic = 159, // 910 GameServerPlayerDataBasic = 159, // 950
GameServerPlayerData = 160, GameServerPlayerData = 160,
GameServerPlayerSkills = 161, GameServerPlayerSkills = 161,
GameServerPlayerState = 162, GameServerPlayerState = 162,

View File

@ -288,9 +288,6 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
parseMultiUseDelay(msg); parseMultiUseDelay(msg);
break; break;
// PROTOCOL>=910 // PROTOCOL>=910
case Proto::GameServerPlayerDataBasic:
parsePlayerInfo(msg);
break;
case Proto::GameServerChannelEvent: case Proto::GameServerChannelEvent:
parseChannelEvent(msg); parseChannelEvent(msg);
break; break;
@ -300,6 +297,10 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerPlayerInventory: case Proto::GameServerPlayerInventory:
parsePlayerInventory(msg); parsePlayerInventory(msg);
break; break;
// PROTOCOL>=950
case Proto::GameServerPlayerDataBasic:
parsePlayerInfo(msg);
break;
// otclient ONLY // otclient ONLY
case Proto::GameServerExtendedOpcode: case Proto::GameServerExtendedOpcode:
parseExtendedOpcode(msg); parseExtendedOpcode(msg);
@ -832,12 +833,15 @@ void ProtocolGame::parseEditList(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerInfo(const InputMessagePtr& msg) void ProtocolGame::parsePlayerInfo(const InputMessagePtr& msg)
{ {
msg->getU8(); // is premium? bool premium = msg->getU8(); // premium
msg->getU8(); // profession int vocation = msg->getU8(); // vocation
int numSpells = msg->getU16(); int spellCount = msg->getU16();
for(int i=0;i<numSpells;++i) { for(int i=0;i<spellCount;++i) {
msg->getU16(); // spell id int spellId = msg->getU16(); // spell id - TODO: add to local player
} }
m_localPlayer->setPremium(premium);
m_localPlayer->setVocation(vocation);
} }
void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg) void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
@ -1221,7 +1225,13 @@ void ProtocolGame::parseChannelEvent(const InputMessagePtr& msg)
void ProtocolGame::parseItemInfo(const InputMessagePtr& msg) void ProtocolGame::parseItemInfo(const InputMessagePtr& msg)
{ {
//TODO /*int count = msg.getU16() - 1;
for(int i = 0; i < count; i++)
{
int unknown1 = msg->getU8();
int unknown2 = msg->getU16();
std::string unknown3 = msg->getString();
}*/
} }
void ProtocolGame::parsePlayerInventory(const InputMessagePtr& msg) void ProtocolGame::parsePlayerInventory(const InputMessagePtr& msg)

View File

@ -83,7 +83,7 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
market.tradeAs = fin->getU16(); market.tradeAs = fin->getU16();
market.showAs = fin->getU16(); market.showAs = fin->getU16();
market.name = fin->getString(); market.name = fin->getString();
market.restrictProfession = fin->getU16(); market.restrictVocation = fin->getU16();
market.requiredLevel = fin->getU16(); market.requiredLevel = fin->getU16();
m_attribs.set(attr, market); m_attribs.set(attr, market);
break; break;

View File

@ -76,7 +76,7 @@ enum ThingAttr : uint8 {
ThingAttrCloth = 32, ThingAttrCloth = 32,
ThingAttrMarket = 33, ThingAttrMarket = 33,
ThingAttrChargeable = 254, // deprecated ThingAttrChargeable = 254, // deprecated
ThingLastAttr = 255, ThingLastAttr = 255
}; };
enum SpriteMask { enum SpriteMask {
@ -90,7 +90,7 @@ struct MarketData {
std::string name; std::string name;
int category; int category;
uint16 requiredLevel; uint16 requiredLevel;
uint16 restrictProfession; uint16 restrictVocation;
uint16 showAs; uint16 showAs;
uint16 tradeAs; uint16 tradeAs;
}; };