Minimap, hotkeys and lot of other changes

* Begin working on a new layout system for UIMinimap and later UIMap,
this new layout system allows to add widgets to the minimap
* Add option to disable motd
* Rework hotkey binding
* Lots of fixes in hotkeys manager
* Add fullmap view using Ctrl+Shift+M
* Prevent some crashs in ThingType draw
* Add function to load minimap from PNG files
* Fixes in minimap saving
* Fixes in Tile::isClickable
* Add UIMapAnchorLayout, new layout for maps
* Fix freezes in win32 when pressing alt key
master
Eduardo Bart 11 years ago
parent f8b078ea91
commit 9a54bfcc90

1
.gitignore vendored

@ -37,3 +37,4 @@ otclient.layout
LOCALTODO LOCALTODO
tags tags
Thumbs.db Thumbs.db
.directory

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

@ -2,9 +2,13 @@ MinimapFlag < UIWidget
size: 11 11 size: 11 11
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
focusable: false
MinimapFlags < UIWidget MinimapCross < UIWidget
anchors.fill: parent focusable: false
phantom: true
image: /images/game/minimap/cross
size: 16 16
MinimapFloorUpButton < Button MinimapFloorUpButton < Button
size: 20 20 size: 20 20
@ -61,12 +65,7 @@ Minimap < UIMinimap
draggable: true draggable: true
focusable: false focusable: false
cross: true cross: true
@onGeometryChange: self:updateFlags() color: black
MinimapFlags
id: flags
phantom: true
focusable: false
MinimapFloorUpButton MinimapFloorUpButton
id: floorUp id: floorUp
@ -245,22 +244,3 @@ MinimapFlagWindow < MainWindow
width: 64 width: 64
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
// Minimap Full Panel
MinimapFullPanel < FlatPanel
phantom: false
anchors.fill: parent
anchors.top: topMenu.bottom
ImageView
id: image
anchors.fill: parent
Button
!text: tr('Close')
margin-right: 4
margin-top: 4
anchors.right: parent.right
anchors.top: parent.top
@onClick: self:getParent():destroy()

@ -55,7 +55,7 @@ end
function init() function init()
connect(g_app, { onRun = startup, connect(g_app, { onRun = startup,
onExit = exit }) onExit = exit })
g_window.setMinimumSize({ width = 600, height = 480 }) g_window.setMinimumSize({ width = 600, height = 480 })
g_sounds.preload(musicFilename) g_sounds.preload(musicFilename)

@ -12,7 +12,6 @@ Module
load-later: load-later:
- client_styles - client_styles
- client_locales - client_locales
//- client_particles
- client_topmenu - client_topmenu
- client_background - client_background
- client_entergame - client_entergame

@ -1,18 +1,19 @@
-- private variables -- private variables
local background local background
local clientVersionLabel
-- public functions -- public functions
function init() function init()
background = g_ui.displayUI('background') background = g_ui.displayUI('background')
background:lower() background:lower()
local clientVersionLabel = background:getChildById('clientVersionLabel') clientVersionLabel = background:getChildById('clientVersionLabel')
clientVersionLabel:setText(g_app.getName() .. ' ' .. g_app.getVersion() .. '\n' .. clientVersionLabel:setText(g_app.getName() .. ' ' .. g_app.getVersion() .. '\n' ..
'Rev ' .. g_app.getBuildRevision() .. ' ('.. g_app.getBuildCommit() .. ')\n' .. 'Rev ' .. g_app.getBuildRevision() .. ' ('.. g_app.getBuildCommit() .. ')\n' ..
'Built on ' .. g_app.getBuildDate()) 'Built on ' .. g_app.getBuildDate())
if not g_game.isOnline() then if not g_game.isOnline() then
g_effects.fadeIn(clientVersionLabel, 1500) addEvent(function() g_effects.fadeIn(clientVersionLabel, 1500) end)
end end
connect(g_game, { onGameStart = hide }) connect(g_game, { onGameStart = hide })
@ -40,3 +41,7 @@ end
function hideVersionLabel() function hideVersionLabel()
background:getChildById('clientVersionLabel'):hide() background:getChildById('clientVersionLabel'):hide()
end end
function setVersionText(text)
clientVersionLabel:setText(text)
end

@ -111,7 +111,7 @@ end
function onGameConnectionError(message, code) function onGameConnectionError(message, code)
CharacterList.destroyLoadBox() CharacterList.destroyLoadBox()
errorBox = displayErrorBox(tr("Login Error"), message) errorBox = displayErrorBox(tr("Connection Error"), message)
errorBox.onOk = function() errorBox.onOk = function()
errorBox = nil errorBox = nil
CharacterList.showAgain() CharacterList.showAgain()

@ -8,6 +8,7 @@ local motdButton
local enterGameButton local enterGameButton
local protocolBox local protocolBox
local protocolLogin local protocolLogin
local motdEnabled = true
-- private functions -- private functions
local function onError(protocol, message, errorCode) local function onError(protocol, message, errorCode)
@ -27,7 +28,9 @@ end
local function onMotd(protocol, motd) local function onMotd(protocol, motd)
G.motdNumber = tonumber(motd:sub(0, motd:find("\n"))) G.motdNumber = tonumber(motd:sub(0, motd:find("\n")))
G.motdMessage = motd:sub(motd:find("\n") + 1, #motd) G.motdMessage = motd:sub(motd:find("\n") + 1, #motd)
motdButton:show() if motdEnabled then
motdButton:show()
end
end end
local function onCharacterList(protocol, characters, account, otui) local function onCharacterList(protocol, characters, account, otui)
@ -45,12 +48,14 @@ local function onCharacterList(protocol, characters, account, otui)
CharacterList.create(characters, account, otui) CharacterList.create(characters, account, otui)
CharacterList.show() CharacterList.show()
local lastMotdNumber = g_settings.getNumber("motd") if motdEnabled then
if G.motdNumber and G.motdNumber ~= lastMotdNumber then local lastMotdNumber = g_settings.getNumber("motd")
g_settings.set("motd", motdNumber) if G.motdNumber and G.motdNumber ~= lastMotdNumber then
motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage) g_settings.set("motd", motdNumber)
connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end }) motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage)
CharacterList.hide() connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end })
CharacterList.hide()
end
end end
end end
@ -81,7 +86,7 @@ function EnterGame.init()
motdButton:hide() motdButton:hide()
g_keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow) g_keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow)
if G.motdNumber then if motdEnabled and G.motdNumber then
motdButton:show() motdButton:show()
end end
@ -127,13 +132,15 @@ function EnterGame.firstShow()
local host = g_settings.get('host') local host = g_settings.get('host')
local autologin = g_settings.getBoolean('autologin') local autologin = g_settings.getBoolean('autologin')
if #host > 0 and #password > 0 and #account > 0 and autologin then if #host > 0 and #password > 0 and #account > 0 and autologin then
autoLoginEvent = addEvent(EnterGame.doLogin) connect(g_app, { onRun = function()
if not g_settings.getBoolean('autologin') then return end
EnterGame.doLogin()
end})
end end
end end
function EnterGame.terminate() function EnterGame.terminate()
g_keyboard.unbindKeyDown('Ctrl+G') g_keyboard.unbindKeyDown('Ctrl+G')
removeEvent(autoLoginEvent)
enterGame:destroy() enterGame:destroy()
enterGame = nil enterGame = nil
enterGameButton:destroy() enterGameButton:destroy()
@ -186,7 +193,6 @@ function EnterGame.clearAccountFields()
end end
function EnterGame.doLogin() function EnterGame.doLogin()
autoLoginEvent = nil
G.account = enterGame:getChildById('accountNameTextEdit'):getText() G.account = enterGame:getChildById('accountNameTextEdit'):getText()
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
G.host = enterGame:getChildById('serverHostTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText()
@ -252,10 +258,6 @@ function EnterGame.setDefaultServer(host, port, protocol)
protocolBox:setCurrentOption(protocol) protocolBox:setCurrentOption(protocol)
accountTextEdit:setText('') accountTextEdit:setText('')
passwordTextEdit:setText('') passwordTextEdit:setText('')
if autoLoginEvent then
autoLoginEvent:cancel()
end
end end
end end
@ -297,3 +299,7 @@ function EnterGame.setServerInfo(message)
label:setText(message) label:setText(message)
end end
function EnterGame.disableMotd()
motdEnabled = false
motdButton:hide()
end

@ -25,6 +25,8 @@ end
function terminate() function terminate()
disconnect(g_game, { onGameStart = onGameStart, disconnect(g_game, { onGameStart = onGameStart,
onGameEnd = onGameEnd }) onGameEnd = onGameEnd })
removeEvent(firstReportEvent)
removeEvent(sendReportEvent)
end end
function configure(host, port, delay) function configure(host, port, delay)
@ -45,13 +47,15 @@ end
function onGameStart() function onGameStart()
if not HOST then return end if not HOST then return end
removeEvent(firstReportEvent)
removeEvent(sendReportEvent)
firstReportEvent = addEvent(sendReport, FIRST_REPORT_DELAY*1000) firstReportEvent = addEvent(sendReport, FIRST_REPORT_DELAY*1000)
sendReportEvent = cycleEvent(sendReport, REPORT_DELAY*1000) sendReportEvent = cycleEvent(sendReport, REPORT_DELAY*1000)
end end
function onGameEnd() function onGameEnd()
removeEvent(sendReportEvent)
removeEvent(firstReportEvent) removeEvent(firstReportEvent)
removeEvent(sendReportEvent)
end end
function onConnect(protocol) function onConnect(protocol)
@ -84,6 +88,7 @@ function onConnect(protocol)
post = post .. '&cpu=' .. g_platform.getCPUName() post = post .. '&cpu=' .. g_platform.getCPUName()
post = post .. '&mem=' .. g_platform.getTotalSystemMemory() post = post .. '&mem=' .. g_platform.getTotalSystemMemory()
post = post .. '&os_name=' .. g_platform.getOSName() post = post .. '&os_name=' .. g_platform.getOSName()
post = post .. getAdditionalData()
local message = '' local message = ''
message = message .. "POST /report HTTP/1.1\r\n" message = message .. "POST /report HTTP/1.1\r\n"
@ -98,6 +103,10 @@ function onConnect(protocol)
protocol:recv() protocol:recv()
end end
function getAdditionalData()
return ''
end
function onRecv(protocol, message) function onRecv(protocol, message)
if string.find(message, 'HTTP/1.1 200 OK') then if string.find(message, 'HTTP/1.1 200 OK') then
--pinfo('Stats sent to server successfully!') --pinfo('Stats sent to server successfully!')

@ -4,7 +4,6 @@ g_keyboard = {}
-- private functions -- private functions
function translateKeyCombo(keyCombo) function translateKeyCombo(keyCombo)
if not keyCombo or #keyCombo == 0 then return nil end if not keyCombo or #keyCombo == 0 then return nil end
table.sort(keyCombo)
local keyComboDesc = '' local keyComboDesc = ''
for k,v in pairs(keyCombo) do for k,v in pairs(keyCombo) do
local keyDesc = KeyCodeDescs[v] local keyDesc = KeyCodeDescs[v]
@ -65,47 +64,29 @@ function determineKeyComboDesc(keyCode, keyboardModifiers)
end end
table.insert(keyCombo, keyCode) table.insert(keyCombo, keyCode)
end end
table.sort(keyCombo)
return translateKeyCombo(keyCombo) return translateKeyCombo(keyCombo)
end end
local function onWidgetKeyDown(widget, keyCode, keyboardModifiers) local function onWidgetKeyDown(widget, keyCode, keyboardModifiers)
if keyCode == KeyUnknown then return false end if keyCode == KeyUnknown then return false end
local callback = widget.boundAloneKeyDownCombos[determineKeyComboDesc(keyCode, KeyboardNoModifier)] local callback = widget.boundAloneKeyDownCombos[determineKeyComboDesc(keyCode, KeyboardNoModifier)]
if callback then signalcall(callback, widget, keyCode)
callback(widget, keyCode)
end
callback = widget.boundKeyDownCombos[determineKeyComboDesc(keyCode, keyboardModifiers)] callback = widget.boundKeyDownCombos[determineKeyComboDesc(keyCode, keyboardModifiers)]
if callback then return signalcall(callback, widget, keyCode)
callback(widget, keyCode)
return true
end
return false
end end
local function onWidgetKeyUp(widget, keyCode, keyboardModifiers) local function onWidgetKeyUp(widget, keyCode, keyboardModifiers)
if keyCode == KeyUnknown then return false end if keyCode == KeyUnknown then return false end
local callback = widget.boundAloneKeyUpCombos[determineKeyComboDesc(keyCode, KeyboardNoModifier)] local callback = widget.boundAloneKeyUpCombos[determineKeyComboDesc(keyCode, KeyboardNoModifier)]
if callback then signalcall(callback, widget, keyCode)
callback(widget, keyCode)
end
callback = widget.boundKeyUpCombos[determineKeyComboDesc(keyCode, keyboardModifiers)] callback = widget.boundKeyUpCombos[determineKeyComboDesc(keyCode, keyboardModifiers)]
if callback then return signalcall(callback, widget, keyCode)
callback(widget, keyCode)
return true
end
return false
end end
local function onWidgetKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks) local function onWidgetKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks)
if keyCode == KeyUnknown then return false end if keyCode == KeyUnknown then return false end
local keyComboDesc = determineKeyComboDesc(keyCode, keyboardModifiers) local callback = widget.boundKeyPressCombos[determineKeyComboDesc(keyCode, keyboardModifiers)]
local comboConf = widget.boundKeyPressCombos[keyComboDesc] return signalcall(callback, widget, keyCode, autoRepeatTicks)
if comboConf and (autoRepeatTicks >= comboConf.autoRepeatDelay or autoRepeatTicks == 0) and comboConf.callback then
comboConf.callback(widget, keyCode)
return true
end
return false
end end
local function connectKeyDownEvent(widget) local function connectKeyDownEvent(widget)
@ -133,13 +114,10 @@ function g_keyboard.bindKeyDown(keyComboDesc, callback, widget, alone)
widget = widget or rootWidget widget = widget or rootWidget
connectKeyDownEvent(widget) connectKeyDownEvent(widget)
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if widget.boundKeyDownCombos[keyComboDesc] then
pwarning('KeyDown event \'' .. keyComboDesc .. '\' redefined on widget ' .. widget:getId())
end
if alone then if alone then
widget.boundAloneKeyDownCombos[keyComboDesc] = callback connect(widget.boundAloneKeyDownCombos, keyComboDesc, callback)
else else
widget.boundKeyDownCombos[keyComboDesc] = callback connect(widget.boundKeyDownCombos, keyComboDesc, callback)
end end
end end
@ -147,53 +125,50 @@ function g_keyboard.bindKeyUp(keyComboDesc, callback, widget, alone)
widget = widget or rootWidget widget = widget or rootWidget
connectKeyUpEvent(widget) connectKeyUpEvent(widget)
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if widget.boundKeyUpCombos[keyComboDesc] then
pwarning('KeyUp event \'' .. keyComboDesc .. '\' redefined on widget ' .. widget:getId())
end
if alone then if alone then
widget.boundAloneKeyUpCombos[keyComboDesc] = callback connect(widget.boundAloneKeyUpCombos, keyComboDesc, callback)
else else
widget.boundKeyUpCombos[keyComboDesc] = callback connect(widget.boundKeyUpCombos, keyComboDesc, callback)
end end
end end
function g_keyboard.bindKeyPress(keyComboDesc, callback, widget, autoRepeatDelay) function g_keyboard.bindKeyPress(keyComboDesc, callback, widget)
autoRepeatDelay = autoRepeatDelay or 500
widget = widget or rootWidget widget = widget or rootWidget
connectKeyPressEvent(widget) connectKeyPressEvent(widget)
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if widget.boundKeyPressCombos[keyComboDesc] then connect(widget.boundKeyPressCombos, keyComboDesc, callback)
pwarning('KeyPress event \'' .. keyComboDesc .. '\' redefined on widget ' .. widget:getId())
end
widget.boundKeyPressCombos[keyComboDesc] = { callback = callback, autoRepeatDelay = autoRepeatDelay }
widget:setAutoRepeatDelay(math.min(autoRepeatDelay, widget:getAutoRepeatDelay()))
end end
function g_keyboard.unbindKeyDown(keyComboDesc, widget) local function getUnbindArgs(arg1, arg2)
local callback
local widget
if type(arg1) == 'function' then callback = arg1
elseif type(arg2) == 'function' then callback = arg2 end
if type(arg1) == 'userdata' then widget = arg1
elseif type(arg2) == 'userdata' then widget = arg2 end
widget = widget or rootWidget widget = widget or rootWidget
return callback, widget
end
function g_keyboard.unbindKeyDown(keyComboDesc, arg1, arg2)
local callback, widget = getUnbindArgs(arg1, arg2)
if widget.boundKeyDownCombos == nil then return end if widget.boundKeyDownCombos == nil then return end
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if keyComboDesc then disconnect(widget.boundKeyDownCombos, keyComboDesc, callback)
widget.boundKeyDownCombos[keyComboDesc] = nil
end
end end
function g_keyboard.unbindKeyUp(keyComboDesc, widget) function g_keyboard.unbindKeyUp(keyComboDesc, widget)
widget = widget or rootWidget local callback, widget = getUnbindArgs(arg1, arg2)
if widget.boundKeyUpCombos == nil then return end if widget.boundKeyUpCombos == nil then return end
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if keyComboDesc then disconnect(widget.boundKeyUpCombos, keyComboDesc, callback)
widget.boundKeyUpCombos[keyComboDesc] = nil
end
end end
function g_keyboard.unbindKeyPress(keyComboDesc, widget) function g_keyboard.unbindKeyPress(keyComboDesc, widget, callback)
widget = widget or rootWidget local callback, widget = getUnbindArgs(arg1, arg2)
if widget.boundKeyPressCombos == nil then return end if widget.boundKeyPressCombos == nil then return end
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
if keyComboDesc then disconnect(widget.boundKeyPressCombos, keyComboDesc, callback)
widget.boundKeyPressCombos[keyComboDesc] = nil
end
end end
function g_keyboard.getModifiers() function g_keyboard.getModifiers()

