diff --git a/modules/core_lib/keyboard.lua b/modules/core_lib/keyboard.lua index 788aba27..ded616c7 100644 --- a/modules/core_lib/keyboard.lua +++ b/modules/core_lib/keyboard.lua @@ -1,7 +1,7 @@ Keyboard = {} -- private functions -local function translateKeyCombo(keyCombo) +function translateKeyCombo(keyCombo) if not keyCombo or #keyCombo == 0 then return nil end table.sort(keyCombo) local keyComboDesc = '' @@ -27,7 +27,7 @@ local function retranslateKeyComboDesc(keyComboDesc) return translateKeyCombo(keyCombo) end -local function determineKeyComboDesc(keyCode, keyboardModifiers) +function determineKeyComboDesc(keyCode, keyboardModifiers) local keyCombo = {} if keyCode == KeyCtrl or keyCode == KeyShift or keyCode == KeyAlt then table.insert(keyCombo, keyCode) diff --git a/modules/game/game.otmod b/modules/game/game.otmod index a23f5cdf..a32671f7 100644 --- a/modules/game/game.otmod +++ b/modules/game/game.otmod @@ -14,6 +14,7 @@ Module - game_outfit - game_containers - game_combatcontrols + - game_hotkeys onLoad: | dofile 'game' diff --git a/modules/game_hotkeys/hotkeys_manager.lua b/modules/game_hotkeys/hotkeys_manager.lua new file mode 100644 index 00000000..92e88114 --- /dev/null +++ b/modules/game_hotkeys/hotkeys_manager.lua @@ -0,0 +1,408 @@ +HotkeysManager = {} + +local hotkeysWindow +local hotkeysButton +local currentHotkeysList +local hotkeyLabelSelectedOnList +local currentItemPreview +local hotkeyList = {} +local itemWidget + +HOTKEY_MANAGER_USEONSELF = 1 +HOTKEY_MANAGER_USEONTARGET = 2 +HOTKEY_MANAGER_USEWITH = 3 + +local hotkeyColors = { + text = '#888888', + textAutoSend = '#FFFFFF', + itemUse = '#8888FF', + itemUseSelf = '#00FF00', + itemUseTarget = '#FF0000', + itemUseWith = '#CC0000', +} + +-- public functions +function HotkeysManager.init() + hotkeysWindow = displayUI('hotkeys_manager.otui') + + hotkeysWindow:setVisible(false) + hotkeysButton = TopMenu.addButton('hotkeysButton', 'Hotkeys (Ctrl+K)', '/game_hotkeys/icon.png', HotkeysManager.toggle) + Keyboard.bindKeyDown('Ctrl+K', HotkeysManager.toggle) + + currentHotkeysList = hotkeysWindow:getChildById('currentHotkeys') + currentItemPreview = hotkeysWindow:getChildById('itemPreview') + + itemWidget = createWidget('UIItem') + itemWidget:setVirtual(true) + itemWidget:setVisible(false) + itemWidget:setFocusable(false) + + HotkeysManager.load() +end + +function HotkeysManager.load() + local hotkeySettings = Settings.getNode('HotkeysManager') + + for i, v in pairs(hotkeySettings) do + HotkeysManager.addKeyCombo(nil, v.keyCombo, v) + end + + HotkeysManager.checkSelectedHotkey() +end + +function HotkeysManager.save() + local hotkeySettings = {} + for i = 1, currentHotkeysList:getChildCount() do + local child = currentHotkeysList:getChildByIndex(i) + table.insert(hotkeySettings, {keyCombo = child.keyCombo, + autoSend = child.autoSend, + itemId = child.itemId, + useType = child.useType, + value = child.value}) + end + Settings.setNode('HotkeysManager', hotkeySettings) +end + +function HotkeysManager.terminate() + Keyboard.unbindKeyDown('Ctrl+K') + HotkeysManager.save() + hotkeysWindow:destroy() + hotkeysWindow = nil + hotkeysButton:destroy() + hotkeysButton = nil +end + +function HotkeysManager.toggle() + if hotkeysWindow:isVisible() then + HotkeysManager.hide() + else + HotkeysManager.show() + end +end + +function HotkeysManager.show() + if Game.isOnline() then + hotkeysWindow:grabKeyboard() + hotkeysWindow:show() + hotkeysWindow:lock() + end +end + +function HotkeysManager.hide() + hotkeysWindow:ungrabKeyboard() + hotkeysWindow:unlock() + hotkeysWindow:hide() +end + +-- private functions +function HotkeysManager.onChooseItemMouseRelease(self, mousePosition, mouseButton) + local item = nil + if mouseButton == MouseLeftButton then + local clickedWidget = Game.gameUi:recursiveGetChildByPos(mousePosition) + if clickedWidget then + if clickedWidget:getClassName() == 'UIMap' then + local tile = clickedWidget:getTile(mousePosition) + if tile then + local thing = tile:getTopMoveThing() + if thing then + local uiitem = thing:asItem() + if uiitem then + item = uiitem:getItem() + end + end + end + elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then + item = clickedWidget:getItem() + end + end + end + + if item then + currentItemPreview:setItemId(item:getId()) + hotkeyLabelSelectedOnList.itemId = item:getId() + HotkeysManager.changeUseType(HOTKEY_MANAGER_USEONSELF) + HotkeysManager.checkSelectedHotkey() + HotkeysManager:show() + end + + Mouse.restoreCursor() + self:ungrabMouse() + self:destroy() +end + +function HotkeysManager.startChooseItem() + mouseGrabberWidget = createWidget('UIWidget') + mouseGrabberWidget:setVisible(false) + mouseGrabberWidget:setFocusable(false) + + connect(mouseGrabberWidget, { onMouseRelease = HotkeysManager.onChooseItemMouseRelease }) + + mouseGrabberWidget:grabMouse() + Mouse.setTargetCursor() + + HotkeysManager:hide() +end + +function HotkeysManager.clearObject() + hotkeyLabelSelectedOnList.itemId = nil + currentItemPreview:setItemId(0) --TODO: call :clear() after get it binded + HotkeysManager.changeUseType(HOTKEY_MANAGER_USEONSELF) + HotkeysManager.sendAutomatically(false) + hotkeyLabelSelectedOnList:setText(hotkeyLabelSelectedOnList.keyCombo .. ': ') + + HotkeysManager.checkSelectedHotkey() +end + +function HotkeysManager.addHotkey() + local messageBox = createWidget('MainWindow', hotkeysWindow) + messageBox:setId('assignWindow') + messageBox:setText('Button Assign') + messageBox:setWidth(420) + messageBox:setHeight(140) + messageBox:setDragable(false) + + local label = createWidget('Label', messageBox) + label:setText('Please, press the key you wish to add onto your hotkeys manager') + label:resizeToText() + label:addAnchor(AnchorHorizontalCenter, 'parent', AnchorHorizontalCenter) + label:addAnchor(AnchorTop, 'parent', AnchorTop) + + local label = createWidget('Label', messageBox) + label:setId('comboPreview') + label:setText('Current hotkey to add: None') + label.keyCombo = '' + label:resizeToText() + label:addAnchor(AnchorHorizontalCenter, 'parent', AnchorHorizontalCenter) + label:addAnchor(AnchorTop, 'prev', AnchorBottom) + label:setMarginTop(20) + + local button = createWidget('Button', messageBox) + button:setId('cancelButton') + button:setText('Cancel') + button:setWidth(64) + button:addAnchor(AnchorBottom, 'parent', AnchorBottom) + button:addAnchor(AnchorRight, 'parent', AnchorRight) + button.onClick = function () messageBox:destroy() end + + local button = createWidget('Button', messageBox) + button:setId('addButton') + button:setText('Add') + button:setWidth(64) + button:disable() + button:addAnchor(AnchorBottom, 'cancelButton', AnchorBottom) + button:addAnchor(AnchorRight, 'cancelButton', AnchorLeft) + button:setMarginRight(10) + button.onClick = function () HotkeysManager.addKeyCombo(messageBox, label.keyCombo) end + + connect(messageBox, { onKeyDown = HotkeysManager.hotkeyCapture }, true) +end + +function HotkeysManager.addKeyCombo(messageBox, keyCombo, keySettings) + if currentHotkeysList:getChildById(keyCombo) == nil then + local label = createWidget('HotkeyListLabel', currentHotkeysList) + label:setId(keyCombo) + label:setColor(hotkeyColors.text) + label:setText(keyCombo..': ') + if keySettings then + hotkeyLabelSelectedOnList = label + label.keyCombo = keyCombo + HotkeysManager.sendAutomatically(keySettings.autoSend) + label.itemId = keySettings.itemId + currentItemPreview:setItemId(keySettings.itemId) + HotkeysManager.changeUseType(tonumber(keySettings.useType)) + label.value = keySettings.value + else + label.keyCombo = keyCombo + label.autoSend = false + label.itemId = nil + label.useType = HOTKEY_MANAGER_USEONSELF + label.value = '' + end + connect(label, { onClick = function () HotkeysManager.checkSelectedHotkey() return true end } ) + + HotkeysManager.checkSelectedHotkey() + + hotkeyList[keyCombo] = label + Keyboard.bindKeyDown(keyCombo, function () HotkeysManager.call(keyCombo) end) + end + + if messageBox then + messageBox:destroy() + end +end + +function HotkeysManager.call(keyCombo) + if Game.isOnline() then + local hotKey = hotkeyList[keyCombo] + if hotKey.itemId == nil and hotKey.value ~= '' then + if hotKey.autoSend then + Game.talk(hotKey.value) + else + rootWidget:getChildById('gameRootInterface'):getChildById('gameBottomPanel'):getChildById('consolePanel'):getChildById('consoleLineEdit'):setText(hotKey.value) + end + elseif hotKey.itemId ~= nil then + if hotKey.useType == HOTKEY_MANAGER_USEONSELF then + Game.useInventoryItem(hotKey.itemId, Game.getLocalPlayer()) + elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then + local attackingCreature = Game.getLocalPlayer():getAttackingCreature() + if attackingCreature then + Game.useInventoryItem(hotKey.itemId, attackingCreature) + end + elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then + itemWidget:setItemId(hotKey.itemId) + Game.startUseWith(itemWidget:getItem()) + end + end + end +end + +function HotkeysManager.checkSelectedHotkey() + hotkeyLabelSelectedOnList = nil + for i = 1, currentHotkeysList:getChildCount() do + local child = currentHotkeysList:getChildByIndex(i) + if child:isFocused() then + hotkeyLabelSelectedOnList = child + break -- stop looping + end + end + + if hotkeyLabelSelectedOnList ~= nil then + hotkeysWindow:getChildById('removeHotkey'):enable() + + if hotkeyLabelSelectedOnList.itemId == nil then + hotkeysWindow:getChildById('hotkeyText'):enable() + hotkeysWindow:getChildById('hotKeyTextLabel'):enable() + hotkeysWindow:getChildById('hotkeyText'):setText(hotkeyLabelSelectedOnList.value) + + if hotkeyLabelSelectedOnList.value ~= '' then + hotkeysWindow:getChildById('sendAutomatically'):enable() + else + hotkeysWindow:getChildById('sendAutomatically'):disable() + end + + hotkeysWindow:getChildById('selectObjectButton'):enable() + hotkeysWindow:getChildById('clearObjectButton'):disable() + + currentItemPreview:setItemId(0) + else + hotkeysWindow:getChildById('hotkeyText'):clearText() + hotkeysWindow:getChildById('hotkeyText'):disable() + hotkeysWindow:getChildById('hotKeyTextLabel'):disable() + hotkeysWindow:getChildById('sendAutomatically'):disable() + + hotkeysWindow:getChildById('selectObjectButton'):disable() + hotkeysWindow:getChildById('clearObjectButton'):enable() + + currentItemPreview:setItemId(hotkeyLabelSelectedOnList.itemId) + end + HotkeysManager.changeUseType(hotkeyLabelSelectedOnList.useType) + else + hotkeysWindow:getChildById('hotkeyText'):clearText() + hotkeysWindow:getChildById('removeHotkey'):disable() + hotkeysWindow:getChildById('hotkeyText'):disable() + hotkeysWindow:getChildById('sendAutomatically'):disable() + hotkeysWindow:getChildById('sendAutomatically'):setChecked(false) + + currentItemPreview:setItemId(0) + hotkeysWindow:getChildById('selectObjectButton'):disable() + hotkeysWindow:getChildById('clearObjectButton'):disable() + hotkeysWindow:getChildById('useOnSelf'):disable() + hotkeysWindow:getChildById('useOnTarget'):disable() + hotkeysWindow:getChildById('useWith'):disable() + hotkeysWindow:getChildById('useOnSelf'):setChecked(false) + hotkeysWindow:getChildById('useOnTarget'):setChecked(false) + hotkeysWindow:getChildById('useWith'):setChecked(false) + end +end + +function HotkeysManager.changeUseType(useType, checked) + if checked == nil or checked then + hotkeyLabelSelectedOnList.useType = useType + if hotkeyLabelSelectedOnList.itemId ~= nil and currentItemPreview:getItem():isMultiUse() then + hotkeysWindow:getChildById('useOnSelf'):enable() + hotkeysWindow:getChildById('useOnTarget'):enable() + hotkeysWindow:getChildById('useWith'):enable() + + if useType == HOTKEY_MANAGER_USEONSELF then + hotkeyLabelSelectedOnList:setText(hotkeyLabelSelectedOnList.keyCombo .. ': (use object on yourself)') + hotkeyLabelSelectedOnList:setColor(hotkeyColors.itemUseSelf) + hotkeysWindow:getChildById('useOnSelf'):setChecked(true) + hotkeysWindow:getChildById('useOnTarget'):setChecked(false) + hotkeysWindow:getChildById('useWith'):setChecked(false) + elseif useType == HOTKEY_MANAGER_USEONTARGET then + hotkeyLabelSelectedOnList:setText(hotkeyLabelSelectedOnList.keyCombo .. ': (use object on target)') + hotkeyLabelSelectedOnList:setColor(hotkeyColors.itemUseTarget) + hotkeysWindow:getChildById('useOnSelf'):setChecked(false) + hotkeysWindow:getChildById('useOnTarget'):setChecked(true) + hotkeysWindow:getChildById('useWith'):setChecked(false) + elseif useType == HOTKEY_MANAGER_USEWITH then + hotkeyLabelSelectedOnList:setText(hotkeyLabelSelectedOnList.keyCombo .. ': (use object with crosshair)') + hotkeyLabelSelectedOnList:setColor(hotkeyColors.itemUseWith) + + hotkeysWindow:getChildById('useOnSelf'):setChecked(false) + hotkeysWindow:getChildById('useOnTarget'):setChecked(false) + hotkeysWindow:getChildById('useWith'):setChecked(true) + end + elseif hotkeyLabelSelectedOnList.itemId ~= nil and not currentItemPreview:getItem():isMultiUse() then + hotkeysWindow:getChildById('useOnSelf'):disable() + hotkeysWindow:getChildById('useOnTarget'):disable() + hotkeysWindow:getChildById('useWith'):disable() + + hotkeyLabelSelectedOnList:setText(hotkeyLabelSelectedOnList.keyCombo .. ': (use object)') + hotkeyLabelSelectedOnList:setColor(hotkeyColors.itemUse) + + hotkeysWindow:getChildById('useOnSelf'):setChecked(false) + hotkeysWindow:getChildById('useOnTarget'):setChecked(false) + hotkeysWindow:getChildById('useWith'):setChecked(false) + else + hotkeysWindow:getChildById('useOnSelf'):disable() + hotkeysWindow:getChildById('useOnTarget'):disable() + hotkeysWindow:getChildById('useWith'):disable() + + hotkeysWindow:getChildById('useOnSelf'):setChecked(false) + hotkeysWindow:getChildById('useOnTarget'):setChecked(false) + hotkeysWindow:getChildById('useWith'):setChecked(false) + end + end +end + +function HotkeysManager.removeHotkey() + if hotkeyLabelSelectedOnList ~= nil then + hotkeyList[hotkeyLabelSelectedOnList.keyCombo] = nil + Keyboard.unbindKeyDown(hotkeyLabelSelectedOnList.keyCombo) + hotkeyLabelSelectedOnList:destroy() + HotkeysManager.checkSelectedHotkey() + end +end + +function HotkeysManager.onHotkeyTextChange(id, value) + hotkeyLabelSelectedOnList:setText(hotkeyLabelSelectedOnList.keyCombo ..': ' .. value) + hotkeyLabelSelectedOnList.value = value + + if value ~= '' then + hotkeysWindow:getChildById('sendAutomatically'):enable() + else + hotkeysWindow:getChildById('sendAutomatically'):disable() + hotkeysWindow:getChildById('sendAutomatically'):setChecked(false) + end +end + +function HotkeysManager.sendAutomatically(value) + hotkeyLabelSelectedOnList.autoSend = value + if value then + hotkeyLabelSelectedOnList:setColor(hotkeyColors.autoSend) + else + hotkeyLabelSelectedOnList:setColor(hotkeyColors.text) + end +end + +function HotkeysManager.hotkeyCapture(widget, keyCode, keyboardModifiers) + local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers) + local comboPreview = hotkeysWindow:getChildById('assignWindow'):getChildById('comboPreview') + comboPreview:setText('Current hotkey to add: '.. keyCombo) + comboPreview.keyCombo = keyCombo + comboPreview:resizeToText() + hotkeysWindow:getChildById('assignWindow'):getChildById('addButton'):enable() + + return true +end \ No newline at end of file diff --git a/modules/game_hotkeys/hotkeys_manager.otmod b/modules/game_hotkeys/hotkeys_manager.otmod new file mode 100644 index 00000000..94cba2c8 --- /dev/null +++ b/modules/game_hotkeys/hotkeys_manager.otmod @@ -0,0 +1,15 @@ +Module + name: game_hotkeys + description: Manage client hotkeys + author: OTClient team + website: https://github.com/edubart/otclient + + dependencies: + - client_tibiafiles + + onLoad: | + require 'hotkeys_manager' + HotkeysManager.init() + + onUnload: | + HotkeysManager.terminate() \ No newline at end of file diff --git a/modules/game_hotkeys/hotkeys_manager.otui b/modules/game_hotkeys/hotkeys_manager.otui new file mode 100644 index 00000000..d2da58b0 --- /dev/null +++ b/modules/game_hotkeys/hotkeys_manager.otui @@ -0,0 +1,159 @@ +HotkeyListLabel < UILabel + font: verdana-11px-monochrome + background-color: alpha + text-offset: 2 0 + focusable: true + phantom: false + + $focus: + background-color: #ffffff22 + +MainWindow + id: hotkeysWindow + text: Hotkeys + size: 340 460 + + @onEnter: HotkeysManager.hide() + @onEscape: HotkeysManager.hide() + + LargerLabel + id: currentHotkeysLabel + text: Current hotkeys: + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + TextList + id: currentHotkeys + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + height: 150 + margin-top: 2 + padding: 1 + focusable: false + + LargerLabel + text: Manage hotkeys: + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + margin-top: 10 + + Button + id: addHotkey + text: Add + width: 64 + anchors.left: parent.left + anchors.top: prev.bottom + margin-top: 2 + @onClick: HotkeysManager.addHotkey() + + Button + id: removeHotkey + text: Remove + width: 64 + enabled: false + anchors.left: prev.right + anchors.top: prev.top + margin-left: 10 + @onClick: HotkeysManager.removeHotkey() + + LargerLabel + id: hotKeyTextLabel + text: Edit hotkey text: + enable: false + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + margin-top: 20 + + LineEdit + id: hotkeyText + enabled: false + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + margin-bottom: 2 + @onTextChange: HotkeysManager.onHotkeyTextChange(self:getId(), self:getText()) + + CheckBox + id: sendAutomatically + text: Send automatically + anchors.left: parent.left + anchors.right: parent.right + anchors.top: prev.bottom + enabled:false + margin-top: 10 + @onCheckChange: HotkeysManager.sendAutomatically(self:isChecked()) + + Item + id: itemPreview + anchors.left: parent.left + anchors.top: prev.bottom + margin-top: 10 + virtual: true + + Button + id: selectObjectButton + text: Select object + width: 128 + enabled: false + anchors.left: prev.right + anchors.top: prev.top + margin-left: 10 + @onClick: HotkeysManager.startChooseItem() + + Button + id: clearObjectButton + text: Clear object + width: 128 + enabled: false + anchors.left: prev.left + anchors.right: prev.right + anchors.top: prev.bottom + margin-top: 2 + @onClick: HotkeysManager.clearObject() + + ButtonBox + id: useOnSelf + text: Use on yourself + width: 128 + enabled: false + anchors.left: selectObjectButton.right + anchors.right: parent.right + anchors.top: selectObjectButton.top + checked: false + margin-left: 10 + @onCheckChange: HotkeysManager.changeUseType(HOTKEY_MANAGER_USEONSELF, self:isChecked()) + + ButtonBox + id: useOnTarget + text: Use on target + width: 128 + enabled: false + anchors.left: prev.left + anchors.right: parent.right + anchors.top: prev.bottom + checked: false + margin-top: 2 + @onCheckChange: HotkeysManager.changeUseType(HOTKEY_MANAGER_USEONTARGET, self:isChecked()) + + ButtonBox + id: useWith + text: With crosshair + width: 128 + enabled: false + anchors.left: prev.left + anchors.right: parent.right + anchors.top: prev.bottom + checked: false + margin-top: 2 + @onCheckChange: HotkeysManager.changeUseType(HOTKEY_MANAGER_USEWITH, self:isChecked()) + + Button + text: Close + width: 64 + anchors.right: parent.right + anchors.bottom: parent.bottom + @onClick: HotkeysManager.hide() \ No newline at end of file diff --git a/modules/game_hotkeys/icon.png b/modules/game_hotkeys/icon.png new file mode 100644 index 00000000..9f0c25ea Binary files /dev/null and b/modules/game_hotkeys/icon.png differ