diff --git a/.gitignore b/.gitignore index e98c2cac..844b0764 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ otclient.layout LOCALTODO tags Thumbs.db +.directory diff --git a/data/images/game/minimap/cross.png b/data/images/game/minimap/cross.png new file mode 100644 index 00000000..dd06cf32 Binary files /dev/null and b/data/images/game/minimap/cross.png differ diff --git a/data/styles/30-minimap.otui b/data/styles/30-minimap.otui index 654af8dc..c23c9485 100644 --- a/data/styles/30-minimap.otui +++ b/data/styles/30-minimap.otui @@ -2,9 +2,13 @@ MinimapFlag < UIWidget size: 11 11 anchors.left: parent.left anchors.top: parent.top + focusable: false -MinimapFlags < UIWidget - anchors.fill: parent +MinimapCross < UIWidget + focusable: false + phantom: true + image: /images/game/minimap/cross + size: 16 16 MinimapFloorUpButton < Button size: 20 20 @@ -61,12 +65,7 @@ Minimap < UIMinimap draggable: true focusable: false cross: true - @onGeometryChange: self:updateFlags() - - MinimapFlags - id: flags - phantom: true - focusable: false + color: black MinimapFloorUpButton id: floorUp @@ -245,22 +244,3 @@ MinimapFlagWindow < MainWindow width: 64 anchors.right: parent.right 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() diff --git a/modules/client/client.lua b/modules/client/client.lua index b29238c2..209b7b7d 100644 --- a/modules/client/client.lua +++ b/modules/client/client.lua @@ -55,7 +55,7 @@ end function init() connect(g_app, { onRun = startup, onExit = exit }) - + g_window.setMinimumSize({ width = 600, height = 480 }) g_sounds.preload(musicFilename) diff --git a/modules/client/client.otmod b/modules/client/client.otmod index 26d0baea..28cec272 100644 --- a/modules/client/client.otmod +++ b/modules/client/client.otmod @@ -12,7 +12,6 @@ Module load-later: - client_styles - client_locales - //- client_particles - client_topmenu - client_background - client_entergame diff --git a/modules/client_background/background.lua b/modules/client_background/background.lua index 2af57648..9935b964 100644 --- a/modules/client_background/background.lua +++ b/modules/client_background/background.lua @@ -1,18 +1,19 @@ -- private variables local background +local clientVersionLabel -- public functions function init() background = g_ui.displayUI('background') background:lower() - local clientVersionLabel = background:getChildById('clientVersionLabel') + clientVersionLabel = background:getChildById('clientVersionLabel') clientVersionLabel:setText(g_app.getName() .. ' ' .. g_app.getVersion() .. '\n' .. 'Rev ' .. g_app.getBuildRevision() .. ' ('.. g_app.getBuildCommit() .. ')\n' .. 'Built on ' .. g_app.getBuildDate()) if not g_game.isOnline() then - g_effects.fadeIn(clientVersionLabel, 1500) + addEvent(function() g_effects.fadeIn(clientVersionLabel, 1500) end) end connect(g_game, { onGameStart = hide }) @@ -40,3 +41,7 @@ end function hideVersionLabel() background:getChildById('clientVersionLabel'):hide() end + +function setVersionText(text) + clientVersionLabel:setText(text) +end diff --git a/modules/client_entergame/characterlist.lua b/modules/client_entergame/characterlist.lua index 365b0c57..586f254f 100644 --- a/modules/client_entergame/characterlist.lua +++ b/modules/client_entergame/characterlist.lua @@ -111,7 +111,7 @@ end function onGameConnectionError(message, code) CharacterList.destroyLoadBox() - errorBox = displayErrorBox(tr("Login Error"), message) + errorBox = displayErrorBox(tr("Connection Error"), message) errorBox.onOk = function() errorBox = nil CharacterList.showAgain() diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 4ed01279..e2cb9027 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -8,6 +8,7 @@ local motdButton local enterGameButton local protocolBox local protocolLogin +local motdEnabled = true -- private functions local function onError(protocol, message, errorCode) @@ -27,7 +28,9 @@ end local function onMotd(protocol, motd) G.motdNumber = tonumber(motd:sub(0, motd:find("\n"))) G.motdMessage = motd:sub(motd:find("\n") + 1, #motd) - motdButton:show() + if motdEnabled then + motdButton:show() + end end local function onCharacterList(protocol, characters, account, otui) @@ -45,12 +48,14 @@ local function onCharacterList(protocol, characters, account, otui) CharacterList.create(characters, account, otui) CharacterList.show() - local lastMotdNumber = g_settings.getNumber("motd") - if G.motdNumber and G.motdNumber ~= lastMotdNumber then - g_settings.set("motd", motdNumber) - motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage) - connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end }) - CharacterList.hide() + if motdEnabled then + local lastMotdNumber = g_settings.getNumber("motd") + if G.motdNumber and G.motdNumber ~= lastMotdNumber then + g_settings.set("motd", motdNumber) + motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage) + connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end }) + CharacterList.hide() + end end end @@ -81,7 +86,7 @@ function EnterGame.init() motdButton:hide() g_keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow) - if G.motdNumber then + if motdEnabled and G.motdNumber then motdButton:show() end @@ -127,13 +132,15 @@ function EnterGame.firstShow() local host = g_settings.get('host') local autologin = g_settings.getBoolean('autologin') 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 function EnterGame.terminate() g_keyboard.unbindKeyDown('Ctrl+G') - removeEvent(autoLoginEvent) enterGame:destroy() enterGame = nil enterGameButton:destroy() @@ -186,7 +193,6 @@ function EnterGame.clearAccountFields() end function EnterGame.doLogin() - autoLoginEvent = nil G.account = enterGame:getChildById('accountNameTextEdit'):getText() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText() @@ -252,10 +258,6 @@ function EnterGame.setDefaultServer(host, port, protocol) protocolBox:setCurrentOption(protocol) accountTextEdit:setText('') passwordTextEdit:setText('') - - if autoLoginEvent then - autoLoginEvent:cancel() - end end end @@ -297,3 +299,7 @@ function EnterGame.setServerInfo(message) label:setText(message) end +function EnterGame.disableMotd() + motdEnabled = false + motdButton:hide() +end diff --git a/modules/client_stats/stats.lua b/modules/client_stats/stats.lua index c4572b70..29a06461 100644 --- a/modules/client_stats/stats.lua +++ b/modules/client_stats/stats.lua @@ -25,6 +25,8 @@ end function terminate() disconnect(g_game, { onGameStart = onGameStart, onGameEnd = onGameEnd }) + removeEvent(firstReportEvent) + removeEvent(sendReportEvent) end function configure(host, port, delay) @@ -45,13 +47,15 @@ end function onGameStart() if not HOST then return end + removeEvent(firstReportEvent) + removeEvent(sendReportEvent) firstReportEvent = addEvent(sendReport, FIRST_REPORT_DELAY*1000) sendReportEvent = cycleEvent(sendReport, REPORT_DELAY*1000) end function onGameEnd() - removeEvent(sendReportEvent) removeEvent(firstReportEvent) + removeEvent(sendReportEvent) end function onConnect(protocol) @@ -84,6 +88,7 @@ function onConnect(protocol) post = post .. '&cpu=' .. g_platform.getCPUName() post = post .. '&mem=' .. g_platform.getTotalSystemMemory() post = post .. '&os_name=' .. g_platform.getOSName() + post = post .. getAdditionalData() local message = '' message = message .. "POST /report HTTP/1.1\r\n" @@ -98,6 +103,10 @@ function onConnect(protocol) protocol:recv() end +function getAdditionalData() + return '' +end + function onRecv(protocol, message) if string.find(message, 'HTTP/1.1 200 OK') then --pinfo('Stats sent to server successfully!') diff --git a/modules/corelib/keyboard.lua b/modules/corelib/keyboard.lua index 4eb9c2e0..dbad3ba2 100644 --- a/modules/corelib/keyboard.lua +++ b/modules/corelib/keyboard.lua @@ -4,7 +4,6 @@ g_keyboard = {} -- private functions function translateKeyCombo(keyCombo) if not keyCombo or #keyCombo == 0 then return nil end - table.sort(keyCombo) local keyComboDesc = '' for k,v in pairs(keyCombo) do local keyDesc = KeyCodeDescs[v] @@ -65,47 +64,29 @@ function determineKeyComboDesc(keyCode, keyboardModifiers) end table.insert(keyCombo, keyCode) end - table.sort(keyCombo) return translateKeyCombo(keyCombo) end local function onWidgetKeyDown(widget, keyCode, keyboardModifiers) if keyCode == KeyUnknown then return false end local callback = widget.boundAloneKeyDownCombos[determineKeyComboDesc(keyCode, KeyboardNoModifier)] - if callback then - callback(widget, keyCode) - end + signalcall(callback, widget, keyCode) callback = widget.boundKeyDownCombos[determineKeyComboDesc(keyCode, keyboardModifiers)] - if callback then - callback(widget, keyCode) - return true - end - return false + return signalcall(callback, widget, keyCode) end local function onWidgetKeyUp(widget, keyCode, keyboardModifiers) if keyCode == KeyUnknown then return false end local callback = widget.boundAloneKeyUpCombos[determineKeyComboDesc(keyCode, KeyboardNoModifier)] - if callback then - callback(widget, keyCode) - end + signalcall(callback, widget, keyCode) callback = widget.boundKeyUpCombos[determineKeyComboDesc(keyCode, keyboardModifiers)] - if callback then - callback(widget, keyCode) - return true - end - return false + return signalcall(callback, widget, keyCode) end local function onWidgetKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks) if keyCode == KeyUnknown then return false end - local keyComboDesc = determineKeyComboDesc(keyCode, keyboardModifiers) - local comboConf = widget.boundKeyPressCombos[keyComboDesc] - if comboConf and (autoRepeatTicks >= comboConf.autoRepeatDelay or autoRepeatTicks == 0) and comboConf.callback then - comboConf.callback(widget, keyCode) - return true - end - return false + local callback = widget.boundKeyPressCombos[determineKeyComboDesc(keyCode, keyboardModifiers)] + return signalcall(callback, widget, keyCode, autoRepeatTicks) end local function connectKeyDownEvent(widget) @@ -133,13 +114,10 @@ function g_keyboard.bindKeyDown(keyComboDesc, callback, widget, alone) widget = widget or rootWidget connectKeyDownEvent(widget) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) - if widget.boundKeyDownCombos[keyComboDesc] then - pwarning('KeyDown event \'' .. keyComboDesc .. '\' redefined on widget ' .. widget:getId()) - end if alone then - widget.boundAloneKeyDownCombos[keyComboDesc] = callback + connect(widget.boundAloneKeyDownCombos, keyComboDesc, callback) else - widget.boundKeyDownCombos[keyComboDesc] = callback + connect(widget.boundKeyDownCombos, keyComboDesc, callback) end end @@ -147,53 +125,50 @@ function g_keyboard.bindKeyUp(keyComboDesc, callback, widget, alone) widget = widget or rootWidget connectKeyUpEvent(widget) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) - if widget.boundKeyUpCombos[keyComboDesc] then - pwarning('KeyUp event \'' .. keyComboDesc .. '\' redefined on widget ' .. widget:getId()) - end if alone then - widget.boundAloneKeyUpCombos[keyComboDesc] = callback + connect(widget.boundAloneKeyUpCombos, keyComboDesc, callback) else - widget.boundKeyUpCombos[keyComboDesc] = callback + connect(widget.boundKeyUpCombos, keyComboDesc, callback) end end -function g_keyboard.bindKeyPress(keyComboDesc, callback, widget, autoRepeatDelay) - autoRepeatDelay = autoRepeatDelay or 500 +function g_keyboard.bindKeyPress(keyComboDesc, callback, widget) widget = widget or rootWidget connectKeyPressEvent(widget) local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) - if widget.boundKeyPressCombos[keyComboDesc] then - pwarning('KeyPress event \'' .. keyComboDesc .. '\' redefined on widget ' .. widget:getId()) - end - widget.boundKeyPressCombos[keyComboDesc] = { callback = callback, autoRepeatDelay = autoRepeatDelay } - widget:setAutoRepeatDelay(math.min(autoRepeatDelay, widget:getAutoRepeatDelay())) + connect(widget.boundKeyPressCombos, keyComboDesc, callback) 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 + return callback, widget +end + +function g_keyboard.unbindKeyDown(keyComboDesc, arg1, arg2) + local callback, widget = getUnbindArgs(arg1, arg2) if widget.boundKeyDownCombos == nil then return end local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) - if keyComboDesc then - widget.boundKeyDownCombos[keyComboDesc] = nil - end + disconnect(widget.boundKeyDownCombos, keyComboDesc, callback) end function g_keyboard.unbindKeyUp(keyComboDesc, widget) - widget = widget or rootWidget + local callback, widget = getUnbindArgs(arg1, arg2) if widget.boundKeyUpCombos == nil then return end local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) - if keyComboDesc then - widget.boundKeyUpCombos[keyComboDesc] = nil - end + disconnect(widget.boundKeyUpCombos, keyComboDesc, callback) end -function g_keyboard.unbindKeyPress(keyComboDesc, widget) - widget = widget or rootWidget +function g_keyboard.unbindKeyPress(keyComboDesc, widget, callback) + local callback, widget = getUnbindArgs(arg1, arg2) if widget.boundKeyPressCombos == nil then return end local keyComboDesc = retranslateKeyComboDesc(keyComboDesc) - if keyComboDesc then - widget.boundKeyPressCombos[keyComboDesc] = nil - end + disconnect(widget.boundKeyPressCombos, keyComboDesc, callback) end function g_keyboard.getModifiers() diff --git a/modules/corelib/table.lua b/modules/corelib/table.lua index d0270e59..c90c8545 100644 --- a/modules/corelib/table.lua +++ b/modules/corelib/table.lua @@ -13,6 +13,12 @@ function table.dump(t, depth) end end +function table.clear(t) + for k,v in pairs(t) do + t[k] = nil + end +end + function table.copy(t) local res = {} for k,v in pairs(t) do diff --git a/modules/corelib/ui/uiscrollbar.lua b/modules/corelib/ui/uiscrollbar.lua index de7bb7c9..e6fce00d 100644 --- a/modules/corelib/ui/uiscrollbar.lua +++ b/modules/corelib/ui/uiscrollbar.lua @@ -224,7 +224,9 @@ end function UIScrollBar:setText(text) local valueLabel = self:getChildById('valueLabel') - valueLabel:setText(text) + if valueLabel then + valueLabel:setText(text) + end end function UIScrollBar:onGeometryChange() diff --git a/modules/corelib/util.lua b/modules/corelib/util.lua index e29f34b5..a827b696 100644 --- a/modules/corelib/util.lua +++ b/modules/corelib/util.lua @@ -67,6 +67,11 @@ function connect(object, arg1, arg2, arg3) elseif type(object[signal]) == 'function' then object[signal] = { object[signal] } end + + if type(slot) ~= 'function' then + perror(debug.traceback('unable to connect a non function value')) + end + if type(object[signal]) == 'table' then if pushFront then table.insert(object[signal], 1, slot) @@ -80,9 +85,15 @@ end function disconnect(object, arg1, arg2) local signalsAndSlots if type(arg1) == 'string' then + if arg2 == nil then + object[arg1] = nil + return + end signalsAndSlots = { [arg1] = arg2 } - else + elseif type(arg1) == 'table' then signalsAndSlots = arg1 + else + perror(debug.traceback('unable to disconnect')) end for signal,slot in pairs(signalsAndSlots) do diff --git a/modules/game_hotkeys/hotkeys_manager.lua b/modules/game_hotkeys/hotkeys_manager.lua index 9cb98de9..6a52781e 100644 --- a/modules/game_hotkeys/hotkeys_manager.lua +++ b/modules/game_hotkeys/hotkeys_manager.lua @@ -34,6 +34,7 @@ perCharacter = true mouseGrabberWidget = nil useRadioGroup = nil currentHotkeys = nil +boundCombosCallback = {} hotkeysList = {} -- public functions @@ -149,6 +150,7 @@ function load(forceDefaults) if not forceDefaults then if not table.empty(hotkeys) then for keyCombo, setting in pairs(hotkeys) do + keyCombo = tostring(keyCombo) addKeyCombo(keyCombo, setting) hotkeyList[keyCombo] = setting end @@ -163,12 +165,13 @@ function load(forceDefaults) end function unload() - for _,child in pairs(currentHotkeys:getChildren()) do - g_keyboard.unbindKeyPress(child.keyCombo) + for keyCombo,callback in pairs(boundCombosCallback) do + g_keyboard.unbindKeyPress(keyCombo, callback) end + boundCombosCallback = {} currentHotkeys:destroyChildren() currentHotkeyLabel = nil - updateHotkeyForm() + updateHotkeyForm(true) hotkeyList = {} end @@ -196,6 +199,8 @@ function save() hotkeys = hotkeys[g_game.getCharacterName()] end + table.clear(hotkeys) + for _,child in pairs(currentHotkeys:getChildren()) do hotkeys[child.keyCombo] = { autoSend = child.autoSend, @@ -207,7 +212,7 @@ function save() hotkeyList = hotkeys g_settings.setNode('game_hotkeys', hotkeySettings) - --g_settings.save() + g_settings.save() end function loadDefautComboKeys() @@ -258,7 +263,7 @@ function onChooseItemMouseRelease(self, mousePosition, mouseButton) currentHotkeyLabel.value = nil currentHotkeyLabel.autoSend = false updateHotkeyLabel(currentHotkeyLabel) - updateHotkeyForm() + updateHotkeyForm(true) end show() @@ -281,7 +286,7 @@ function clearObject() currentHotkeyLabel.autoSend = nil currentHotkeyLabel.value = nil updateHotkeyLabel(currentHotkeyLabel) - updateHotkeyForm() + updateHotkeyForm(true) end function addHotkey() @@ -294,6 +299,7 @@ function addHotkey() end function addKeyCombo(keyCombo, keySettings, focus) + if keyCombo == nil or #keyCombo == 0 then return end if not keyCombo then return end local hotkeyLabel = currentHotkeys:getChildById(keyCombo) if not hotkeyLabel then @@ -321,27 +327,28 @@ function addKeyCombo(keyCombo, keySettings, focus) if keySettings then currentHotkeyLabel = hotkeyLabel hotkeyLabel.keyCombo = keyCombo - hotkeyLabel.autoSend = keySettings.autoSend - hotkeyLabel.itemId = keySettings.itemId + hotkeyLabel.autoSend = toboolean(keySettings.autoSend) + hotkeyLabel.itemId = tonumber(keySettings.itemId) hotkeyLabel.useType = tonumber(keySettings.useType) - hotkeyLabel.value = keySettings.value + if keySettings.value then hotkeyLabel.value = tostring(keySettings.value) end else hotkeyLabel.keyCombo = keyCombo - hotkeyLabel.autoSend = nil + hotkeyLabel.autoSend = false hotkeyLabel.itemId = nil hotkeyLabel.useType = nil - hotkeyLabel.value = nil + hotkeyLabel.value = '' end 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 if focus then currentHotkeys:focusChild(hotkeyLabel) currentHotkeys:ensureChildVisible(hotkeyLabel) - updateHotkeyForm() + updateHotkeyForm(true) end end @@ -398,7 +405,7 @@ function updateHotkeyLabel(hotkeyLabel) end end -function updateHotkeyForm() +function updateHotkeyForm(reset) if currentHotkeyLabel then removeHotkeyButton:enable() if currentHotkeyLabel.itemId ~= nil then @@ -435,8 +442,10 @@ function updateHotkeyForm() hotkeyText:enable() hotkeyText:focus() hotKeyTextLabel:enable() + if reset then + hotkeyText:setCursorPos(-1) + end hotkeyText:setText(currentHotkeyLabel.value) - hotkeyText:setCursorPos(-1) sendAutomatically:setChecked(currentHotkeyLabel.autoSend) sendAutomatically:setEnabled(currentHotkeyLabel.value and #currentHotkeyLabel.value > 0) selectObjectButton:enable() @@ -461,7 +470,8 @@ end function removeHotkey() 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 = nil end @@ -504,7 +514,7 @@ end function onSelectHotkeyLabel(hotkeyLabel) currentHotkeyLabel = hotkeyLabel - updateHotkeyForm() + updateHotkeyForm(true) end function hotkeyCapture(assignWindow, keyCode, keyboardModifiers) diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index bc661731..394f8117 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -1,4 +1,3 @@ -WALK_AUTO_REPEAT_DELAY = 90 WALK_STEPS_RETRY = 10 gameRootPanel = nil @@ -25,7 +24,7 @@ function init() onGameStart = onGameStart, onGMActions = onGMActions, onGameEnd = onGameEnd, - onLoginAdvice = onLoginAdvice + onLoginAdvice = onLoginAdvice, }, true) gameRootPanel = g_ui.displayUI('gameinterface') @@ -57,6 +56,7 @@ function init() end function bindKeys() + gameRootPanel:setAutoRepeatDelay(250) g_keyboard.bindKeyDown('Up', function() changeWalkDir(North) end, gameRootPanel, true) g_keyboard.bindKeyDown('Right', function() changeWalkDir(East) end, gameRootPanel, true) g_keyboard.bindKeyDown('Down', function() changeWalkDir(South) end, gameRootPanel, true) @@ -81,30 +81,30 @@ function bindKeys() g_keyboard.bindKeyUp('Numpad1', function() changeWalkDir(SouthWest, true) end, gameRootPanel, true) g_keyboard.bindKeyUp('Numpad4', function() changeWalkDir(West, true) end, gameRootPanel, true) g_keyboard.bindKeyUp('Numpad7', function() changeWalkDir(NorthWest, true) end, gameRootPanel, true) - g_keyboard.bindKeyPress('Up', function() smartWalk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Right', function() smartWalk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad3', function() smartWalk(SouthEast) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad2', function() smartWalk(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad1', function() smartWalk(SouthWest) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad4', function() smartWalk(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Numpad7', function() smartWalk(NorthWest) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - - g_keyboard.bindKeyPress('Ctrl+Up', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Right', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Down', function() g_game.turn(South) changeWalkDir(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Left', function() g_game.turn(West) changeWalkDir(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Numpad8', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Numpad6', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Numpad2', function() g_game.turn(South) changeWalkDir(South) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+Numpad4', function() g_game.turn(West) changeWalkDir(West) end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel, WALK_AUTO_REPEAT_DELAY) - g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel, 250) - g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel, 250) + g_keyboard.bindKeyPress('Up', function() smartWalk(North) end, gameRootPanel) + g_keyboard.bindKeyPress('Right', function() smartWalk(East) end, gameRootPanel) + g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel) + g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad3', function() smartWalk(SouthEast) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad2', function() smartWalk(South) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad1', function() smartWalk(SouthWest) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad4', function() smartWalk(West) end, gameRootPanel) + g_keyboard.bindKeyPress('Numpad7', function() smartWalk(NorthWest) end, gameRootPanel) + + 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) + 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) + 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) + 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) + g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel) + g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel) + g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel) @@ -591,7 +591,6 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u player:stopAutoWalk() 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) return true end diff --git a/modules/game_minimap/minimap.lua b/modules/game_minimap/minimap.lua index 6e61d68a..a387a58e 100644 --- a/modules/game_minimap/minimap.lua +++ b/modules/game_minimap/minimap.lua @@ -3,6 +3,9 @@ minimapButton = nil minimapWindow = nil otmm = true preloaded = false +fullmapView = false +oldZoom = nil +oldPos = nil function init() 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+Down', function() minimapWidget:move(0,-1) end, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+M', toggle) + g_keyboard.bindKeyDown('Ctrl+Shift+M', toggleFullMap) minimapWindow:setup() @@ -27,6 +31,10 @@ function init() onGameEnd = offline, }) + connect(LocalPlayer, { + onPositionChange = updateCameraPosition + }) + if g_game.isOnline() then online() end @@ -48,6 +56,7 @@ function terminate() g_keyboard.unbindKeyPress('Alt+Up', gameRootPanel) g_keyboard.unbindKeyPress('Alt+Down', gameRootPanel) g_keyboard.unbindKeyDown('Ctrl+M') + g_keyboard.unbindKeyDown('Ctrl+Shift+M') minimapWindow:destroy() minimapButton:destroy() @@ -74,7 +83,6 @@ end function online() loadMap(not preloaded) - minimapWidget:followLocalPlayer() end function offline() @@ -114,6 +122,36 @@ function saveMap() minimapWidget:save() end -function getMinimap() - return minimapWidget +function updateCameraPosition() + 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 diff --git a/modules/game_npctrade/npctrade.lua b/modules/game_npctrade/npctrade.lua index d04e22a3..905dad2a 100644 --- a/modules/game_npctrade/npctrade.lua +++ b/modules/game_npctrade/npctrade.lua @@ -151,7 +151,8 @@ function onTradeTypeChange(radioTabs, selected, deselected) ignoreCapacity:setVisible(currentTradeType == BUY) ignoreEquipped:setVisible(currentTradeType == SELL) showAllItems:setVisible(currentTradeType == SELL) - sellAllButton:setVisible(currentTradeType == SELL) + sellAllButton:setVisible(false) + --sellAllButton:setVisible(currentTradeType == SELL) refreshTradeItems() refreshPlayerGoods() diff --git a/modules/game_npctrade/npctrade.otui b/modules/game_npctrade/npctrade.otui index 7c766a10..5dc30b46 100644 --- a/modules/game_npctrade/npctrade.otui +++ b/modules/game_npctrade/npctrade.otui @@ -252,6 +252,7 @@ MainWindow margin-right: 10 visible: false @onClick: modules.game_npctrade.sellAll() + visible: false Button id: tradeButton diff --git a/modules/game_textmessage/textmessage.lua b/modules/game_textmessage/textmessage.lua index 96d933ff..8ef5bfd9 100644 --- a/modules/game_textmessage/textmessage.lua +++ b/modules/game_textmessage/textmessage.lua @@ -123,3 +123,7 @@ function clearMessages() end end end + +function LocalPlayer:onAutoWalkFail(player) + modules.game_textmessage.displayFailureMessage(tr('There is no way.')) +end diff --git a/modules/game_viplist/viplist.lua b/modules/game_viplist/viplist.lua index af705159..c6e32aa2 100644 --- a/modules/game_viplist/viplist.lua +++ b/modules/game_viplist/viplist.lua @@ -3,7 +3,8 @@ vipButton = nil addVipWindow = nil function init() - connect(g_game, { onGameEnd = clear, + connect(g_game, { onGameStart = refresh, + onGameEnd = clear, onAddVip = onAddVip, onVipStateChange = onVipStateChange }) @@ -20,7 +21,8 @@ end function terminate() g_keyboard.unbindKeyDown('Ctrl+P') - disconnect(g_game, { onGameEnd = clear, + disconnect(g_game, { onGameStart = refresh, + onGameEnd = clear, onAddVip = onAddVip, onVipStateChange = onVipStateChange }) diff --git a/modules/gamelib/player.lua b/modules/gamelib/player.lua index b02a96a4..533d30e5 100644 --- a/modules/gamelib/player.lua +++ b/modules/gamelib/player.lua @@ -161,4 +161,4 @@ function Player:hasState(_state, states) end end return false -end \ No newline at end of file +end diff --git a/modules/gamelib/ui/uiminimap.lua b/modules/gamelib/ui/uiminimap.lua index 6c2d99ce..b57e7146 100644 --- a/modules/gamelib/ui/uiminimap.lua +++ b/modules/gamelib/ui/uiminimap.lua @@ -1,45 +1,39 @@ function UIMinimap:onSetup() self.flagWindow = nil - self.flagsWidget = self:getChildById('flags') self.floorUpWidget = self:getChildById('floorUp') self.floorDownWidget = self:getChildById('floorDown') self.zoomInWidget = self:getChildById('zoomIn') self.zoomOutWidget = self:getChildById('zoomOut') - self.dx = 0 - self.dy = 0 + self.flags = {} + self.alternatives = {} 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.onRemoveAutomapFlag = function(pos, icon, description) self:removeFlag(pos, icon, description) end connect(g_game, { onAddAutomapFlag = self.onAddAutomapFlag, onRemoveAutomapFlag = self.onRemoveAutomapFlag, }) - connect(LocalPlayer, { onPositionChange = self.onPositionChange }) end function UIMinimap:onDestroy() - disconnect(LocalPlayer, { onPositionChange = self.onPositionChange }) + for _,widget in pairs(self.alternatives) do + widget:destroy() + end + self.alternatives = {} disconnect(g_game, { onAddAutomapFlag = self.onAddAutomapFlag, onRemoveAutomapFlag = self.onRemoveAutomapFlag, }) self:destroyFlagWindow() - self:destroyFullPanel() + self.flags = {} end function UIMinimap:onVisibilityChange() if not self:isVisible() then self:destroyFlagWindow() - self:destroyFullPanel() end end -function UIMinimap:hideFlags() - self.flagsWidget:hide() -end - function UIMinimap:hideFloor() self.floorUpWidget:hide() self.floorDownWidget:hide() @@ -54,33 +48,21 @@ function UIMinimap:disableAutoWalk() self.autowalk = false end -function UIMinimap:disableFollowPlayer() - self.allowFollowLocalPlayer = false -end - -function UIMinimap:enableFullPanel(image) - self.fullImage = image -end - function UIMinimap:load() local settings = g_settings.getNode('Minimap') if settings then if settings.flags then - for i=1,#settings.flags do - local flag = settings.flags[i] + for _,flag in pairs(settings.flags) do self:addFlag(flag.position, flag.icon, flag.description) end end self:setZoom(settings.zoom) end - self:updateFlags() end function UIMinimap:save() local settings = { flags={} } - local children = self.flagsWidget:getChildren() - for i=1,#children do - local flag = children[i] + for _,flag in pairs(self.flags) do table.insert(settings.flags, { position = flag.pos, icon = flag.icon, @@ -91,134 +73,114 @@ function UIMinimap:save() g_settings.setNode('Minimap', settings) 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) + if not pos or not icon then return end local flag = self:getFlag(pos, icon, description) - if flag then + if flag or not icon then return end - - flag = g_ui.createWidget('MinimapFlag', self.flagsWidget) + + flag = g_ui.createWidget('MinimapFlag', self) flag.pos = pos - flag.icon = icon flag.description = description + flag.icon = icon flag:setIcon('/images/game/minimap/flag' .. icon) flag:setTooltip(description) - flag.onMouseRelease = function(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 + flag.onMouseRelease = onFlagMouseRelease + table.insert(self.flags, flag) + self:centerInPosition(flag, pos) +end + +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 - return false 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 -function UIMinimap:getFlag(pos, icon, description) - local children = self.flagsWidget:getChildren() - for i=1,#children do - local flag = children[i] +function UIMinimap:getFlag(pos) + for _,flag in pairs(self.flags) do if flag.pos.x == pos.x and flag.pos.y == pos.y and flag.pos.z == pos.z then return flag end end + return nil end function UIMinimap:removeFlag(pos, icon, description) - local flag = self:getFlag(pos, icon, description) + local flag = self:getFlag(pos) if flag then flag:destroy() - end -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() + table.removevalue(self.flags, flag) end end function UIMinimap:reset() - self:followLocalPlayer() self:setZoom(0) + if self.cross then + self:setCameraPosition(self.cross.pos) + end end 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 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} self:setCameraPosition(pos) - self:updateFlags() end function UIMinimap:onMouseWheel(mousePos, direction) @@ -232,7 +194,6 @@ function UIMinimap:onMouseWheel(mousePos, direction) elseif direction == MouseWheelUp and keyboardModifiers == KeyboardCtrlModifier then self:floorDown(1) end - self:updateFlags() end function UIMinimap:onMousePress(pos, button) @@ -245,20 +206,18 @@ function UIMinimap:onMouseRelease(pos, button) if not self.allowNextRelease then return true end self.allowNextRelease = false - local mapPos = self:getPosition(pos) + local mapPos = self:getTilePosition(pos) if not mapPos then return end if button == MouseLeftButton then local player = g_game.getLocalPlayer() if self.autowalk then - player.onAutoWalkFail = function() modules.game_textmessage.displayFailureMessage(tr('There is no way.')) end player:autoWalk(mapPos) end return true elseif button == MouseRightButton then local menu = g_ui.createWidget('PopupMenu') 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) return true end @@ -266,11 +225,17 @@ function UIMinimap:onMouseRelease(pos, button) end function UIMinimap:onDragEnter(pos) + self.dragReference = pos + self.dragCameraReference = self:getCameraPosition() return true end 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 end @@ -278,20 +243,6 @@ function UIMinimap:onDragLeave(widget, pos) return true 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) if self.flagWindow then return end if not pos then return end @@ -311,16 +262,14 @@ function UIMinimap:createFlagWindow(pos) checkbox.icon = i flagRadioGroup:addWidget(checkbox) end - + flagRadioGroup:selectWidget(flagRadioGroup:getFirstWidget()) - + okButton.onClick = function() - self:addFlag(pos, flagRadioGroup:getSelectedWidget().icon, description:getText()) - self:destroyFlagWindow() - end - cancelButton.onClick = function() - self:destroyFlagWindow() - end + self:addFlag(pos, flagRadioGroup:getSelectedWidget().icon, description:getText()) + self:destroyFlagWindow() + end + cancelButton.onClick = function() self:destroyFlagWindow() end self.flagWindow.onDestroy = function() flagRadioGroup:destroy() end end @@ -331,9 +280,3 @@ function UIMinimap:destroyFlagWindow() self.flagWindow = nil 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 diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index f4363bf0..27eeac50 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -98,6 +98,8 @@ set(client_SOURCES ${client_SOURCES} ${CMAKE_CURRENT_LIST_DIR}/uiminimap.h ${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.cpp ${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.h + ${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.cpp + ${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.h # util ${CMAKE_CURRENT_LIST_DIR}/position.h diff --git a/src/client/declarations.h b/src/client/declarations.h index c0aa3fed..8cd03fc8 100644 --- a/src/client/declarations.h +++ b/src/client/declarations.h @@ -94,11 +94,15 @@ class UICreature; class UIMap; class UIMinimap; class UIProgressRect; +class UIMapAnchorLayout; +class UIPositionAnchor; typedef stdext::shared_object_ptr UIItemPtr; typedef stdext::shared_object_ptr UICreaturePtr; typedef stdext::shared_object_ptr UIMapPtr; typedef stdext::shared_object_ptr UIMinimapPtr; typedef stdext::shared_object_ptr UIProgressRectPtr; +typedef stdext::shared_object_ptr UIMapAnchorLayoutPtr; +typedef stdext::shared_object_ptr UIPositionAnchorPtr; #endif diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 390c4c38..9e38f002 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -45,6 +45,7 @@ #include "uicreature.h" #include "uimap.h" #include "uiminimap.h" +#include "uimapanchorlayout.h" #include "uiprogressrect.h" #include "outfit.h" @@ -121,6 +122,8 @@ void Client::registerLuaFunctions() g_lua.registerSingletonClass("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", "saveOtmm", &Minimap::saveOtmm, &g_minimap); @@ -597,20 +600,24 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("setMixZoom", &UIMinimap::setMinZoom); g_lua.bindClassMemberFunction("setMaxZoom", &UIMinimap::setMaxZoom); g_lua.bindClassMemberFunction("setCameraPosition", &UIMinimap::setCameraPosition); - g_lua.bindClassMemberFunction("setCross", &UIMinimap::setCross); - g_lua.bindClassMemberFunction("followCreature", &UIMinimap::followCreature); - g_lua.bindClassMemberFunction("getPoint", &UIMinimap::getPoint); - g_lua.bindClassMemberFunction("getPosition", &UIMinimap::getPosition); + g_lua.bindClassMemberFunction("floorUp", &UIMinimap::floorUp); + g_lua.bindClassMemberFunction("floorDown", &UIMinimap::floorDown); + g_lua.bindClassMemberFunction("getTilePoint", &UIMinimap::getTilePoint); + g_lua.bindClassMemberFunction("getTilePosition", &UIMinimap::getTilePosition); + g_lua.bindClassMemberFunction("getTileRect", &UIMinimap::getTileRect); g_lua.bindClassMemberFunction("getCameraPosition", &UIMinimap::getCameraPosition); - g_lua.bindClassMemberFunction("getFollowingCreature", &UIMinimap::getFollowingCreature); g_lua.bindClassMemberFunction("getMinZoom", &UIMinimap::getMinZoom); g_lua.bindClassMemberFunction("getMaxZoom", &UIMinimap::getMaxZoom); g_lua.bindClassMemberFunction("getZoom", &UIMinimap::getZoom); - g_lua.bindClassMemberFunction("getCross", &UIMinimap::getCross); - g_lua.bindClassMemberFunction("getScale", &UIMinimap::getScale); + g_lua.bindClassMemberFunction("getScale", &UIMinimap::getScale); + g_lua.bindClassMemberFunction("anchorPosition", &UIMinimap::anchorPosition); + g_lua.bindClassMemberFunction("fillPosition", &UIMinimap::fillPosition); + g_lua.bindClassMemberFunction("centerInPosition", &UIMinimap::centerInPosition); g_lua.registerClass(); g_lua.bindClassStaticFunction("create", []{ return UIProgressRectPtr(new UIProgressRect); } ); g_lua.bindClassMemberFunction("setPercent", &UIProgressRect::setPercent); g_lua.bindClassMemberFunction("getPercent", &UIProgressRect::getPercent); + + g_lua.registerClass(); } diff --git a/src/client/map.cpp b/src/client/map.cpp index 3aa4f9fc..64e65be5 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -621,6 +621,8 @@ std::tuple, Otc::PathFindResult> Map::findPath(const wasSeen = mtile.hasFlag(MinimapTileWasSeen); isNotWalkable = mtile.hasFlag(MinimapTileNotWalkable); isNotPathable = mtile.hasFlag(MinimapTileNotPathable); + if(isNotWalkable || isNotPathable) + wasSeen = true; speed = mtile.getSpeed(); } diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 14d26417..50649de1 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -23,13 +23,14 @@ #include "minimap.h" #include "tile.h" + #include #include #include +#include #include #include #include -#include #include Minimap g_minimap; @@ -51,10 +52,14 @@ void MinimapBlock::update() bool shouldDraw = false; for(int x=0;xsetPixel(x, y, col); - if(col != 0) + uint8 c = getTile(x, y).color; + uint32 col; + if(c != 255) { + col = Color::from8bit(c).rgba(); shouldDraw = true; + } else + col = Color::alpha.rgba(); + image->setPixel(x, y, col); } } @@ -93,14 +98,14 @@ void Minimap::clean() 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()) return; Rect mapRect = calcMapRect(screenRect, mapCenter, scale); g_painter->saveState(); - g_painter->setColor(Color::black); + g_painter->setColor(color); g_painter->drawFilledRect(screenRect); g_painter->resetColor(); g_painter->setClipRect(screenRect); @@ -137,15 +142,16 @@ void Minimap::draw(const Rect& screenRect, const Position& mapCenter, float scal tex->setSmooth(scale < 1.0f); g_painter->drawTexturedRect(dest, tex, src); } + //g_painter->drawBoundingRect(Rect(xs,ys, MMBLOCK_SIZE * scale, MMBLOCK_SIZE * scale)); } } 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); 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; } -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(); 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); } +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) { - int w, h; - 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); + int w = screenRect.width() / scale, h = std::ceil(screenRect.height() / scale); Rect mapRect(0,0,w,h); mapRect.moveCenter(Point(mapCenter.x, mapCenter.y)); return mapRect; } - void Minimap::updateTile(const Position& pos, const TilePtr& tile) { MinimapTile minimapTile; @@ -200,6 +207,7 @@ void Minimap::updateTile(const Position& pos, const TilePtr& tile) MinimapBlock& block = getBlock(pos); Point offsetPos = getBlockOffset(Point(pos.x, pos.y)); 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; } +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;ygetHeight();++y) { + for(int x=0;xgetWidth();++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) { try { @@ -263,6 +352,7 @@ bool Minimap::loadOtmm(const std::string& fileName) assert(ret == Z_OK); assert(destLen == blockSize); block.mustUpdate(); + block.justSaw(); } fin->close(); @@ -307,6 +397,9 @@ void Minimap::saveOtmm(const std::string& fileName) for(auto& it : m_tileBlocks[z]) { int index = it.first; MinimapBlock& block = it.second; + if(!block.wasSeen()) + continue; + Position pos = getIndexPosition(index, z); fin->addU16(pos.x); fin->addU16(pos.y); @@ -319,7 +412,7 @@ void Minimap::saveOtmm(const std::string& fileName) fin->write(compressBuffer.data(), len); } } - + // end of file Position invalidPos; fin->addU16(invalidPos.x); diff --git a/src/client/minimap.h b/src/client/minimap.h index 73e574b6..22cc3f0c 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -42,7 +42,7 @@ enum MinimapTileFlags { #pragma pack(push,1) // disable memory alignment struct MinimapTile { - MinimapTile() : flags(0), color(0), speed(10) { } + MinimapTile() : flags(0), color(255), speed(10) { } uint8 flags; uint8 color; uint8 speed; @@ -64,10 +64,13 @@ public: const TexturePtr& getTexture() { return m_texture; } std::array& getTiles() { return m_tiles; } void mustUpdate() { m_mustUpdate = true; } + void justSaw() { m_wasSeen = true; } + bool wasSeen() { return m_wasSeen; } private: TexturePtr m_texture; std::array m_tiles; stdext::boolean m_mustUpdate; + stdext::boolean m_wasSeen; }; #pragma pack(pop) @@ -81,13 +84,16 @@ public: void clean(); - void draw(const Rect& screenRect, const Position& mapCenter, float scale); - Point getPoint(const Position& pos, const Rect& screenRect, const Position& mapCenter, float scale); - Position getPosition(const Point& point, const Rect& screenRect, const Position& mapCenter, float scale); + void draw(const Rect& screenRect, const Position& mapCenter, float scale, const Color& color); + Point getTilePoint(const Position& pos, 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); 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); void saveOtmm(const std::string& fileName); diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index 1eaa6052..4b0a4716 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -763,6 +763,9 @@ void ProtocolGame::parseMagicEffect(const InputMessagePtr& msg) else effectId = msg->getU8(); + if(!g_things.isValidDatId(effectId, ThingCategoryEffect)) + g_logger.traceError("invalid effect id"); + EffectPtr effect = EffectPtr(new Effect()); effect->setId(effectId); g_map.addThing(effect, pos); @@ -786,6 +789,9 @@ void ProtocolGame::parseDistanceMissile(const InputMessagePtr& msg) Position toPos = getPosition(msg); int shotId = msg->getU8(); + if(!g_things.isValidDatId(shotId, ThingCategoryMissile)) + g_logger.traceError("invalid effect id"); + MissilePtr missile = MissilePtr(new Missile()); missile->setId(shotId); missile->setPath(fromPos, toPos); @@ -1642,6 +1648,9 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg) int feet = msg->getU8(); int addons = msg->getU8(); + if(!g_things.isValidDatId(lookType, ThingCategoryCreature)) + lookType = 0; + outfit.setId(lookType); outfit.setHead(head); outfit.setBody(body); @@ -1653,9 +1662,11 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg) int lookTypeEx = msg->getU16(); if(lookTypeEx == 0) { outfit.setCategory(ThingCategoryEffect); - outfit.setAuxId(13); + outfit.setAuxId(13); // invisible effect id } else { + if(!g_things.isValidDatId(lookTypeEx, ThingCategoryItem)) + lookTypeEx = 0; outfit.setCategory(ThingCategoryItem); outfit.setAuxId(lookTypeEx); } diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index 8d53940c..94f5d523 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -164,9 +164,17 @@ void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPatte if(m_null) return; + if(animationPhase >= m_animationPhases) + return; + 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; Rect textureRect; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 05f296a7..f659729b 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -338,7 +338,7 @@ int Tile::getGroundSpeed() uint8 Tile::getMinimapColorByte() { - uint8 color = 0; + uint8 color = 255; // alpha if(m_minimapColor != 0) { return m_minimapColor; } @@ -542,18 +542,8 @@ bool Tile::isLookPossible() bool Tile::isClickable() { - bool hasGround = false; - bool hasOnBottom = false; - bool hasIgnoreLook = false; for(const ThingPtr& thing : m_things) { - if(thing->isGround()) - hasGround = true; - if(thing->isOnBottom()) - hasOnBottom = true; - if(thing->isIgnoreLook()) - hasIgnoreLook = true; - - if((hasGround || hasOnBottom) && !hasIgnoreLook) + if(!thing->isOnTop() && !thing->isIgnoreLook()) return true; } return false; diff --git a/src/client/uimapanchorlayout.cpp b/src/client/uimapanchorlayout.cpp new file mode 100644 index 00000000..663e3dd6 --- /dev/null +++ b/src/client/uimapanchorlayout.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010-2013 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 + +int UIPositionAnchor::getHookedPoint(const UIWidgetPtr& hookedWidget, const UIWidgetPtr& parentWidget) +{ + UIMinimapPtr minimap = hookedWidget->static_self_cast(); + 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); +} diff --git a/src/client/uimapanchorlayout.h b/src/client/uimapanchorlayout.h new file mode 100644 index 00000000..2ff3c28a --- /dev/null +++ b/src/client/uimapanchorlayout.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2013 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 + +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 diff --git a/src/client/uiminimap.cpp b/src/client/uiminimap.cpp index 98942ce6..43d10e67 100644 --- a/src/client/uiminimap.cpp +++ b/src/client/uiminimap.cpp @@ -23,16 +23,19 @@ #include "uiminimap.h" #include "minimap.h" #include "game.h" +#include "uimapanchorlayout.h" +#include "luavaluecasts.h" #include +#include "uimapanchorlayout.h" UIMinimap::UIMinimap() { - m_crossEnabled = true; m_zoom = 0; m_scale = 1.0f; m_minZoom = -5; m_maxZoom = 5; + m_layout = UIMapAnchorLayoutPtr(new UIMapAnchorLayout(static_self_cast())); } void UIMinimap::drawSelf(Fw::DrawPane drawPane) @@ -42,22 +45,18 @@ void UIMinimap::drawSelf(Fw::DrawPane drawPane) if((drawPane & Fw::ForegroundPane) == 0) return; - g_minimap.draw(getPaddingRect(), getCameraPosition(), m_scale); - - // 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); + g_minimap.draw(getPaddingRect(), getCameraPosition(), m_scale, m_color); } bool UIMinimap::setZoom(int zoom) { + if(zoom == m_zoom) + return true; + if(zoom < m_minZoom || zoom > m_maxZoom) return false; + + int oldZoom = m_zoom; m_zoom = zoom; if(m_zoom < 0) 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)); else m_scale = 1; + m_layout->update(); + + onZoomChange(zoom, oldZoom); return true; } -void UIMinimap::followCreature(const CreaturePtr& creature) +void UIMinimap::setCameraPosition(const Position& pos) { - m_followingCreature = creature; - m_cameraPosition = Position(); + Position oldPos = m_cameraPosition; + m_cameraPosition = pos; + m_layout->update(); + + onCameraPositionChange(pos, oldPos); } -void UIMinimap::setCameraPosition(const Position& pos) +bool UIMinimap::floorUp() { - m_followingCreature = nullptr; - m_cameraPosition = pos; + Position pos = getCameraPosition(); + 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 m_followingCreature->getPosition(); - else - return m_cameraPosition; + return g_minimap.getTilePosition(mousePos, getPaddingRect(), getCameraPosition(), m_scale); +} + +void UIMinimap::anchorPosition(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge anchoredEdge, const Position& hookedPosition, Fw::AnchorEdge hookedEdge) +{ + UIMapAnchorLayoutPtr layout = m_layout->static_self_cast(); + assert(layout); + layout->addPositionAnchor(anchoredWidget, anchoredEdge, hookedPosition, hookedEdge); +} + +void UIMinimap::fillPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition) +{ + UIMapAnchorLayoutPtr layout = m_layout->static_self_cast(); + assert(layout); + layout->fillPosition(anchoredWidget, hookedPosition); +} + +void UIMinimap::centerInPosition(const UIWidgetPtr& anchoredWidget, const Position& hookedPosition) +{ + UIMapAnchorLayoutPtr layout = m_layout->static_self_cast(); + 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) @@ -108,7 +153,5 @@ void UIMinimap::onStyleApply(const std::string& styleName, const OTMLNodePtr& st setMaxZoom(node->value()); else if(node->tag() == "min-zoom") setMinZoom(node->value()); - else if(node->tag() == "cross") - setCross(node->value()); } } diff --git a/src/client/uiminimap.h b/src/client/uiminimap.h index 7e713d90..aaea30f3 100644 --- a/src/client/uiminimap.h +++ b/src/client/uiminimap.h @@ -40,26 +40,32 @@ public: void setMinZoom(int minZoom) { m_minZoom = minZoom; } void setMaxZoom(int maxZoom) { m_maxZoom = maxZoom; } void setCameraPosition(const Position& pos); - void setCross(bool enable) { m_crossEnabled = enable; } - void followCreature(const CreaturePtr& creature); + bool floorUp(); + bool floorDown(); - Point getPoint(const Position& pos); - Position getPosition(const Point& mousePos); - Position getCameraPosition(); - CreaturePtr getFollowingCreature() { return m_followingCreature; } + Point getTilePoint(const Position& pos); + Rect getTileRect(const Position& pos); + Position getTilePosition(const Point& mousePos); + + Position getCameraPosition() { return m_cameraPosition; } int getMinZoom() { return m_minZoom; } int getMaxZoom() { return m_maxZoom; } int getZoom() { return m_zoom; } - bool getCross() { return m_crossEnabled; } 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: + 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); private: + void update(); + Rect m_mapArea; - bool m_crossEnabled; - CreaturePtr m_followingCreature; Position m_cameraPosition; float m_scale; int m_zoom; diff --git a/src/framework/luaengine/luavaluecasts.cpp b/src/framework/luaengine/luavaluecasts.cpp index db22ea74..ef751209 100644 --- a/src/framework/luaengine/luavaluecasts.cpp +++ b/src/framework/luaengine/luavaluecasts.cpp @@ -289,8 +289,12 @@ bool luavalue_cast(int index, OTMLNodePtr& node) g_lua.pushNil(); while(g_lua.next(index < 0 ? index-1 : index)) { std::string cnodeName; - if(!g_lua.isNumber(-2)) - cnodeName = g_lua.toString(-2); + if(g_lua.isString(-2)) { + g_lua.pushValue(-2); + cnodeName = g_lua.toString(); + g_lua.pop(); + } else + assert(g_lua.isNumber()); if(g_lua.isTable()) { OTMLNodePtr cnode; if(luavalue_cast(-1, cnode)) { diff --git a/src/framework/pch.h b/src/framework/pch.h index 2206ed1b..c8c28259 100644 --- a/src/framework/pch.h +++ b/src/framework/pch.h @@ -26,6 +26,7 @@ // common C headers #include #include +#include #include #include #include @@ -42,5 +43,8 @@ #include #include #include +#include +#include +#include #endif diff --git a/src/framework/platform/win32window.cpp b/src/framework/platform/win32window.cpp index 764ae902..5bf5519a 100644 --- a/src/framework/platform/win32window.cpp +++ b/src/framework/platform/win32window.cpp @@ -618,9 +618,12 @@ LRESULT WIN32Window::windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar case WM_SYSKEYUP: case WM_SYSKEYDOWN: { // F10 is the shortcut key to enter a windows menu, this is a workaround to get F10 working - if(wParam != VK_F10) - return DefWindowProc(hWnd, uMsg, wParam, lParam); - else { + if(wParam != VK_F10) { + if(wParam != VK_MENU && wParam != VK_LMENU && wParam != VK_RMENU) + return DefWindowProc(hWnd, uMsg, wParam, lParam); + else + return 0; + } else { if(uMsg == WM_SYSKEYUP) processKeyUp(retranslateVirtualKey(wParam, lParam)); else diff --git a/src/framework/ui/declarations.h b/src/framework/ui/declarations.h index 6df9cd72..3573acb7 100644 --- a/src/framework/ui/declarations.h +++ b/src/framework/ui/declarations.h @@ -33,6 +33,8 @@ class UIBoxLayout; class UIHorizontalLayout; class UIVerticalLayout; class UIGridLayout; +class UIAnchor; +class UIAnchorGroup; class UIAnchorLayout; class UIParticles; @@ -44,8 +46,11 @@ typedef stdext::shared_object_ptr UIBoxLayoutPtr; typedef stdext::shared_object_ptr UIHorizontalLayoutPtr; typedef stdext::shared_object_ptr UIVerticalLayoutPtr; typedef stdext::shared_object_ptr UIGridLayoutPtr; +typedef stdext::shared_object_ptr UIAnchorPtr; +typedef stdext::shared_object_ptr UIAnchorGroupPtr; typedef stdext::shared_object_ptr UIAnchorLayoutPtr; typedef std::deque UIWidgetList; +typedef std::vector UIAnchorList; #endif diff --git a/src/framework/ui/uianchorlayout.cpp b/src/framework/ui/uianchorlayout.cpp index 6ea668ab..a47326a5 100644 --- a/src/framework/ui/uianchorlayout.cpp +++ b/src/framework/ui/uianchorlayout.cpp @@ -23,11 +23,81 @@ #include "uianchorlayout.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 - for(UIAnchor& other : m_anchors) { - if(other.getAnchoredEdge() == anchor.getAnchoredEdge()) { + for(UIAnchorPtr& other : m_anchors) { + if(other->getAnchoredEdge() == anchor->getAnchoredEdge()) { other = anchor; return; } @@ -43,9 +113,12 @@ void UIAnchorLayout::addAnchor(const UIWidgetPtr& anchoredWidget, Fw::AnchorEdge assert(anchoredWidget != getParentWidget()); - UIAnchor anchor(anchoredEdge, hookedWidgetId, hookedEdge); - UIAnchorGroup& anchorGroup = m_anchorsGroups[anchoredWidget]; - anchorGroup.addAnchor(anchor); + UIAnchorPtr anchor(new UIAnchor(anchoredEdge, hookedWidgetId, 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(); @@ -86,7 +159,7 @@ void UIAnchorLayout::removeWidget(const UIWidgetPtr& 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(); if(!parentWidget) @@ -105,23 +178,13 @@ bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch bool horizontalMoved = false; // calculates new rect based on anchors - for(const UIAnchor& anchor : anchorGroup.getAnchors()) { + for(const UIAnchorPtr& anchor : anchorGroup->getAnchors()) { // skip invalid anchors - if(anchor.getHookedEdge() == Fw::AnchorNone) + if(anchor->getHookedEdge() == Fw::AnchorNone) continue; // determine hooked widget - UIWidgetPtr hookedWidget; - 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()); - } + UIWidgetPtr hookedWidget = anchor->getHookedWidget(widget, parentWidget); // skip invalid anchors if(!hookedWidget) @@ -131,61 +194,15 @@ bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch // update this hooked widget anchors auto it = m_anchorsGroups.find(hookedWidget); if(it != m_anchorsGroups.end()) { - UIAnchorGroup& hookedAnchorGroup = it->second; - if(!hookedAnchorGroup.isUpdated()) + const UIAnchorGroupPtr& hookedAnchorGroup = it->second; + if(!hookedAnchorGroup->isUpdated()) updateWidget(hookedWidget, hookedAnchorGroup, first); } } - // determine hooked widget edge point - 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; - } - } + int point = anchor->getHookedPoint(hookedWidget, parentWidget); - switch(anchor.getAnchoredEdge()) { + switch(anchor->getAnchoredEdge()) { case Fw::AnchorHorizontalCenter: newRect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight()); horizontalMoved = true; @@ -230,7 +247,7 @@ bool UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch bool changed = false; if(widget->setRect(newRect)) changed = true; - anchorGroup.setUpdated(true); + anchorGroup->setUpdated(true); return changed; } @@ -240,15 +257,15 @@ bool UIAnchorLayout::internalUpdate() // reset all anchors groups update state for(auto& it : m_anchorsGroups) { - UIAnchorGroup& anchorGroup = it.second; - anchorGroup.setUpdated(false); + const UIAnchorGroupPtr& anchorGroup = it.second; + anchorGroup->setUpdated(false); } // update all anchors for(auto& it : m_anchorsGroups) { const UIWidgetPtr& widget = it.first; - UIAnchorGroup& anchorGroup = it.second; - if(!anchorGroup.isUpdated()) { + const UIAnchorGroupPtr& anchorGroup = it.second; + if(!anchorGroup->isUpdated()) { if(updateWidget(widget, anchorGroup)) changed = true; } diff --git a/src/framework/ui/uianchorlayout.h b/src/framework/ui/uianchorlayout.h index a8c5a215..5c8010ad 100644 --- a/src/framework/ui/uianchorlayout.h +++ b/src/framework/ui/uianchorlayout.h @@ -25,30 +25,30 @@ #include "uilayout.h" -class UIAnchor +class UIAnchor : public stdext::shared_object { public: UIAnchor(Fw::AnchorEdge anchoredEdge, const std::string& hookedWidgetId, Fw::AnchorEdge hookedEdge) : m_anchoredEdge(anchoredEdge), m_hookedEdge(hookedEdge), m_hookedWidgetId(hookedWidgetId) { } Fw::AnchorEdge getAnchoredEdge() const { return m_anchoredEdge; } - std::string getHookedWidgetId() const { return m_hookedWidgetId; } 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_hookedEdge; std::string m_hookedWidgetId; }; -typedef std::vector UIAnchorList; - -class UIAnchorGroup +class UIAnchorGroup : public stdext::shared_object { public: UIAnchorGroup() : m_updated(true) { } - void addAnchor(const UIAnchor& anchor); + void addAnchor(const UIAnchorPtr& anchor); const UIAnchorList& getAnchors() { return m_anchors; } bool isUpdated() { return m_updated; } void setUpdated(bool updated) { m_updated = updated; } @@ -77,11 +77,9 @@ public: bool isUIAnchorLayout() { return true; } protected: - bool internalUpdate(); - -private: - bool updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup, UIWidgetPtr first = nullptr); - std::unordered_map m_anchorsGroups; + virtual bool internalUpdate(); + virtual bool updateWidget(const UIWidgetPtr& widget, const UIAnchorGroupPtr& anchorGroup, UIWidgetPtr first = nullptr); + std::unordered_map m_anchorsGroups; }; #endif diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 31e97e3f..d384b436 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -31,6 +31,7 @@ #include #include #include +#include UIWidget::UIWidget() { diff --git a/src/framework/ui/uiwidgetbasestyle.cpp b/src/framework/ui/uiwidgetbasestyle.cpp index 8d51a1c4..0c15286b 100644 --- a/src/framework/ui/uiwidgetbasestyle.cpp +++ b/src/framework/ui/uiwidgetbasestyle.cpp @@ -407,4 +407,4 @@ void UIWidget::setIcon(const std::string& iconFile) m_icon = g_textures.getTexture(iconFile); if(m_icon && !m_iconClipRect.isValid()) m_iconClipRect = Rect(0, 0, m_icon->getSize()); -} \ No newline at end of file +}