@ -13,6 +13,12 @@ function table.dump(t, depth)
end end
end end
function table.clear(t)
for k,v in pairs(t) do
t[k] = nil
end
end
function table.copy(t) function table.copy(t)
local res = {} local res = {}
for k,v in pairs(t) do for k,v in pairs(t) do

@ -224,7 +224,9 @@ end
function UIScrollBar:setText(text) function UIScrollBar:setText(text)
local valueLabel = self:getChildById('valueLabel') local valueLabel = self:getChildById('valueLabel')
valueLabel:setText(text) if valueLabel then
valueLabel:setText(text)
end
end end
function UIScrollBar:onGeometryChange() function UIScrollBar:onGeometryChange()

@ -67,6 +67,11 @@ function connect(object, arg1, arg2, arg3)
elseif type(object[signal]) == 'function' then elseif type(object[signal]) == 'function' then
object[signal] = { object[signal] } object[signal] = { object[signal] }
end end
if type(slot) ~= 'function' then
perror(debug.traceback('unable to connect a non function value'))
end
if type(object[signal]) == 'table' then if type(object[signal]) == 'table' then
if pushFront then if pushFront then
table.insert(object[signal], 1, slot) table.insert(object[signal], 1, slot)
@ -80,9 +85,15 @@ end
function disconnect(object, arg1, arg2) function disconnect(object, arg1, arg2)
local signalsAndSlots local signalsAndSlots
if type(arg1) == 'string' then if type(arg1) == 'string' then
if arg2 == nil then
object[arg1] = nil
return
end
signalsAndSlots = { [arg1] = arg2 } signalsAndSlots = { [arg1] = arg2 }
else elseif type(arg1) == 'table' then
signalsAndSlots = arg1 signalsAndSlots = arg1
else
perror(debug.traceback('unable to disconnect'))
end end
for signal,slot in pairs(signalsAndSlots) do for signal,slot in pairs(signalsAndSlots) do

