No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

hotkeys_manager.lua 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. HOTKEY_MANAGER_USE = nil
  2. HOTKEY_MANAGER_USEONSELF = 1
  3. HOTKEY_MANAGER_USEONTARGET = 2
  4. HOTKEY_MANAGER_USEWITH = 3
  5. HotkeyColors = {
  6. text = '#888888',
  7. textAutoSend = '#FFFFFF',
  8. itemUse = '#8888FF',
  9. itemUseSelf = '#00FF00',
  10. itemUseTarget = '#FF0000',
  11. itemUseWith = '#F5B325',
  12. }
  13. hotkeysManagerLoaded = false
  14. hotkeysWindow = nil
  15. hotkeysButton = nil
  16. currentHotkeyLabel = nil
  17. currentItemPreview = nil
  18. itemWidget = nil
  19. addHotkeyButton = nil
  20. removeHotkeyButton = nil
  21. hotkeyText = nil
  22. hotKeyTextLabel = nil
  23. sendAutomatically = nil
  24. selectObjectButton = nil
  25. clearObjectButton = nil
  26. useOnSelf = nil
  27. useOnTarget = nil
  28. useWith = nil
  29. defaultComboKeys = nil
  30. perServer = true
  31. perCharacter = true
  32. mouseGrabberWidget = nil
  33. useRadioGroup = nil
  34. currentHotkeys = nil
  35. boundCombosCallback = {}
  36. hotkeysList = {}
  37. lastHotkeyTime = g_clock.millis()
  38. -- public functions
  39. function init()
  40. hotkeysButton = modules.client_topmenu.addLeftGameButton('hotkeysButton', tr('Hotkeys') .. ' (Ctrl+K)', '/images/topbuttons/hotkeys', toggle)
  41. g_keyboard.bindKeyDown('Ctrl+K', toggle)
  42. hotkeysWindow = g_ui.displayUI('hotkeys_manager')
  43. hotkeysWindow:setVisible(false)
  44. currentHotkeys = hotkeysWindow:getChildById('currentHotkeys')
  45. currentItemPreview = hotkeysWindow:getChildById('itemPreview')
  46. addHotkeyButton = hotkeysWindow:getChildById('addHotkeyButton')
  47. removeHotkeyButton = hotkeysWindow:getChildById('removeHotkeyButton')
  48. hotkeyText = hotkeysWindow:getChildById('hotkeyText')
  49. hotKeyTextLabel = hotkeysWindow:getChildById('hotKeyTextLabel')
  50. sendAutomatically = hotkeysWindow:getChildById('sendAutomatically')
  51. selectObjectButton = hotkeysWindow:getChildById('selectObjectButton')
  52. clearObjectButton = hotkeysWindow:getChildById('clearObjectButton')
  53. useOnSelf = hotkeysWindow:getChildById('useOnSelf')
  54. useOnTarget = hotkeysWindow:getChildById('useOnTarget')
  55. useWith = hotkeysWindow:getChildById('useWith')
  56. useRadioGroup = UIRadioGroup.create()
  57. useRadioGroup:addWidget(useOnSelf)
  58. useRadioGroup:addWidget(useOnTarget)
  59. useRadioGroup:addWidget(useWith)
  60. useRadioGroup.onSelectionChange = function(self, selected) onChangeUseType(selected) end
  61. mouseGrabberWidget = g_ui.createWidget('UIWidget')
  62. mouseGrabberWidget:setVisible(false)
  63. mouseGrabberWidget:setFocusable(false)
  64. mouseGrabberWidget.onMouseRelease = onChooseItemMouseRelease
  65. currentHotkeys.onChildFocusChange = function(self, hotkeyLabel) onSelectHotkeyLabel(hotkeyLabel) end
  66. g_keyboard.bindKeyPress('Down', function() currentHotkeys:focusNextChild(KeyboardFocusReason) end, hotkeysWindow)
  67. g_keyboard.bindKeyPress('Up', function() currentHotkeys:focusPreviousChild(KeyboardFocusReason) end, hotkeysWindow)
  68. connect(g_game, {
  69. onGameStart = online,
  70. onGameEnd = offline
  71. })
  72. load()
  73. end
  74. function terminate()
  75. disconnect(g_game, {
  76. onGameStart = online,
  77. onGameEnd = offline
  78. })
  79. g_keyboard.unbindKeyDown('Ctrl+K')
  80. unload()
  81. hotkeysWindow:destroy()
  82. hotkeysButton:destroy()
  83. mouseGrabberWidget:destroy()
  84. end
  85. function configure(savePerServer, savePerCharacter)
  86. perServer = savePerServer
  87. perCharacter = savePerCharacter
  88. reload()
  89. end
  90. function online()
  91. reload()
  92. hide()
  93. end
  94. function offline()
  95. unload()
  96. hide()
  97. end
  98. function show()
  99. if not g_game.isOnline() then
  100. return
  101. end
  102. hotkeysWindow:show()
  103. hotkeysWindow:raise()
  104. hotkeysWindow:focus()
  105. end
  106. function hide()
  107. hotkeysWindow:hide()
  108. end
  109. function toggle()
  110. if not hotkeysWindow:isVisible() then
  111. show()
  112. else
  113. hide()
  114. end
  115. end
  116. function ok()
  117. save()
  118. hide()
  119. end
  120. function cancel()
  121. reload()
  122. hide()
  123. end
  124. function load(forceDefaults)
  125. hotkeysManagerLoaded = false
  126. local hotkeySettings = g_settings.getNode('game_hotkeys')
  127. local hotkeys = {}
  128. if not table.empty(hotkeySettings) then hotkeys = hotkeySettings end
  129. if perServer and not table.empty(hotkeys) then hotkeys = hotkeys[G.host] end
  130. if perCharacter and not table.empty(hotkeys) then hotkeys = hotkeys[g_game.getCharacterName()] end
  131. hotkeyList = {}
  132. if not forceDefaults then
  133. if not table.empty(hotkeys) then
  134. for keyCombo, setting in pairs(hotkeys) do
  135. keyCombo = tostring(keyCombo)
  136. addKeyCombo(keyCombo, setting)
  137. hotkeyList[keyCombo] = setting
  138. end
  139. end
  140. end
  141. if currentHotkeys:getChildCount() == 0 then
  142. loadDefautComboKeys()
  143. end
  144. hotkeysManagerLoaded = true
  145. end
  146. function unload()
  147. for keyCombo,callback in pairs(boundCombosCallback) do
  148. g_keyboard.unbindKeyPress(keyCombo, callback)
  149. end
  150. boundCombosCallback = {}
  151. currentHotkeys:destroyChildren()
  152. currentHotkeyLabel = nil
  153. updateHotkeyForm(true)
  154. hotkeyList = {}
  155. end
  156. function reset()
  157. unload()
  158. load(true)
  159. end
  160. function reload()
  161. unload()
  162. load()
  163. end
  164. function save()
  165. local hotkeySettings = g_settings.getNode('game_hotkeys') or {}
  166. local hotkeys = hotkeySettings
  167. if perServer then
  168. if not hotkeys[G.host] then
  169. hotkeys[G.host] = {}
  170. end
  171. hotkeys = hotkeys[G.host]
  172. end
  173. if perCharacter then
  174. local char = g_game.getCharacterName()
  175. if not hotkeys[char] then
  176. hotkeys[char] = {}
  177. end
  178. hotkeys = hotkeys[char]
  179. end
  180. table.clear(hotkeys)
  181. for _,child in pairs(currentHotkeys:getChildren()) do
  182. hotkeys[child.keyCombo] = {
  183. autoSend = child.autoSend,
  184. itemId = child.itemId,
  185. subType = child.subType,
  186. useType = child.useType,
  187. value = child.value
  188. }
  189. end
  190. hotkeyList = hotkeys
  191. g_settings.setNode('game_hotkeys', hotkeySettings)
  192. g_settings.save()
  193. end
  194. function loadDefautComboKeys()
  195. if not defaultComboKeys then
  196. for i=1,12 do
  197. addKeyCombo('F' .. i)
  198. end
  199. for i=1,4 do
  200. addKeyCombo('Shift+F' .. i)
  201. end
  202. else
  203. for keyCombo, keySettings in pairs(defaultComboKeys) do
  204. addKeyCombo(keyCombo, keySettings)
  205. end
  206. end
  207. end
  208. function setDefaultComboKeys(combo)
  209. defaultComboKeys = combo
  210. end
  211. function onChooseItemMouseRelease(self, mousePosition, mouseButton)
  212. local item = nil
  213. if mouseButton == MouseLeftButton then
  214. local clickedWidget = modules.game_interface.getRootPanel():recursiveGetChildByPos(mousePosition, false)
  215. if clickedWidget then
  216. if clickedWidget:getClassName() == 'UIGameMap' then
  217. local tile = clickedWidget:getTile(mousePosition)
  218. if tile then
  219. local thing = tile:getTopMoveThing()
  220. if thing and thing:isItem() then
  221. item = thing
  222. end
  223. end
  224. elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then
  225. item = clickedWidget:getItem()
  226. end
  227. end
  228. end
  229. if item and currentHotkeyLabel then
  230. currentHotkeyLabel.itemId = item:getId()
  231. if item:isFluidContainer() then
  232. currentHotkeyLabel.subType = item:getSubType()
  233. end
  234. if item:isMultiUse() then
  235. currentHotkeyLabel.useType = HOTKEY_MANAGER_USEWITH
  236. else
  237. currentHotkeyLabel.useType = HOTKEY_MANAGER_USE
  238. end
  239. currentHotkeyLabel.value = nil
  240. currentHotkeyLabel.autoSend = false
  241. updateHotkeyLabel(currentHotkeyLabel)
  242. updateHotkeyForm(true)
  243. end
  244. show()
  245. g_mouse.popCursor('target')
  246. self:ungrabMouse()
  247. return true
  248. end
  249. function startChooseItem()
  250. if g_ui.isMouseGrabbed() then return end
  251. mouseGrabberWidget:grabMouse()
  252. g_mouse.pushCursor('target')
  253. hide()
  254. end
  255. function clearObject()
  256. currentHotkeyLabel.itemId = nil
  257. currentHotkeyLabel.subType = nil
  258. currentHotkeyLabel.useType = nil
  259. currentHotkeyLabel.autoSend = nil
  260. currentHotkeyLabel.value = nil
  261. updateHotkeyLabel(currentHotkeyLabel)
  262. updateHotkeyForm(true)
  263. end
  264. function addHotkey()
  265. local assignWindow = g_ui.createWidget('HotkeyAssignWindow', rootWidget)
  266. assignWindow:grabKeyboard()
  267. local comboLabel = assignWindow:getChildById('comboPreview')
  268. comboLabel.keyCombo = ''
  269. assignWindow.onKeyDown = hotkeyCapture
  270. end
  271. function addKeyCombo(keyCombo, keySettings, focus)
  272. if keyCombo == nil or #keyCombo == 0 then return end
  273. if not keyCombo then return end
  274. local hotkeyLabel = currentHotkeys:getChildById(keyCombo)
  275. if not hotkeyLabel then
  276. hotkeyLabel = g_ui.createWidget('HotkeyListLabel')
  277. hotkeyLabel:setId(keyCombo)
  278. local children = currentHotkeys:getChildren()
  279. children[#children+1] = hotkeyLabel
  280. table.sort(children, function(a,b)
  281. if a:getId():len() < b:getId():len() then
  282. return true
  283. elseif a:getId():len() == b:getId():len() then
  284. return a:getId() < b:getId()
  285. else
  286. return false
  287. end
  288. end)
  289. for i=1,#children do
  290. if children[i] == hotkeyLabel then
  291. currentHotkeys:insertChild(i, hotkeyLabel)
  292. break
  293. end
  294. end
  295. if keySettings then
  296. currentHotkeyLabel = hotkeyLabel
  297. hotkeyLabel.keyCombo = keyCombo
  298. hotkeyLabel.autoSend = toboolean(keySettings.autoSend)
  299. hotkeyLabel.itemId = tonumber(keySettings.itemId)
  300. hotkeyLabel.subType = tonumber(keySettings.subType)
  301. hotkeyLabel.useType = tonumber(keySettings.useType)
  302. if keySettings.value then hotkeyLabel.value = tostring(keySettings.value) end
  303. else
  304. hotkeyLabel.keyCombo = keyCombo
  305. hotkeyLabel.autoSend = false
  306. hotkeyLabel.itemId = nil
  307. hotkeyLabel.subType = nil
  308. hotkeyLabel.useType = nil
  309. hotkeyLabel.value = ''
  310. end
  311. updateHotkeyLabel(hotkeyLabel)
  312. boundCombosCallback[keyCombo] = function() doKeyCombo(keyCombo) end
  313. g_keyboard.bindKeyPress(keyCombo, boundCombosCallback[keyCombo])
  314. end
  315. if focus then
  316. currentHotkeys:focusChild(hotkeyLabel)
  317. currentHotkeys:ensureChildVisible(hotkeyLabel)
  318. updateHotkeyForm(true)
  319. end
  320. end
  321. function doKeyCombo(keyCombo)
  322. if not g_game.isOnline() then return end
  323. local hotKey = hotkeyList[keyCombo]
  324. if not hotKey then return end
  325. if g_clock.millis() - lastHotkeyTime < modules.client_options.getOption('hotkeyDelay') then
  326. return
  327. end
  328. lastHotkeyTime = g_clock.millis()
  329. if hotKey.itemId == nil then
  330. if not hotKey.value or #hotKey.value == 0 then return end
  331. if hotKey.autoSend then
  332. modules.game_console.sendMessage(hotKey.value)
  333. else
  334. modules.game_console.setTextEditText(hotKey.value)
  335. end
  336. elseif hotKey.useType == HOTKEY_MANAGER_USE then
  337. if g_game.getClientVersion() < 780 or hotKey.subType then
  338. local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
  339. if item then
  340. g_game.use(item)
  341. end
  342. else
  343. g_game.useInventoryItem(hotKey.itemId)
  344. end
  345. elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then
  346. if g_game.getClientVersion() < 780 or hotKey.subType then
  347. local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
  348. if item then
  349. g_game.useWith(item, g_game.getLocalPlayer())
  350. end
  351. else
  352. g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer())
  353. end
  354. elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then
  355. local attackingCreature = g_game.getAttackingCreature()
  356. if not attackingCreature then
  357. local item = Item.create(hotKey.itemId)
  358. if g_game.getClientVersion() < 780 or hotKey.subType then
  359. local tmpItem = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
  360. if not tmpItem then return end
  361. item = tmpItem
  362. end
  363. modules.game_interface.startUseWith(item)
  364. return
  365. end
  366. if not attackingCreature:getTile() then return end
  367. if g_game.getClientVersion() < 780 or hotKey.subType then
  368. local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
  369. if item then
  370. g_game.useWith(item, attackingCreature)
  371. end
  372. else
  373. g_game.useInventoryItemWith(hotKey.itemId, attackingCreature)
  374. end
  375. elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then
  376. local item = Item.create(hotKey.itemId)
  377. if g_game.getClientVersion() < 780 or hotKey.subType then
  378. local tmpItem = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
  379. if not tmpItem then return true end
  380. item = tmpItem
  381. end
  382. modules.game_interface.startUseWith(item)
  383. end
  384. end
  385. function updateHotkeyLabel(hotkeyLabel)
  386. if not hotkeyLabel then return end
  387. if hotkeyLabel.useType == HOTKEY_MANAGER_USEONSELF then
  388. hotkeyLabel:setText(tr('%s: (use object on yourself)', hotkeyLabel.keyCombo))
  389. hotkeyLabel:setColor(HotkeyColors.itemUseSelf)
  390. elseif hotkeyLabel.useType == HOTKEY_MANAGER_USEONTARGET then
  391. hotkeyLabel:setText(tr('%s: (use object on target)', hotkeyLabel.keyCombo))
  392. hotkeyLabel:setColor(HotkeyColors.itemUseTarget)
  393. elseif hotkeyLabel.useType == HOTKEY_MANAGER_USEWITH then
  394. hotkeyLabel:setText(tr('%s: (use object with crosshair)', hotkeyLabel.keyCombo))
  395. hotkeyLabel:setColor(HotkeyColors.itemUseWith)
  396. elseif hotkeyLabel.itemId ~= nil then
  397. hotkeyLabel:setText(tr('%s: (use object)', hotkeyLabel.keyCombo))
  398. hotkeyLabel:setColor(HotkeyColors.itemUse)
  399. else
  400. local text = hotkeyLabel.keyCombo .. ': '
  401. if hotkeyLabel.value then
  402. text = text .. hotkeyLabel.value
  403. end
  404. hotkeyLabel:setText(text)
  405. if hotkeyLabel.autoSend then
  406. hotkeyLabel:setColor(HotkeyColors.autoSend)
  407. else
  408. hotkeyLabel:setColor(HotkeyColors.text)
  409. end
  410. end
  411. end
  412. function updateHotkeyForm(reset)
  413. if currentHotkeyLabel then
  414. removeHotkeyButton:enable()
  415. if currentHotkeyLabel.itemId ~= nil then
  416. hotkeyText:clearText()
  417. hotkeyText:disable()
  418. hotKeyTextLabel:disable()
  419. sendAutomatically:setChecked(false)
  420. sendAutomatically:disable()
  421. selectObjectButton:disable()
  422. clearObjectButton:enable()
  423. currentItemPreview:setItemId(currentHotkeyLabel.itemId)
  424. if currentHotkeyLabel.subType then
  425. currentItemPreview:setItemSubType(currentHotkeyLabel.subType)
  426. end
  427. if currentItemPreview:getItem():isMultiUse() then
  428. useOnSelf:enable()
  429. useOnTarget:enable()
  430. useWith:enable()
  431. if currentHotkeyLabel.useType == HOTKEY_MANAGER_USEONSELF then
  432. useRadioGroup:selectWidget(useOnSelf)
  433. elseif currentHotkeyLabel.useType == HOTKEY_MANAGER_USEONTARGET then
  434. useRadioGroup:selectWidget(useOnTarget)
  435. elseif currentHotkeyLabel.useType == HOTKEY_MANAGER_USEWITH then
  436. useRadioGroup:selectWidget(useWith)
  437. end
  438. else
  439. useOnSelf:disable()
  440. useOnTarget:disable()
  441. useWith:disable()
  442. useRadioGroup:clearSelected()
  443. end
  444. else
  445. useOnSelf:disable()
  446. useOnTarget:disable()
  447. useWith:disable()
  448. useRadioGroup:clearSelected()
  449. hotkeyText:enable()
  450. hotkeyText:focus()
  451. hotKeyTextLabel:enable()
  452. if reset then
  453. hotkeyText:setCursorPos(-1)
  454. end
  455. hotkeyText:setText(currentHotkeyLabel.value)
  456. sendAutomatically:setChecked(currentHotkeyLabel.autoSend)
  457. sendAutomatically:setEnabled(currentHotkeyLabel.value and #currentHotkeyLabel.value > 0)
  458. selectObjectButton:enable()
  459. clearObjectButton:disable()
  460. currentItemPreview:clearItem()
  461. end
  462. else
  463. removeHotkeyButton:disable()
  464. hotkeyText:disable()
  465. sendAutomatically:disable()
  466. selectObjectButton:disable()
  467. clearObjectButton:disable()
  468. useOnSelf:disable()
  469. useOnTarget:disable()
  470. useWith:disable()
  471. hotkeyText:clearText()
  472. useRadioGroup:clearSelected()
  473. sendAutomatically:setChecked(false)
  474. currentItemPreview:clearItem()
  475. end
  476. end
  477. function removeHotkey()
  478. if currentHotkeyLabel == nil then return end
  479. g_keyboard.unbindKeyPress(currentHotkeyLabel.keyCombo, boundCombosCallback[currentHotkeyLabel.keyCombo])
  480. boundCombosCallback[currentHotkeyLabel.keyCombo] = nil
  481. currentHotkeyLabel:destroy()
  482. currentHotkeyLabel = nil
  483. end
  484. function onHotkeyTextChange(value)
  485. if not hotkeysManagerLoaded then return end
  486. if currentHotkeyLabel == nil then return end
  487. currentHotkeyLabel.value = value
  488. if value == '' then
  489. currentHotkeyLabel.autoSend = false
  490. end
  491. updateHotkeyLabel(currentHotkeyLabel)
  492. updateHotkeyForm()
  493. end
  494. function onSendAutomaticallyChange(autoSend)
  495. if not hotkeysManagerLoaded then return end
  496. if currentHotkeyLabel == nil then return end
  497. if not currentHotkeyLabel.value or #currentHotkeyLabel.value == 0 then return end
  498. currentHotkeyLabel.autoSend = autoSend
  499. updateHotkeyLabel(currentHotkeyLabel)
  500. updateHotkeyForm()
  501. end
  502. function onChangeUseType(useTypeWidget)
  503. if not hotkeysManagerLoaded then return end
  504. if currentHotkeyLabel == nil then return end
  505. if useTypeWidget == useOnSelf then
  506. currentHotkeyLabel.useType = HOTKEY_MANAGER_USEONSELF
  507. elseif useTypeWidget == useOnTarget then
  508. currentHotkeyLabel.useType = HOTKEY_MANAGER_USEONTARGET
  509. elseif useTypeWidget == useWith then
  510. currentHotkeyLabel.useType = HOTKEY_MANAGER_USEWITH
  511. else
  512. currentHotkeyLabel.useType = HOTKEY_MANAGER_USE
  513. end
  514. updateHotkeyLabel(currentHotkeyLabel)
  515. updateHotkeyForm()
  516. end
  517. function onSelectHotkeyLabel(hotkeyLabel)
  518. currentHotkeyLabel = hotkeyLabel
  519. updateHotkeyForm(true)
  520. end
  521. function hotkeyCapture(assignWindow, keyCode, keyboardModifiers)
  522. local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers)
  523. local comboPreview = assignWindow:getChildById('comboPreview')
  524. comboPreview:setText(tr('Current hotkey to add: %s', keyCombo))
  525. comboPreview.keyCombo = keyCombo
  526. comboPreview:resizeToText()
  527. assignWindow:getChildById('addButton'):enable()
  528. return true
  529. end
  530. function hotkeyCaptureOk(assignWindow, keyCombo)
  531. addKeyCombo(keyCombo, nil, true)
  532. assignWindow:destroy()
  533. end