mirror of
https://github.com/edubart/otclient.git
synced 2025-09-17 16:13:34 +02:00
Compare commits
36 Commits
multi-grap
...
v0.6.3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b5911cf1de | ||
![]() |
5fbb71157d | ||
![]() |
0ff36a1a0a | ||
![]() |
4a04a18835 | ||
![]() |
6fa9631d6a | ||
![]() |
261642190b | ||
![]() |
95abf2a1d2 | ||
![]() |
c4adf2d817 | ||
![]() |
c7c259ef80 | ||
![]() |
987c6d6c91 | ||
![]() |
c8185474de | ||
![]() |
be071c7103 | ||
![]() |
2f9e2c3e33 | ||
![]() |
b81590f297 | ||
![]() |
e062562888 | ||
![]() |
18d23653c4 | ||
![]() |
6c119627bb | ||
![]() |
d847a78a4d | ||
![]() |
0dccc870b5 | ||
![]() |
e4c7ca604b | ||
![]() |
d427560b98 | ||
![]() |
cc12db0d1f | ||
![]() |
1ce6df99ac | ||
![]() |
57bb6ff974 | ||
![]() |
9bae1b9e25 | ||
![]() |
1415de222c | ||
![]() |
34ceb3c95e | ||
![]() |
b43a196eac | ||
![]() |
a3a65d40ce | ||
![]() |
6ef3508362 | ||
![]() |
a71e07f063 | ||
![]() |
4bdd1e79fd | ||
![]() |
e9e4dcd71b | ||
![]() |
0891e2b30a | ||
![]() |
24664714bd | ||
![]() |
da51dd467e |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
/modules/.project
|
||||
build*
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
@@ -39,3 +38,4 @@ tags
|
||||
Thumbs.db
|
||||
.directory
|
||||
src/framework/graphics/dx/
|
||||
modules/.project/modules.sublime-workspace
|
||||
|
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(otclient)
|
||||
|
||||
set(VERSION "0.6.2")
|
||||
set(VERSION "0.6.3")
|
||||
|
||||
option(FRAMEWORK_SOUND "Use SOUND " ON)
|
||||
option(FRAMEWORK_GRAPHICS "Use GRAPHICS " ON)
|
||||
|
@@ -11,3 +11,9 @@ HorizontalList < UIScrollArea
|
||||
border-width: 1
|
||||
border-color: #1d222b
|
||||
background-color: #222833
|
||||
|
||||
VerticalList < UIScrollArea
|
||||
layout: verticalBox
|
||||
border-width: 1
|
||||
border-color: #1d222b
|
||||
background-color: #222833
|
10
modules/.project/modules.sublime-project
Normal file
10
modules/.project/modules.sublime-project
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": "..",
|
||||
"folder_exclude_patterns": [".*", "*.*~"],
|
||||
"file_exclude_patterns": [".*", "*.*~"]
|
||||
}
|
||||
]
|
||||
}
|
@@ -104,11 +104,6 @@ function terminate()
|
||||
g_settings.set('window-size', g_window.getUnmaximizedSize())
|
||||
g_settings.set('window-pos', g_window.getUnmaximizedPos())
|
||||
g_settings.set('window-maximized', g_window.isMaximized())
|
||||
|
||||
local protocolVersion = g_game.getProtocolVersion()
|
||||
if protocolVersion ~= 0 then
|
||||
g_settings.set('protocol-version', protocolVersion)
|
||||
end
|
||||
end
|
||||
|
||||
function exit()
|
||||
|
@@ -19,4 +19,4 @@ Module
|
||||
- client_terminal
|
||||
- client_modulemanager
|
||||
- client_serverlist
|
||||
//- client_stats
|
||||
- client_stats
|
||||
|
@@ -6,7 +6,7 @@ local enterGame
|
||||
local motdWindow
|
||||
local motdButton
|
||||
local enterGameButton
|
||||
local protocolBox
|
||||
local clientBox
|
||||
local protocolLogin
|
||||
local motdEnabled = true
|
||||
|
||||
@@ -73,11 +73,6 @@ local function onCharacterList(protocol, characters, account, otui)
|
||||
end
|
||||
end
|
||||
|
||||
local function onChangeProtocol(combobox, option)
|
||||
local clients = g_game.getSupportedClients(option)
|
||||
protocolBox:setTooltip("Supports Client" .. (#clients > 1 and "s" or "") .. ": " .. table.tostring(clients))
|
||||
end
|
||||
|
||||
local function onUpdateNeeded(protocol, signature)
|
||||
loadBox:destroy()
|
||||
loadBox = nil
|
||||
@@ -109,7 +104,8 @@ function EnterGame.init()
|
||||
local host = g_settings.get('host')
|
||||
local port = g_settings.get('port')
|
||||
local autologin = g_settings.getBoolean('autologin')
|
||||
local protocolVersion = g_settings.getInteger('protocol-version')
|
||||
local clientVersion = g_settings.getInteger('client-version')
|
||||
if clientVersion == 0 then clientVersion = 860 end
|
||||
|
||||
if port == nil or port == 0 then port = 7171 end
|
||||
|
||||
@@ -120,11 +116,11 @@ function EnterGame.init()
|
||||
enterGame:getChildById('serverPortTextEdit'):setText(port)
|
||||
enterGame:getChildById('autoLoginBox'):setChecked(autologin)
|
||||
|
||||
protocolBox = enterGame:getChildById('protocolComboBox')
|
||||
protocolBox.onOptionChange = onChangeProtocol
|
||||
if protocolVersion then
|
||||
protocolBox:setCurrentOption(protocolVersion)
|
||||
clientBox = enterGame:getChildById('clientComboBox')
|
||||
for _, proto in pairs(g_game.getSupportedClients()) do
|
||||
clientBox:addOption(proto)
|
||||
end
|
||||
clientBox:setCurrentOption(clientVersion)
|
||||
|
||||
enterGame:hide()
|
||||
|
||||
@@ -154,7 +150,7 @@ function EnterGame.terminate()
|
||||
enterGame = nil
|
||||
enterGameButton:destroy()
|
||||
enterGameButton = nil
|
||||
protocolBox = nil
|
||||
clientBox = nil
|
||||
if motdWindow then
|
||||
motdWindow:destroy()
|
||||
motdWindow = nil
|
||||
@@ -218,8 +214,7 @@ function EnterGame.doLogin()
|
||||
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
|
||||
G.host = enterGame:getChildById('serverHostTextEdit'):getText()
|
||||
G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
|
||||
local protocolVersion = tonumber(protocolBox:getText())
|
||||
local clientVersions = g_game.getSupportedClients(protocolVersion)
|
||||
local clientVersion = tonumber(clientBox:getText())
|
||||
EnterGame.hide()
|
||||
|
||||
if g_game.isOnline() then
|
||||
@@ -230,6 +225,7 @@ function EnterGame.doLogin()
|
||||
|
||||
g_settings.set('host', G.host)
|
||||
g_settings.set('port', G.port)
|
||||
g_settings.set('client-version', clientVersion)
|
||||
|
||||
protocolLogin = ProtocolLogin.create()
|
||||
protocolLogin.onLoginError = onError
|
||||
@@ -245,10 +241,8 @@ function EnterGame.doLogin()
|
||||
end })
|
||||
|
||||
g_game.chooseRsa(G.host)
|
||||
g_game.setProtocolVersion(protocolVersion)
|
||||
if #clientVersions > 0 then
|
||||
g_game.setClientVersion(clientVersions[#clientVersions])
|
||||
end
|
||||
g_game.setClientVersion(clientVersion)
|
||||
g_game.setProtocolVersion(g_game.getProtocolVersionForClient(clientVersion))
|
||||
|
||||
if modules.game_things.isLoaded() then
|
||||
protocolLogin:login(G.host, G.port, G.account, G.password)
|
||||
@@ -269,14 +263,14 @@ end
|
||||
function EnterGame.setDefaultServer(host, port, protocol)
|
||||
local hostTextEdit = enterGame:getChildById('serverHostTextEdit')
|
||||
local portTextEdit = enterGame:getChildById('serverPortTextEdit')
|
||||
local protocolLabel = enterGame:getChildById('protocolLabel')
|
||||
local clientLabel = enterGame:getChildById('clientLabel')
|
||||
local accountTextEdit = enterGame:getChildById('accountNameTextEdit')
|
||||
local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit')
|
||||
|
||||
if hostTextEdit:getText() ~= host then
|
||||
hostTextEdit:setText(host)
|
||||
portTextEdit:setText(port)
|
||||
protocolBox:setCurrentOption(protocol)
|
||||
clientBox:setCurrentOption(protocol)
|
||||
accountTextEdit:setText('')
|
||||
passwordTextEdit:setText('')
|
||||
end
|
||||
@@ -292,9 +286,9 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
|
||||
portTextEdit:setVisible(false)
|
||||
portTextEdit:setHeight(0)
|
||||
|
||||
protocolBox:setCurrentOption(protocol)
|
||||
protocolBox:setVisible(false)
|
||||
protocolBox:setHeight(0)
|
||||
clientBox:setCurrentOption(protocol)
|
||||
clientBox:setVisible(false)
|
||||
clientBox:setHeight(0)
|
||||
|
||||
local serverLabel = enterGame:getChildById('serverLabel')
|
||||
serverLabel:setVisible(false)
|
||||
@@ -302,9 +296,9 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
|
||||
local portLabel = enterGame:getChildById('portLabel')
|
||||
portLabel:setVisible(false)
|
||||
portLabel:setHeight(0)
|
||||
local protocolLabel = enterGame:getChildById('protocolLabel')
|
||||
protocolLabel:setVisible(false)
|
||||
protocolLabel:setHeight(0)
|
||||
local clientLabel = enterGame:getChildById('clientLabel')
|
||||
clientLabel:setVisible(false)
|
||||
clientLabel:setHeight(0)
|
||||
|
||||
local serverListButton = enterGame:getChildById('serverListButton')
|
||||
serverListButton:setVisible(false)
|
||||
|
@@ -68,7 +68,7 @@ EnterGameWindow
|
||||
|
||||
TextEdit
|
||||
id: serverHostTextEdit
|
||||
!tooltip: tr('Make sure that your client uses\nthe correct game protocol version')
|
||||
!tooltip: tr('Make sure that your client uses\nthe correct game client version')
|
||||
anchors.left: parent.left
|
||||
anchors.right: serverListButton.left
|
||||
anchors.top: serverLabel.bottom
|
||||
@@ -76,8 +76,8 @@ EnterGameWindow
|
||||
margin-right: 4
|
||||
|
||||
MenuLabel
|
||||
id: protocolLabel
|
||||
!text: tr('Protocol')
|
||||
id: clientLabel
|
||||
!text: tr('Client Version')
|
||||
anchors.left: parent.left
|
||||
anchors.top: serverHostTextEdit.bottom
|
||||
text-auto-resize: true
|
||||
@@ -85,17 +85,13 @@ EnterGameWindow
|
||||
margin-top: 8
|
||||
|
||||
ComboBox
|
||||
id: protocolComboBox
|
||||
id: clientComboBox
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.horizontalCenter
|
||||
anchors.top: protocolLabel.bottom
|
||||
anchors.top: clientLabel.bottom
|
||||
margin-top: 2
|
||||
margin-right: 3
|
||||
width: 90
|
||||
@onSetup: |
|
||||
for _, proto in pairs(g_game.getSupportedProtocols()) do
|
||||
self:addOption(proto)
|
||||
end
|
||||
|
||||
MenuLabel
|
||||
id: portLabel
|
||||
@@ -110,7 +106,7 @@ EnterGameWindow
|
||||
text: 7171
|
||||
anchors.right: parent.right
|
||||
anchors.left: parent.horizontalCenter
|
||||
anchors.top: protocolComboBox.top
|
||||
anchors.top: clientComboBox.top
|
||||
margin-left: 3
|
||||
|
||||
CheckBox
|
||||
|
@@ -137,7 +137,7 @@ function unloadCurrentModule()
|
||||
local module = g_modules.getModule(focusedChild:getText())
|
||||
if module then
|
||||
module:unload()
|
||||
if ModuleManager == nil then return end
|
||||
if modules.client_modulemanager == nil then return end
|
||||
updateModuleInfo(module:getName())
|
||||
refreshLoadedModules()
|
||||
end
|
||||
|
@@ -49,7 +49,7 @@ MainWindow
|
||||
anchors.left: protocolLabel.left
|
||||
anchors.right: port.right
|
||||
@onSetup: |
|
||||
for _, proto in pairs(g_game.getSupportedProtocols()) do
|
||||
for _, proto in pairs(g_game.getSupportedClients()) do
|
||||
self:addOption(proto)
|
||||
end
|
||||
|
||||
|
@@ -257,10 +257,13 @@ function flushLines()
|
||||
for _,line in pairs(cachedLines) do
|
||||
-- delete old lines if needed
|
||||
if numLines > MaxLogLines then
|
||||
local len = #terminalBuffer:getChildByIndex(1):getText()
|
||||
terminalBuffer:getChildByIndex(1):destroy()
|
||||
table.remove(allLines, 1)
|
||||
fulltext = string.sub(fulltext, len)
|
||||
local firstChild = terminalBuffer:getChildByIndex(1)
|
||||
if firstChild then
|
||||
local len = #firstChild:getText()
|
||||
firstChild:destroy()
|
||||
table.remove(allLines, 1)
|
||||
fulltext = string.sub(fulltext, len)
|
||||
end
|
||||
end
|
||||
|
||||
local label = g_ui.createWidget('TerminalLabel', terminalBuffer)
|
||||
@@ -285,6 +288,7 @@ function addLine(text, color)
|
||||
flushEvent = scheduleEvent(flushLines, 10)
|
||||
end
|
||||
|
||||
text = string.gsub(text, '\t', ' ')
|
||||
table.insert(cachedLines, {text=text, color=color})
|
||||
end
|
||||
|
||||
|
@@ -20,8 +20,8 @@ function string:starts(start)
|
||||
return string.sub(self, 1, #start) == start
|
||||
end
|
||||
|
||||
function string.ends(s, test)
|
||||
return test =='' or string.sub(s,-string.len(test)) == test
|
||||
function string:ends(test)
|
||||
return test =='' or string.sub(self,-string.len(test)) == test
|
||||
end
|
||||
|
||||
function string:trim()
|
||||
|
@@ -110,7 +110,9 @@ function UITabBar:selectTab(tab)
|
||||
tab:setOn(false)
|
||||
|
||||
local parent = tab:getParent()
|
||||
parent:focusChild(tab, MouseFocusReason)
|
||||
if parent then
|
||||
parent:focusChild(tab, MouseFocusReason)
|
||||
end
|
||||
end
|
||||
|
||||
function UITabBar:selectNextTab()
|
||||
|
@@ -1,11 +1,16 @@
|
||||
battleWindow = nil
|
||||
battleButton = nil
|
||||
battlePanel = nil
|
||||
filterPanel = nil
|
||||
toggleFilterButton = nil
|
||||
lastBattleButtonSwitched = nil
|
||||
battleButtonsByCreaturesList = {}
|
||||
creatureAgeList = {}
|
||||
|
||||
mouseWidget = nil
|
||||
|
||||
sortTypeBox = nil
|
||||
sortOrderBox = nil
|
||||
hidePlayersButton = nil
|
||||
hideNPCsButton = nil
|
||||
hideMonstersButton = nil
|
||||
@@ -25,6 +30,15 @@ function init()
|
||||
|
||||
battlePanel = battleWindow:recursiveGetChildById('battlePanel')
|
||||
|
||||
filterPanel = battleWindow:recursiveGetChildById('filterPanel')
|
||||
toggleFilterButton = battleWindow:recursiveGetChildById('toggleFilterButton')
|
||||
|
||||
if isHidingFilters() then
|
||||
hideFilterPanel()
|
||||
end
|
||||
|
||||
sortTypeBox = battleWindow:recursiveGetChildById('sortTypeBox')
|
||||
sortOrderBox = battleWindow:recursiveGetChildById('sortOrderBox')
|
||||
hidePlayersButton = battleWindow:recursiveGetChildById('hidePlayers')
|
||||
hideNPCsButton = battleWindow:recursiveGetChildById('hideNPCs')
|
||||
hideMonstersButton = battleWindow:recursiveGetChildById('hideMonsters')
|
||||
@@ -38,6 +52,18 @@ function init()
|
||||
|
||||
battleWindow:setContentMinimumHeight(80)
|
||||
|
||||
sortTypeBox:addOption('Name', 'name')
|
||||
sortTypeBox:addOption('Distance', 'distance')
|
||||
sortTypeBox:addOption('Age', 'age')
|
||||
sortTypeBox:addOption('Health', 'health')
|
||||
sortTypeBox:setCurrentOptionByData(getSortType())
|
||||
sortTypeBox.onOptionChange = onChangeSortType
|
||||
|
||||
sortOrderBox:addOption('Asc.', 'asc')
|
||||
sortOrderBox:addOption('Desc.', 'desc')
|
||||
sortOrderBox:setCurrentOptionByData(getSortOrder())
|
||||
sortOrderBox.onOptionChange = onChangeSortOrder
|
||||
|
||||
connect(Creature, {
|
||||
onSkullChange = updateCreatureSkull,
|
||||
onEmblemChange = updateCreatureEmblem,
|
||||
@@ -48,6 +74,10 @@ function init()
|
||||
onDisappear = onCreatureDisappear
|
||||
})
|
||||
|
||||
connect(LocalPlayer, {
|
||||
onPositionChange = onCreaturePositionChange
|
||||
})
|
||||
|
||||
connect(g_game, {
|
||||
onAttackingCreatureChange = onAttack,
|
||||
onFollowingCreatureChange = onFollow,
|
||||
@@ -75,6 +105,10 @@ function terminate()
|
||||
onDisappear = onCreatureDisappear
|
||||
})
|
||||
|
||||
disconnect(LocalPlayer, {
|
||||
onPositionChange = onCreaturePositionChange
|
||||
})
|
||||
|
||||
disconnect(g_game, {
|
||||
onAttackingCreatureChange = onAttack,
|
||||
onFollowingCreatureChange = onFollow,
|
||||
@@ -96,6 +130,93 @@ function onMiniWindowClose()
|
||||
battleButton:setOn(false)
|
||||
end
|
||||
|
||||
function getSortType()
|
||||
local settings = g_settings.getNode('BattleList')
|
||||
if not settings then
|
||||
return 'name'
|
||||
end
|
||||
return settings['sortType']
|
||||
end
|
||||
|
||||
function setSortType(state)
|
||||
settings = {}
|
||||
settings['sortType'] = state
|
||||
g_settings.mergeNode('BattleList', settings)
|
||||
|
||||
checkCreatures()
|
||||
end
|
||||
|
||||
function getSortOrder()
|
||||
local settings = g_settings.getNode('BattleList')
|
||||
if not settings then
|
||||
return 'asc'
|
||||
end
|
||||
return settings['sortOrder']
|
||||
end
|
||||
|
||||
function setSortOrder(state)
|
||||
settings = {}
|
||||
settings['sortOrder'] = state
|
||||
g_settings.mergeNode('BattleList', settings)
|
||||
|
||||
checkCreatures()
|
||||
end
|
||||
|
||||
function isSortAsc()
|
||||
return getSortOrder() == 'asc'
|
||||
end
|
||||
|
||||
function isSortDesc()
|
||||
return getSortOrder() == 'desc'
|
||||
end
|
||||
|
||||
function isHidingFilters()
|
||||
local settings = g_settings.getNode('BattleList')
|
||||
if not settings then
|
||||
return false
|
||||
end
|
||||
return settings['hidingFilters']
|
||||
end
|
||||
|
||||
function setHidingFilters(state)
|
||||
settings = {}
|
||||
settings['hidingFilters'] = state
|
||||
g_settings.mergeNode('BattleList', settings)
|
||||
end
|
||||
|
||||
function hideFilterPanel()
|
||||
filterPanel.originalHeight = filterPanel:getHeight()
|
||||
filterPanel:setHeight(0)
|
||||
toggleFilterButton:getParent():setMarginTop(0)
|
||||
toggleFilterButton:setImageClip(torect("0 0 21 12"))
|
||||
setHidingFilters(true)
|
||||
filterPanel:setVisible(false)
|
||||
end
|
||||
|
||||
function showFilterPanel()
|
||||
toggleFilterButton:getParent():setMarginTop(5)
|
||||
filterPanel:setHeight(filterPanel.originalHeight)
|
||||
toggleFilterButton:setImageClip(torect("21 0 21 12"))
|
||||
setHidingFilters(false)
|
||||
filterPanel:setVisible(true)
|
||||
end
|
||||
|
||||
function toggleFilterPanel()
|
||||
if filterPanel:isVisible() then
|
||||
hideFilterPanel()
|
||||
else
|
||||
showFilterPanel()
|
||||
end
|
||||
end
|
||||
|
||||
function onChangeSortType(comboBox, option)
|
||||
setSortType(option:lower())
|
||||
end
|
||||
|
||||
function onChangeSortOrder(comboBox, option)
|
||||
setSortOrder(option:lower():gsub('[.]', '')) -- Replace dot in option name
|
||||
end
|
||||
|
||||
function checkCreatures()
|
||||
removeAllCreatures()
|
||||
|
||||
@@ -151,15 +272,42 @@ end
|
||||
function onCreatureHealthPercentChange(creature, health)
|
||||
local battleButton = battleButtonsByCreaturesList[creature:getId()]
|
||||
if battleButton then
|
||||
if getSortType() == 'health' then
|
||||
removeCreature(creature)
|
||||
addCreature(creature)
|
||||
return
|
||||
end
|
||||
battleButton:setLifeBarPercent(creature:getHealthPercent())
|
||||
end
|
||||
end
|
||||
|
||||
local function getDistanceBetween(p1, p2)
|
||||
return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
|
||||
end
|
||||
|
||||
function onCreaturePositionChange(creature, newPos, oldPos)
|
||||
if creature:isLocalPlayer() then
|
||||
if oldPos and newPos and newPos.z ~= oldPos.z then
|
||||
checkCreatures()
|
||||
else
|
||||
-- Distance will change when moving, recalculate and move to correct index
|
||||
if getSortType() == 'distance' then
|
||||
local distanceList = {}
|
||||
for id, creatureButton in pairs(battleButtonsByCreaturesList) do
|
||||
table.insert(distanceList, {distance = getDistanceBetween(newPos, creatureButton.creature:getPosition()), widget = creatureButton})
|
||||
end
|
||||
|
||||
if isSortAsc() then
|
||||
table.sort(distanceList, function(a, b) return a.distance < b.distance end)
|
||||
else
|
||||
table.sort(distanceList, function(a, b) return a.distance > b.distance end)
|
||||
end
|
||||
|
||||
for i = 1, #distanceList do
|
||||
battlePanel:moveChildToIndex(distanceList[i].widget, i)
|
||||
end
|
||||
end
|
||||
|
||||
for id, creatureButton in pairs(battleButtonsByCreaturesList) do
|
||||
addCreature(creatureButton.creature)
|
||||
end
|
||||
@@ -170,6 +318,9 @@ function onCreaturePositionChange(creature, newPos, oldPos)
|
||||
if has and not fit then
|
||||
removeCreature(creature)
|
||||
elseif fit then
|
||||
if has and getSortType() == 'distance' then
|
||||
removeCreature(creature)
|
||||
end
|
||||
addCreature(creature)
|
||||
end
|
||||
end
|
||||
@@ -201,8 +352,13 @@ function addCreature(creature)
|
||||
local creatureId = creature:getId()
|
||||
local battleButton = battleButtonsByCreaturesList[creatureId]
|
||||
|
||||
-- Register when creature is added to battlelist for the first time
|
||||
if not creatureAgeList[creatureId] then
|
||||
creatureAgeList[creatureId] = os.time()
|
||||
end
|
||||
|
||||
if not battleButton then
|
||||
battleButton = g_ui.createWidget('BattleButton', battlePanel)
|
||||
battleButton = g_ui.createWidget('BattleButton')
|
||||
battleButton:setup(creature)
|
||||
|
||||
battleButton.onHoverChange = onBattleButtonHoverChange
|
||||
@@ -217,6 +373,77 @@ function addCreature(creature)
|
||||
if creature == g_game.getFollowingCreature() then
|
||||
onFollow(creature)
|
||||
end
|
||||
|
||||
local inserted = false
|
||||
local nameLower = creature:getName():lower()
|
||||
local healthPercent = creature:getHealthPercent()
|
||||
local playerPosition = g_game.getLocalPlayer():getPosition()
|
||||
local distance = getDistanceBetween(playerPosition, creature:getPosition())
|
||||
local age = creatureAgeList[creatureId]
|
||||
|
||||
local childCount = battlePanel:getChildCount()
|
||||
for i = 1, childCount do
|
||||
local child = battlePanel:getChildByIndex(i)
|
||||
local childName = child:getCreature():getName():lower()
|
||||
local equal = false
|
||||
if getSortType() == 'age' then
|
||||
local childAge = creatureAgeList[child:getCreature():getId()]
|
||||
if (age < childAge and isSortAsc()) or (age > childAge and isSortDesc()) then
|
||||
battlePanel:insertChild(i, battleButton)
|
||||
inserted = true
|
||||
break
|
||||
elseif age == childAge then
|
||||
equal = true
|
||||
end
|
||||
elseif getSortType() == 'distance' then
|
||||
local childDistance = getDistanceBetween(child:getCreature():getPosition(), playerPosition)
|
||||
if (distance < childDistance and isSortAsc()) or (distance > childDistance and isSortDesc()) then
|
||||
battlePanel:insertChild(i, battleButton)
|
||||
inserted = true
|
||||
break
|
||||
elseif childDistance == distance then
|
||||
equal = true
|
||||
end
|
||||
elseif getSortType() == 'health' then
|
||||
local childHealth = child:getCreature():getHealthPercent()
|
||||
if (healthPercent < childHealth and isSortAsc()) or (healthPercent > childHealth and isSortDesc()) then
|
||||
battlePanel:insertChild(i, battleButton)
|
||||
inserted = true
|
||||
break
|
||||
elseif healthPercent == childHealth then
|
||||
equal = true
|
||||
end
|
||||
end
|
||||
|
||||
-- If any other sort type is selected and values are equal, sort it by name also
|
||||
if getSortType() == 'name' or equal then
|
||||
local length = math.min(childName:len(), nameLower:len())
|
||||
for j=1,length do
|
||||
if (nameLower:byte(j) < childName:byte(j) and isSortAsc()) or (nameLower:byte(j) > childName:byte(j) and isSortDesc()) then
|
||||
battlePanel:insertChild(i, battleButton)
|
||||
inserted = true
|
||||
break
|
||||
elseif (nameLower:byte(j) > childName:byte(j) and isSortAsc()) or (nameLower:byte(j) < childName:byte(j) and isSortDesc()) then
|
||||
break
|
||||
elseif j == nameLower:len() and isSortAsc() then
|
||||
battlePanel:insertChild(i, battleButton)
|
||||
inserted = true
|
||||
elseif j == childName:len() and isSortDesc() then
|
||||
battlePanel:insertChild(i, battleButton)
|
||||
inserted = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if inserted then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Insert at the end if no other place is found
|
||||
if not inserted then
|
||||
battlePanel:insertChild(childCount + 1, battleButton)
|
||||
end
|
||||
else
|
||||
battleButton:setLifeBarPercent(creature:getHealthPercent())
|
||||
end
|
||||
@@ -226,6 +453,7 @@ function addCreature(creature)
|
||||
end
|
||||
|
||||
function removeAllCreatures()
|
||||
creatureAgeList = {}
|
||||
for i, v in pairs(battleButtonsByCreaturesList) do
|
||||
removeCreature(v.creature)
|
||||
end
|
||||
|
@@ -45,11 +45,12 @@ MiniWindow
|
||||
&save: true
|
||||
|
||||
Panel
|
||||
id: filterPanel
|
||||
margin-top: 26
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: miniwindowScrollBar.left
|
||||
height: 20
|
||||
height: 45
|
||||
|
||||
Panel
|
||||
anchors.top: parent.top
|
||||
@@ -85,16 +86,56 @@ MiniWindow
|
||||
!tooltip: tr('Hide party members')
|
||||
@onCheckChange: modules.game_battle.checkCreatures()
|
||||
|
||||
HorizontalSeparator
|
||||
Panel
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 20
|
||||
width: 128
|
||||
margin-top: 6
|
||||
|
||||
ComboBox
|
||||
id: sortTypeBox
|
||||
width: 74
|
||||
anchors.top: parent.top
|
||||
anchors.left: prev.right
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
margin-left: -28
|
||||
|
||||
ComboBox
|
||||
id: sortOrderBox
|
||||
width: 54
|
||||
anchors.top: parent.top
|
||||
anchors.left: prev.right
|
||||
margin-left: 4
|
||||
|
||||
Panel
|
||||
height: 18
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: miniwindowScrollBar.left
|
||||
margin-top: 5
|
||||
|
||||
UIWidget
|
||||
id: toggleFilterButton
|
||||
anchors.top: prev.top
|
||||
width: 21
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
image-source: /images/ui/arrow_vertical
|
||||
image-rect: 0 0 21 12
|
||||
image-clip: 21 0 21 12
|
||||
@onClick: modules.game_battle.toggleFilterPanel()
|
||||
phantom: false
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.top: prev.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: miniwindowScrollBar.left
|
||||
margin-right: 1
|
||||
margin-top: 4
|
||||
margin-top: 11
|
||||
|
||||
MiniWindowContents
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 0
|
||||
margin-top: 6
|
||||
|
||||
Panel
|
||||
id: battlePanel
|
||||
|
206
modules/game_console/communicationwindow.otui
Normal file
206
modules/game_console/communicationwindow.otui
Normal file
@@ -0,0 +1,206 @@
|
||||
IgnoreListLabel < Label
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 2 0
|
||||
focusable: true
|
||||
phantom: false
|
||||
|
||||
$focus:
|
||||
background-color: #ffffff22
|
||||
color: #ffffff
|
||||
|
||||
WhiteListLabel < Label
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 2 0
|
||||
focusable: true
|
||||
phantom: false
|
||||
|
||||
$focus:
|
||||
background-color: #ffffff22
|
||||
color: #ffffff
|
||||
|
||||
|
||||
MainWindow
|
||||
id: communicationWindow
|
||||
!text: tr('Ignore List')
|
||||
size: 515 410
|
||||
@onEscape: self:destroy()
|
||||
|
||||
CheckBox
|
||||
id: checkboxUseIgnoreList
|
||||
!text: tr('Activate ignorelist')
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
width: 180
|
||||
|
||||
Label
|
||||
!text: tr('Ignored Players:')
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 10
|
||||
|
||||
TextList
|
||||
id: ignoreList
|
||||
vertical-scrollbar: ignoreListScrollBar
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
height: 150
|
||||
width: 230
|
||||
margin-bottom: 10
|
||||
margin-top: 3
|
||||
padding: 1
|
||||
focusable: false
|
||||
|
||||
TextEdit
|
||||
id: ignoreNameEdit
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
width: 110
|
||||
margin-top: 5
|
||||
|
||||
Button
|
||||
id: buttonIgnoreAdd
|
||||
!text: tr('Add')
|
||||
width: 48
|
||||
height: 20
|
||||
margin-left: 5
|
||||
anchors.top: prev.top
|
||||
anchors.left: prev.right
|
||||
|
||||
Button
|
||||
id: buttonIgnoreRemove
|
||||
!text: tr('Remove')
|
||||
width: 64
|
||||
height: 20
|
||||
margin-left: 5
|
||||
anchors.top: prev.top
|
||||
anchors.left: prev.right
|
||||
|
||||
Label
|
||||
!text: tr('Global ignore settings')
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 20
|
||||
|
||||
CheckBox
|
||||
id: checkboxIgnorePrivateMessages
|
||||
!text: tr('Ignore Private Messages')
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
width: 180
|
||||
margin-top: 5
|
||||
|
||||
CheckBox
|
||||
id: checkboxIgnoreYelling
|
||||
!text: tr('Ignore Yelling')
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
width: 180
|
||||
margin-top: 5
|
||||
|
||||
CheckBox
|
||||
id: checkboxUseWhiteList
|
||||
!text: tr('Activate whitelist')
|
||||
anchors.top: parent.top
|
||||
anchors.left: ignoreList.right
|
||||
margin-left: 20
|
||||
width: 180
|
||||
|
||||
Label
|
||||
!text: tr('Allowed Players:')
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
margin-top: 10
|
||||
|
||||
TextList
|
||||
id: whiteList
|
||||
vertical-scrollbar: whiteListScrollBar
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
height: 150
|
||||
width: 230
|
||||
margin-bottom: 10
|
||||
margin-top: 3
|
||||
padding: 1
|
||||
focusable: false
|
||||
|
||||
TextEdit
|
||||
id: whitelistNameEdit
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
width: 110
|
||||
margin-top: 5
|
||||
|
||||
Button
|
||||
id: buttonWhitelistAdd
|
||||
!text: tr('Add')
|
||||
width: 48
|
||||
height: 20
|
||||
margin-left: 5
|
||||
anchors.top: prev.top
|
||||
anchors.left: prev.right
|
||||
|
||||
Button
|
||||
id: buttonWhitelistRemove
|
||||
!text: tr('Remove')
|
||||
width: 64
|
||||
height: 20
|
||||
margin-left: 5
|
||||
anchors.top: prev.top
|
||||
anchors.left: prev.right
|
||||
|
||||
Label
|
||||
!text: tr('Global whitelist settings')
|
||||
anchors.left: whiteList.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 20
|
||||
|
||||
CheckBox
|
||||
id: checkboxAllowVIPs
|
||||
!text: tr('Allow VIPs to message you')
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
width: 180
|
||||
margin-top: 5
|
||||
|
||||
Panel
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 30
|
||||
|
||||
Panel
|
||||
size: 160 30
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Button
|
||||
id: buttonSave
|
||||
!text: tr('Save')
|
||||
width: 75
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
|
||||
Button
|
||||
id: buttonCancel
|
||||
!text: tr('Cancel')
|
||||
width: 75
|
||||
anchors.top: parent.top
|
||||
anchors.left: prev.right
|
||||
margin-left: 10
|
||||
|
||||
VerticalScrollBar
|
||||
id: ignoreListScrollBar
|
||||
anchors.top: ignoreList.top
|
||||
anchors.bottom: ignoreList.bottom
|
||||
anchors.right: ignoreList.right
|
||||
step: 14
|
||||
pixels-scroll: true
|
||||
|
||||
VerticalScrollBar
|
||||
id: whiteListScrollBar
|
||||
anchors.top: whiteList.top
|
||||
anchors.bottom: whiteList.bottom
|
||||
anchors.right: whiteList.right
|
||||
step: 14
|
||||
pixels-scroll: true
|
@@ -61,7 +61,7 @@ consoleTabBar = nil
|
||||
consoleTextEdit = nil
|
||||
channels = nil
|
||||
channelsWindow = nil
|
||||
ignoreWindow = nil
|
||||
communicationWindow = nil
|
||||
ownPrivateName = nil
|
||||
messageHistory = {}
|
||||
currentMessageIndex = 0
|
||||
@@ -74,10 +74,14 @@ violationReportTab = nil
|
||||
ignoredChannels = {}
|
||||
filters = {}
|
||||
|
||||
local ignoreSettings = {
|
||||
local communicationSettings = {
|
||||
useIgnoreList = true,
|
||||
useWhiteList = true,
|
||||
privateMessages = false,
|
||||
yelling = false,
|
||||
players = {}
|
||||
allowVIPs = false,
|
||||
ignoredPlayers = {},
|
||||
whitelistedPlayers = {}
|
||||
}
|
||||
|
||||
function init()
|
||||
@@ -140,9 +144,58 @@ function init()
|
||||
g_keyboard.bindKeyDown('Ctrl+E', removeCurrentTab)
|
||||
g_keyboard.bindKeyDown('Ctrl+H', openHelp)
|
||||
|
||||
consoleToggleChat = consolePanel:getChildById('toggleChat')
|
||||
load()
|
||||
end
|
||||
|
||||
function toggleChat()
|
||||
if consoleToggleChat:isChecked() then
|
||||
disableChat()
|
||||
else
|
||||
enableChat()
|
||||
end
|
||||
end
|
||||
|
||||
function enableChat()
|
||||
local gameInterface = modules.game_interface
|
||||
|
||||
consoleTextEdit:setVisible(true)
|
||||
consoleTextEdit:setText("")
|
||||
|
||||
g_keyboard.unbindKeyUp("Space")
|
||||
g_keyboard.unbindKeyUp("Enter")
|
||||
|
||||
gameInterface.unbindWalkKey("W")
|
||||
gameInterface.unbindWalkKey("D")
|
||||
gameInterface.unbindWalkKey("S")
|
||||
gameInterface.unbindWalkKey("A")
|
||||
|
||||
consoleToggleChat:setTooltip(tr("Disable chat mode, allow to walk using ASDW"))
|
||||
end
|
||||
|
||||
function disableChat()
|
||||
local gameInterface = modules.game_interface
|
||||
|
||||
consoleTextEdit:setVisible(false)
|
||||
consoleTextEdit:setText("")
|
||||
|
||||
local quickFunc = function()
|
||||
if consoleToggleChat:isChecked() then
|
||||
consoleToggleChat:setChecked(false)
|
||||
end
|
||||
enableChat()
|
||||
end
|
||||
g_keyboard.bindKeyUp("Space", quickFunc)
|
||||
g_keyboard.bindKeyUp("Enter", quickFunc)
|
||||
|
||||
gameInterface.bindWalkKey("W", North)
|
||||
gameInterface.bindWalkKey("D", East)
|
||||
gameInterface.bindWalkKey("S", South)
|
||||
gameInterface.bindWalkKey("A", West)
|
||||
|
||||
consoleToggleChat:setTooltip(tr("Enable chat mode"))
|
||||
end
|
||||
|
||||
function terminate()
|
||||
save()
|
||||
disconnect(g_game, {
|
||||
@@ -166,14 +219,14 @@ function terminate()
|
||||
g_keyboard.unbindKeyDown('Ctrl+E')
|
||||
g_keyboard.unbindKeyDown('Ctrl+H')
|
||||
|
||||
saveIgnoreSettings()
|
||||
saveCommunicationSettings()
|
||||
|
||||
if channelsWindow then
|
||||
channelsWindow:destroy()
|
||||
end
|
||||
|
||||
if ignoreWindow then
|
||||
ignoreWindow:destroy()
|
||||
if communicationWindow then
|
||||
communicationWindow:destroy()
|
||||
end
|
||||
|
||||
if violationWindow then
|
||||
@@ -197,7 +250,7 @@ function load()
|
||||
if settings then
|
||||
messageHistory = settings.messageHistory or {}
|
||||
end
|
||||
loadIgnoreSettings()
|
||||
loadCommunicationSettings()
|
||||
end
|
||||
|
||||
function onTabChange(tabBar, tab)
|
||||
@@ -466,7 +519,7 @@ function addTabText(text, speaktype, tab, creatureName)
|
||||
-- Remove the curly braces
|
||||
for i = 1, #highlightData / 3 do
|
||||
local dataBlock = { _start = highlightData[(i-1)*3+1], _end = highlightData[(i-1)*3+2], words = highlightData[(i-1)*3+3] }
|
||||
text = text:gsub("{"..dataBlock.words.."}", dataBlock.words)
|
||||
text = text:gsub("%{(.-)%}", dataBlock.words, 1)
|
||||
|
||||
-- Recalculate positions as braces are removed
|
||||
highlightData[(i-1)*3+1] = dataBlock._start - ((i-1) * 2)
|
||||
@@ -779,7 +832,11 @@ function onTalk(name, level, mode, message, channelId, creaturePos)
|
||||
return
|
||||
end
|
||||
|
||||
if name ~= g_game.getCharacterName() then
|
||||
local localPlayer = g_game.getLocalPlayer()
|
||||
if name ~= g_game.getCharacterName()
|
||||
and isUsingIgnoreList()
|
||||
and not(isUsingWhiteList()) or (isUsingWhiteList() and not(isWhitelisted(name)) and not(isAllowingVIPs() and localPlayer:hasVip(name))) then
|
||||
|
||||
if mode == MessageModes.Yell and isIgnoringYelling() then
|
||||
return
|
||||
elseif speaktype.private and isIgnoringPrivate() and mode ~= MessageModes.NpcFrom then
|
||||
@@ -957,107 +1014,221 @@ function onChannelList(channelList)
|
||||
end
|
||||
end
|
||||
|
||||
function loadIgnoreSettings()
|
||||
function loadCommunicationSettings()
|
||||
communicationSettings.whitelistedPlayers = {}
|
||||
communicationSettings.ignoredPlayers = {}
|
||||
|
||||
local ignoreNode = g_settings.getNode('IgnorePlayers')
|
||||
if ignoreNode then
|
||||
for i = 1, #ignoreNode do
|
||||
table.insert(ignoreSettings.players, ignoreNode[i])
|
||||
table.insert(communicationSettings.ignoredPlayers, ignoreNode[i])
|
||||
end
|
||||
end
|
||||
ignoreSettings.privateMessages = g_settings.getBoolean('IgnorePrivateMessages')
|
||||
ignoreSettings.yelling = g_settings.getBoolean('IgnoreYelling')
|
||||
end
|
||||
|
||||
function saveIgnoreSettings()
|
||||
local tmpSettings = {}
|
||||
for i = 1, #ignoreSettings.players do
|
||||
table.insert(tmpSettings, ignoreSettings.players[i])
|
||||
local whitelistNode = g_settings.getNode('WhitelistedPlayers')
|
||||
if whitelistNode then
|
||||
for i = 1, #whitelistNode do
|
||||
table.insert(communicationSettings.whitelistedPlayers, whitelistNode[i])
|
||||
end
|
||||
end
|
||||
g_settings.set('IgnorePrivateMessages', ignoreSettings.privateMessages)
|
||||
g_settings.set('IgnoreYelling', ignoreSettings.yelling)
|
||||
g_settings.setNode('IgnorePlayers', tmpSettings)
|
||||
|
||||
communicationSettings.useIgnoreList = g_settings.getBoolean('UseIgnoreList')
|
||||
communicationSettings.useWhiteList = g_settings.getBoolean('UseWhiteList')
|
||||
communicationSettings.privateMessages = g_settings.getBoolean('IgnorePrivateMessages')
|
||||
communicationSettings.yelling = g_settings.getBoolean('IgnoreYelling')
|
||||
communicationSettings.allowVIPs = g_settings.getBoolean('AllowVIPs')
|
||||
end
|
||||
|
||||
function saveCommunicationSettings()
|
||||
local tmpIgnoreList = {}
|
||||
local ignoredPlayers = getIgnoredPlayers()
|
||||
for i = 1, #ignoredPlayers do
|
||||
table.insert(tmpIgnoreList, ignoredPlayers[i])
|
||||
end
|
||||
|
||||
local tmpWhiteList = {}
|
||||
local whitelistedPlayers = getWhitelistedPlayers()
|
||||
for i = 1, #whitelistedPlayers do
|
||||
table.insert(tmpWhiteList, whitelistedPlayers[i])
|
||||
end
|
||||
|
||||
g_settings.set('UseIgnoreList', communicationSettings.useIgnoreList)
|
||||
g_settings.set('UseWhiteList', communicationSettings.useWhiteList)
|
||||
g_settings.set('IgnorePrivateMessages', communicationSettings.privateMessages)
|
||||
g_settings.set('IgnoreYelling', communicationSettings.yelling)
|
||||
g_settings.setNode('IgnorePlayers', tmpIgnoreList)
|
||||
g_settings.setNode('WhitelistedPlayers', tmpWhiteList)
|
||||
end
|
||||
|
||||
function getIgnoredPlayers()
|
||||
return communicationSettings.ignoredPlayers
|
||||
end
|
||||
|
||||
function getWhitelistedPlayers()
|
||||
return communicationSettings.whitelistedPlayers
|
||||
end
|
||||
|
||||
function isUsingIgnoreList()
|
||||
return communicationSettings.useIgnoreList
|
||||
end
|
||||
|
||||
function isUsingWhiteList()
|
||||
return communicationSettings.useWhiteList
|
||||
end
|
||||
function isIgnored(name)
|
||||
return table.find(ignoreSettings.players, name, true)
|
||||
return table.find(communicationSettings.ignoredPlayers, name, true)
|
||||
end
|
||||
|
||||
function addIgnoredPlayer(name)
|
||||
if not isIgnored(name) then
|
||||
table.insert(ignoreSettings.players, name)
|
||||
end
|
||||
if isIgnored(name) then return end
|
||||
table.insert(communicationSettings.ignoredPlayers, name)
|
||||
end
|
||||
|
||||
function removeIgnoredPlayer(name)
|
||||
table.removevalue(ignoreSettings.players, name)
|
||||
table.removevalue(communicationSettings.ignoredPlayers, name)
|
||||
end
|
||||
|
||||
function isWhitelisted(name)
|
||||
return table.find(communicationSettings.whitelistedPlayers, name, true)
|
||||
end
|
||||
|
||||
function addWhitelistedPlayer(name)
|
||||
if isWhitelisted(name) then return end
|
||||
table.insert(communicationSettings.whitelistedPlayers, name)
|
||||
end
|
||||
|
||||
function removeWhitelistedPlayer(name)
|
||||
table.removevalue(communicationSettings.whitelistedPlayers, name)
|
||||
end
|
||||
|
||||
function isIgnoringPrivate()
|
||||
return ignoreSettings.privateMessages
|
||||
return communicationSettings.privateMessages
|
||||
end
|
||||
|
||||
function isIgnoringYelling()
|
||||
return ignoreSettings.yelling
|
||||
return communicationSettings.yelling
|
||||
end
|
||||
|
||||
function isAllowingVIPs()
|
||||
return communicationSettings.allowVIPs
|
||||
end
|
||||
|
||||
function onClickIgnoreButton()
|
||||
if ignoreWindow then return end
|
||||
ignoreWindow = g_ui.displayUI('ignorewindow')
|
||||
local ignoreListPanel = ignoreWindow:getChildById('ignoreList')
|
||||
ignoreWindow.onDestroy = function() ignoreWindow = nil end
|
||||
if communicationWindow then return end
|
||||
communicationWindow = g_ui.displayUI('communicationwindow')
|
||||
local ignoreListPanel = communicationWindow:getChildById('ignoreList')
|
||||
local whiteListPanel = communicationWindow:getChildById('whiteList')
|
||||
communicationWindow.onDestroy = function() communicationWindow = nil end
|
||||
|
||||
local removeButton = ignoreWindow:getChildById('buttonRemove')
|
||||
removeButton:disable()
|
||||
ignoreListPanel.onChildFocusChange = function() removeButton:enable() end
|
||||
removeButton.onClick = function()
|
||||
local useIgnoreListBox = communicationWindow:getChildById('checkboxUseIgnoreList')
|
||||
useIgnoreListBox:setChecked(communicationSettings.useIgnoreList)
|
||||
local useWhiteListBox = communicationWindow:getChildById('checkboxUseWhiteList')
|
||||
useWhiteListBox:setChecked(communicationSettings.useWhiteList)
|
||||
|
||||
local removeIgnoreButton = communicationWindow:getChildById('buttonIgnoreRemove')
|
||||
removeIgnoreButton:disable()
|
||||
ignoreListPanel.onChildFocusChange = function() removeIgnoreButton:enable() end
|
||||
removeIgnoreButton.onClick = function()
|
||||
local selection = ignoreListPanel:getFocusedChild()
|
||||
if selection then
|
||||
ignoreListPanel:removeChild(selection)
|
||||
selection:destroy()
|
||||
ignoreListPanel:removeChild(selection)
|
||||
selection:destroy()
|
||||
end
|
||||
if ignoreListPanel:getChildCount() == 0 then
|
||||
removeButton:disable()
|
||||
removeIgnoreButton:disable()
|
||||
end
|
||||
|
||||
local removeWhitelistButton = communicationWindow:getChildById('buttonWhitelistRemove')
|
||||
removeWhitelistButton:disable()
|
||||
whiteListPanel.onChildFocusChange = function() removeWhitelistButton:enable() end
|
||||
removeWhitelistButton.onClick = function()
|
||||
local selection = whiteListPanel:getFocusedChild()
|
||||
if selection then
|
||||
whiteListPanel:removeChild(selection)
|
||||
selection:destroy()
|
||||
end
|
||||
removeWhitelistButton:disable()
|
||||
end
|
||||
|
||||
local newlyIgnoredPlayers = {}
|
||||
local addName = ignoreWindow:getChildById('ignoreNameEdit')
|
||||
local addButton = ignoreWindow:getChildById('buttonAdd')
|
||||
local addFunction = function()
|
||||
if addName:getText() == '' then return end
|
||||
if table.find(ignoreSettings.players, addName:getText()) then return end
|
||||
if table.find(newlyIgnoredPlayers, addName:getText()) then return end
|
||||
local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
|
||||
label:setText(addName:getText())
|
||||
table.insert(newlyIgnoredPlayers, addName:getText())
|
||||
label:setPhantom(false)
|
||||
addName:setText('')
|
||||
end
|
||||
addButton.onClick = addFunction
|
||||
ignoreWindow.onEnter = addFunction
|
||||
|
||||
local ignorePrivateMessageBox = ignoreWindow:getChildById('checkboxIgnorePrivateMessages')
|
||||
ignorePrivateMessageBox:setChecked(ignoreSettings.privateMessages)
|
||||
local ignoreYellingBox = ignoreWindow:getChildById('checkboxIgnoreYelling')
|
||||
ignoreYellingBox:setChecked(ignoreSettings.yelling)
|
||||
|
||||
local saveButton = ignoreWindow:getChildById('buttonSave')
|
||||
saveButton.onClick = function()
|
||||
ignoreSettings.players = {}
|
||||
for i = 1, ignoreListPanel:getChildCount() do
|
||||
addIgnoredPlayer(ignoreListPanel:getChildByIndex(i):getText())
|
||||
--table.insert(ignoreSettings.players, ignoreListPanel:getChildByIndex(i):getText())
|
||||
end
|
||||
|
||||
ignoreSettings.yelling = ignoreYellingBox:isChecked()
|
||||
ignoreSettings.privateMessages = ignorePrivateMessageBox:isChecked()
|
||||
ignoreWindow:destroy()
|
||||
end
|
||||
|
||||
for _, name in pairs(ignoreSettings.players) do
|
||||
local addIgnoreName = communicationWindow:getChildById('ignoreNameEdit')
|
||||
local addIgnoreButton = communicationWindow:getChildById('buttonIgnoreAdd')
|
||||
local addIgnoreFunction = function()
|
||||
local newEntry = addIgnoreName:getText()
|
||||
if newEntry == '' then return end
|
||||
if table.find(getIgnoredPlayers(), newEntry) then return end
|
||||
if table.find(newlyIgnoredPlayers, newEntry) then return end
|
||||
local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
|
||||
label:setText(name)
|
||||
label:setPhantom(false)
|
||||
label:setText(newEntry)
|
||||
table.insert(newlyIgnoredPlayers, newEntry)
|
||||
addIgnoreName:setText('')
|
||||
end
|
||||
addIgnoreButton.onClick = addIgnoreFunction
|
||||
|
||||
local newlyWhitelistedPlayers = {}
|
||||
local addWhitelistName = communicationWindow:getChildById('whitelistNameEdit')
|
||||
local addWhitelistButton = communicationWindow:getChildById('buttonWhitelistAdd')
|
||||
local addWhitelistFunction = function()
|
||||
local newEntry = addWhitelistName:getText()
|
||||
if newEntry == '' then return end
|
||||
if table.find(getWhitelistedPlayers(), newEntry) then return end
|
||||
if table.find(newlyWhitelistedPlayers, newEntry) then return end
|
||||
local label = g_ui.createWidget('WhiteListLabel', whiteListPanel)
|
||||
label:setText(newEntry)
|
||||
table.insert(newlyWhitelistedPlayers, newEntry)
|
||||
addWhitelistName:setText('')
|
||||
end
|
||||
addWhitelistButton.onClick = addWhitelistFunction
|
||||
|
||||
communicationWindow.onEnter = function()
|
||||
if addWhitelistName:isFocused() then
|
||||
addWhitelistFunction()
|
||||
elseif addIgnoreName:isFocused() then
|
||||
addIgnoreFunction()
|
||||
end
|
||||
end
|
||||
|
||||
local ignorePrivateMessageBox = communicationWindow:getChildById('checkboxIgnorePrivateMessages')
|
||||
ignorePrivateMessageBox:setChecked(communicationSettings.privateMessages)
|
||||
local ignoreYellingBox = communicationWindow:getChildById('checkboxIgnoreYelling')
|
||||
ignoreYellingBox:setChecked(communicationSettings.yelling)
|
||||
local allowVIPsBox = communicationWindow:getChildById('checkboxAllowVIPs')
|
||||
allowVIPsBox:setChecked(communicationSettings.allowVIPs)
|
||||
|
||||
local saveButton = communicationWindow:recursiveGetChildById('buttonSave')
|
||||
saveButton.onClick = function()
|
||||
communicationSettings.ignoredPlayers = {}
|
||||
for i = 1, ignoreListPanel:getChildCount() do
|
||||
addIgnoredPlayer(ignoreListPanel:getChildByIndex(i):getText())
|
||||
end
|
||||
|
||||
communicationSettings.whitelistedPlayers = {}
|
||||
for i = 1, whiteListPanel:getChildCount() do
|
||||
addWhitelistedPlayer(whiteListPanel:getChildByIndex(i):getText())
|
||||
end
|
||||
|
||||
communicationSettings.useIgnoreList = useIgnoreListBox:isChecked()
|
||||
communicationSettings.useWhiteList = useWhiteListBox:isChecked()
|
||||
communicationSettings.yelling = ignoreYellingBox:isChecked()
|
||||
communicationSettings.privateMessages = ignorePrivateMessageBox:isChecked()
|
||||
communicationSettings.allowVIPs = allowVIPsBox:isChecked()
|
||||
communicationWindow:destroy()
|
||||
end
|
||||
|
||||
local cancelButton = communicationWindow:recursiveGetChildById('buttonCancel')
|
||||
cancelButton.onClick = function()
|
||||
communicationWindow:destroy()
|
||||
end
|
||||
|
||||
local ignoredPlayers = getIgnoredPlayers()
|
||||
for i = 1, #ignoredPlayers do
|
||||
local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
|
||||
label:setText(ignoredPlayers[i])
|
||||
end
|
||||
|
||||
local whitelistedPlayers = getWhitelistedPlayers()
|
||||
for i = 1, #whitelistedPlayers do
|
||||
local label = g_ui.createWidget('WhiteListLabel', whiteListPanel)
|
||||
label:setText(whitelistedPlayers[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -56,12 +56,21 @@ Panel
|
||||
id: consolePanel
|
||||
anchors.fill: parent
|
||||
|
||||
CheckBox
|
||||
id: toggleChat
|
||||
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
margin-left: 13
|
||||
margin-top: 8
|
||||
@onCheckChange: toggleChat()
|
||||
|
||||
TabButton
|
||||
id: prevChannelButton
|
||||
icon: /images/game/console/leftarrow
|
||||
anchors.left: parent.left
|
||||
anchors.left: toggleChat.right
|
||||
anchors.top: parent.top
|
||||
margin-left: 6
|
||||
margin-left: 3
|
||||
margin-top: 6
|
||||
|
||||
ConsoleTabBar
|
||||
|
@@ -1,98 +0,0 @@
|
||||
IgnoreListLabel < Label
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 2 0
|
||||
focusable: true
|
||||
|
||||
$focus:
|
||||
background-color: #ffffff22
|
||||
color: #ffffff
|
||||
|
||||
MainWindow
|
||||
id: ignoreWindow
|
||||
!text: tr('Ignore List')
|
||||
size: 500 240
|
||||
@onEscape: self:destroy()
|
||||
|
||||
Label
|
||||
!text: tr('Ignored players:')
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
TextList
|
||||
id: ignoreList
|
||||
vertical-scrollbar: ignoreListScrollBar
|
||||
anchors.left: parent.left
|
||||
anchors.top: prev.bottom
|
||||
height: 150
|
||||
width: 230
|
||||
margin-bottom: 10
|
||||
margin-top: 3
|
||||
padding: 1
|
||||
focusable: false
|
||||
|
||||
Button
|
||||
id: buttonRemove
|
||||
!text: tr('Remove')
|
||||
width: 64
|
||||
anchors.right: prev.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
TextEdit
|
||||
id: ignoreNameEdit
|
||||
anchors.left: ignoreList.right
|
||||
anchors.top: ignoreList.top
|
||||
width: 180
|
||||
margin-left: 8
|
||||
margin-right: 3
|
||||
|
||||
Button
|
||||
id: buttonAdd
|
||||
!text: tr('Add')
|
||||
width: 48
|
||||
height: 20
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.top
|
||||
|
||||
CheckBox
|
||||
id: checkboxIgnorePrivateMessages
|
||||
!text: tr('Ignore Private Messages')
|
||||
anchors.left: ignoreList.right
|
||||
anchors.top: ignoreList.top
|
||||
width: 180
|
||||
margin-top: 25
|
||||
margin-left: 8
|
||||
|
||||
CheckBox
|
||||
id: checkboxIgnoreYelling
|
||||
!text: tr('Ignore Yelling')
|
||||
anchors.left: ignoreList.right
|
||||
anchors.top: prev.top
|
||||
width: 180
|
||||
margin-top: 25
|
||||
margin-left: 8
|
||||
|
||||
Button
|
||||
id: buttonSave
|
||||
!text: tr('Save')
|
||||
width: 64
|
||||
anchors.right: next.left
|
||||
anchors.bottom: parent.bottom
|
||||
margin-right: 10
|
||||
@onClick: self:getParent():onEnter()
|
||||
|
||||
Button
|
||||
id: buttonCancel
|
||||
!text: tr('Cancel')
|
||||
width: 64
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
@onClick: self:getParent():destroy()
|
||||
|
||||
VerticalScrollBar
|
||||
id: ignoreListScrollBar
|
||||
anchors.top: ignoreList.top
|
||||
anchors.bottom: ignoreList.bottom
|
||||
anchors.right: ignoreList.right
|
||||
step: 14
|
||||
pixels-scroll: true
|
@@ -42,7 +42,8 @@ function init()
|
||||
gameBottomPanel = gameRootPanel:getChildById('gameBottomPanel')
|
||||
connect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange })
|
||||
|
||||
logoutButton = modules.client_topmenu.addLeftButton('logoutButton', tr('Exit'), '/images/topbuttons/logout', tryLogout, true)
|
||||
logoutButton = modules.client_topmenu.addLeftButton('logoutButton', tr('Exit'),
|
||||
'/images/topbuttons/logout', tryLogout, true)
|
||||
|
||||
setupViewMode(0)
|
||||
|
||||
@@ -56,42 +57,19 @@ end
|
||||
|
||||
function bindKeys()
|
||||
gameRootPanel:setAutoRepeatDelay(250)
|
||||
g_keyboard.bindKeyDown('Up', function() changeWalkDir(North) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Right', function() changeWalkDir(East) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Down', function() changeWalkDir(South) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Left', function() changeWalkDir(West) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad8', function() changeWalkDir(North) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad9', function() changeWalkDir(NorthEast) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad6', function() changeWalkDir(East) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad3', function() changeWalkDir(SouthEast) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad2', function() changeWalkDir(South) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad1', function() changeWalkDir(SouthWest) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad4', function() changeWalkDir(West) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyDown('Numpad7', function() changeWalkDir(NorthWest) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Up', function() changeWalkDir(North, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Right', function() changeWalkDir(East, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Down', function() changeWalkDir(South, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Left', function() changeWalkDir(West, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad8', function() changeWalkDir(North, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad9', function() changeWalkDir(NorthEast, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad6', function() changeWalkDir(East, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad3', function() changeWalkDir(SouthEast, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad2', function() changeWalkDir(South, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad1', function() changeWalkDir(SouthWest, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad4', function() changeWalkDir(West, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyUp('Numpad7', function() changeWalkDir(NorthWest, true) end, gameRootPanel, true)
|
||||
g_keyboard.bindKeyPress('Up', function() smartWalk(North) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Right', function() smartWalk(East) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad3', function() smartWalk(SouthEast) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad2', function() smartWalk(South) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad1', function() smartWalk(SouthWest) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad4', function() smartWalk(West) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Numpad7', function() smartWalk(NorthWest) end, gameRootPanel)
|
||||
|
||||
bindWalkKey('Up', North)
|
||||
bindWalkKey('Right', East)
|
||||
bindWalkKey('Down', South)
|
||||
bindWalkKey('Left', West)
|
||||
bindWalkKey('Numpad8', North)
|
||||
bindWalkKey('Numpad9', NorthEast)
|
||||
bindWalkKey('Numpad6', East)
|
||||
bindWalkKey('Numpad3', SouthEast)
|
||||
bindWalkKey('Numpad2', South)
|
||||
bindWalkKey('Numpad1', SouthWest)
|
||||
bindWalkKey('Numpad4', West)
|
||||
bindWalkKey('Numpad7', NorthWest)
|
||||
|
||||
g_keyboard.bindKeyPress('Ctrl+Up', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Ctrl+Right', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel)
|
||||
@@ -104,12 +82,24 @@ function bindKeys()
|
||||
g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+Q', function() tryLogout(false) end, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+L', function() tryLogout(false) end, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+.', nextViewMode, gameRootPanel)
|
||||
end
|
||||
|
||||
function bindWalkKey(key, dir)
|
||||
g_keyboard.bindKeyDown(key, function() changeWalkDir(dir) end)
|
||||
g_keyboard.bindKeyUp(key, function() changeWalkDir(dir, true) end)
|
||||
g_keyboard.bindKeyPress(key, function() smartWalk(dir) end)
|
||||
end
|
||||
|
||||
function unbindWalkKey(key)
|
||||
g_keyboard.unbindKeyDown(key)
|
||||
g_keyboard.unbindKeyUp(key)
|
||||
g_keyboard.unbindKeyPress(key)
|
||||
end
|
||||
|
||||
function terminate()
|
||||
save()
|
||||
hide()
|
||||
@@ -215,8 +205,8 @@ function tryExit()
|
||||
return true
|
||||
end
|
||||
|
||||
local exitFunc = function() logout() forceExit() end
|
||||
local logoutFunc = function() logout() exitWindow:destroy() exitWindow = nil end
|
||||
local exitFunc = function() g_game.safeLogout() forceExit() end
|
||||
local logoutFunc = function() g_game.safeLogout() exitWindow:destroy() exitWindow = nil end
|
||||
local cancelFunc = function() exitWindow:destroy() exitWindow = nil end
|
||||
|
||||
exitWindow = displayGeneralBox(tr('Exit'), tr("If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."),
|
||||
@@ -228,29 +218,55 @@ function tryExit()
|
||||
return true
|
||||
end
|
||||
|
||||
function logout()
|
||||
if g_game.isOnline() then
|
||||
g_game.safeLogout()
|
||||
return true
|
||||
function tryLogout(prompt)
|
||||
if type(prompt) ~= "boolean" then
|
||||
prompt = true
|
||||
end
|
||||
end
|
||||
|
||||
function tryLogout()
|
||||
if not g_game.isOnline() then
|
||||
exit()
|
||||
return
|
||||
end
|
||||
|
||||
if logoutWindow then
|
||||
return
|
||||
end
|
||||
|
||||
local yesCallback = function() logout() logoutWindow:destroy() logoutWindow=nil end
|
||||
local noCallback = function() logoutWindow:destroy() logoutWindow=nil end
|
||||
local msg, yesCallback
|
||||
if not g_game.isConnectionOk() then
|
||||
msg = 'Your connection is failing, if you logout now your character will be still online, do you want to force logout?'
|
||||
|
||||
logoutWindow = displayGeneralBox(tr('Logout'), tr('Are you sure you want to logout?'), {
|
||||
{ text=tr('Yes'), callback=yesCallback },
|
||||
{ text=tr('No'), callback=noCallback },
|
||||
anchor=AnchorHorizontalCenter}, yesCallback, noCallback)
|
||||
yesCallback = function()
|
||||
g_game.forceLogout()
|
||||
if logoutWindow then
|
||||
logoutWindow:destroy()
|
||||
logoutWindow=nil
|
||||
end
|
||||
end
|
||||
else
|
||||
msg = 'Are you sure you want to logout?'
|
||||
|
||||
yesCallback = function()
|
||||
g_game.safeLogout()
|
||||
if logoutWindow then
|
||||
logoutWindow:destroy()
|
||||
logoutWindow=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local noCallback = function()
|
||||
logoutWindow:destroy()
|
||||
logoutWindow=nil
|
||||
end
|
||||
|
||||
if prompt then
|
||||
logoutWindow = displayGeneralBox(tr('Logout'), tr(msg), {
|
||||
{ text=tr('Yes'), callback=yesCallback },
|
||||
{ text=tr('No'), callback=noCallback },
|
||||
anchor=AnchorHorizontalCenter}, yesCallback, noCallback)
|
||||
else
|
||||
yesCallback()
|
||||
end
|
||||
end
|
||||
|
||||
function stopSmartWalk()
|
||||
@@ -462,17 +478,20 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
|
||||
end
|
||||
|
||||
else
|
||||
local localPosition = localPlayer:getPosition()
|
||||
if not classic then shortcut = '(Alt)' else shortcut = nil end
|
||||
if g_game.getAttackingCreature() ~= creatureThing then
|
||||
menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end, shortcut)
|
||||
else
|
||||
menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end, shortcut)
|
||||
end
|
||||
if creatureThing:getPosition().z == localPosition.z then
|
||||
if g_game.getAttackingCreature() ~= creatureThing then
|
||||
menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end, shortcut)
|
||||
else
|
||||
menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end, shortcut)
|
||||
end
|
||||
|
||||
if g_game.getFollowingCreature() ~= creatureThing then
|
||||
menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end)
|
||||
else
|
||||
menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end)
|
||||
if g_game.getFollowingCreature() ~= creatureThing then
|
||||
menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end)
|
||||
else
|
||||
menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end)
|
||||
end
|
||||
end
|
||||
|
||||
if creatureThing:isPlayer() then
|
||||
@@ -518,7 +537,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
|
||||
end
|
||||
end
|
||||
|
||||
if modules.game_ruleviolation.hasWindowAccess() then
|
||||
if modules.game_ruleviolation.hasWindowAccess() and creatureThing:isPlayer() then
|
||||
menu:addSeparator()
|
||||
menu:addOption(tr('Rule Violation'), function() modules.game_ruleviolation.show(creatureThing:getName()) end)
|
||||
end
|
||||
@@ -530,7 +549,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
|
||||
menu:display(menuPosition)
|
||||
end
|
||||
|
||||
function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing)
|
||||
function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, attackCreature)
|
||||
local keyboardModifiers = g_keyboard.getModifiers()
|
||||
|
||||
if not modules.client_options.getOption('classicControl') then
|
||||
@@ -556,7 +575,10 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
|
||||
return true
|
||||
end
|
||||
return true
|
||||
elseif creatureThing and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
elseif attackCreature and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
g_game.attack(attackCreature)
|
||||
return true
|
||||
elseif creatureThing and creatureThing:getPosition().z == autoWalkPos.z and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
g_game.attack(creatureThing)
|
||||
return true
|
||||
end
|
||||
@@ -565,7 +587,10 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
|
||||
else
|
||||
if useThing and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then
|
||||
local player = g_game.getLocalPlayer()
|
||||
if creatureThing and creatureThing ~= player then
|
||||
if attackCreature and attackCreature ~= player then
|
||||
g_game.attack(attackCreature)
|
||||
return true
|
||||
elseif creatureThing and creatureThing ~= player and creatureThing:getPosition().z == autoWalkPos.z then
|
||||
g_game.attack(creatureThing)
|
||||
return true
|
||||
elseif useThing:isContainer() then
|
||||
@@ -593,7 +618,10 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
|
||||
elseif useThing and keyboardModifiers == KeyboardCtrlModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
createThingMenu(menuPosition, lookThing, useThing, creatureThing)
|
||||
return true
|
||||
elseif creatureThing and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
elseif attackCreature and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
g_game.attack(attackCreature)
|
||||
return true
|
||||
elseif creatureThing and creatureThing:getPosition().z == autoWalkPos.z and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
|
||||
g_game.attack(creatureThing)
|
||||
return true
|
||||
end
|
||||
@@ -654,6 +682,8 @@ function moveStackableItem(item, toPos)
|
||||
end
|
||||
g_keyboard.bindKeyPress("Up", function() check() spinbox:up() end, spinbox)
|
||||
g_keyboard.bindKeyPress("Down", function() check() spinbox:down() end, spinbox)
|
||||
g_keyboard.bindKeyPress("Right", function() check() spinbox:up() end, spinbox)
|
||||
g_keyboard.bindKeyPress("Left", function() check() spinbox:down() end, spinbox)
|
||||
g_keyboard.bindKeyPress("PageUp", function() check() spinbox:setValue(spinbox:getValue()+10) end, spinbox)
|
||||
g_keyboard.bindKeyPress("PageDown", function() check() spinbox:setValue(spinbox:getValue()-10) end, spinbox)
|
||||
|
||||
|
@@ -6,11 +6,11 @@ CountWindow < MainWindow
|
||||
SpinBox
|
||||
id: spinBox
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.bottom
|
||||
width: 0
|
||||
height: 0
|
||||
anchors.top: parent.top
|
||||
width: 1
|
||||
height: 1
|
||||
phantom: true
|
||||
padding-bottom: -40
|
||||
margin-top: 2
|
||||
focusable: true
|
||||
|
||||
Item
|
||||
|
@@ -78,6 +78,7 @@ function UIGameMap:onMouseRelease(mousePosition, mouseButton)
|
||||
local useThing
|
||||
local creatureThing
|
||||
local multiUseThing
|
||||
local attackCreature
|
||||
|
||||
local tile = self:getTile(mousePosition)
|
||||
if tile then
|
||||
@@ -86,7 +87,12 @@ function UIGameMap:onMouseRelease(mousePosition, mouseButton)
|
||||
creatureThing = tile:getTopCreature()
|
||||
end
|
||||
|
||||
local ret = modules.game_interface.processMouseAction(mousePosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing)
|
||||
local autoWalkTile = g_map.getTile(autoWalkPos)
|
||||
if autoWalkTile then
|
||||
attackCreature = autoWalkTile:getTopCreature()
|
||||
end
|
||||
|
||||
local ret = modules.game_interface.processMouseAction(mousePosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, attackCreature)
|
||||
if ret then
|
||||
self.allowNextRelease = false
|
||||
end
|
||||
|
@@ -86,7 +86,7 @@ function UIItem:onMouseRelease(mousePosition, mouseButton)
|
||||
g_game.look(item)
|
||||
self.cancelNextRelease = true
|
||||
return true
|
||||
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, item) then
|
||||
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, nil) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
|
@@ -151,8 +151,7 @@ function onTradeTypeChange(radioTabs, selected, deselected)
|
||||
ignoreCapacity:setVisible(currentTradeType == BUY)
|
||||
ignoreEquipped:setVisible(currentTradeType == SELL)
|
||||
showAllItems:setVisible(currentTradeType == SELL)
|
||||
sellAllButton:setVisible(false)
|
||||
--sellAllButton:setVisible(currentTradeType == SELL)
|
||||
sellAllButton:setVisible(currentTradeType == SELL)
|
||||
|
||||
refreshTradeItems()
|
||||
refreshPlayerGoods()
|
||||
|
@@ -252,7 +252,6 @@ MainWindow
|
||||
margin-right: 10
|
||||
visible: false
|
||||
@onClick: modules.game_npctrade.sellAll()
|
||||
visible: false
|
||||
|
||||
Button
|
||||
id: tradeButton
|
||||
|
@@ -47,7 +47,7 @@ function openWindow()
|
||||
deathWindow = nil
|
||||
end
|
||||
local cancelFunc = function()
|
||||
modules.game_interface.logout()
|
||||
g_game.safeLogout()
|
||||
cancelButton:getParent():destroy()
|
||||
deathWindow = nil
|
||||
end
|
||||
|
@@ -18,7 +18,7 @@ function isLoaded()
|
||||
end
|
||||
|
||||
function load()
|
||||
local version = g_game.getProtocolVersion()
|
||||
local version = g_game.getClientVersion()
|
||||
|
||||
local datPath, sprPath
|
||||
if filename then
|
||||
|
@@ -88,6 +88,23 @@ function isHiddingOffline()
|
||||
return settings['hideOffline']
|
||||
end
|
||||
|
||||
function getSortedBy()
|
||||
local settings = g_settings.getNode('VipList')
|
||||
if not settings then
|
||||
return 'status'
|
||||
end
|
||||
return settings['sortedBy']
|
||||
end
|
||||
|
||||
function sortBy(state)
|
||||
settings = {}
|
||||
settings['sortedBy'] = state
|
||||
g_settings.mergeNode('VipList', settings)
|
||||
|
||||
refresh()
|
||||
end
|
||||
|
||||
|
||||
function onAddVip(id, name, state)
|
||||
local vipList = vipWindow:getChildById('contentsPanel')
|
||||
|
||||
@@ -118,13 +135,13 @@ function onAddVip(id, name, state)
|
||||
|
||||
for i=1,childrenCount do
|
||||
local child = vipList:getChildByIndex(i)
|
||||
if state == VipState.Online and child.vipState ~= VipState.Online then
|
||||
if state == VipState.Online and child.vipState ~= VipState.Online and getSortedBy() == 'status' then
|
||||
vipList:insertChild(i, label)
|
||||
return
|
||||
end
|
||||
|
||||
if (state ~= VipState.Online and child.vipState ~= VipState.Online)
|
||||
or (state == VipState.Online and child.vipState == VipState.Online) then
|
||||
if ((state ~= VipState.Online and child.vipState ~= VipState.Online)
|
||||
or (state == VipState.Online and child.vipState == VipState.Online)) or getSortedBy() == 'name' then
|
||||
|
||||
local childText = child:getText():lower()
|
||||
local length = math.min(childText:len(), nameLower:len())
|
||||
@@ -168,6 +185,14 @@ function onVipListMousePress(widget, mousePos, mouseButton)
|
||||
menu:addOption(tr('Show Offline'), function() hideOffline(false) end)
|
||||
end
|
||||
|
||||
if not(getSortedBy() == 'name') then
|
||||
menu:addOption(tr('Sort by name'), function() sortBy('name') end)
|
||||
end
|
||||
|
||||
if not(getSortedBy() == 'status') then
|
||||
menu:addOption(tr('Sort by status'), function() sortBy('status') end)
|
||||
end
|
||||
|
||||
menu:display(mousePos)
|
||||
|
||||
return true
|
||||
@@ -196,6 +221,15 @@ function onVipListLabelMousePress(widget, mousePos, mouseButton)
|
||||
else
|
||||
menu:addOption(tr('Show Offline'), function() hideOffline(false) end)
|
||||
end
|
||||
|
||||
if not(getSortedBy() == 'name') then
|
||||
menu:addOption(tr('Sort by name'), function() sortBy('name') end)
|
||||
end
|
||||
|
||||
if not(getSortedBy() == 'status') then
|
||||
menu:addOption(tr('Sort by status'), function() sortBy('status') end)
|
||||
end
|
||||
|
||||
menu:display(mousePos)
|
||||
|
||||
return true
|
||||
|
@@ -6,7 +6,7 @@ end
|
||||
|
||||
function g_game.chooseRsa(host)
|
||||
if currentRsa ~= CIPSOFT_RSA and currentRsa ~= OTSERV_RSA then return end
|
||||
if string.ends(host, '.tibia.com') or string.ends(host, '.cipsoft.com') then
|
||||
if host:ends('.tibia.com') or host:ends('.cipsoft.com') then
|
||||
g_game.setRsa(CIPSOFT_RSA)
|
||||
|
||||
if g_app.getOs() == 'windows' then
|
||||
@@ -32,22 +32,29 @@ function g_game.isOfficialTibia()
|
||||
return currentRsa == CIPSOFT_RSA
|
||||
end
|
||||
|
||||
function g_game.getSupportedProtocols()
|
||||
function g_game.getSupportedClients()
|
||||
return {
|
||||
810, 811, 840, 842, 850, 853, 854,
|
||||
860, 861, 862, 870, 910, 940, 944,
|
||||
953, 954, 960, 961, 963, 970, 971,
|
||||
973, 974
|
||||
953, 954, 960, 961, 963, 970, 980,
|
||||
981, 982, 983, 984, 985, 986, 1001,
|
||||
1002, 1010
|
||||
}
|
||||
end
|
||||
|
||||
function g_game.getSupportedClients(protocol)
|
||||
function g_game.getProtocolVersionForClient(client)
|
||||
clients = {
|
||||
[971] = {980},
|
||||
[973] = {981},
|
||||
[974] = {982}
|
||||
[980] = 971,
|
||||
[981] = 973,
|
||||
[982] = 974,
|
||||
[983] = 975,
|
||||
[984] = 976,
|
||||
[985] = 977,
|
||||
[986] = 978,
|
||||
[1001] = 979,
|
||||
[1002] = 980,
|
||||
}
|
||||
return clients[protocol] or {protocol}
|
||||
return clients[client] or client
|
||||
end
|
||||
|
||||
g_game.setRsa(OTSERV_RSA)
|
||||
|
@@ -353,6 +353,8 @@ namespace Otc
|
||||
GameForceFirstAutoWalkStep = 37,
|
||||
GameMinimapRemove = 38,
|
||||
GameDoubleShopSellAmount = 39,
|
||||
GameContainerPagination = 40,
|
||||
GameThingMarks = 41,
|
||||
// 51-100 reserved to be defined in lua
|
||||
LastGameFeature = 101
|
||||
};
|
||||
|
@@ -128,7 +128,7 @@ void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWal
|
||||
dest -= datType->getDisplacement() * scaleFactor;
|
||||
datType->draw(dest, scaleFactor, 0, xPattern, 0, 0, animationPhase, lightView);
|
||||
dest += getDisplacement() * scaleFactor;
|
||||
zPattern = 1;
|
||||
zPattern = std::min(1, getNumPatternZ() - 1);
|
||||
}
|
||||
|
||||
PointF jumpOffset = m_jumpOffset * scaleFactor;
|
||||
|
@@ -99,6 +99,11 @@ void Game::resetGameStates()
|
||||
m_walkEvent = nullptr;
|
||||
}
|
||||
|
||||
if(m_checkConnectionEvent) {
|
||||
m_checkConnectionEvent->cancel();
|
||||
m_checkConnectionEvent = nullptr;
|
||||
}
|
||||
|
||||
m_containers.clear();
|
||||
m_vips.clear();
|
||||
m_gmActions.clear();
|
||||
@@ -183,6 +188,16 @@ void Game::processGameStart()
|
||||
g_game.ping();
|
||||
}, m_pingDelay);
|
||||
}
|
||||
|
||||
m_checkConnectionEvent = g_dispatcher.cycleEvent([this] {
|
||||
if(!g_game.isConnectionOk() && !m_connectionFailWarned) {
|
||||
g_lua.callGlobalField("g_game", "onConnectionFailing", true);
|
||||
m_connectionFailWarned = true;
|
||||
} else if(g_game.isConnectionOk() && m_connectionFailWarned) {
|
||||
g_lua.callGlobalField("g_game", "onConnectionFailing", false);
|
||||
m_connectionFailWarned = false;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
void Game::processGameEnd()
|
||||
@@ -190,6 +205,11 @@ void Game::processGameEnd()
|
||||
m_online = false;
|
||||
g_lua.callGlobalField("g_game", "onGameEnd");
|
||||
|
||||
if(m_connectionFailWarned) {
|
||||
g_lua.callGlobalField("g_game", "onConnectionFailing", false);
|
||||
m_connectionFailWarned = false;
|
||||
}
|
||||
|
||||
// reset game state
|
||||
resetGameStates();
|
||||
|
||||
@@ -554,7 +574,7 @@ bool Game::walk(Otc::Direction direction)
|
||||
if(m_lastWalkDir != direction) {
|
||||
// must add a new walk event
|
||||
float ticks = m_localPlayer->getStepTicksLeft();
|
||||
if(ticks < 0) { ticks = 0; }
|
||||
if(ticks <= 0) { ticks = 1; }
|
||||
|
||||
if(m_walkEvent) {
|
||||
m_walkEvent->cancel();
|
||||
@@ -697,15 +717,16 @@ void Game::autoWalk(std::vector<Otc::Direction> dirs)
|
||||
|
||||
auto it = dirs.begin();
|
||||
Otc::Direction direction = *it;
|
||||
if(m_localPlayer->canWalk(direction)) {
|
||||
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
|
||||
if(toTile && toTile->isWalkable() && !m_localPlayer->isServerWalking()) {
|
||||
m_localPlayer->preWalk(direction);
|
||||
if(!m_localPlayer->canWalk(direction))
|
||||
return;
|
||||
|
||||
if(getFeature(Otc::GameForceFirstAutoWalkStep)) {
|
||||
forceWalk(direction);
|
||||
dirs.erase(it);
|
||||
}
|
||||
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
|
||||
if(toTile && toTile->isWalkable() && !m_localPlayer->isServerWalking()) {
|
||||
m_localPlayer->preWalk(direction);
|
||||
|
||||
if(getFeature(Otc::GameForceFirstAutoWalkStep)) {
|
||||
forceWalk(direction);
|
||||
dirs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1379,7 +1400,7 @@ void Game::setProtocolVersion(int version)
|
||||
if(isOnline())
|
||||
stdext::throw_exception("Unable to change protocol version while online");
|
||||
|
||||
if(version != 0 && (version < 810 || version > 974))
|
||||
if(version != 0 && (version < 810 || version > 1010))
|
||||
stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
|
||||
|
||||
m_features.reset();
|
||||
@@ -1443,6 +1464,14 @@ void Game::setProtocolVersion(int version)
|
||||
enableFeature(Otc::GameNewSpeedLaw);
|
||||
}
|
||||
|
||||
if(version >= 976) {
|
||||
enableFeature(Otc::GameContainerPagination);
|
||||
}
|
||||
|
||||
if(version >= 979) {
|
||||
enableFeature(Otc::GameThingMarks);
|
||||
}
|
||||
|
||||
m_protocolVersion = version;
|
||||
|
||||
Proto::buildMessageModesMap(version);
|
||||
@@ -1458,7 +1487,7 @@ void Game::setClientVersion(int version)
|
||||
if(isOnline())
|
||||
stdext::throw_exception("Unable to change client version while online");
|
||||
|
||||
if(version != 0 && (version < 810 || version > 982))
|
||||
if(version != 0 && (version < 810 || version > 1010))
|
||||
stdext::throw_exception(stdext::format("Client version %d not supported", version));
|
||||
|
||||
m_clientVersion = version;
|
||||
|
@@ -281,6 +281,7 @@ public:
|
||||
bool isDead() { return m_dead; }
|
||||
bool isAttacking() { return !!m_attackingCreature; }
|
||||
bool isFollowing() { return !!m_followingCreature; }
|
||||
bool isConnectionOk() { return m_protocolGame && m_protocolGame->getElapsedTicksSinceLastRead() < 5000; }
|
||||
|
||||
int getPing() { return m_ping >= 0 ? std::max(m_ping, m_pingTimer.elapsed_millis()) : -1; }
|
||||
ContainerPtr getContainer(int index) { return m_containers[index]; }
|
||||
@@ -340,6 +341,8 @@ private:
|
||||
std::bitset<Otc::LastGameFeature> m_features;
|
||||
ScheduledEventPtr m_pingEvent;
|
||||
ScheduledEventPtr m_walkEvent;
|
||||
ScheduledEventPtr m_checkConnectionEvent;
|
||||
bool m_connectionFailWarned;
|
||||
int m_protocolVersion;
|
||||
int m_clientVersion;
|
||||
std::string m_clientSignature;
|
||||
|
@@ -72,7 +72,7 @@ bool LocalPlayer::canWalk(Otc::Direction direction)
|
||||
return false;
|
||||
|
||||
// last walk is not done yet
|
||||
if(m_walkTimer.ticksElapsed() < getStepDuration())
|
||||
if((m_walkTimer.ticksElapsed() < getStepDuration()) && !isAutoWalking())
|
||||
return false;
|
||||
|
||||
// prewalk has a timeout, because for some reason that I don't know yet the server sometimes doesn't answer the prewalk
|
||||
@@ -83,7 +83,7 @@ bool LocalPlayer::canWalk(Otc::Direction direction)
|
||||
return false;
|
||||
|
||||
// cannot walk while already walking
|
||||
if(m_walking && !prewalkTimeouted)
|
||||
if((m_walking && !isAutoWalking()) && (!prewalkTimeouted || m_secondPreWalk))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -95,6 +95,7 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos)
|
||||
if(m_preWalking) {
|
||||
// switch to normal walking
|
||||
m_preWalking = false;
|
||||
m_secondPreWalk = false;
|
||||
m_lastPrewalkDone = true;
|
||||
// if is to the last prewalk destination, updates the walk preserving the animation
|
||||
if(newPos == m_lastPrewalkDestination) {
|
||||
@@ -118,7 +119,8 @@ void LocalPlayer::preWalk(Otc::Direction direction)
|
||||
Position newPos = m_position.translatedToDirection(direction);
|
||||
|
||||
// avoid reanimating prewalks
|
||||
if(m_preWalking && m_lastPrewalkDestination == newPos) {
|
||||
if(m_preWalking) {
|
||||
m_secondPreWalk = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -277,6 +279,7 @@ void LocalPlayer::terminateWalk()
|
||||
{
|
||||
Creature::terminateWalk();
|
||||
m_preWalking = false;
|
||||
m_secondPreWalk = false;
|
||||
m_idleTimer.restart();
|
||||
|
||||
auto self = asLocalPlayer();
|
||||
|
@@ -126,6 +126,7 @@ private:
|
||||
ticks_t m_walkLockExpiration;
|
||||
stdext::boolean<false> m_preWalking;
|
||||
stdext::boolean<true> m_lastPrewalkDone;
|
||||
stdext::boolean<false> m_secondPreWalk;
|
||||
stdext::boolean<false> m_serverWalking;
|
||||
stdext::boolean<false> m_knownCompletePath;
|
||||
|
||||
|
@@ -230,6 +230,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_game", "isDead", &Game::isDead, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "isAttacking", &Game::isAttacking, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "isFollowing", &Game::isFollowing, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "isConnectionOk", &Game::isConnectionOk, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "getPing", &Game::getPing, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "getContainer", &Game::getContainer, &g_game);
|
||||
g_lua.bindSingletonFunction("g_game", "getContainers", &Game::getContainers, &g_game);
|
||||
|
@@ -608,21 +608,21 @@ int MapView::calcFirstVisibleFloor()
|
||||
Position pos = cameraPosition.translated(ix, iy);
|
||||
|
||||
// process tiles that we can look through, e.g. windows, doors
|
||||
if((ix == 0 && iy == 0) || (/*(std::abs(ix) != std::abs(iy)) && */g_map.isLookPossible(pos))) {
|
||||
if((ix == 0 && iy == 0) || ((std::abs(ix) != std::abs(iy)) && g_map.isLookPossible(pos))) {
|
||||
Position upperPos = pos;
|
||||
Position coveredPos = pos;
|
||||
|
||||
while(coveredPos.coveredUp() && upperPos.up() && upperPos.z >= firstFloor) {
|
||||
// check tiles physically above
|
||||
TilePtr tile = g_map.getTile(upperPos);
|
||||
if(tile && tile->limitsFloorsView()) {
|
||||
if(tile && tile->limitsFloorsView(!g_map.isLookPossible(pos))) {
|
||||
firstFloor = upperPos.z + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// check tiles geometrically above
|
||||
tile = g_map.getTile(coveredPos);
|
||||
if(tile && tile->limitsFloorsView()) {
|
||||
if(tile && tile->limitsFloorsView(g_map.isLookPossible(pos))) {
|
||||
firstFloor = coveredPos.z + 1;
|
||||
break;
|
||||
}
|
||||
|
@@ -603,6 +603,14 @@ void ProtocolGame::parseOpenContainer(const InputMessagePtr& msg)
|
||||
std::string name = msg->getString();
|
||||
int capacity = msg->getU8();
|
||||
bool hasParent = (msg->getU8() != 0);
|
||||
|
||||
if(g_game.getFeature(Otc::GameContainerPagination)) {
|
||||
msg->getU8(); // drag and drop
|
||||
msg->getU8(); // pagination
|
||||
msg->getU16(); // container size
|
||||
msg->getU16(); // first index
|
||||
}
|
||||
|
||||
int itemCount = msg->getU8();
|
||||
|
||||
std::vector<ItemPtr> items(itemCount);
|
||||
@@ -622,13 +630,21 @@ void ProtocolGame::parseContainerAddItem(const InputMessagePtr& msg)
|
||||
{
|
||||
int containerId = msg->getU8();
|
||||
ItemPtr item = getItem(msg);
|
||||
if(g_game.getFeature(Otc::GameContainerPagination)) {
|
||||
msg->getU16(); // slot
|
||||
}
|
||||
g_game.processContainerAddItem(containerId, item);
|
||||
}
|
||||
|
||||
void ProtocolGame::parseContainerUpdateItem(const InputMessagePtr& msg)
|
||||
{
|
||||
int containerId = msg->getU8();
|
||||
int slot = msg->getU8();
|
||||
int slot;
|
||||
if(g_game.getFeature(Otc::GameContainerPagination)) {
|
||||
slot = msg->getU16();
|
||||
} else {
|
||||
slot = msg->getU8();
|
||||
}
|
||||
ItemPtr item = getItem(msg);
|
||||
g_game.processContainerUpdateItem(containerId, slot, item);
|
||||
}
|
||||
@@ -636,7 +652,13 @@ void ProtocolGame::parseContainerUpdateItem(const InputMessagePtr& msg)
|
||||
void ProtocolGame::parseContainerRemoveItem(const InputMessagePtr& msg)
|
||||
{
|
||||
int containerId = msg->getU8();
|
||||
int slot = msg->getU8();
|
||||
int slot;
|
||||
if(g_game.getFeature(Otc::GameContainerPagination)) {
|
||||
slot = msg->getU16();
|
||||
getItem(msg);
|
||||
} else {
|
||||
slot = msg->getU8();
|
||||
}
|
||||
g_game.processContainerRemoveItem(containerId, slot);
|
||||
}
|
||||
|
||||
@@ -1808,6 +1830,12 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
|
||||
if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
|
||||
emblem = msg->getU8();
|
||||
|
||||
if(g_game.getFeature(Otc::GameThingMarks)) {
|
||||
msg->getU8(); // creature type for summons
|
||||
msg->getU8(); // mark
|
||||
msg->getU16(); // helpers
|
||||
}
|
||||
|
||||
if(g_game.getProtocolVersion() >= 854)
|
||||
unpass = msg->getU8();
|
||||
|
||||
@@ -1860,6 +1888,10 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
|
||||
if(item->getId() == 0)
|
||||
stdext::throw_exception(stdext::format("unable to create item with invalid id %d", id));
|
||||
|
||||
if(g_game.getFeature(Otc::GameThingMarks)) {
|
||||
msg->getU8(); // mark
|
||||
}
|
||||
|
||||
if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
|
||||
item->setCountOrSubType(msg->getU8());
|
||||
|
||||
|
@@ -67,6 +67,17 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
|
||||
attr -= 1;
|
||||
}
|
||||
|
||||
if(g_game.getProtocolVersion() >= 1010) {
|
||||
/* In 10.10 all attributes from 16 and up were
|
||||
* incremented by 1 to make space for 16 as
|
||||
* "No Movement Animation" flag.
|
||||
*/
|
||||
if(attr == 16)
|
||||
attr = ThingAttrNoMoveAnimation;
|
||||
else if(attr > 16)
|
||||
attr -= 1;
|
||||
}
|
||||
|
||||
switch(attr) {
|
||||
case ThingAttrDisplacement: {
|
||||
m_displacement.x = fin->getU16();
|
||||
@@ -126,8 +137,8 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
|
||||
|
||||
int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
|
||||
|
||||
if(totalSprites == 0)
|
||||
stdext::throw_exception("a thing type has no sprites");
|
||||
// if(totalSprites == 0)
|
||||
// stdext::throw_exception("a thing type has no sprites");
|
||||
if(totalSprites > 4096)
|
||||
stdext::throw_exception("a thing type has more than 4096 sprites");
|
||||
|
||||
|
@@ -81,6 +81,7 @@ enum ThingAttr : uint8 {
|
||||
ThingAttrOpacity = 100,
|
||||
ThingAttrNotPreWalkable = 101,
|
||||
|
||||
ThingAttrNoMoveAnimation = 253, // real value is 16, but we need to do this for backwards compatibility
|
||||
ThingAttrChargeable = 254, // deprecated
|
||||
ThingLastAttr = 255
|
||||
};
|
||||
|
@@ -590,15 +590,24 @@ bool Tile::hasCreature()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Tile::limitsFloorsView()
|
||||
bool Tile::limitsFloorsView(bool isFreeView)
|
||||
{
|
||||
// ground and walls limits the view
|
||||
ThingPtr firstThing = getThing(0);
|
||||
if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || firstThing->isOnBottom()))
|
||||
return true;
|
||||
|
||||
if(isFreeView){
|
||||
if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || firstThing->isOnBottom()))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || (firstThing->isOnBottom() && firstThing->blockProjectile())))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Tile::canErase()
|
||||
{
|
||||
return m_walkingCreatures.empty() && m_effects.empty() && m_things.empty() && m_flags == 0 && m_minimapColor == 0;
|
||||
|
@@ -105,7 +105,7 @@ public:
|
||||
bool mustHookSouth();
|
||||
bool mustHookEast();
|
||||
bool hasCreature();
|
||||
bool limitsFloorsView();
|
||||
bool limitsFloorsView(bool isFreeView = false);
|
||||
bool canErase();
|
||||
bool hasElevation(int elevation = 1);
|
||||
void overwriteMinimapColor(uint8 color) { m_minimapColor = color; }
|
||||
|
@@ -28,6 +28,7 @@
|
||||
|
||||
#ifdef FW_GRAPHICS
|
||||
#include <framework/platform/platformwindow.h>
|
||||
#include <framework/platform/platform.h>
|
||||
#include <framework/luaengine/luainterface.h>
|
||||
#endif
|
||||
|
||||
@@ -104,16 +105,16 @@ void Logger::logFunc(Fw::LogLevel level, const std::string& message, std::string
|
||||
prettyFunction = prettyFunction.substr(prettyFunction.find_last_of(' ') + 1);
|
||||
|
||||
|
||||
std::string out = message;
|
||||
std::stringstream ss;
|
||||
ss << message;
|
||||
|
||||
if(!prettyFunction.empty()) {
|
||||
if(g_lua.isInCppCallback())
|
||||
out = g_lua.traceback(out, 1);
|
||||
else
|
||||
out += "\nat:\t[C++]: " + prettyFunction;
|
||||
ss << g_lua.traceback("", 1);
|
||||
ss << g_platform.traceback(prettyFunction, 1, 8);
|
||||
}
|
||||
|
||||
log(level, out);
|
||||
log(level, ss.str());
|
||||
}
|
||||
|
||||
void Logger::fireOldMessages()
|
||||
|
@@ -577,20 +577,19 @@ int LuaInterface::lua_dofile(lua_State* L)
|
||||
|
||||
int LuaInterface::lua_dofiles(lua_State* L)
|
||||
{
|
||||
std::string directory = g_lua.popString();
|
||||
|
||||
for(const std::string& fileName : g_resources.listDirectoryFiles(directory)) {
|
||||
if(!g_resources.isFileType(fileName, "lua"))
|
||||
continue;
|
||||
|
||||
try {
|
||||
g_lua.loadScript(directory + "/" + fileName);
|
||||
g_lua.call(0, 0);
|
||||
} catch(stdext::exception& e) {
|
||||
g_lua.pushString(e.what());
|
||||
g_lua.error();
|
||||
}
|
||||
std::string contains = "";
|
||||
if(g_lua.getTop() > 2) {
|
||||
contains = g_lua.popString();
|
||||
}
|
||||
|
||||
bool recursive = false;
|
||||
if(g_lua.getTop() > 1) {
|
||||
recursive = g_lua.popBoolean();
|
||||
}
|
||||
|
||||
std::string directory = g_lua.popString();
|
||||
g_lua.loadFiles(directory, recursive, contains);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1247,3 +1246,29 @@ int LuaInterface::getTop()
|
||||
{
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
void LuaInterface::loadFiles(std::string directory, bool recursive, std::string contains)
|
||||
{
|
||||
for(const std::string& fileName : g_resources.listDirectoryFiles(directory)) {
|
||||
std::string fullPath = directory + "/" + fileName;
|
||||
|
||||
if(recursive && g_resources.directoryExists(fullPath)) {
|
||||
loadFiles(fullPath, true, contains);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!g_resources.isFileType(fileName, "lua"))
|
||||
continue;
|
||||
|
||||
if(!contains.empty() && fileName.find(contains) == std::string::npos)
|
||||
continue;
|
||||
|
||||
try {
|
||||
g_lua.loadScript(fullPath);
|
||||
g_lua.call(0, 0);
|
||||
} catch(stdext::exception& e) {
|
||||
g_lua.pushString(e.what());
|
||||
g_lua.error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -321,6 +321,8 @@ public:
|
||||
void clearStack() { pop(stackSize()); }
|
||||
bool hasIndex(int index) { return (stackSize() >= (index < 0 ? -index : index) && index != 0); }
|
||||
|
||||
void loadFiles(std::string directory, bool recursive = false, std::string contains = "");
|
||||
|
||||
/// Pushes any type onto the stack
|
||||
template<typename T, typename... Args>
|
||||
int polymorphicPush(const T& v, const Args&... args);
|
||||
|
@@ -569,6 +569,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageFixedRatio", &UIWidget::setImageFixedRatio);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageRepeated", &UIWidget::setImageRepeated);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageSmooth", &UIWidget::setImageSmooth);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageAutoResize", &UIWidget::setImageAutoResize);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageBorderTop", &UIWidget::setImageBorderTop);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageBorderRight", &UIWidget::setImageBorderRight);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("setImageBorderBottom", &UIWidget::setImageBorderBottom);
|
||||
@@ -585,6 +586,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<UIWidget>("getImageColor", &UIWidget::getImageColor);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("isImageFixedRatio", &UIWidget::isImageFixedRatio);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("isImageSmooth", &UIWidget::isImageSmooth);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("isImageAutoResize", &UIWidget::isImageAutoResize);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("getImageBorderTop", &UIWidget::getImageBorderTop);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("getImageBorderRight", &UIWidget::getImageBorderRight);
|
||||
g_lua.bindClassMemberFunction<UIWidget>("getImageBorderBottom", &UIWidget::getImageBorderBottom);
|
||||
|
@@ -27,16 +27,17 @@
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
asio::io_service g_ioService;
|
||||
std::list<std::shared_ptr<asio::streambuf>> Connection::m_outputStreams;
|
||||
|
||||
Connection::Connection() :
|
||||
m_readTimer(g_ioService),
|
||||
m_writeTimer(g_ioService),
|
||||
m_delayedWriteTimer(g_ioService),
|
||||
m_resolver(g_ioService),
|
||||
m_socket(g_ioService)
|
||||
{
|
||||
m_connected = false;
|
||||
m_connecting = false;
|
||||
m_sendBufferSize = 0;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
@@ -56,34 +57,7 @@ void Connection::poll()
|
||||
void Connection::terminate()
|
||||
{
|
||||
g_ioService.stop();
|
||||
}
|
||||
|
||||
void Connection::connect(const std::string& host, uint16 port, const std::function<void()>& connectCallback)
|
||||
{
|
||||
m_connected = false;
|
||||
m_connecting = true;
|
||||
m_error.clear();
|
||||
m_connectCallback = connectCallback;
|
||||
|
||||
asio::ip::tcp::resolver::query query(host, stdext::unsafe_cast<std::string>(port));
|
||||
|
||||
auto self = asConnection();
|
||||
m_resolver.async_resolve(query, [=](const boost::system::error_code& error, asio::ip::tcp::resolver::iterator endpointIterator) {
|
||||
if(self.is_unique())
|
||||
return;
|
||||
m_readTimer.cancel();
|
||||
|
||||
if(!error) {
|
||||
m_socket.async_connect(*endpointIterator, std::bind(&Connection::onConnect, asConnection(), std::placeholders::_1));
|
||||
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
} else
|
||||
handleError(error);
|
||||
});
|
||||
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
m_outputStreams.clear();
|
||||
}
|
||||
|
||||
void Connection::close()
|
||||
@@ -92,8 +66,8 @@ void Connection::close()
|
||||
return;
|
||||
|
||||
// flush send data before disconnecting on clean connections
|
||||
if(m_connected && !m_error && m_sendBufferSize > 0 && m_sendEvent)
|
||||
m_sendEvent->execute();
|
||||
if(m_connected && !m_error && m_outputStream)
|
||||
internal_write();
|
||||
|
||||
m_connecting = false;
|
||||
m_connected = false;
|
||||
@@ -104,6 +78,7 @@ void Connection::close()
|
||||
m_resolver.cancel();
|
||||
m_readTimer.cancel();
|
||||
m_writeTimer.cancel();
|
||||
m_delayedWriteTimer.cancel();
|
||||
|
||||
if(m_socket.is_open()) {
|
||||
boost::system::error_code ec;
|
||||
@@ -112,94 +87,138 @@ void Connection::close()
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::write(uint8* buffer, uint16 size)
|
||||
void Connection::connect(const std::string& host, uint16 port, const std::function<void()>& connectCallback)
|
||||
{
|
||||
m_connected = false;
|
||||
m_connecting = true;
|
||||
m_error.clear();
|
||||
m_connectCallback = connectCallback;
|
||||
|
||||
asio::ip::tcp::resolver::query query(host, stdext::unsafe_cast<std::string>(port));
|
||||
m_resolver.async_resolve(query, std::bind(&Connection::onResolve, asConnection(), std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_readTimer.cancel();
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::internal_connect(asio::ip::basic_resolver<asio::ip::tcp>::iterator endpointIterator)
|
||||
{
|
||||
m_socket.async_connect(*endpointIterator, std::bind(&Connection::onConnect, asConnection(), std::placeholders::_1));
|
||||
|
||||
m_readTimer.cancel();
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::write(uint8* buffer, size_t size)
|
||||
{
|
||||
if(!m_connected)
|
||||
return;
|
||||
|
||||
// send old buffer if we can't add more data
|
||||
if(m_sendBufferSize + size >= SEND_BUFFER_SIZE && m_sendEvent)
|
||||
m_sendEvent->execute();
|
||||
|
||||
// we can't send the data right away, otherwise we could create tcp congestion
|
||||
memcpy(m_sendBuffer + m_sendBufferSize, buffer, size);
|
||||
m_sendBufferSize += size;
|
||||
if(!m_outputStream) {
|
||||
if(!m_outputStreams.empty()) {
|
||||
m_outputStream = m_outputStreams.front();
|
||||
m_outputStreams.pop_front();
|
||||
} else
|
||||
m_outputStream = std::shared_ptr<asio::streambuf>(new asio::streambuf);
|
||||
|
||||
if(!m_sendEvent || m_sendEvent->isExecuted() || m_sendEvent->isCanceled()) {
|
||||
auto self = asConnection();
|
||||
|
||||
// wait 1 ms to do the real send
|
||||
m_sendEvent = g_dispatcher.scheduleEvent([=] {
|
||||
if(self.is_unique())
|
||||
return;
|
||||
//m_writeTimer.cancel();
|
||||
|
||||
asio::async_write(m_socket,
|
||||
asio::buffer(m_sendBuffer, m_sendBufferSize),
|
||||
std::bind(&Connection::onWrite, asConnection(), std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_writeTimer.expires_from_now(boost::posix_time::seconds(WRITE_TIMEOUT));
|
||||
m_writeTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
|
||||
m_sendBufferSize = 0;
|
||||
}, SEND_INTERVAL);
|
||||
m_delayedWriteTimer.cancel();
|
||||
m_delayedWriteTimer.expires_from_now(boost::posix_time::milliseconds(10));
|
||||
m_delayedWriteTimer.async_wait(std::bind(&Connection::onCanWrite, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
std::ostream os(m_outputStream.get());
|
||||
os.write((const char*)buffer, size);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
void Connection::internal_write()
|
||||
{
|
||||
if(!m_connected)
|
||||
return;
|
||||
|
||||
std::shared_ptr<asio::streambuf> outputStream = m_outputStream;
|
||||
m_outputStream = nullptr;
|
||||
|
||||
asio::async_write(m_socket,
|
||||
*outputStream,
|
||||
std::bind(&Connection::onWrite, asConnection(), std::placeholders::_1, std::placeholders::_2, outputStream));
|
||||
|
||||
m_writeTimer.cancel();
|
||||
m_writeTimer.expires_from_now(boost::posix_time::seconds(WRITE_TIMEOUT));
|
||||
m_writeTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::read(uint16 bytes, const RecvCallback& callback)
|
||||
{
|
||||
m_readTimer.cancel();
|
||||
|
||||
if(!m_connected)
|
||||
return;
|
||||
|
||||
m_recvCallback = callback;
|
||||
|
||||
asio::async_read(m_socket,
|
||||
asio::buffer(m_streamBuffer.prepare(bytes)),
|
||||
std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, bytes));
|
||||
asio::buffer(m_inputStream.prepare(bytes)),
|
||||
std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_readTimer.cancel();
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::read_until(const std::string& what, const RecvCallback& callback)
|
||||
{
|
||||
m_readTimer.cancel();
|
||||
|
||||
if(!m_connected)
|
||||
return;
|
||||
|
||||
m_recvCallback = callback;
|
||||
|
||||
asio::async_read_until(m_socket,
|
||||
m_streamBuffer,
|
||||
m_inputStream,
|
||||
what.c_str(),
|
||||
std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_readTimer.cancel();
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::read_some(const RecvCallback& callback)
|
||||
{
|
||||
m_readTimer.cancel();
|
||||
|
||||
if(!m_connected)
|
||||
return;
|
||||
|
||||
m_recvCallback = callback;
|
||||
|
||||
m_socket.async_read_some(asio::buffer(m_streamBuffer.prepare(RECV_BUFFER_SIZE)),
|
||||
m_socket.async_read_some(asio::buffer(m_inputStream.prepare(RECV_BUFFER_SIZE)),
|
||||
std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
m_readTimer.cancel();
|
||||
m_readTimer.expires_from_now(boost::posix_time::seconds(READ_TIMEOUT));
|
||||
m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::onResolve(const boost::system::error_code& error, asio::ip::basic_resolver<asio::ip::tcp>::iterator endpointIterator)
|
||||
{
|
||||
m_readTimer.cancel();
|
||||
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
if(!error)
|
||||
internal_connect(endpointIterator);
|
||||
else
|
||||
handleError(error);
|
||||
}
|
||||
|
||||
void Connection::onConnect(const boost::system::error_code& error)
|
||||
{
|
||||
m_readTimer.cancel();
|
||||
m_activityTimer.restart();
|
||||
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
if(!error) {
|
||||
m_connected = true;
|
||||
@@ -216,51 +235,72 @@ void Connection::onConnect(const boost::system::error_code& error)
|
||||
m_connecting = false;
|
||||
}
|
||||
|
||||
void Connection::onWrite(const boost::system::error_code& error, size_t)
|
||||
void Connection::onCanWrite(const boost::system::error_code& error)
|
||||
{
|
||||
m_delayedWriteTimer.cancel();
|
||||
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
if(m_connected)
|
||||
internal_write();
|
||||
}
|
||||
|
||||
void Connection::onWrite(const boost::system::error_code& error, size_t writeSize, std::shared_ptr<asio::streambuf> outputStream)
|
||||
{
|
||||
m_writeTimer.cancel();
|
||||
|
||||
if(!m_connected)
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
if(error)
|
||||
// free output stream and store for using it again later
|
||||
outputStream->consume(outputStream->size());
|
||||
m_outputStreams.push_back(outputStream);
|
||||
|
||||
if(m_connected && error)
|
||||
handleError(error);
|
||||
}
|
||||
|
||||
void Connection::onRecv(const boost::system::error_code& error, size_t recvSize)
|
||||
{
|
||||
m_readTimer.cancel();
|
||||
m_activityTimer.restart();
|
||||
|
||||
if(!m_connected)
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
if(!error) {
|
||||
if(m_recvCallback) {
|
||||
const char* header = boost::asio::buffer_cast<const char*>(m_streamBuffer.data());
|
||||
m_recvCallback((uint8*)header, recvSize);
|
||||
}
|
||||
if(m_connected) {
|
||||
if(!error) {
|
||||
if(m_recvCallback) {
|
||||
const char* header = boost::asio::buffer_cast<const char*>(m_inputStream.data());
|
||||
m_recvCallback((uint8*)header, recvSize);
|
||||
}
|
||||
} else
|
||||
handleError(error);
|
||||
}
|
||||
else
|
||||
handleError(error);
|
||||
|
||||
m_streamBuffer.consume(recvSize);
|
||||
if(!error)
|
||||
m_inputStream.consume(recvSize);
|
||||
}
|
||||
|
||||
void Connection::onTimeout(const boost::system::error_code& error)
|
||||
{
|
||||
if(error != asio::error::operation_aborted)
|
||||
handleError(asio::error::timed_out);
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
handleError(asio::error::timed_out);
|
||||
}
|
||||
|
||||
void Connection::handleError(const boost::system::error_code& error)
|
||||
{
|
||||
if(error != asio::error::operation_aborted) {
|
||||
m_error = error;
|
||||
if(m_errorCallback)
|
||||
m_errorCallback(error);
|
||||
if(m_connected || m_connecting)
|
||||
close();
|
||||
}
|
||||
if(error == asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
m_error = error;
|
||||
if(m_errorCallback)
|
||||
m_errorCallback(error);
|
||||
if(m_connected || m_connecting)
|
||||
close();
|
||||
}
|
||||
|
||||
int Connection::getIp()
|
||||
|
@@ -36,7 +36,6 @@ class Connection : public LuaObject
|
||||
enum {
|
||||
READ_TIMEOUT = 30,
|
||||
WRITE_TIMEOUT = 30,
|
||||
SEND_INTERVAL = 1,
|
||||
SEND_BUFFER_SIZE = 65536,
|
||||
RECV_BUFFER_SIZE = 65536
|
||||
};
|
||||
@@ -51,7 +50,7 @@ public:
|
||||
void connect(const std::string& host, uint16 port, const std::function<void()>& connectCallback);
|
||||
void close();
|
||||
|
||||
void write(uint8* buffer, uint16 size);
|
||||
void write(uint8* buffer, size_t size);
|
||||
void read(uint16 bytes, const RecvCallback& callback);
|
||||
void read_until(const std::string& what, const RecvCallback& callback);
|
||||
void read_some(const RecvCallback& callback);
|
||||
@@ -62,11 +61,17 @@ public:
|
||||
boost::system::error_code getError() { return m_error; }
|
||||
bool isConnecting() { return m_connecting; }
|
||||
bool isConnected() { return m_connected; }
|
||||
ticks_t getElapsedTicksSinceLastRead() { return m_connected ? m_activityTimer.elapsed_millis() : -1; }
|
||||
|
||||
ConnectionPtr asConnection() { return static_self_cast<Connection>(); }
|
||||
|
||||
protected:
|
||||
void internal_connect(asio::ip::basic_resolver<asio::ip::tcp>::iterator endpointIterator);
|
||||
void internal_write();
|
||||
void onResolve(const boost::system::error_code& error, asio::ip::tcp::resolver::iterator endpointIterator);
|
||||
void onConnect(const boost::system::error_code& error);
|
||||
void onWrite(const boost::system::error_code& error, size_t);
|
||||
void onCanWrite(const boost::system::error_code& error);
|
||||
void onWrite(const boost::system::error_code& error, size_t writeSize, std::shared_ptr<asio::streambuf> outputStream);
|
||||
void onRecv(const boost::system::error_code& error, size_t recvSize);
|
||||
void onTimeout(const boost::system::error_code& error);
|
||||
void handleError(const boost::system::error_code& error);
|
||||
@@ -77,17 +82,17 @@ protected:
|
||||
|
||||
asio::deadline_timer m_readTimer;
|
||||
asio::deadline_timer m_writeTimer;
|
||||
asio::deadline_timer m_delayedWriteTimer;
|
||||
asio::ip::tcp::resolver m_resolver;
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
uint8 m_sendBuffer[SEND_BUFFER_SIZE];
|
||||
asio::streambuf m_streamBuffer;
|
||||
static std::list<std::shared_ptr<asio::streambuf>> m_outputStreams;
|
||||
std::shared_ptr<asio::streambuf> m_outputStream;
|
||||
asio::streambuf m_inputStream;
|
||||
bool m_connected;
|
||||
bool m_connecting;
|
||||
boost::system::error_code m_error;
|
||||
int m_sendBufferSize;
|
||||
Timer m_sendTimer;
|
||||
ScheduledEventPtr m_sendEvent;
|
||||
stdext::timer m_activityTimer;
|
||||
|
||||
friend class Server;
|
||||
};
|
||||
|
@@ -42,6 +42,8 @@ public:
|
||||
|
||||
bool isConnected();
|
||||
bool isConnecting();
|
||||
ticks_t getElapsedTicksSinceLastRead() { return m_connection ? m_connection->getElapsedTicksSinceLastRead() : -1; }
|
||||
|
||||
ConnectionPtr getConnection() { return m_connection; }
|
||||
void setConnection(const ConnectionPtr& connection) { m_connection = connection; }
|
||||
|
||||
|
@@ -45,6 +45,7 @@ public:
|
||||
std::string getCPUName();
|
||||
double getTotalSystemMemory();
|
||||
std::string getOSName();
|
||||
std::string traceback(const std::string& where, int level = 1, int maxDepth = 32);
|
||||
};
|
||||
|
||||
extern Platform g_platform;
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include <framework/stdext/stdext.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <execinfo.h>
|
||||
|
||||
void Platform::processArgs(std::vector<std::string>& args)
|
||||
{
|
||||
@@ -168,5 +169,35 @@ std::string Platform::getOSName()
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string Platform::traceback(const std::string& where, int level, int maxDepth)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "\nC++ stack traceback:";
|
||||
if(!where.empty())
|
||||
ss << "\n\t[C++]: " << where;
|
||||
|
||||
void* buffer[maxDepth + level + 1];
|
||||
int numLevels = backtrace(buffer, maxDepth + level + 1);
|
||||
char **tracebackBuffer = backtrace_symbols(buffer, numLevels);
|
||||
if(tracebackBuffer) {
|
||||
for(int i = 1 + level; i < numLevels; i++) {
|
||||
std::string line = tracebackBuffer[i];
|
||||
if(line.find("__libc_start_main") != std::string::npos)
|
||||
break;
|
||||
std::size_t demanglePos = line.find("(_Z");
|
||||
if(demanglePos != std::string::npos) {
|
||||
demanglePos++;
|
||||
int len = std::min(line.find_first_of("+", demanglePos), line.find_first_of(")", demanglePos)) - demanglePos;
|
||||
std::string funcName = line.substr(demanglePos, len);
|
||||
line.replace(demanglePos, len, stdext::demangle_name(funcName.c_str()));
|
||||
}
|
||||
ss << "\n\t" << line;
|
||||
}
|
||||
free(tracebackBuffer);
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -414,4 +414,13 @@ std::string Platform::getOSName()
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
std::string Platform::traceback(const std::string& where, int level, int maxDepth)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "\nat:";
|
||||
ss << "\n\t[C++]: " << where;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -624,6 +624,9 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
|
||||
break;
|
||||
}
|
||||
case WM_SYSKEYDOWN: {
|
||||
if(wParam == VK_F4 && m_inputEvent.keyboardModifiers & Fw::KeyboardAltModifier)
|
||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||
|
||||
processKeyDown(retranslateVirtualKey(wParam, lParam));
|
||||
break;
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include <boost/thread/future.hpp>
|
||||
|
||||
// hack to enable std::thread on mingw32 4.6
|
||||
#if !defined(_GLIBCXX_HAS_GTHREADS) && defined(__GNUG__)
|
||||
#if !defined(_GLIBCXX_HAS_GTHREADS) && defined(__GNUG__) && !defined(__clang__)
|
||||
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
@@ -98,9 +98,9 @@ void UIWidget::drawSelf(Fw::DrawPane drawPane)
|
||||
}
|
||||
|
||||
drawImage(m_rect);
|
||||
drawBorder(m_rect);
|
||||
drawIcon(m_rect);
|
||||
drawText(m_rect);
|
||||
drawBorder(m_rect);
|
||||
}
|
||||
|
||||
void UIWidget::drawChildren(const Rect& visibleRect, Fw::DrawPane drawPane)
|
||||
|
@@ -419,6 +419,7 @@ protected:
|
||||
stdext::boolean<false> m_imageFixedRatio;
|
||||
stdext::boolean<false> m_imageRepeated;
|
||||
stdext::boolean<false> m_imageSmooth;
|
||||
stdext::boolean<false> m_imageAutoResize;
|
||||
EdgeGroup<int> m_imageBorder;
|
||||
|
||||
public:
|
||||
@@ -435,6 +436,7 @@ public:
|
||||
void setImageFixedRatio(bool fixedRatio) { m_imageFixedRatio = fixedRatio; updateImageCache(); }
|
||||
void setImageRepeated(bool repeated) { m_imageRepeated = repeated; updateImageCache(); }
|
||||
void setImageSmooth(bool smooth) { m_imageSmooth = smooth; }
|
||||
void setImageAutoResize(bool autoResize) { m_imageAutoResize = autoResize; }
|
||||
void setImageBorderTop(int border) { m_imageBorder.top = border; configureBorderImage(); }
|
||||
void setImageBorderRight(int border) { m_imageBorder.right = border; configureBorderImage(); }
|
||||
void setImageBorderBottom(int border) { m_imageBorder.bottom = border; configureBorderImage(); }
|
||||
@@ -452,6 +454,7 @@ public:
|
||||
Color getImageColor() { return m_imageColor; }
|
||||
bool isImageFixedRatio() { return m_imageFixedRatio; }
|
||||
bool isImageSmooth() { return m_imageSmooth; }
|
||||
bool isImageAutoResize() { return m_imageAutoResize; }
|
||||
int getImageBorderTop() { return m_imageBorder.top; }
|
||||
int getImageBorderRight() { return m_imageBorder.right; }
|
||||
int getImageBorderBottom() { return m_imageBorder.bottom; }
|
||||
|
@@ -68,9 +68,10 @@ void UIWidget::parseImageStyle(const OTMLNodePtr& styleNode)
|
||||
setImageBorderBottom(node->value<int>());
|
||||
else if(node->tag() == "image-border-left")
|
||||
setImageBorderLeft(node->value<int>());
|
||||
else if(node->tag() == "image-border") {
|
||||
else if(node->tag() == "image-border")
|
||||
setImageBorder(node->value<int>());
|
||||
}
|
||||
else if(node->tag() == "image-auto-resize")
|
||||
setImageAutoResize(node->value<bool>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,5 +181,16 @@ void UIWidget::setImageSource(const std::string& source)
|
||||
m_imageTexture = nullptr;
|
||||
else
|
||||
m_imageTexture = g_textures.getTexture(source);
|
||||
|
||||
if(m_imageTexture && (!m_rect.isValid() || m_imageAutoResize)) {
|
||||
Size size = getSize();
|
||||
Size imageSize = m_imageTexture->getSize();
|
||||
if(size.width() <= 0 || m_imageAutoResize)
|
||||
size.setWidth(imageSize.width());
|
||||
if(size.height() <= 0 || m_imageAutoResize)
|
||||
size.setHeight(imageSize.height());
|
||||
setSize(size);
|
||||
}
|
||||
|
||||
m_imageMustRecache = true;
|
||||
}
|
||||
|
@@ -365,7 +365,7 @@ index b980be0..7a84f61 100644
|
||||
+ std::string buffer = msg.getString();
|
||||
+
|
||||
+ // process additional opcodes via lua script event
|
||||
+ addGameTask(&Game::parsePlayerExtendedOpcode, player->getId(), opcode, buffer);
|
||||
+ addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
|
||||
+}
|
||||
+
|
||||
+void ProtocolGame::sendExtendedOpcode(uint8_t opcode, const std::string& buffer)
|
||||
|
Reference in New Issue
Block a user