@ -34,6 +34,7 @@ perCharacter = true
mouseGrabberWidget = nil mouseGrabberWidget = nil
useRadioGroup = nil useRadioGroup = nil
currentHotkeys = nil currentHotkeys = nil
boundCombosCallback = {}
hotkeysList = {} hotkeysList = {}
-- public functions -- public functions
@ -149,6 +150,7 @@ function load(forceDefaults)
if not forceDefaults then if not forceDefaults then
if not table.empty(hotkeys) then if not table.empty(hotkeys) then
for keyCombo, setting in pairs(hotkeys) do for keyCombo, setting in pairs(hotkeys) do
keyCombo = tostring(keyCombo)
addKeyCombo(keyCombo, setting) addKeyCombo(keyCombo, setting)
hotkeyList[keyCombo] = setting hotkeyList[keyCombo] = setting
end end
@ -163,12 +165,13 @@ function load(forceDefaults)
end end
function unload() function unload()
for _,child in pairs(currentHotkeys:getChildren()) do for keyCombo,callback in pairs(boundCombosCallback) do
g_keyboard.unbindKeyPress(child.keyCombo) g_keyboard.unbindKeyPress(keyCombo, callback)
end end
boundCombosCallback = {}
currentHotkeys:destroyChildren() currentHotkeys:destroyChildren()
currentHotkeyLabel = nil currentHotkeyLabel = nil
updateHotkeyForm() updateHotkeyForm(true)
hotkeyList = {} hotkeyList = {}
end end
@ -196,6 +199,8 @@ function save()
hotkeys = hotkeys[g_game.getCharacterName()] hotkeys = hotkeys[g_game.getCharacterName()]
end end
table.clear(hotkeys)
for _,child in pairs(currentHotkeys:getChildren()) do for _,child in pairs(currentHotkeys:getChildren()) do
hotkeys[child.keyCombo] = { hotkeys[child.keyCombo] = {
autoSend = child.autoSend, autoSend = child.autoSend,
@ -207,7 +212,7 @@ function save()
hotkeyList = hotkeys hotkeyList = hotkeys
g_settings.setNode('game_hotkeys', hotkeySettings) g_settings.setNode('game_hotkeys', hotkeySettings)
--g_settings.save() g_settings.save()
end end
function loadDefautComboKeys() function loadDefautComboKeys()
@ -258,7 +263,7 @@ function onChooseItemMouseRelease(self, mousePosition, mouseButton)
currentHotkeyLabel.value = nil currentHotkeyLabel.value = nil
currentHotkeyLabel.autoSend = false currentHotkeyLabel.autoSend = false
updateHotkeyLabel(currentHotkeyLabel) updateHotkeyLabel(currentHotkeyLabel)
updateHotkeyForm() updateHotkeyForm(true)
end end
show() show()
@ -281,7 +286,7 @@ function clearObject()
currentHotkeyLabel.autoSend = nil currentHotkeyLabel.autoSend = nil
currentHotkeyLabel.value = nil currentHotkeyLabel.value = nil
updateHotkeyLabel(currentHotkeyLabel) updateHotkeyLabel(currentHotkeyLabel)
updateHotkeyForm() updateHotkeyForm(true)
end end
function addHotkey() function addHotkey()
@ -294,6 +299,7 @@ function addHotkey()
end end
function addKeyCombo(keyCombo, keySettings, focus) function addKeyCombo(keyCombo, keySettings, focus)
if keyCombo == nil or #keyCombo == 0 then return end
if not keyCombo then return end if not keyCombo then return end
local hotkeyLabel = currentHotkeys:getChildById(keyCombo) local hotkeyLabel = currentHotkeys:getChildById(keyCombo)
if not hotkeyLabel then if not hotkeyLabel then
@ -321,27 +327,28 @@ function addKeyCombo(keyCombo, keySettings, focus)
if keySettings then if keySettings then
currentHotkeyLabel = hotkeyLabel currentHotkeyLabel = hotkeyLabel
hotkeyLabel.keyCombo = keyCombo hotkeyLabel.keyCombo = keyCombo
hotkeyLabel.autoSend = keySettings.autoSend hotkeyLabel.autoSend = toboolean(keySettings.autoSend)
hotkeyLabel.itemId = keySettings.itemId hotkeyLabel.itemId = tonumber(keySettings.itemId)
hotkeyLabel.useType = tonumber(keySettings.useType) hotkeyLabel.useType = tonumber(keySettings.useType)
hotkeyLabel.value = keySettings.value if keySettings.value then hotkeyLabel.value = tostring(keySettings.value) end
else else
hotkeyLabel.keyCombo = keyCombo hotkeyLabel.keyCombo = keyCombo
hotkeyLabel.autoSend = nil hotkeyLabel.autoSend = false
hotkeyLabel.itemId = nil hotkeyLabel.itemId = nil
hotkeyLabel.useType = nil hotkeyLabel.useType = nil
hotkeyLabel.value = nil hotkeyLabel.value = ''
end end
updateHotkeyLabel(hotkeyLabel) updateHotkeyLabel(hotkeyLabel)
g_keyboard.bindKeyPress(keyCombo, function() doKeyCombo(keyCombo) end, nil, 350) boundCombosCallback[keyCombo] = function() doKeyCombo(keyCombo) end
g_keyboard.bindKeyPress(keyCombo, boundCombosCallback[keyCombo])
end end
if focus then if focus then
currentHotkeys:focusChild(hotkeyLabel) currentHotkeys:focusChild(hotkeyLabel)
currentHotkeys:ensureChildVisible(hotkeyLabel) currentHotkeys:ensureChildVisible(hotkeyLabel)
updateHotkeyForm() updateHotkeyForm(true)
end end
end end
@ -398,7 +405,7 @@ function updateHotkeyLabel(hotkeyLabel)
end end
end end
function updateHotkeyForm() function updateHotkeyForm(reset)
if currentHotkeyLabel then if currentHotkeyLabel then
removeHotkeyButton:enable() removeHotkeyButton:enable()
if currentHotkeyLabel.itemId ~= nil then if currentHotkeyLabel.itemId ~= nil then
@ -435,8 +442,10 @@ function updateHotkeyForm()
hotkeyText:enable() hotkeyText:enable()
hotkeyText:focus() hotkeyText:focus()
hotKeyTextLabel:enable() hotKeyTextLabel:enable()
if reset then
hotkeyText:setCursorPos(-1)
end
hotkeyText:setText(currentHotkeyLabel.value) hotkeyText:setText(currentHotkeyLabel.value)
hotkeyText:setCursorPos(-1)
sendAutomatically:setChecked(currentHotkeyLabel.autoSend) sendAutomatically:setChecked(currentHotkeyLabel.autoSend)
sendAutomatically:setEnabled(currentHotkeyLabel.value and #currentHotkeyLabel.value > 0) sendAutomatically:setEnabled(currentHotkeyLabel.value and #currentHotkeyLabel.value > 0)
selectObjectButton:enable() selectObjectButton:enable()
@ -461,7 +470,8 @@ end
function removeHotkey() function removeHotkey()
if currentHotkeyLabel == nil then return end if currentHotkeyLabel == nil then return end
g_keyboard.unbindKeyPress(currentHotkeyLabel.keyCombo) g_keyboard.unbindKeyPress(currentHotkeyLabel.keyCombo, boundCombosCallback[currentHotkeyLabel.keyCombo])
boundCombosCallback[currentHotkeyLabel.keyCombo] = nil
currentHotkeyLabel:destroy() currentHotkeyLabel:destroy()
currentHotkeyLabel = nil currentHotkeyLabel = nil
end end
@ -504,7 +514,7 @@ end
function onSelectHotkeyLabel(hotkeyLabel) function onSelectHotkeyLabel(hotkeyLabel)
currentHotkeyLabel = hotkeyLabel currentHotkeyLabel = hotkeyLabel
updateHotkeyForm() updateHotkeyForm(true)
end end
function hotkeyCapture(assignWindow, keyCode, keyboardModifiers) function hotkeyCapture(assignWindow, keyCode, keyboardModifiers)

@ -1,4 +1,3 @@
WALK_AUTO_REPEAT_DELAY = 90
WALK_STEPS_RETRY = 10 WALK_STEPS_RETRY = 10
gameRootPanel = nil gameRootPanel = nil
@ -25,7 +24,7 @@ function init()
onGameStart = onGameStart, onGameStart = onGameStart,
onGMActions = onGMActions, onGMActions = onGMActions,
onGameEnd = onGameEnd, onGameEnd = onGameEnd,
onLoginAdvice = onLoginAdvice onLoginAdvice = onLoginAdvice,
}, true) }, true)
gameRootPanel = g_ui.displayUI('gameinterface') gameRootPanel = g_ui.displayUI('gameinterface')
@ -57,6 +56,7 @@ function init()
end end
function bindKeys() function bindKeys()
gameRootPanel:setAutoRepeatDelay(250)
g_keyboard.bindKeyDown('Up', function() changeWalkDir(North) end, gameRootPanel, true) g_keyboard.bindKeyDown('Up', function() changeWalkDir(North) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Right', function() changeWalkDir(East) 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('Down', function() changeWalkDir(South) end, gameRootPanel, true)
@ -81,30 +81,30 @@ function bindKeys()
g_keyboard.bindKeyUp('Numpad1', function() changeWalkDir(SouthWest, 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('Numpad4', function() changeWalkDir(West, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad7', function() changeWalkDir(NorthWest, true) end, gameRootPanel, true) g_keyboard.bindKeyUp('Numpad7', function() changeWalkDir(NorthWest, true) end, gameRootPanel, true)
g_keyboard.bindKeyPress('Up', function() smartWalk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Up', function() smartWalk(North) end, gameRootPanel)
g_keyboard.bindKeyPress('Right', function() smartWalk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Right', function() smartWalk(East) end, gameRootPanel)
g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel)
g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad3', function() smartWalk(SouthEast) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad3', function() smartWalk(SouthEast) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad2', function() smartWalk(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad2', function() smartWalk(South) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad1', function() smartWalk(SouthWest) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad1', function() smartWalk(SouthWest) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad4', function() smartWalk(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad4', function() smartWalk(West) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad7', function() smartWalk(NorthWest) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Numpad7', function() smartWalk(NorthWest) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Up', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) 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, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Right', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Down', function() g_game.turn(South) changeWalkDir(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Down', function() g_game.turn(South) changeWalkDir(South) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Left', function() g_game.turn(West) changeWalkDir(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Left', function() g_game.turn(West) changeWalkDir(West) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Numpad8', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Numpad8', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Numpad6', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Numpad6', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Numpad2', function() g_game.turn(South) changeWalkDir(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Numpad2', function() g_game.turn(South) changeWalkDir(South) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Numpad4', function() g_game.turn(West) changeWalkDir(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Ctrl+Numpad4', function() g_game.turn(West) changeWalkDir(West) end, gameRootPanel)
g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel, 250) g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel, 250) g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel)
@ -591,7 +591,6 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
player:stopAutoWalk() player:stopAutoWalk()
if autoWalkPos and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseLeftButton then if autoWalkPos and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseLeftButton then
player.onAutoWalkFail = function() modules.game_textmessage.displayFailureMessage(tr('There is no way.')) end
player:autoWalk(autoWalkPos) player:autoWalk(autoWalkPos)
return true return true
end end

@ -3,6 +3,9 @@ minimapButton = nil
minimapWindow = nil minimapWindow = nil
otmm = true otmm = true
preloaded = false preloaded = false
fullmapView = false
oldZoom = nil
oldPos = nil
function init() function init()
minimapButton = modules.client_topmenu.addRightGameToggleButton('minimapButton', tr('Minimap') .. ' (Ctrl+M)', '/images/topbuttons/minimap', toggle) minimapButton = modules.client_topmenu.addRightGameToggleButton('minimapButton', tr('Minimap') .. ' (Ctrl+M)', '/images/topbuttons/minimap', toggle)
@ -19,6 +22,7 @@ function init()
g_keyboard.bindKeyPress('Alt+Up', function() minimapWidget:move(0,1) end, gameRootPanel) g_keyboard.bindKeyPress('Alt+Up', function() minimapWidget:move(0,1) end, gameRootPanel)
g_keyboard.bindKeyPress('Alt+Down', function() minimapWidget:move(0,-1) end, gameRootPanel) g_keyboard.bindKeyPress('Alt+Down', function() minimapWidget:move(0,-1) end, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+M', toggle) g_keyboard.bindKeyDown('Ctrl+M', toggle)
g_keyboard.bindKeyDown('Ctrl+Shift+M', toggleFullMap)
minimapWindow:setup() minimapWindow:setup()
@ -27,6 +31,10 @@ function init()
onGameEnd = offline, onGameEnd = offline,
}) })
connect(LocalPlayer, {
onPositionChange = updateCameraPosition
})
if g_game.isOnline() then if g_game.isOnline() then
online() online()
end end
@ -48,6 +56,7 @@ function terminate()
g_keyboard.unbindKeyPress('Alt+Up', gameRootPanel) g_keyboard.unbindKeyPress('Alt+Up', gameRootPanel)
g_keyboard.unbindKeyPress('Alt+Down', gameRootPanel) g_keyboard.unbindKeyPress('Alt+Down', gameRootPanel)
g_keyboard.unbindKeyDown('Ctrl+M') g_keyboard.unbindKeyDown('Ctrl+M')
g_keyboard.unbindKeyDown('Ctrl+Shift+M')
minimapWindow:destroy() minimapWindow:destroy()
minimapButton:destroy() minimapButton:destroy()
@ -74,7 +83,6 @@ end
function online() function online()
loadMap(not preloaded) loadMap(not preloaded)
minimapWidget:followLocalPlayer()
end end
function offline() function offline()
@ -114,6 +122,36 @@ function saveMap()
minimapWidget:save() minimapWidget:save()
end end
function getMinimap() function updateCameraPosition()
return minimapWidget local player = g_game.getLocalPlayer()
if not minimapWidget:isDragging() then
if not fullmapView then
minimapWidget:setCameraPosition(player:getPosition())
end
minimapWidget:setCrossPosition(player:getPosition())
end
end
function toggleFullMap()
if not fullmapView then
fullmapView = true
minimapWindow:hide()
minimapWidget:setParent(modules.game_interface.getRootPanel())
minimapWidget:fill('parent')
minimapWidget:setAlternativeWidgetsVisible(true)
else
fullmapView = false
minimapWidget:setParent(minimapWindow:getChildById('contentsPanel'))
minimapWidget:fill('parent')
minimapWindow:show()
minimapWidget:setAlternativeWidgetsVisible(false)
end
local zoom = oldZoom or 0
local pos = oldPos or minimapWidget:getCameraPosition()
pos.z = 7
oldZoom = minimapWidget:getZoom()
oldPos = minimapWidget:getCameraPosition()
minimapWidget:setZoom(zoom)
minimapWidget:setCameraPosition(pos)
end end

@ -151,7 +151,8 @@ function onTradeTypeChange(radioTabs, selected, deselected)
ignoreCapacity:setVisible(currentTradeType == BUY) ignoreCapacity:setVisible(currentTradeType == BUY)
ignoreEquipped:setVisible(currentTradeType == SELL) ignoreEquipped:setVisible(currentTradeType == SELL)
showAllItems:setVisible(currentTradeType == SELL) showAllItems:setVisible(currentTradeType == SELL)
sellAllButton:setVisible(currentTradeType == SELL) sellAllButton:setVisible(false)
--sellAllButton:setVisible(currentTradeType == SELL)
refreshTradeItems() refreshTradeItems()
refreshPlayerGoods() refreshPlayerGoods()

@ -252,6 +252,7 @@ MainWindow
margin-right: 10 margin-right: 10
visible: false visible: false
@onClick: modules.game_npctrade.sellAll() @onClick: modules.game_npctrade.sellAll()
visible: false
Button Button
id: tradeButton id: tradeButton

@ -123,3 +123,7 @@ function clearMessages()
end end
end end
end end
function LocalPlayer:onAutoWalkFail(player)
modules.game_textmessage.displayFailureMessage(tr('There is no way.'))
end

@ -3,7 +3,8 @@ vipButton = nil
addVipWindow = nil addVipWindow = nil
function init() function init()
connect(g_game, { onGameEnd = clear, connect(g_game, { onGameStart = refresh,
onGameEnd = clear,
onAddVip = onAddVip, onAddVip = onAddVip,
onVipStateChange = onVipStateChange }) onVipStateChange = onVipStateChange })
@ -20,7 +21,8 @@ end
function terminate() function terminate()
g_keyboard.unbindKeyDown('Ctrl+P') g_keyboard.unbindKeyDown('Ctrl+P')
disconnect(g_game, { onGameEnd = clear, disconnect(g_game, { onGameStart = refresh,
onGameEnd = clear,
onAddVip = onAddVip, onAddVip = onAddVip,
onVipStateChange = onVipStateChange }) onVipStateChange = onVipStateChange })

@ -161,4 +161,4 @@ function Player:hasState(_state, states)
end end
end end
return false return false
end end

@ -1,45 +1,39 @@
function UIMinimap:onSetup() function UIMinimap:onSetup()
self.flagWindow = nil self.flagWindow = nil
self.flagsWidget = self:getChildById('flags')
self.floorUpWidget = self:getChildById('floorUp') self.floorUpWidget = self:getChildById('floorUp')
self.floorDownWidget = self:getChildById('floorDown') self.floorDownWidget = self:getChildById('floorDown')
self.zoomInWidget = self:getChildById('zoomIn') self.zoomInWidget = self:getChildById('zoomIn')
self.zoomOutWidget = self:getChildById('zoomOut') self.zoomOutWidget = self:getChildById('zoomOut')
self.dx = 0 self.flags = {}
self.dy = 0 self.alternatives = {}
self.autowalk = true self.autowalk = true
self.allowFollowLocalPlayer = true
self.onPositionChange = function() self:followLocalPlayer() end
self.onAddAutomapFlag = function(pos, icon, description) self:addFlag(pos, icon, description) end self.onAddAutomapFlag = function(pos, icon, description) self:addFlag(pos, icon, description) end
self.onRemoveAutomapFlag = function(pos, icon, description) self:removeFlag(pos, icon, description) end self.onRemoveAutomapFlag = function(pos, icon, description) self:removeFlag(pos, icon, description) end
connect(g_game, { connect(g_game, {
onAddAutomapFlag = self.onAddAutomapFlag, onAddAutomapFlag = self.onAddAutomapFlag,
onRemoveAutomapFlag = self.onRemoveAutomapFlag, onRemoveAutomapFlag = self.onRemoveAutomapFlag,
}) })
connect(LocalPlayer, { onPositionChange = self.onPositionChange })
end end
function UIMinimap:onDestroy() function UIMinimap:onDestroy()
disconnect(LocalPlayer, { onPositionChange = self.onPositionChange }) for _,widget in pairs(self.alternatives) do
widget:destroy()
end
self.alternatives = {}
disconnect(g_game, { disconnect(g_game, {
onAddAutomapFlag = self.onAddAutomapFlag, onAddAutomapFlag = self.onAddAutomapFlag,
onRemoveAutomapFlag = self.onRemoveAutomapFlag, onRemoveAutomapFlag = self.onRemoveAutomapFlag,
}) })
self:destroyFlagWindow() self:destroyFlagWindow()
self:destroyFullPanel() self.flags = {}
end end
function UIMinimap:onVisibilityChange() function UIMinimap:onVisibilityChange()
if not self:isVisible() then if not self:isVisible() then
self:destroyFlagWindow() self:destroyFlagWindow()
self:destroyFullPanel()
end end
end end
function UIMinimap:hideFlags()
self.flagsWidget:hide()
end
function UIMinimap:hideFloor() function UIMinimap:hideFloor()
self.floorUpWidget:hide() self.floorUpWidget:hide()
self.floorDownWidget:hide() self.floorDownWidget:hide()
@ -54,33 +48,21 @@ function UIMinimap:disableAutoWalk()
self.autowalk = false self.autowalk = false
end end
function UIMinimap:disableFollowPlayer()
self.allowFollowLocalPlayer = false
end
function UIMinimap:enableFullPanel(image)
self.fullImage = image
end
function UIMinimap:load() function UIMinimap:load()
local settings = g_settings.getNode('Minimap') local settings = g_settings.getNode('Minimap')
if settings then if settings then
if settings.flags then if settings.flags then
for i=1,#settings.flags do for _,flag in pairs(settings.flags) do
local flag = settings.flags[i]
self:addFlag(flag.position, flag.icon, flag.description) self:addFlag(flag.position, flag.icon, flag.description)
end end
end end
self:setZoom(settings.zoom) self:setZoom(settings.zoom)
end end
self:updateFlags()
end end
function UIMinimap:save() function UIMinimap:save()
local settings = { flags={} } local settings = { flags={} }
local children = self.flagsWidget:getChildren() for _,flag in pairs(self.flags) do
for i=1,#children do
local flag = children[i]
table.insert(settings.flags, { table.insert(settings.flags, {
position = flag.pos, position = flag.pos,
icon = flag.icon, icon = flag.icon,
@ -91,134 +73,114 @@ function UIMinimap:save()
g_settings.setNode('Minimap', settings) g_settings.setNode('Minimap', settings)
end end
local function onFlagMouseRelease(widget, pos, button)
if button == MouseRightButton then
local menu = g_ui.createWidget('PopupMenu')
menu:addOption(tr('Delete mark'), function() widget:destroy() end)
menu:display(pos)
return true
end
return false
end
function UIMinimap:setCrossPosition(pos)
local cross = self.cross
if not self.cross then
cross = g_ui.createWidget('MinimapCross', self)
cross:setIcon('/images/game/minimap/cross')
self.cross = cross
end
cross.pos = pos
if pos then
self:centerInPosition(cross, pos)
else
cross:breakAnchors()
end
end
function UIMinimap:addFlag(pos, icon, description) function UIMinimap:addFlag(pos, icon, description)
if not pos or not icon then return end
local flag = self:getFlag(pos, icon, description) local flag = self:getFlag(pos, icon, description)
if flag then if flag or not icon then
return return
end end
flag = g_ui.createWidget('MinimapFlag', self.flagsWidget) flag = g_ui.createWidget('MinimapFlag', self)
flag.pos = pos flag.pos = pos
flag.icon = icon
flag.description = description flag.description = description
flag.icon = icon
flag:setIcon('/images/game/minimap/flag' .. icon) flag:setIcon('/images/game/minimap/flag' .. icon)
flag:setTooltip(description) flag:setTooltip(description)
flag.onMouseRelease = function(widget, pos, button) flag.onMouseRelease = onFlagMouseRelease
if button == MouseRightButton then table.insert(self.flags, flag)
local menu = g_ui.createWidget('PopupMenu') self:centerInPosition(flag, pos)
menu:addOption(tr('Delete mark'), function() widget:destroy() end) end
menu:display(pos)
return true function UIMinimap:addAlternativeWidget(widget, pos, maxZoom)
widget.pos = pos
widget.maxZoom = maxZoom or 0
widget.minZoom = minZoom
table.insert(self.alternatives, widget)
end
function UIMinimap:setAlternativeWidgetsVisible(show)
local layout = self:getLayout()
layout:disableUpdates()
for _,widget in pairs(self.alternatives) do
if show then
self:addChild(widget)
self:centerInPosition(widget, widget.pos)
else
self:removeChild(widget)
end end
return false
end end
layout:enableUpdates()
layout:update()
end
self:updateFlag(flag) function UIMinimap:onZoomChange(zoom)
for _,widget in pairs(self.alternatives) do
if (not widget.minZoom or widget.minZoom >= zoom) and widget.maxZoom <= zoom then
widget:show()
else
widget:hide()
end
end
end end
function UIMinimap:getFlag(pos, icon, description) function UIMinimap:getFlag(pos)
local children = self.flagsWidget:getChildren() for _,flag in pairs(self.flags) do
for i=1,#children do
local flag = children[i]
if flag.pos.x == pos.x and flag.pos.y == pos.y and flag.pos.z == pos.z then if flag.pos.x == pos.x and flag.pos.y == pos.y and flag.pos.z == pos.z then
return flag return flag
end end
end end
return nil
end end
function UIMinimap:removeFlag(pos, icon, description) function UIMinimap:removeFlag(pos, icon, description)
local flag = self:getFlag(pos, icon, description) local flag = self:getFlag(pos)
if flag then if flag then
flag:destroy() flag:destroy()
end table.removevalue(self.flags, flag)
end
function UIMinimap:updateFlag(flag)
local point = self:getPoint(flag.pos)
if self:containsPoint(point) and self:getZoom() >= 0 and flag.pos.z == self:getCameraPosition().z then
flag:setVisible(true)
flag:setMarginLeft(point.x - self:getX() - flag:getWidth()/2)
flag:setMarginTop(point.y - self:getY() - flag:getHeight()/2)
else
flag:setVisible(false)
end
end
function UIMinimap:updateFlags()
local children = self.flagsWidget:getChildren()
for i=1,#children do
self:updateFlag(children[i])
end
end
UIMinimap.realSetCameraPosition = UIMinimap.realSetCameraPosition or UIMinimap.setCameraPosition
function UIMinimap:setCameraPosition(pos)
self:realSetCameraPosition(pos)
self:updateFlags()
end
UIMinimap.realZoomIn = UIMinimap.realZoomIn or UIMinimap.zoomIn
function UIMinimap:zoomIn()
self:realZoomIn()
self:updateFlags()
end
UIMinimap.realZoomOut = UIMinimap.realZoomOut or UIMinimap.zoomOut
function UIMinimap:zoomOut()
self:realZoomOut()
self:updateFlags()
end
UIMinimap.realSetZoom = UIMinimap.realSetZoom or UIMinimap.setZoom
function UIMinimap:setZoom(zoom)
self:realSetZoom(zoom)
self:updateFlags()
end
function UIMinimap:floorUp(floors)
local pos = self:getCameraPosition()
pos.z = pos.z - floors
if pos.z >= FloorHigher then
self:setCameraPosition(pos)
end
self:updateFlags()
end
function UIMinimap:floorDown(floors)
local pos = self:getCameraPosition()
pos.z = pos.z + floors
if pos.z <= FloorLower then
self:setCameraPosition(pos)
end
self:updateFlags()
end
function UIMinimap:followLocalPlayer()
if not self:isDragging() and self.allowFollowLocalPlayer then
local player = g_game.getLocalPlayer()
self:followCreature(player)
self:updateFlags()
end end
end end
function UIMinimap:reset() function UIMinimap:reset()
self:followLocalPlayer()
self:setZoom(0) self:setZoom(0)
if self.cross then
self:setCameraPosition(self.cross.pos)
end
end end
function UIMinimap:move(x, y) function UIMinimap:move(x, y)
local topLeft, bottomRight = self:getArea()
self.dx = self.dx + ((bottomRight.x - topLeft.x) / self:getWidth() ) * x
self.dy = self.dy + ((bottomRight.y - topLeft.y) / self:getHeight()) * y
local dx = math.floor(self.dx)
local dy = math.floor(self.dy)
self.dx = self.dx - dx
self.dy = self.dy - dy
local cameraPos = self:getCameraPosition() local cameraPos = self:getCameraPosition()
local scale = self:getScale()
if scale > 1 then scale = 1 end
local dx = x/scale
local dy = y/scale
local pos = {x = cameraPos.x - dx, y = cameraPos.y - dy, z = cameraPos.z} local pos = {x = cameraPos.x - dx, y = cameraPos.y - dy, z = cameraPos.z}
self:setCameraPosition(pos) self:setCameraPosition(pos)
self:updateFlags()
end end
function UIMinimap:onMouseWheel(mousePos, direction) function UIMinimap:onMouseWheel(mousePos, direction)
@ -232,7 +194,6 @@ function UIMinimap:onMouseWheel(mousePos, direction)
elseif direction == MouseWheelUp and keyboardModifiers == KeyboardCtrlModifier then elseif direction == MouseWheelUp and keyboardModifiers == KeyboardCtrlModifier then
self:floorDown(1) self:floorDown(1)
end end
self:updateFlags()
end end
function UIMinimap:onMousePress(pos, button) function UIMinimap:onMousePress(pos, button)
@ -245,20 +206,18 @@ function UIMinimap:onMouseRelease(pos, button)
if not self.allowNextRelease then return true end if not self.allowNextRelease then return true end
self.allowNextRelease = false self.allowNextRelease = false
local mapPos = self:getPosition(pos) local mapPos = self:getTilePosition(pos)
if not mapPos then return end if not mapPos then return end
if button == MouseLeftButton then if button == MouseLeftButton then
local player = g_game.getLocalPlayer() local player = g_game.getLocalPlayer()
if self.autowalk then if self.autowalk then
player.onAutoWalkFail = function() modules.game_textmessage.displayFailureMessage(tr('There is no way.')) end
player:autoWalk(mapPos) player:autoWalk(mapPos)
end end
return true return true
elseif button == MouseRightButton then elseif button == MouseRightButton then
local menu = g_ui.createWidget('PopupMenu') local menu = g_ui.createWidget('PopupMenu')
menu:addOption(tr('Create mark'), function() self:createFlagWindow(mapPos) end) menu:addOption(tr('Create mark'), function() self:createFlagWindow(mapPos) end)
if self.fullImage then menu:addOption(tr('Full map'), function() self:createFullPanel() end) end
menu:display(pos) menu:display(pos)
return true return true
end end
@ -266,11 +225,17 @@ function UIMinimap:onMouseRelease(pos, button)
end end
function UIMinimap:onDragEnter(pos) function UIMinimap:onDragEnter(pos)
self.dragReference = pos
self.dragCameraReference = self:getCameraPosition()
return true return true
end end
function UIMinimap:onDragMove(pos, moved) function UIMinimap:onDragMove(pos, moved)
self:move(moved.x, moved.y) local scale = self:getScale()
local dx = (self.dragReference.x - pos.x)/scale
local dy = (self.dragReference.y - pos.y)/scale
local pos = {x = self.dragCameraReference.x + dx, y = self.dragCameraReference.y + dy, z = self.dragCameraReference.z}
self:setCameraPosition(pos)
return true return true
end end
@ -278,20 +243,6 @@ function UIMinimap:onDragLeave(widget, pos)
return true return true
end end
function UIMinimap:createFullPanel()
self.fullPanel = g_ui.createWidget('MinimapFullPanel', rootWidget)
self.fullPanel.onDestroy = function() self.fullPanel = nil end
local image = self.fullPanel:getChildById('image')
image:setImage(self.fullImage)
end
function UIMinimap:destroyFullPanel()
if self.fullPanel then
self.fullPanel:destroy()
self.fullPanel = nil
end
end
function UIMinimap:createFlagWindow(pos) function UIMinimap:createFlagWindow(pos)
if self.flagWindow then return end if self.flagWindow then return end
if not pos then return end if not pos then return end
@ -311,16 +262,14 @@ function UIMinimap:createFlagWindow(pos)
checkbox.icon = i checkbox.icon = i
flagRadioGroup:addWidget(checkbox) flagRadioGroup:addWidget(checkbox)
end end
flagRadioGroup:selectWidget(flagRadioGroup:getFirstWidget()) flagRadioGroup:selectWidget(flagRadioGroup:getFirstWidget())
okButton.onClick = function() okButton.onClick = function()
self:addFlag(pos, flagRadioGroup:getSelectedWidget().icon, description:getText()) self:addFlag(pos, flagRadioGroup:getSelectedWidget().icon, description:getText())
self:destroyFlagWindow() self:destroyFlagWindow()
end end
cancelButton.onClick = function() cancelButton.onClick = function() self:destroyFlagWindow() end
self:destroyFlagWindow()
end
self.flagWindow.onDestroy = function() flagRadioGroup:destroy() end self.flagWindow.onDestroy = function() flagRadioGroup:destroy() end
end end
@ -331,9 +280,3 @@ function UIMinimap:destroyFlagWindow()
self.flagWindow = nil self.flagWindow = nil
end end
end end
function UIMinimap:getArea()
local topLeft = self:getPosition({ x = self:getX() + 1, y = self:getY() + 1 })
local bottomRight = self:getPosition({ x = self:getX() + self:getWidth() - 2, y = self:getY() + self:getHeight() - 2 })
return topLeft, bottomRight
end

@ -98,6 +98,8 @@ set(client_SOURCES ${client_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/uiminimap.h ${CMAKE_CURRENT_LIST_DIR}/uiminimap.h
${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.cpp ${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.cpp
${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.h ${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.h
${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.cpp
${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.h
# util # util
${CMAKE_CURRENT_LIST_DIR}/position.h ${CMAKE_CURRENT_LIST_DIR}/position.h

@ -94,11 +94,15 @@ class UICreature;
class UIMap; class UIMap;
class UIMinimap; class UIMinimap;
class UIProgressRect; class UIProgressRect;
class UIMapAnchorLayout;
class UIPositionAnchor;
typedef stdext::shared_object_ptr<UIItem> UIItemPtr; typedef stdext::shared_object_ptr<UIItem> UIItemPtr;
typedef stdext::shared_object_ptr<UICreature> UICreaturePtr; typedef stdext::shared_object_ptr<UICreature> UICreaturePtr;
typedef stdext::shared_object_ptr<UIMap> UIMapPtr; typedef stdext::shared_object_ptr<UIMap> UIMapPtr;
typedef stdext::shared_object_ptr<UIMinimap> UIMinimapPtr; typedef stdext::shared_object_ptr<UIMinimap> UIMinimapPtr;
typedef stdext::shared_object_ptr<UIProgressRect> UIProgressRectPtr; typedef stdext::shared_object_ptr<UIProgressRect> UIProgressRectPtr;
typedef stdext::shared_object_ptr<UIMapAnchorLayout> UIMapAnchorLayoutPtr;
typedef stdext::shared_object_ptr<UIPositionAnchor> UIPositionAnchorPtr;
#endif #endif

@ -45,6 +45,7 @@
#include "uicreature.h" #include "uicreature.h"
#include "uimap.h" #include "uimap.h"
#include "uiminimap.h" #include "uiminimap.h"
#include "uimapanchorlayout.h"
#include "uiprogressrect.h" #include "uiprogressrect.h"
#include "outfit.h" #include "outfit.h"
@ -121,6 +122,8 @@ void Client::registerLuaFunctions()
g_lua.registerSingletonClass("g_minimap"); g_lua.registerSingletonClass("g_minimap");
g_lua.bindSingletonFunction("g_minimap", "clean", &Minimap::clean, &g_minimap); g_lua.bindSingletonFunction("g_minimap", "clean", &Minimap::clean, &g_minimap);
g_lua.bindSingletonFunction("g_minimap", "loadImage", &Minimap::loadImage, &g_minimap);
g_lua.bindSingletonFunction("g_minimap", "saveImage", &Minimap::saveImage, &g_minimap);
g_lua.bindSingletonFunction("g_minimap", "loadOtmm", &Minimap::loadOtmm, &g_minimap); g_lua.bindSingletonFunction("g_minimap", "loadOtmm", &Minimap::loadOtmm, &g_minimap);
g_lua.bindSingletonFunction("g_minimap", "saveOtmm", &Minimap::saveOtmm, &g_minimap); g_lua.bindSingletonFunction("g_minimap", "saveOtmm", &Minimap::saveOtmm, &g_minimap);
@ -597,20 +600,24 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIMinimap>("setMixZoom", &UIMinimap::setMinZoom); g_lua.bindClassMemberFunction<UIMinimap>("setMixZoom", &UIMinimap::setMinZoom);
g_lua.bindClassMemberFunction<UIMinimap>("setMaxZoom", &UIMinimap::setMaxZoom); g_lua.bindClassMemberFunction<UIMinimap>("setMaxZoom", &UIMinimap::setMaxZoom);
g_lua.bindClassMemberFunction<UIMinimap>("setCameraPosition", &UIMinimap::setCameraPosition); g_lua.bindClassMemberFunction<UIMinimap>("setCameraPosition", &UIMinimap::setCameraPosition);
g_lua.bindClassMemberFunction<UIMinimap>("setCross", &UIMinimap::setCross); g_lua.bindClassMemberFunction<UIMinimap>("floorUp", &UIMinimap::floorUp);
g_lua.bindClassMemberFunction<UIMinimap>("followCreature", &UIMinimap::followCreature); g_lua.bindClassMemberFunction<UIMinimap>("floorDown", &UIMinimap::floorDown);
g_lua.bindClassMemberFunction<UIMinimap>("getPoint", &UIMinimap::getPoint); g_lua.bindClassMemberFunction<UIMinimap>("getTilePoint", &UIMinimap::getTilePoint);
g_lua.bindClassMemberFunction<UIMinimap>("getPosition", &UIMinimap::getPosition); g_lua.bindClassMemberFunction<UIMinimap>("getTilePosition", &UIMinimap::getTilePosition);
g_lua.bindClassMemberFunction<UIMinimap>("getTileRect", &UIMinimap::getTileRect);
g_lua.bindClassMemberFunction<UIMinimap>("getCameraPosition", &UIMinimap::getCameraPosition); g_lua.bindClassMemberFunction<UIMinimap>("getCameraPosition", &UIMinimap::getCameraPosition);
g_lua.bindClassMemberFunction<UIMinimap>("getFollowingCreature", &UIMinimap::getFollowingCreature);
g_lua.bindClassMemberFunction<UIMinimap>("getMinZoom", &UIMinimap::getMinZoom); g_lua.bindClassMemberFunction<UIMinimap>("getMinZoom", &UIMinimap::getMinZoom);
g_lua.bindClassMemberFunction<UIMinimap>("getMaxZoom", &UIMinimap::getMaxZoom); g_lua.bindClassMemberFunction<UIMinimap>("getMaxZoom", &UIMinimap::getMaxZoom);
g_lua.bindClassMemberFunction<UIMinimap>("getZoom", &UIMinimap::getZoom); g_lua.bindClassMemberFunction<UIMinimap>("getZoom", &UIMinimap::getZoom);
g_lua.bindClassMemberFunction<UIMinimap>("getCross", &UIMinimap::getCross); g_lua.bindClassMemberFunction<UIMinimap>("getScale", &UIMinimap::getScale);
g_lua.bindClassMemberFunction<UIMinimap>("getScale", &UIMinimap::getScale); g_lua.bindClassMemberFunction<UIMinimap>("anchorPosition", &UIMinimap::anchorPosition);
g_lua.bindClassMemberFunction<UIMinimap>("fillPosition", &UIMinimap::fillPosition);
g_lua.bindClassMemberFunction<UIMinimap>("centerInPosition", &UIMinimap::centerInPosition);
g_lua.registerClass<UIProgressRect, UIWidget>(); g_lua.registerClass<UIProgressRect, UIWidget>();
g_lua.bindClassStaticFunction<UIProgressRect>("create", []{ return UIProgressRectPtr(new UIProgressRect); } ); g_lua.bindClassStaticFunction<UIProgressRect>("create", []{ return UIProgressRectPtr(new UIProgressRect); } );
g_lua.bindClassMemberFunction<UIProgressRect>("setPercent", &UIProgressRect::setPercent); g_lua.bindClassMemberFunction<UIProgressRect>("setPercent", &UIProgressRect::setPercent);
g_lua.bindClassMemberFunction<UIProgressRect>("getPercent", &UIProgressRect::getPercent); g_lua.bindClassMemberFunction<UIProgressRect>("getPercent", &UIProgressRect::getPercent);
g_lua.registerClass<UIMapAnchorLayout, UIAnchorLayout>();
} }

@ -621,6 +621,8 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
wasSeen = mtile.hasFlag(MinimapTileWasSeen); wasSeen = mtile.hasFlag(MinimapTileWasSeen);
isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable); isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable);
isNotPathable = mtile.hasFlag(MinimapTileNotPathable); isNotPathable = mtile.hasFlag(MinimapTileNotPathable);
if(isNotWalkable || isNotPathable)
wasSeen = true;
speed = mtile.getSpeed(); speed = mtile.getSpeed();
} }

@ -23,13 +23,14 @@
#include "minimap.h" #include "minimap.h"
#include "tile.h" #include "tile.h"
#include <framework/graphics/image.h> #include <framework/graphics/image.h>
#include <framework/graphics/texture.h> #include <framework/graphics/texture.h>
#include <framework/graphics/painter.h> #include <framework/graphics/painter.h>
#include <framework/graphics/image.h>
#include <framework/graphics/framebuffermanager.h> #include <framework/graphics/framebuffermanager.h>
#include <framework/core/resourcemanager.h> #include <framework/core/resourcemanager.h>
#include <framework/core/filestream.h> #include <framework/core/filestream.h>
#include <boost/concept_check.hpp>
#include <zlib.h> #include <zlib.h>
Minimap g_minimap; Minimap g_minimap;
@ -51,10 +52,14 @@ void MinimapBlock::update()
bool shouldDraw = false; bool shouldDraw = false;
for(int x=0;x<MMBLOCK_SIZE;++x) { for(int x=0;x<MMBLOCK_SIZE;++x) {
for(int y=0;y<MMBLOCK_SIZE;++y) { for(int y=0;y<MMBLOCK_SIZE;++y) {
uint32 col = Color::from8bit(getTile(x, y).color).rgba(); uint8 c = getTile(x, y).color;
image->setPixel(x, y, col); uint32 col;
if(col != 0) if(c != 255) {
col = Color::from8bit(c).rgba();
shouldDraw = true; shouldDraw = true;
} else
col = Color::alpha.rgba();
image->setPixel(x, y, col);
} }
} }
@ -93,14 +98,14 @@ void Minimap::clean()
m_tileBlocks[i].clear(); m_tileBlocks[i].clear();
} }
void Minimap::draw(const Rect& screenRect, const Position& mapCenter, float scale) void Minimap::draw(const Rect& screenRect, const Position& mapCenter, float scale, const Color& color)
{ {
if(screenRect.isEmpty()) if(screenRect.isEmpty())
return; return;
Rect mapRect = calcMapRect(screenRect, mapCenter, scale); Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
g_painter->saveState(); g_painter->saveState();
g_painter->setColor(Color::black); g_painter->setColor(color);
g_painter->drawFilledRect(screenRect); g_painter->drawFilledRect(screenRect);
g_painter->resetColor(); g_painter->resetColor();
g_painter->setClipRect(screenRect); g_painter->setClipRect(screenRect);
@ -137,15 +142,16 @@ void Minimap::draw(const Rect& screenRect, const Position& mapCenter, float scal
tex->setSmooth(scale < 1.0f); tex->setSmooth(scale < 1.0f);
g_painter->drawTexturedRect(dest, tex, src); g_painter->drawTexturedRect(dest, tex, src);
} }
//g_painter->drawBoundingRect(Rect(xs,ys, MMBLOCK_SIZE * scale, MMBLOCK_SIZE * scale));
} }
} }
g_painter->restoreSavedState(); g_painter->restoreSavedState();
} }
Point Minimap::getPoint(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale) Point Minimap::getTilePoint(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale)
{ {
if(screenRect.isEmpty() || MMBLOCK_SIZE*scale <= 1 || !mapCenter.isMapPosition()) if(screenRect.isEmpty() || pos.z != mapCenter.z)
return Point(-1,-1); return Point(-1,-1);
Rect mapRect = calcMapRect(screenRect, mapCenter, scale); Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
@ -154,9 +160,9 @@ Point Minimap::getPoint(const Position& pos, const Rect& screenRect, const Posit
return posoff + screenRect.topLeft() - off + (Point(1,1)*scale)/2; return posoff + screenRect.topLeft() - off + (Point(1,1)*scale)/2;
} }
Position Minimap::getPosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale) Position Minimap::getTilePosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale)
{ {
if(screenRect.isEmpty() || MMBLOCK_SIZE*scale <= 1 || !mapCenter.isMapPosition()) if(screenRect.isEmpty())
return Position(); return Position();
Rect mapRect = calcMapRect(screenRect, mapCenter, scale); Rect mapRect = calcMapRect(screenRect, mapCenter, scale);
@ -165,24 +171,25 @@ Position Minimap::getPosition(const Point& point, const Rect& screenRect, const
return Position(pos2d.x, pos2d.y, mapCenter.z); return Position(pos2d.x, pos2d.y, mapCenter.z);
} }
Rect Minimap::getTileRect(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale)
{
if(screenRect.isEmpty() || pos.z != mapCenter.z)
return Rect();
int tileSize = 32 * scale;
Rect tileRect(0,0,tileSize, tileSize);
tileRect.moveCenter(getTilePoint(pos, screenRect, mapCenter, scale));
return tileRect;
}
Rect Minimap::calcMapRect(const Rect& screenRect, const Position& mapCenter, float scale) Rect Minimap::calcMapRect(const Rect& screenRect, const Position& mapCenter, float scale)
{ {
int w, h; int w = screenRect.width() / scale, h = std::ceil(screenRect.height() / scale);
do {
w = std::ceil(screenRect.width() / scale);
h = std::ceil(screenRect.height() / scale);
if(w % 2 == 0)
w++;
if(h % 2 == 0)
h++;
scale *= 2;
} while(w > 8192 || h > 8192);
Rect mapRect(0,0,w,h); Rect mapRect(0,0,w,h);
mapRect.moveCenter(Point(mapCenter.x, mapCenter.y)); mapRect.moveCenter(Point(mapCenter.x, mapCenter.y));
return mapRect; return mapRect;
} }
void Minimap::updateTile(const Position& pos, const TilePtr& tile) void Minimap::updateTile(const Position& pos, const TilePtr& tile)
{ {
MinimapTile minimapTile; MinimapTile minimapTile;
@ -200,6 +207,7 @@ void Minimap::updateTile(const Position& pos, const TilePtr& tile)
MinimapBlock& block = getBlock(pos); MinimapBlock& block = getBlock(pos);
Point offsetPos = getBlockOffset(Point(pos.x, pos.y)); Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
block.updateTile(pos.x - offsetPos.x, pos.y - offsetPos.y, minimapTile); block.updateTile(pos.x - offsetPos.x, pos.y - offsetPos.y, minimapTile);
block.justSaw();
} }
} }
@ -214,6 +222,87 @@ const MinimapTile& Minimap::getTile(const Position& pos)
return nulltile; return nulltile;
} }
bool Minimap::loadImage(const std::string& fileName, const Position& topLeft, float colorFactor)
{
if(colorFactor <= 0.01f)
colorFactor = 1.0f;
try {
ImagePtr image = Image::load(fileName);
uint8 waterc = Color::to8bit(std::string("#3300cc"));
// non pathable colors
Color nonPathableColors[] = {
std::string("#ffff00"), // yellow
};
// non walkable colors
Color nonWalkableColors[] = {
std::string("#000000"), // oil, black
std::string("#006600"), // trees, dark green
std::string("#ff3300"), // walls, red
std::string("#666666"), // mountain, grey
std::string("#ff6600"), // lava, orange
std::string("#00ff00"), // positon
std::string("#ccffff"), // ice, very light blue
};
for(int y=0;y<image->getHeight();++y) {
for(int x=0;x<image->getWidth();++x) {
Color color = *(uint32*)image->getPixel(x,y);
uint8 c = Color::to8bit(color * colorFactor);
int flags = 0;
if(c == waterc || color.a() == 0) {
flags |= MinimapTileNotWalkable;
c = 255; // alpha
}
if(flags != 0) {
for(Color &col : nonWalkableColors) {
if(col == color) {
flags |= MinimapTileNotWalkable;
break;
}
}
}
if(flags != 0) {
for(Color &col : nonPathableColors) {
if(col == color) {
flags |= MinimapTileNotPathable;
break;
}
}
}
if(c == 255)
continue;
Position pos(topLeft.x + x, topLeft.y + y, topLeft.z);
MinimapBlock& block = getBlock(pos);
Point offsetPos = getBlockOffset(Point(pos.x, pos.y));
MinimapTile& tile = block.getTile(pos.x - offsetPos.x, pos.y - offsetPos.y);
if(!(tile.flags & MinimapTileWasSeen)) {
tile.color = c;
tile.flags = flags;
block.mustUpdate();
}
}
}
return true;
} catch(stdext::exception& e) {
g_logger.error(stdext::format("failed to load OTMM minimap: %s", e.what()));
return false;
}
}
void Minimap::saveImage(const std::string& fileName, const Rect& mapRect)
{
//TODO
}
bool Minimap::loadOtmm(const std::string& fileName) bool Minimap::loadOtmm(const std::string& fileName)
{ {
try { try {
@ -263,6 +352,7 @@ bool Minimap::loadOtmm(const std::string& fileName)
assert(ret == Z_OK); assert(ret == Z_OK);
assert(destLen == blockSize); assert(destLen == blockSize);
block.mustUpdate(); block.mustUpdate();
block.justSaw();
} }
fin->close(); fin->close();
@ -307,6 +397,9 @@ void Minimap::saveOtmm(const std::string& fileName)
for(auto& it : m_tileBlocks[z]) { for(auto& it : m_tileBlocks[z]) {
int index = it.first; int index = it.first;
MinimapBlock& block = it.second; MinimapBlock& block = it.second;
if(!block.wasSeen())
continue;
Position pos = getIndexPosition(index, z); Position pos = getIndexPosition(index, z);
fin->addU16(pos.x); fin->addU16(pos.x);
fin->addU16(pos.y); fin->addU16(pos.y);
@ -319,7 +412,7 @@ void Minimap::saveOtmm(const std::string& fileName)
fin->write(compressBuffer.data(), len); fin->write(compressBuffer.data(), len);
} }
} }
// end of file // end of file
Position invalidPos; Position invalidPos;
fin->addU16(invalidPos.x); fin->addU16(invalidPos.x);

@ -42,7 +42,7 @@ enum MinimapTileFlags {
#pragma pack(push,1) // disable memory alignment #pragma pack(push,1) // disable memory alignment
struct MinimapTile struct MinimapTile
{ {
MinimapTile() : flags(0), color(0), speed(10) { } MinimapTile() : flags(0), color(255), speed(10) { }
uint8 flags; uint8 flags;
uint8 color; uint8 color;
uint8 speed; uint8 speed;
@ -64,10 +64,13 @@ public:
const TexturePtr& getTexture() { return m_texture; } const TexturePtr& getTexture() { return m_texture; }
std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE>& getTiles() { return m_tiles; } std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE>& getTiles() { return m_tiles; }
void mustUpdate() { m_mustUpdate = true; } void mustUpdate() { m_mustUpdate = true; }
void justSaw() { m_wasSeen = true; }
bool wasSeen() { return m_wasSeen; }
private: private:
TexturePtr m_texture; TexturePtr m_texture;
std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE> m_tiles; std::array<MinimapTile, MMBLOCK_SIZE *MMBLOCK_SIZE> m_tiles;
stdext::boolean<true> m_mustUpdate; stdext::boolean<true> m_mustUpdate;
stdext::boolean<false> m_wasSeen;
}; };
#pragma pack(pop) #pragma pack(pop)
@ -81,13 +84,16 @@ public:
void clean(); void clean();
void draw(const Rect& screenRect, const Position& mapCenter, float scale); void draw(const Rect& screenRect, const Position& mapCenter, float scale, const Color& color);
Point getPoint(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale); Point getTilePoint(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale);
Position getPosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale); Position getTilePosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale);
Rect getTileRect(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale);
void updateTile(const Position& pos, const TilePtr& tile); void updateTile(const Position& pos, const TilePtr& tile);
const MinimapTile& getTile(const Position& pos); const MinimapTile& getTile(const Position& pos);
bool loadImage(const std::string& fileName, const Position& topLeft, float colorFactor);
void saveImage(const std::string& fileName, const Rect& mapRect);
bool loadOtmm(const std::string& fileName); bool loadOtmm(const std::string& fileName);
void saveOtmm(const std::string& fileName); void saveOtmm(const std::string& fileName);

@ -763,6 +763,9 @@ void ProtocolGame::parseMagicEffect(const InputMessagePtr& msg)
else else
effectId = msg->getU8(); effectId = msg->getU8();
if(!g_things.isValidDatId(effectId, ThingCategoryEffect))
g_logger.traceError("invalid effect id");
EffectPtr effect = EffectPtr(new Effect()); EffectPtr effect = EffectPtr(new Effect());
effect->setId(effectId); effect->setId(effectId);
g_map.addThing(effect, pos); g_map.addThing(effect, pos);
@ -786,6 +789,9 @@ void ProtocolGame::parseDistanceMissile(const InputMessagePtr& msg)
Position toPos = getPosition(msg); Position toPos = getPosition(msg);
int shotId = msg->getU8(); int shotId = msg->getU8();
if(!g_things.isValidDatId(shotId, ThingCategoryMissile))
g_logger.traceError("invalid effect id");
MissilePtr missile = MissilePtr(new Missile()); MissilePtr missile = MissilePtr(new Missile());
missile->setId(shotId); missile->setId(shotId);
missile->setPath(fromPos, toPos); missile->setPath(fromPos, toPos);
@ -1642,6 +1648,9 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
int feet = msg->getU8(); int feet = msg->getU8();
int addons = msg->getU8(); int addons = msg->getU8();
if(!g_things.isValidDatId(lookType, ThingCategoryCreature))
lookType = 0;
outfit.setId(lookType); outfit.setId(lookType);
outfit.setHead(head); outfit.setHead(head);
outfit.setBody(body); outfit.setBody(body);
@ -1653,9 +1662,11 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
int lookTypeEx = msg->getU16(); int lookTypeEx = msg->getU16();
if(lookTypeEx == 0) { if(lookTypeEx == 0) {
outfit.setCategory(ThingCategoryEffect); outfit.setCategory(ThingCategoryEffect);
outfit.setAuxId(13); outfit.setAuxId(13); // invisible effect id
} }
else { else {
if(!g_things.isValidDatId(lookTypeEx, ThingCategoryItem))
lookTypeEx = 0;
outfit.setCategory(ThingCategoryItem); outfit.setCategory(ThingCategoryItem);
outfit.setAuxId(lookTypeEx); outfit.setAuxId(lookTypeEx);
} }

@ -164,9 +164,17 @@ void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPatte
if(m_null) if(m_null)
return; return;
if(animationPhase >= m_animationPhases)
return;
const TexturePtr& texture = getTexture(animationPhase); // texture might not exists, neither its rects. const TexturePtr& texture = getTexture(animationPhase); // texture might not exists, neither its rects.
if(!texture)
return;
uint frameIndex = getTextureIndex(layer, xPattern, yPattern, zPattern);
if(frameIndex >= m_texturesFramesRects[animationPhase].size())
return;
int frameIndex = getTextureIndex(layer, xPattern, yPattern, zPattern);
Point textureOffset; Point textureOffset;
Rect textureRect; Rect textureRect;

@ -338,7 +338,7 @@ int Tile::getGroundSpeed()
uint8 Tile::getMinimapColorByte() uint8 Tile::getMinimapColorByte()
{ {
uint8 color = 0; uint8 color = 255; // alpha
if(m_minimapColor != 0) { if(m_minimapColor != 0) {
return m_minimapColor; return m_minimapColor;
} }
@ -542,18 +542,8 @@ bool Tile::isLookPossible()
bool Tile::isClickable() bool Tile::isClickable()
{ {
bool hasGround = false;
bool hasOnBottom = false;
bool hasIgnoreLook = false;
for(const ThingPtr& thing : m_things) { for(const ThingPtr& thing : m_things) {
if(thing->isGround()) if(!thing->isOnTop() && !thing->isIgnoreLook())
hasGround = true;
if(thing->isOnBottom())
hasOnBottom = true;
if(thing->isIgnoreLook())
hasIgnoreLook = true;
if((hasGround || hasOnBottom) && !hasIgnoreLook)
return true; return true;
} }
return false; return false;

@ -0,0 +1,92 @@
/*
* Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "declarations.h"
#include "uimapanchorlayout.h"
#include "uiminimap.h"
#include <framework/ui/uiwidget.h>
int UIPositionAnchor::getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget)
{
UIMinimapPtr minimap = hookedWidget->static_self_cast<UIMinimap>();
Rect hookedRect = minimap->getTileRect(m_hookedPosition);
int point = 0;
if(hookedRect.isValid()) {
switch(m_hookedEdge) {
case Fw::AnchorLeft:
point = hookedRect.left();
break;
case Fw::AnchorRight:
point = hookedRect.right();
break;
case Fw::AnchorTop:
point = hookedRect.top();
break;
case Fw::AnchorBottom:
point = hookedRect.bottom();
break;
case Fw::AnchorHorizontalCenter:
point = hookedRect.horizontalCenter();
break;
case Fw::AnchorVerticalCenter:
point = hookedRect.verticalCenter();
break;
default:
// must never happens
assert(false);
break;
}
}
return point;
}
void UIMapAnchorLayout::addPositionAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge, const Position& hookedPosition, Fw::AnchorEdge hookedEdge)
{
if(!anchoredWidget)
return;
assert(anchoredWidget != getParentWidget());
UIPositionAnchorPtr anchor(new UIPositionAnchor(anchoredEdge, hookedPosition, hookedEdge));
UIAnchorGroupPtr& anchorGroup = m_anchorsGroups[anchoredWidget];
if(!anchorGroup)
anchorGroup = UIAnchorGroupPtr(new UIAnchorGroup);
anchorGroup->addAnchor(anchor);
// layout must be updated because a new anchor got in
update();
}
void UIMapAnchorLayout::centerInPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition)
{
addPositionAnchor(anchoredWidget, Fw::AnchorHorizontalCenter, hookedPosition, Fw::AnchorHorizontalCenter);
addPositionAnchor(anchoredWidget, Fw::AnchorVerticalCenter, hookedPosition, Fw::AnchorVerticalCenter);
}
void UIMapAnchorLayout::fillPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition)
{
addPositionAnchor(anchoredWidget, Fw::AnchorLeft, hookedPosition, Fw::AnchorLeft);
addPositionAnchor(anchoredWidget, Fw::AnchorRight, hookedPosition, Fw::AnchorRight);
addPositionAnchor(anchoredWidget, Fw::AnchorTop, hookedPosition, Fw::AnchorTop);
addPositionAnchor(anchoredWidget, Fw::AnchorBottom, hookedPosition, Fw::AnchorBottom);
}

@ -0,0 +1,55 @@
/*
* Copyright (c) 2010-2013 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef UIMAPANCHORLAYOUT_H
#define UIMAPANCHORLAYOUT_H
#include "declarations.h"
#include <framework/ui/uianchorlayout.h>
class UIPositionAnchor : public UIAnchor
{
public:
UIPositionAnchor(Fw::AnchorEdge anchoredEdge, const Position& hookedPosition, Fw::AnchorEdge hookedEdge) :
UIAnchor(anchoredEdge, std::string(), hookedEdge), m_hookedPosition(hookedPosition) { }
UIWidgetPtr getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget) { return parentWidget; }
int getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget);
private:
Position m_hookedPosition;
};
class UIMapAnchorLayout : public UIAnchorLayout
{
public:
UIMapAnchorLayout(UIWidgetPtr parentWidget) : UIAnchorLayout(parentWidget) { }
void addPositionAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge,
const Position& hookedPosition, Fw::AnchorEdge hookedEdge);
void centerInPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition);
void fillPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition);
protected:
};
#endif

@ -23,16 +23,19 @@
#include "uiminimap.h" #include "uiminimap.h"
#include "minimap.h" #include "minimap.h"
#include "game.h" #include "game.h"
#include "uimapanchorlayout.h"
#include "luavaluecasts.h"
#include <framework/graphics/painter.h> #include <framework/graphics/painter.h>
#include "uimapanchorlayout.h"
UIMinimap::UIMinimap() UIMinimap::UIMinimap()
{ {
m_crossEnabled = true;
m_zoom = 0; m_zoom = 0;
m_scale = 1.0f; m_scale = 1.0f;
m_minZoom = -5; m_minZoom = -5;
m_maxZoom = 5; m_maxZoom = 5;
m_layout = UIMapAnchorLayoutPtr(new UIMapAnchorLayout(static_self_cast<UIWidget>()));
} }
void UIMinimap::drawSelf(Fw::DrawPane drawPane) void UIMinimap::drawSelf(Fw::DrawPane drawPane)
@ -42,22 +45,18 @@ void UIMinimap::drawSelf(Fw::DrawPane drawPane)
if((drawPane & Fw::ForegroundPane) == 0) if((drawPane & Fw::ForegroundPane) == 0)
return; return;
g_minimap.draw(getPaddingRect(), getCameraPosition(), m_scale); g_minimap.draw(getPaddingRect(), getCameraPosition(), m_scale, m_color);
// draw a cross in the center
Rect vRect(0, 0, 2, 10);
Rect hRect(0, 0, 10, 2);
vRect.moveCenter(m_rect.center());
hRect.moveCenter(m_rect.center());
g_painter->setColor(Color::white);
g_painter->drawFilledRect(vRect);
g_painter->drawFilledRect(hRect);
} }
bool UIMinimap::setZoom(int zoom) bool UIMinimap::setZoom(int zoom)
{ {
if(zoom == m_zoom)
return true;
if(zoom < m_minZoom || zoom > m_maxZoom) if(zoom < m_minZoom || zoom > m_maxZoom)
return false; return false;
int oldZoom = m_zoom;
m_zoom = zoom; m_zoom = zoom;
if(m_zoom < 0) if(m_zoom < 0)
m_scale = 1.0f / (1 << std::abs(zoom)); m_scale = 1.0f / (1 << std::abs(zoom));
@ -65,37 +64,83 @@ bool UIMinimap::setZoom(int zoom)
m_scale = 1.0f * (1 << std::abs(zoom)); m_scale = 1.0f * (1 << std::abs(zoom));
else else
m_scale = 1; m_scale = 1;
m_layout->update();
onZoomChange(zoom, oldZoom);
return true; return true;
} }
void UIMinimap::followCreature(const CreaturePtr& creature) void UIMinimap::setCameraPosition(const Position& pos)
{ {
m_followingCreature = creature; Position oldPos = m_cameraPosition;
m_cameraPosition = Position(); m_cameraPosition = pos;
m_layout->update();
onCameraPositionChange(pos, oldPos);
} }
void UIMinimap::setCameraPosition(const Position& pos) bool UIMinimap::floorUp()
{ {
m_followingCreature = nullptr; Position pos = getCameraPosition();
m_cameraPosition = pos; if(!pos.up())
return false;
setCameraPosition(pos);
return true;
}
bool UIMinimap::floorDown()
{
Position pos = getCameraPosition();
if(!pos.down())
return false;
setCameraPosition(pos);
return true;
} }
Point UIMinimap::getPoint(const Position& pos) Point UIMinimap::getTilePoint(const Position& pos)
{ {
return g_minimap.getPoint(pos, getPaddingRect(), getCameraPosition(), m_scale); return g_minimap.getTilePoint(pos, getPaddingRect(), getCameraPosition(), m_scale);
} }
Position UIMinimap::getPosition(const Point& mousePos) Rect UIMinimap::getTileRect(const Position& pos)
{ {
return g_minimap.getPosition(mousePos, getPaddingRect(), getCameraPosition(), m_scale); return g_minimap.getTileRect(pos, getPaddingRect(), getCameraPosition(), m_scale);
} }
Position UIMinimap::getCameraPosition() Position UIMinimap::getTilePosition(const Point& mousePos)
{ {
if(m_followingCreature) return g_minimap.getTilePosition(mousePos, getPaddingRect(), getCameraPosition(), m_scale);
return m_followingCreature->getPosition(); }
else
return m_cameraPosition; void UIMinimap::anchorPosition(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge, const Position& hookedPosition, Fw::AnchorEdge hookedEdge)
{
UIMapAnchorLayoutPtr layout = m_layout->static_self_cast<UIMapAnchorLayout>();
assert(layout);
layout->addPositionAnchor(anchoredWidget, anchoredEdge, hookedPosition, hookedEdge);
}
void UIMinimap::fillPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition)
{
UIMapAnchorLayoutPtr layout = m_layout->static_self_cast<UIMapAnchorLayout>();
assert(layout);
layout->fillPosition(anchoredWidget, hookedPosition);
}
void UIMinimap::centerInPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition)
{
UIMapAnchorLayoutPtr layout = m_layout->static_self_cast<UIMapAnchorLayout>();
assert(layout);
layout->centerInPosition(anchoredWidget, hookedPosition);
}
void UIMinimap::onZoomChange(int zoom, int oldZoom)
{
callLuaField("onZoomChange", zoom, oldZoom);
}
void UIMinimap::onCameraPositionChange(const Position& position, const Position& oldPosition)
{
callLuaField("onCameraPositionChange", position, oldPosition);
} }
void UIMinimap::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode) void UIMinimap::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
@ -108,7 +153,5 @@ void UIMinimap::onStyleApply(const std::string& styleName, const OTMLNodePtr& st
setMaxZoom(node->value<int>()); setMaxZoom(node->value<int>());
else if(node->tag() == "min-zoom") else if(node->tag() == "min-zoom")
setMinZoom(node->value<int>()); setMinZoom(node->value<int>());
else if(node->tag() == "cross")
setCross(node->value<bool>());
} }
} }

@ -40,26 +40,32 @@ public:
void setMinZoom(int minZoom) { m_minZoom = minZoom; } void setMinZoom(int minZoom) { m_minZoom = minZoom; }
void setMaxZoom(int maxZoom) { m_maxZoom = maxZoom; } void setMaxZoom(int maxZoom) { m_maxZoom = maxZoom; }
void setCameraPosition(const Position& pos); void setCameraPosition(const Position& pos);
void setCross(bool enable) { m_crossEnabled = enable; } bool floorUp();
void followCreature(const CreaturePtr& creature); bool floorDown();
Point getPoint(const Position& pos); Point getTilePoint(const Position& pos);
Position getPosition(const Point& mousePos); Rect getTileRect(const Position& pos);
Position getCameraPosition(); Position getTilePosition(const Point& mousePos);
CreaturePtr getFollowingCreature() { return m_followingCreature; }
Position getCameraPosition() { return m_cameraPosition; }
int getMinZoom() { return m_minZoom; } int getMinZoom() { return m_minZoom; }
int getMaxZoom() { return m_maxZoom; } int getMaxZoom() { return m_maxZoom; }
int getZoom() { return m_zoom; } int getZoom() { return m_zoom; }
bool getCross() { return m_crossEnabled; }
float getScale() { return m_scale; } float getScale() { return m_scale; }
void anchorPosition(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge, const Position& hookedPosition, Fw::AnchorEdge hookedEdge);
void fillPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition);
void centerInPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition);
protected: protected:
virtual void onZoomChange(int zoom, int oldZoom);
virtual void onCameraPositionChange(const Position& position, const Position& oldPosition);
virtual void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode); virtual void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
private: private:
void update();
Rect m_mapArea; Rect m_mapArea;
bool m_crossEnabled;
CreaturePtr m_followingCreature;
Position m_cameraPosition; Position m_cameraPosition;
float m_scale; float m_scale;
int m_zoom; int m_zoom;

@ -289,8 +289,12 @@ bool luavalue_cast(int index, OTMLNodePtr& node)
g_lua.pushNil(); g_lua.pushNil();
while(g_lua.next(index < 0 ? index-1 : index)) { while(g_lua.next(index < 0 ? index-1 : index)) {
std::string cnodeName; std::string cnodeName;
if(!g_lua.isNumber(-2)) if(g_lua.isString(-2)) {
cnodeName = g_lua.toString(-2); g_lua.pushValue(-2);
cnodeName = g_lua.toString();
g_lua.pop();
} else
assert(g_lua.isNumber());
if(g_lua.isTable()) { if(g_lua.isTable()) {
OTMLNodePtr cnode; OTMLNodePtr cnode;
if(luavalue_cast(-1, cnode)) { if(luavalue_cast(-1, cnode)) {

@ -26,6 +26,7 @@
// common C headers // common C headers
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstddef>
#include <cstring> #include <cstring>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
@ -42,5 +43,8 @@
#include <functional> #include <functional>
#include <array> #include <array>
#include <unordered_map> #include <unordered_map>
#include <tuple>
#include <iomanip>
#include <typeinfo>
#endif #endif

@ -618,9 +618,12 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
case WM_SYSKEYUP: case WM_SYSKEYUP:
case WM_SYSKEYDOWN: { case WM_SYSKEYDOWN: {
// F10 is the shortcut key to enter a windows menu, this is a workaround to get F10 working // F10 is the shortcut key to enter a windows menu, this is a workaround to get F10 working
if(wParam != VK_F10) if(wParam != VK_F10) {
return DefWindowProc(hWnd, uMsg, wParam, lParam); if(wParam != VK_MENU && wParam != VK_LMENU && wParam != VK_RMENU)
else { return DefWindowProc(hWnd, uMsg, wParam, lParam);
else
return 0;
} else {
if(uMsg == WM_SYSKEYUP) if(uMsg == WM_SYSKEYUP)
processKeyUp(retranslateVirtualKey(wParam, lParam)); processKeyUp(retranslateVirtualKey(wParam, lParam));
else else

@ -33,6 +33,8 @@ class UIBoxLayout;
class UIHorizontalLayout; class UIHorizontalLayout;
class UIVerticalLayout; class UIVerticalLayout;
class UIGridLayout; class UIGridLayout;
class UIAnchor;
class UIAnchorGroup;
class UIAnchorLayout; class UIAnchorLayout;
class UIParticles; class UIParticles;
@ -44,8 +46,11 @@ typedef stdext::shared_object_ptr<UIBoxLayout> UIBoxLayoutPtr;
typedef stdext::shared_object_ptr<UIHorizontalLayout> UIHorizontalLayoutPtr; typedef stdext::shared_object_ptr<UIHorizontalLayout> UIHorizontalLayoutPtr;
typedef stdext::shared_object_ptr<UIVerticalLayout> UIVerticalLayoutPtr; typedef stdext::shared_object_ptr<UIVerticalLayout> UIVerticalLayoutPtr;
typedef stdext::shared_object_ptr<UIGridLayout> UIGridLayoutPtr; typedef stdext::shared_object_ptr<UIGridLayout> UIGridLayoutPtr;
typedef stdext::shared_object_ptr<UIAnchor> UIAnchorPtr;
typedef stdext::shared_object_ptr<UIAnchorGroup> UIAnchorGroupPtr;
typedef stdext::shared_object_ptr<UIAnchorLayout> UIAnchorLayoutPtr; typedef stdext::shared_object_ptr<UIAnchorLayout> UIAnchorLayoutPtr;
typedef std::deque<UIWidgetPtr> UIWidgetList; typedef std::deque<UIWidgetPtr> UIWidgetList;
typedef std::vector<UIAnchorPtr> UIAnchorList;
#endif #endif

@ -23,11 +23,81 @@
#include "uianchorlayout.h" #include "uianchorlayout.h"
#include "uiwidget.h" #include "uiwidget.h"
void UIAnchorGroup::addAnchor(const UIAnchor& anchor) UIWidgetPtr UIAnchor::getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget)
{
// determine hooked widget
UIWidgetPtr hookedWidget;
if(parentWidget) {
if(m_hookedWidgetId == "parent")
hookedWidget = parentWidget;
else if(m_hookedWidgetId == "next")
hookedWidget = parentWidget->getChildAfter(widget);
else if(m_hookedWidgetId == "prev")
hookedWidget = parentWidget->getChildBefore(widget);
else
hookedWidget = parentWidget->getChildById(m_hookedWidgetId);
}
return hookedWidget;
}
int UIAnchor::getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget)
{
// determine hooked widget edge point
Rect hookedWidgetRect = hookedWidget->getRect();
if(hookedWidget == parentWidget)
hookedWidgetRect = parentWidget->getPaddingRect();
int point = 0;
switch(m_hookedEdge) {
case Fw::AnchorLeft:
point = hookedWidgetRect.left();
break;
case Fw::AnchorRight:
point = hookedWidgetRect.right();
break;
case Fw::AnchorTop:
point = hookedWidgetRect.top();
break;
case Fw::AnchorBottom:
point = hookedWidgetRect.bottom();
break;
case Fw::AnchorHorizontalCenter:
point = hookedWidgetRect.horizontalCenter();
break;
case Fw::AnchorVerticalCenter:
point = hookedWidgetRect.verticalCenter();
break;
default:
// must never happens
assert(false);
break;
}
if(hookedWidget == parentWidget) {
switch(m_hookedEdge) {
case Fw::AnchorLeft:
case Fw::AnchorRight:
case Fw::AnchorHorizontalCenter:
point -= parentWidget->getVirtualOffset().x;
break;
case Fw::AnchorBottom:
case Fw::AnchorTop:
case Fw::AnchorVerticalCenter:
point -= parentWidget->getVirtualOffset().y;
break;
default:
break;
}
}
return point;
}
void UIAnchorGroup::addAnchor(const UIAnchorPtr& anchor)
{ {
// duplicated anchors must be replaced // duplicated anchors must be replaced
for(UIAnchor& other : m_anchors) { for(UIAnchorPtr& other : m_anchors) {
if(other.getAnchoredEdge() == anchor.getAnchoredEdge()) { if(other->getAnchoredEdge() == anchor->getAnchoredEdge()) {
other = anchor; other = anchor;
return; return;
} }
@ -43,9 +113,12 @@ void UIAnchorLayout::addAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge
assert(anchoredWidget != getParentWidget()); assert(anchoredWidget != getParentWidget());
UIAnchor anchor(anchoredEdge, hookedWidgetId, hookedEdge); UIAnchorPtr anchor(new UIAnchor(anchoredEdge, hookedWidgetId, hookedEdge));
UIAnchorGroup& anchorGroup = m_anchorsGroups[anchoredWidget]; UIAnchorGroupPtr& anchorGroup = m_anchorsGroups[anchoredWidget];
anchorGroup.addAnchor(anchor); if(!anchorGroup)
anchorGroup = UIAnchorGroupPtr(new UIAnchorGroup);
anchorGroup->addAnchor(anchor);
// layout must be updated because a new anchor got in // layout must be updated because a new anchor got in
update(); update();
@ -86,7 +159,7 @@ void UIAnchorLayout::removeWidget(const UIWidgetPtr& widget)
removeAnchors(widget); removeAnchors(widget);
} }
bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup, UIWidgetPtr first) bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, const UIAnchorGroupPtr& anchorGroup, UIWidgetPtr first)
{ {
UIWidgetPtr parentWidget = getParentWidget(); UIWidgetPtr parentWidget = getParentWidget();
if(!parentWidget) if(!parentWidget)
@ -105,23 +178,13 @@ bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch
bool horizontalMoved = false; bool horizontalMoved = false;
// calculates new rect based on anchors // calculates new rect based on anchors
for(const UIAnchor& anchor : anchorGroup.getAnchors()) { for(const UIAnchorPtr& anchor : anchorGroup->getAnchors()) {
// skip invalid anchors // skip invalid anchors
if(anchor.getHookedEdge() == Fw::AnchorNone) if(anchor->getHookedEdge() == Fw::AnchorNone)
continue; continue;
// determine hooked widget // determine hooked widget
UIWidgetPtr hookedWidget; UIWidgetPtr hookedWidget = anchor->getHookedWidget(widget, parentWidget);
if(parentWidget) {
if(anchor.getHookedWidgetId() == "parent")
hookedWidget = parentWidget;
else if(anchor.getHookedWidgetId() == "next")
hookedWidget = parentWidget->getChildAfter(widget);
else if(anchor.getHookedWidgetId() == "prev")
hookedWidget = parentWidget->getChildBefore(widget);
else
hookedWidget = parentWidget->getChildById(anchor.getHookedWidgetId());
}
// skip invalid anchors // skip invalid anchors
if(!hookedWidget) if(!hookedWidget)
@ -131,61 +194,15 @@ bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch
// update this hooked widget anchors // update this hooked widget anchors
auto it = m_anchorsGroups.find(hookedWidget); auto it = m_anchorsGroups.find(hookedWidget);
if(it != m_anchorsGroups.end()) { if(it != m_anchorsGroups.end()) {
UIAnchorGroup& hookedAnchorGroup = it->second; const UIAnchorGroupPtr& hookedAnchorGroup = it->second;
if(!hookedAnchorGroup.isUpdated()) if(!hookedAnchorGroup->isUpdated())
updateWidget(hookedWidget, hookedAnchorGroup, first); updateWidget(hookedWidget, hookedAnchorGroup, first);
} }
} }
// determine hooked widget edge point int point = anchor->getHookedPoint(hookedWidget, parentWidget);
Rect hookedWidgetRect = hookedWidget->getRect();
if(hookedWidget == parentWidget)
hookedWidgetRect = parentWidget->getPaddingRect();
int point = 0;
switch(anchor.getHookedEdge()) {
case Fw::AnchorLeft:
point = hookedWidgetRect.left();
break;
case Fw::AnchorRight:
point = hookedWidgetRect.right();
break;
case Fw::AnchorTop:
point = hookedWidgetRect.top();
break;
case Fw::AnchorBottom:
point = hookedWidgetRect.bottom();
break;
case Fw::AnchorHorizontalCenter:
point = hookedWidgetRect.horizontalCenter();
break;
case Fw::AnchorVerticalCenter:
point = hookedWidgetRect.verticalCenter();
break;
default:
// must never happens
assert(false);
break;
}
if(hookedWidget == parentWidget) {
switch(anchor.getHookedEdge()) {
case Fw::AnchorLeft:
case Fw::AnchorRight:
case Fw::AnchorHorizontalCenter:
point -= parentWidget->getVirtualOffset().x;
break;
case Fw::AnchorBottom:
case Fw::AnchorTop:
case Fw::AnchorVerticalCenter:
point -= parentWidget->getVirtualOffset().y;
break;
default:
break;
}
}
switch(anchor.getAnchoredEdge()) { switch(anchor->getAnchoredEdge()) {
case Fw::AnchorHorizontalCenter: case Fw::AnchorHorizontalCenter:
newRect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight()); newRect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight());
horizontalMoved = true; horizontalMoved = true;
@ -230,7 +247,7 @@ bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch
bool changed = false; bool changed = false;
if(widget->setRect(newRect)) if(widget->setRect(newRect))
changed = true; changed = true;
anchorGroup.setUpdated(true); anchorGroup->setUpdated(true);
return changed; return changed;
} }
@ -240,15 +257,15 @@ bool UIAnchorLayout::internalUpdate()
// reset all anchors groups update state // reset all anchors groups update state
for(auto& it : m_anchorsGroups) { for(auto& it : m_anchorsGroups) {
UIAnchorGroup& anchorGroup = it.second; const UIAnchorGroupPtr& anchorGroup = it.second;
anchorGroup.setUpdated(false); anchorGroup->setUpdated(false);
} }
// update all anchors // update all anchors
for(auto& it : m_anchorsGroups) { for(auto& it : m_anchorsGroups) {
const UIWidgetPtr& widget = it.first; const UIWidgetPtr& widget = it.first;
UIAnchorGroup& anchorGroup = it.second; const UIAnchorGroupPtr& anchorGroup = it.second;
if(!anchorGroup.isUpdated()) { if(!anchorGroup->isUpdated()) {
if(updateWidget(widget, anchorGroup)) if(updateWidget(widget, anchorGroup))
changed = true; changed = true;
} }

@ -25,30 +25,30 @@
#include "uilayout.h" #include "uilayout.h"
class UIAnchor class UIAnchor : public stdext::shared_object
{ {
public: public:
UIAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge) : UIAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge) :
m_anchoredEdge(anchoredEdge), m_hookedEdge(hookedEdge), m_hookedWidgetId(hookedWidgetId) { } m_anchoredEdge(anchoredEdge), m_hookedEdge(hookedEdge), m_hookedWidgetId(hookedWidgetId) { }
Fw::AnchorEdge getAnchoredEdge() const { return m_anchoredEdge; } Fw::AnchorEdge getAnchoredEdge() const { return m_anchoredEdge; }
std::string getHookedWidgetId() const { return m_hookedWidgetId; }
Fw::AnchorEdge getHookedEdge() const { return m_hookedEdge; } Fw::AnchorEdge getHookedEdge() const { return m_hookedEdge; }
private: virtual UIWidgetPtr getHookedWidget(const UIWidgetPtr& widget, const UIWidgetPtr& parentWidget);
virtual int getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget);
protected:
Fw::AnchorEdge m_anchoredEdge; Fw::AnchorEdge m_anchoredEdge;
Fw::AnchorEdge m_hookedEdge; Fw::AnchorEdge m_hookedEdge;
std::string m_hookedWidgetId; std::string m_hookedWidgetId;
}; };
typedef std::vector<UIAnchor> UIAnchorList; class UIAnchorGroup : public stdext::shared_object
class UIAnchorGroup
{ {
public: public:
UIAnchorGroup() : m_updated(true) { } UIAnchorGroup() : m_updated(true) { }
void addAnchor(const UIAnchor& anchor); void addAnchor(const UIAnchorPtr& anchor);
const UIAnchorList& getAnchors() { return m_anchors; } const UIAnchorList& getAnchors() { return m_anchors; }
bool isUpdated() { return m_updated; } bool isUpdated() { return m_updated; }
void setUpdated(bool updated) { m_updated = updated; } void setUpdated(bool updated) { m_updated = updated; }
@ -77,11 +77,9 @@ public:
bool isUIAnchorLayout() { return true; } bool isUIAnchorLayout() { return true; }
protected: protected:
bool internalUpdate(); virtual bool internalUpdate();
virtual bool updateWidget(const UIWidgetPtr& widget, const UIAnchorGroupPtr& anchorGroup, UIWidgetPtr first = nullptr);
private: std::unordered_map<UIWidgetPtr, UIAnchorGroupPtr> m_anchorsGroups;
bool updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup, UIWidgetPtr first = nullptr);
std::unordered_map<UIWidgetPtr, UIAnchorGroup> m_anchorsGroups;
}; };
#endif #endif

@ -31,6 +31,7 @@
#include <framework/platform/platformwindow.h> #include <framework/platform/platformwindow.h>
#include <framework/graphics/texturemanager.h> #include <framework/graphics/texturemanager.h>
#include <framework/core/application.h> #include <framework/core/application.h>
#include <framework/luaengine/luainterface.h>
UIWidget::UIWidget() UIWidget::UIWidget()
{ {

@ -407,4 +407,4 @@ void UIWidget::setIcon(const std::string& iconFile)
m_icon = g_textures.getTexture(iconFile); m_icon = g_textures.getTexture(iconFile);
if(m_icon && !m_iconClipRect.isValid()) if(m_icon && !m_iconClipRect.isValid())
m_iconClipRect = Rect(0, 0, m_icon->getSize()); m_iconClipRect = Rect(0, 0, m_icon->getSize());
} }

Loading…
Cancel
Save