| @@ -0,0 +1,712 @@ | |||
| 1 | + | HOTKEY_MANAGER_USE = nil | |
| 2 | + | HOTKEY_MANAGER_USEONSELF = 1 | |
| 3 | + | HOTKEY_MANAGER_USEONTARGET = 2 | |
| 4 | + | HOTKEY_MANAGER_USEWITH = 3 | |
| 5 | + | ||
| 6 | + | HotkeyColors = { | |
| 7 | + | text = '#888888', | |
| 8 | + | textAutoSend = '#FFFFFF', | |
| 9 | + | itemUse = '#8888FF', | |
| 10 | + | itemUseSelf = '#00FF00', | |
| 11 | + | itemUseTarget = '#FF0000', | |
| 12 | + | itemUseWith = '#F5B325', | |
| 13 | + | extraAction = '#FFAA00' | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | hotkeysManagerLoaded = false | |
| 17 | + | hotkeysWindow = nil | |
| 18 | + | configSelector = nil | |
| 19 | + | hotkeysButton = nil | |
| 20 | + | currentHotkeyLabel = nil | |
| 21 | + | currentItemPreview = nil | |
| 22 | + | itemWidget = nil | |
| 23 | + | addHotkeyButton = nil | |
| 24 | + | removeHotkeyButton = nil | |
| 25 | + | hotkeyText = nil | |
| 26 | + | hotKeyTextLabel = nil | |
| 27 | + | sendAutomatically = nil | |
| 28 | + | selectObjectButton = nil | |
| 29 | + | clearObjectButton = nil | |
| 30 | + | useOnSelf = nil | |
| 31 | + | useOnTarget = nil | |
| 32 | + | useWith = nil | |
| 33 | + | defaultComboKeys = nil | |
| 34 | + | perCharacter = true | |
| 35 | + | mouseGrabberWidget = nil | |
| 36 | + | useRadioGroup = nil | |
| 37 | + | currentHotkeys = nil | |
| 38 | + | boundCombosCallback = {} | |
| 39 | + | hotkeysList = {} | |
| 40 | + | hotkeyConfigs = {} | |
| 41 | + | currentConfig = 1 | |
| 42 | + | configValueChanged = false | |
| 43 | + | ||
| 44 | + | -- public functions | |
| 45 | + | function init() | |
| 46 | + | if not g_app.isMobile() then | |
| 47 | + | --hotkeysButton = modules.client_topmenu.addLeftGameButton('hotkeysButton', tr('Hotkeys') .. ' (Ctrl+K)', '/images/topbuttons/hotkeys', toggle, false, 7) | |
| 48 | + | end | |
| 49 | + | --g_keyboard.bindKeyDown('Ctrl+K', toggle) | |
| 50 | + | hotkeysWindow = g_ui.displayUI('hotkeys_manager') | |
| 51 | + | hotkeysWindow:setVisible(false) | |
| 52 | + | ||
| 53 | + | configSelector = hotkeysWindow:getChildById('configSelector') | |
| 54 | + | currentHotkeys = hotkeysWindow:getChildById('currentHotkeys') | |
| 55 | + | currentItemPreview = hotkeysWindow:getChildById('itemPreview') | |
| 56 | + | addHotkeyButton = hotkeysWindow:getChildById('addHotkeyButton') | |
| 57 | + | removeHotkeyButton = hotkeysWindow:getChildById('removeHotkeyButton') | |
| 58 | + | hotkeyText = hotkeysWindow:getChildById('hotkeyText') | |
| 59 | + | hotKeyTextLabel = hotkeysWindow:getChildById('hotKeyTextLabel') | |
| 60 | + | sendAutomatically = hotkeysWindow:getChildById('sendAutomatically') | |
| 61 | + | selectObjectButton = hotkeysWindow:getChildById('selectObjectButton') | |
| 62 | + | clearObjectButton = hotkeysWindow:getChildById('clearObjectButton') | |
| 63 | + | useOnSelf = hotkeysWindow:getChildById('useOnSelf') | |
| 64 | + | useOnTarget = hotkeysWindow:getChildById('useOnTarget') | |
| 65 | + | useWith = hotkeysWindow:getChildById('useWith') | |
| 66 | + | ||
| 67 | + | useRadioGroup = UIRadioGroup.create() | |
| 68 | + | useRadioGroup:addWidget(useOnSelf) | |
| 69 | + | useRadioGroup:addWidget(useOnTarget) | |
| 70 | + | useRadioGroup:addWidget(useWith) | |
| 71 | + | useRadioGroup.onSelectionChange = function(self, selected) onChangeUseType(selected) end | |
| 72 | + | ||
| 73 | + | mouseGrabberWidget = g_ui.createWidget('UIWidget') | |
| 74 | + | mouseGrabberWidget:setVisible(false) | |
| 75 | + | mouseGrabberWidget:setFocusable(false) | |
| 76 | + | mouseGrabberWidget.onMouseRelease = onChooseItemMouseRelease | |
| 77 | + | ||
| 78 | + | currentHotkeys.onChildFocusChange = function(self, hotkeyLabel) onSelectHotkeyLabel(hotkeyLabel) end | |
| 79 | + | g_keyboard.bindKeyPress('Down', function() currentHotkeys:focusNextChild(KeyboardFocusReason) end, hotkeysWindow) | |
| 80 | + | g_keyboard.bindKeyPress('Up', function() currentHotkeys:focusPreviousChild(KeyboardFocusReason) end, hotkeysWindow) | |
| 81 | + | ||
| 82 | + | if hotkeysWindow.action and setupExtraHotkeys then | |
| 83 | + | setupExtraHotkeys(hotkeysWindow.action) | |
| 84 | + | end | |
| 85 | + | ||
| 86 | + | connect(g_game, { | |
| 87 | + | onGameStart = online, | |
| 88 | + | onGameEnd = offline | |
| 89 | + | }) | |
| 90 | + | ||
| 91 | + | for i = 1, configSelector:getOptionsCount() do | |
| 92 | + | hotkeyConfigs[i] = g_configs.create("/hotkeys_" .. i .. ".otml") | |
| 93 | + | end | |
| 94 | + | ||
| 95 | + | load() | |
| 96 | + | end | |
| 97 | + | ||
| 98 | + | function onSpaceAttack() | |
| 99 | + | --print("Space Attack") | |
| 100 | + | setupExtraHotkeys(01) | |
| 101 | + | end | |
| 102 | + | function terminate() | |
| 103 | + | disconnect(g_game, { | |
| 104 | + | onGameStart = online, | |
| 105 | + | onGameEnd = offline | |
| 106 | + | }) | |
| 107 | + | ||
| 108 | + | g_keyboard.unbindKeyDown('Ctrl+K') | |
| 109 | + | ||
| 110 | + | unload() | |
| 111 | + | ||
| 112 | + | hotkeysWindow:destroy() | |
| 113 | + | --if hotkeysButton then | |
| 114 | + | -- hotkeysButton:destroy() | |
| 115 | + | --end | |
| 116 | + | mouseGrabberWidget:destroy() | |
| 117 | + | end | |
| 118 | + | ||
| 119 | + | function online() | |
| 120 | + | reload() | |
| 121 | + | hide() | |
| 122 | + | end | |
| 123 | + | ||
| 124 | + | function offline() | |
| 125 | + | unload() | |
| 126 | + | hide() | |
| 127 | + | end | |
| 128 | + | ||
| 129 | + | function show() | |
| 130 | + | if not g_game.isOnline() then | |
| 131 | + | return | |
| 132 | + | end | |
| 133 | + | hotkeysWindow:show() | |
| 134 | + | hotkeysWindow:raise() | |
| 135 | + | hotkeysWindow:focus() | |
| 136 | + | end | |
| 137 | + | ||
| 138 | + | function hide() | |
| 139 | + | hotkeysWindow:hide() | |
| 140 | + | end | |
| 141 | + | ||
| 142 | + | function toggle() | |
| 143 | + | if not hotkeysWindow:isVisible() then | |
| 144 | + | show() | |
| 145 | + | else | |
| 146 | + | hide() | |
| 147 | + | end | |
| 148 | + | end | |
| 149 | + | ||
| 150 | + | function ok() | |
| 151 | + | save() | |
| 152 | + | hide() | |
| 153 | + | end | |
| 154 | + | ||
| 155 | + | function cancel() | |
| 156 | + | reload() | |
| 157 | + | hide() | |
| 158 | + | end | |
| 159 | + | ||
| 160 | + | function load(forceDefaults) | |
| 161 | + | hotkeysManagerLoaded = false | |
| 162 | + | currentConfig = 1 | |
| 163 | + | ||
| 164 | + | local hotkeysNode = g_settings.getNode('hotkeys') or {} | |
| 165 | + | local index = g_game.getCharacterName() .. "_" .. g_game.getClientVersion() | |
| 166 | + | if hotkeysNode[index] ~= nil and hotkeysNode[index] > 0 and hotkeysNode[index] <= #hotkeyConfigs then | |
| 167 | + | currentConfig = hotkeysNode[index] | |
| 168 | + | end | |
| 169 | + | ||
| 170 | + | configSelector:setCurrentIndex(currentConfig, true) | |
| 171 | + | ||
| 172 | + | local hotkeySettings = hotkeyConfigs[currentConfig]:getNode('hotkeys') | |
| 173 | + | local hotkeys = {} | |
| 174 | + | ||
| 175 | + | if not table.empty(hotkeySettings) then hotkeys = hotkeySettings end | |
| 176 | + | ||
| 177 | + | hotkeyList = {} | |
| 178 | + | if not forceDefaults then | |
| 179 | + | if not table.empty(hotkeys) then | |
| 180 | + | for keyCombo, setting in pairs(hotkeys) do | |
| 181 | + | keyCombo = tostring(keyCombo) | |
| 182 | + | addKeyCombo(keyCombo, setting) | |
| 183 | + | hotkeyList[keyCombo] = setting | |
| 184 | + | end | |
| 185 | + | end | |
| 186 | + | end | |
| 187 | + | ||
| 188 | + | if currentHotkeys:getChildCount() == 0 then | |
| 189 | + | loadDefautComboKeys() | |
| 190 | + | end | |
| 191 | + | ||
| 192 | + | configValueChanged = false | |
| 193 | + | hotkeysManagerLoaded = true | |
| 194 | + | end | |
| 195 | + | ||
| 196 | + | function unload() | |
| 197 | + | local gameRootPanel = modules.game_interface.getRootPanel() | |
| 198 | + | for keyCombo,callback in pairs(boundCombosCallback) do | |
| 199 | + | g_keyboard.unbindKeyPress(keyCombo, callback, gameRootPanel) | |
| 200 | + | end | |
| 201 | + | boundCombosCallback = {} | |
| 202 | + | currentHotkeys:destroyChildren() | |
| 203 | + | currentHotkeyLabel = nil | |
| 204 | + | updateHotkeyForm(true) | |
| 205 | + | hotkeyList = {} | |
| 206 | + | end | |
| 207 | + | ||
| 208 | + | function reset() | |
| 209 | + | unload() | |
| 210 | + | load(true) | |
| 211 | + | end | |
| 212 | + | ||
| 213 | + | function reload() | |
| 214 | + | unload() | |
| 215 | + | load() | |
| 216 | + | end | |
| 217 | + | ||
| 218 | + | function save() | |
| 219 | + | if not configValueChanged then | |
| 220 | + | return | |
| 221 | + | end | |
| 222 | + | ||
| 223 | + | local hotkeySettings = hotkeyConfigs[currentConfig]:getNode('hotkeys') or {} | |
| 224 | + | ||
| 225 | + | table.clear(hotkeySettings) | |
| 226 | + | ||
| 227 | + | for _,child in pairs(currentHotkeys:getChildren()) do | |
| 228 | + | hotkeySettings[child.keyCombo] = { | |
| 229 | + | autoSend = child.autoSend, | |
| 230 | + | itemId = child.itemId, | |
| 231 | + | subType = child.subType, | |
| 232 | + | useType = child.useType, | |
| 233 | + | value = child.value, | |
| 234 | + | action = child.action | |
| 235 | + | } | |
| 236 | + | end | |
| 237 | + | ||
| 238 | + | hotkeyList = hotkeySettings | |
| 239 | + | hotkeyConfigs[currentConfig]:setNode('hotkeys', hotkeySettings) | |
| 240 | + | hotkeyConfigs[currentConfig]:save() | |
| 241 | + | ||
| 242 | + | local index = g_game.getCharacterName() .. "_" .. g_game.getClientVersion() | |
| 243 | + | local hotkeysNode = g_settings.getNode('hotkeys') or {} | |
| 244 | + | hotkeysNode[index] = currentConfig | |
| 245 | + | g_settings.setNode('hotkeys', hotkeysNode) | |
| 246 | + | g_settings.save() | |
| 247 | + | end | |
| 248 | + | ||
| 249 | + | function onConfigChange() | |
| 250 | + | if not configSelector then return end | |
| 251 | + | local index = g_game.getCharacterName() .. "_" .. g_game.getClientVersion() | |
| 252 | + | local hotkeysNode = g_settings.getNode('hotkeys') or {} | |
| 253 | + | hotkeysNode[index] = configSelector.currentIndex | |
| 254 | + | g_settings.setNode('hotkeys', hotkeysNode) | |
| 255 | + | reload() | |
| 256 | + | end | |
| 257 | + | ||
| 258 | + | function loadDefautComboKeys() | |
| 259 | + | if not defaultComboKeys then | |
| 260 | + | for i=1,12 do | |
| 261 | + | addKeyCombo('F' .. i) | |
| 262 | + | end | |
| 263 | + | for i=1,4 do | |
| 264 | + | addKeyCombo('Shift+F' .. i) | |
| 265 | + | end | |
| 266 | + | else | |
| 267 | + | for keyCombo, keySettings in pairs(defaultComboKeys) do | |
| 268 | + | addKeyCombo(keyCombo, keySettings) | |
| 269 | + | end | |
| 270 | + | end | |
| 271 | + | end | |
| 272 | + | ||
| 273 | + | function setDefaultComboKeys(combo) | |
| 274 | + | defaultComboKeys = combo | |
| 275 | + | end | |
| 276 | + | ||
| 277 | + | function onChooseItemMouseRelease(self, mousePosition, mouseButton) | |
| 278 | + | local item = nil | |
| 279 | + | if mouseButton == MouseLeftButton then | |
| 280 | + | local clickedWidget = modules.game_interface.getRootPanel():recursiveGetChildByPos(mousePosition, false) | |
| 281 | + | if clickedWidget then | |
| 282 | + | if clickedWidget:getClassName() == 'UIGameMap' then | |
| 283 | + | local tile = clickedWidget:getTile(mousePosition) | |
| 284 | + | if tile then | |
| 285 | + | local thing = tile:getTopMoveThing() | |
| 286 | + | if thing and thing:isItem() then | |
| 287 | + | item = thing | |
| 288 | + | end | |
| 289 | + | end | |
| 290 | + | elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then | |
| 291 | + | item = clickedWidget:getItem() | |
| 292 | + | end | |
| 293 | + | end | |
| 294 | + | end | |
| 295 | + | ||
| 296 | + | if item and currentHotkeyLabel then | |
| 297 | + | currentHotkeyLabel.itemId = item:getId() | |
| 298 | + | if item:isFluidContainer() then | |
| 299 | + | currentHotkeyLabel.subType = item:getSubType() | |
| 300 | + | end | |
| 301 | + | if item:isMultiUse() then | |
| 302 | + | currentHotkeyLabel.useType = HOTKEY_MANAGER_USEWITH | |
| 303 | + | else | |
| 304 | + | currentHotkeyLabel.useType = HOTKEY_MANAGER_USE | |
| 305 | + | end | |
| 306 | + | currentHotkeyLabel.value = nil | |
| 307 | + | currentHotkeyLabel.autoSend = false | |
| 308 | + | updateHotkeyLabel(currentHotkeyLabel) | |
| 309 | + | updateHotkeyForm(true) | |
| 310 | + | end | |
| 311 | + | ||
| 312 | + | show() | |
| 313 | + | ||
| 314 | + | g_mouse.popCursor('target') | |
| 315 | + | self:ungrabMouse() | |
| 316 | + | return true | |
| 317 | + | end | |
| 318 | + | ||
| 319 | + | function startChooseItem() | |
| 320 | + | if g_ui.isMouseGrabbed() then return end | |
| 321 | + | mouseGrabberWidget:grabMouse() | |
| 322 | + | g_mouse.pushCursor('target') | |
| 323 | + | hide() | |
| 324 | + | end | |
| 325 | + | ||
| 326 | + | function clearObject() | |
| 327 | + | currentHotkeyLabel.action = nil | |
| 328 | + | currentHotkeyLabel.itemId = nil | |
| 329 | + | currentHotkeyLabel.subType = nil | |
| 330 | + | currentHotkeyLabel.useType = nil | |
| 331 | + | currentHotkeyLabel.autoSend = nil | |
| 332 | + | currentHotkeyLabel.value = nil | |
| 333 | + | updateHotkeyLabel(currentHotkeyLabel) | |
| 334 | + | updateHotkeyForm(true) | |
| 335 | + | end | |
| 336 | + | ||
| 337 | + | function addHotkey() | |
| 338 | + | local assignWindow = g_ui.createWidget('HotkeyAssignWindow', rootWidget) | |
| 339 | + | assignWindow:grabKeyboard() | |
| 340 | + | ||
| 341 | + | local comboLabel = assignWindow:getChildById('comboPreview') | |
| 342 | + | comboLabel.keyCombo = '' | |
| 343 | + | assignWindow.onKeyDown = hotkeyCapture | |
| 344 | + | end | |
| 345 | + | ||
| 346 | + | function addKeyCombo(keyCombo, keySettings, focus) | |
| 347 | + | if keyCombo == nil or #keyCombo == 0 then return end | |
| 348 | + | if not keyCombo then return end | |
| 349 | + | local hotkeyLabel = currentHotkeys:getChildById(keyCombo) | |
| 350 | + | if not hotkeyLabel then | |
| 351 | + | hotkeyLabel = g_ui.createWidget('HotkeyListLabel') | |
| 352 | + | hotkeyLabel:setId(keyCombo) | |
| 353 | + | ||
| 354 | + | local children = currentHotkeys:getChildren() | |
| 355 | + | children[#children+1] = hotkeyLabel | |
| 356 | + | table.sort(children, function(a,b) | |
| 357 | + | if a:getId():len() < b:getId():len() then | |
| 358 | + | return true | |
| 359 | + | elseif a:getId():len() == b:getId():len() then | |
| 360 | + | return a:getId() < b:getId() | |
| 361 | + | else | |
| 362 | + | return false | |
| 363 | + | end | |
| 364 | + | end) | |
| 365 | + | for i=1,#children do | |
| 366 | + | if children[i] == hotkeyLabel then | |
| 367 | + | currentHotkeys:insertChild(i, hotkeyLabel) | |
| 368 | + | break | |
| 369 | + | end | |
| 370 | + | end | |
| 371 | + | ||
| 372 | + | if keySettings then | |
| 373 | + | currentHotkeyLabel = hotkeyLabel | |
| 374 | + | hotkeyLabel.keyCombo = keyCombo | |
| 375 | + | hotkeyLabel.autoSend = toboolean(keySettings.autoSend) | |
| 376 | + | hotkeyLabel.action = keySettings.action | |
| 377 | + | hotkeyLabel.itemId = tonumber(keySettings.itemId) | |
| 378 | + | hotkeyLabel.subType = tonumber(keySettings.subType) | |
| 379 | + | hotkeyLabel.useType = tonumber(keySettings.useType) | |
| 380 | + | if keySettings.value then hotkeyLabel.value = tostring(keySettings.value) end | |
| 381 | + | else | |
| 382 | + | hotkeyLabel.keyCombo = keyCombo | |
| 383 | + | hotkeyLabel.autoSend = false | |
| 384 | + | hotkeyLabel.itemId = nil | |
| 385 | + | hotkeyLabel.subType = nil | |
| 386 | + | hotkeyLabel.useType = nil | |
| 387 | + | hotkeyLabel.action = nil | |
| 388 | + | hotkeyLabel.value = '' | |
| 389 | + | end | |
| 390 | + | ||
| 391 | + | updateHotkeyLabel(hotkeyLabel) | |
| 392 | + | ||
| 393 | + | local gameRootPanel = modules.game_interface.getRootPanel() | |
| 394 | + | if keyCombo:lower():find("ctrl") then | |
| 395 | + | if boundCombosCallback[keyCombo] then | |
| 396 | + | g_keyboard.unbindKeyPress(keyCombo, boundCombosCallback[keyCombo], gameRootPanel) | |
| 397 | + | end | |
| 398 | + | end | |
| 399 | + | ||
| 400 | + | boundCombosCallback[keyCombo] = function(k, c, ticks) prepareKeyCombo(keyCombo, ticks) end | |
| 401 | + | g_keyboard.bindKeyPress(keyCombo, boundCombosCallback[keyCombo], gameRootPanel) | |
| 402 | + | ||
| 403 | + | if not keyCombo:lower():find("ctrl") then | |
| 404 | + | local keyComboCtrl = "Ctrl+" .. keyCombo | |
| 405 | + | if not boundCombosCallback[keyComboCtrl] then | |
| 406 | + | boundCombosCallback[keyComboCtrl] = function(k, c, ticks) prepareKeyCombo(keyComboCtrl, ticks) end | |
| 407 | + | g_keyboard.bindKeyPress(keyComboCtrl, boundCombosCallback[keyComboCtrl], gameRootPanel) | |
| 408 | + | end | |
| 409 | + | end | |
| 410 | + | end | |
| 411 | + | ||
| 412 | + | if focus then | |
| 413 | + | currentHotkeys:focusChild(hotkeyLabel) | |
| 414 | + | currentHotkeys:ensureChildVisible(hotkeyLabel) | |
| 415 | + | updateHotkeyForm(true) | |
| 416 | + | end | |
| 417 | + | configValueChanged = true | |
| 418 | + | end | |
| 419 | + | ||
| 420 | + | function prepareKeyCombo(keyCombo, ticks) | |
| 421 | + | local hotKey = hotkeyList[keyCombo] | |
| 422 | + | if (keyCombo:lower():find("ctrl") and not hotKey) or (hotKey and hotKey.itemId == nil and (not hotKey.value or #hotKey.value == 0) and not hotKey.action) then | |
| 423 | + | keyCombo = keyCombo:gsub("Ctrl%+", "") | |
| 424 | + | keyCombo = keyCombo:gsub("ctrl%+", "") | |
| 425 | + | hotKey = hotkeyList[keyCombo] | |
| 426 | + | end | |
| 427 | + | if not hotKey then | |
| 428 | + | return | |
| 429 | + | end | |
| 430 | + | ||
| 431 | + | if hotKey.itemId == nil and hotKey.action == nil then -- say | |
| 432 | + | scheduleEvent(function() doKeyCombo(keyCombo, ticks >= 5) end, g_settings.getNumber('hotkeyDelay')) | |
| 433 | + | else | |
| 434 | + | doKeyCombo(keyCombo, ticks >= 5) | |
| 435 | + | end | |
| 436 | + | end | |
| 437 | + | ||
| 438 | + | function doKeyCombo(keyCombo, repeated) | |
| 439 | + | if not g_game.isOnline() then return end | |
| 440 | + | if modules.game_console and modules.game_console.isChatEnabled() then | |
| 441 | + | if keyCombo:len() == 1 then | |
| 442 | + | return | |
| 443 | + | end | |
| 444 | + | end | |
| 445 | + | if modules.game_walking then | |
| 446 | + | modules.game_walking.checkTurn() | |
| 447 | + | end | |
| 448 | + | ||
| 449 | + | local hotKey = hotkeyList[keyCombo] | |
| 450 | + | if not hotKey then return end | |
| 451 | + | ||
| 452 | + | local hotkeyDelay = 100 | |
| 453 | + | if hotKey.hotkeyDelayTo == nil or g_clock.millis() > hotKey.hotkeyDelayTo + hotkeyDelay then | |
| 454 | + | hotkeyDelay = 200 -- for first use | |
| 455 | + | end | |
| 456 | + | if hotKey.hotkeyDelayTo ~= nil and g_clock.millis() < hotKey.hotkeyDelayTo then | |
| 457 | + | return | |
| 458 | + | end | |
| 459 | + | if hotKey.action then | |
| 460 | + | executeExtraHotkey(hotKey.action, repeated) | |
| 461 | + | elseif hotKey.itemId == nil then | |
| 462 | + | if not hotKey.value or #hotKey.value == 0 then return end | |
| 463 | + | if hotKey.autoSend then | |
| 464 | + | modules.game_console.sendMessage(hotKey.value) | |
| 465 | + | else | |
| 466 | + | modules.game_console.setTextEditText(hotKey.value) | |
| 467 | + | end | |
| 468 | + | hotKey.hotkeyDelayTo = g_clock.millis() + hotkeyDelay | |
| 469 | + | elseif hotKey.useType == HOTKEY_MANAGER_USE then | |
| 470 | + | if g_game.getClientVersion() < 780 then | |
| 471 | + | local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1) | |
| 472 | + | if item then | |
| 473 | + | g_game.use(item) | |
| 474 | + | end | |
| 475 | + | else | |
| 476 | + | g_game.useInventoryItem(hotKey.itemId) | |
| 477 | + | end | |
| 478 | + | hotKey.hotkeyDelayTo = g_clock.millis() + hotkeyDelay | |
| 479 | + | elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then | |
| 480 | + | if g_game.getClientVersion() < 780 then | |
| 481 | + | local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1) | |
| 482 | + | if item then | |
| 483 | + | g_game.useWith(item, g_game.getLocalPlayer()) | |
| 484 | + | end | |
| 485 | + | else | |
| 486 | + | g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer(), hotKey.subType or -1) | |
| 487 | + | end | |
| 488 | + | hotKey.hotkeyDelayTo = g_clock.millis() + hotkeyDelay | |
| 489 | + | elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then | |
| 490 | + | local attackingCreature = g_game.getAttackingCreature() | |
| 491 | + | if not attackingCreature then | |
| 492 | + | local item = Item.create(hotKey.itemId) | |
| 493 | + | if g_game.getClientVersion() < 780 then | |
| 494 | + | local tmpItem = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1) | |
| 495 | + | if not tmpItem then return end | |
| 496 | + | item = tmpItem | |
| 497 | + | end | |
| 498 | + | ||
| 499 | + | modules.game_interface.startUseWith(item, hotKey.subType or - 1) | |
| 500 | + | return | |
| 501 | + | end | |
| 502 | + | ||
| 503 | + | if not attackingCreature:getTile() then return end | |
| 504 | + | if g_game.getClientVersion() < 780 then | |
| 505 | + | local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1) | |
| 506 | + | if item then | |
| 507 | + | g_game.useWith(item, attackingCreature, hotKey.subType or -1) | |
| 508 | + | end | |
| 509 | + | else | |
| 510 | + | g_game.useInventoryItemWith(hotKey.itemId, attackingCreature, hotKey.subType or -1) | |
| 511 | + | end | |
| 512 | + | hotKey.hotkeyDelayTo = g_clock.millis() + hotkeyDelay | |
| 513 | + | elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then | |
| 514 | + | local item = Item.create(hotKey.itemId) | |
| 515 | + | if g_game.getClientVersion() < 780 then | |
| 516 | + | local tmpItem = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1) | |
| 517 | + | if not tmpItem then return true end | |
| 518 | + | item = tmpItem | |
| 519 | + | end | |
| 520 | + | modules.game_interface.startUseWith(item, hotKey.subType or - 1) | |
| 521 | + | end | |
| 522 | + | end | |
| 523 | + | ||
| 524 | + | function updateHotkeyLabel(hotkeyLabel) | |
| 525 | + | if not hotkeyLabel then return end | |
| 526 | + | if hotkeyLabel.action ~= nil then | |
| 527 | + | hotkeyLabel:setText(tr('%s: (Action: %s)', hotkeyLabel.keyCombo, getActionDescription(hotkeyLabel.action))) | |
| 528 | + | hotkeyLabel:setColor(HotkeyColors.extraAction) | |
| 529 | + | elseif hotkeyLabel.useType == HOTKEY_MANAGER_USEONSELF then | |
| 530 | + | hotkeyLabel:setText(tr('%s: (use object on yourself)', hotkeyLabel.keyCombo)) | |
| 531 | + | hotkeyLabel:setColor(HotkeyColors.itemUseSelf) | |
| 532 | + | elseif hotkeyLabel.useType == HOTKEY_MANAGER_USEONTARGET then | |
| 533 | + | hotkeyLabel:setText(tr('%s: (use object on target)', hotkeyLabel.keyCombo)) | |
| 534 | + | hotkeyLabel:setColor(HotkeyColors.itemUseTarget) | |
| 535 | + | elseif hotkeyLabel.useType == HOTKEY_MANAGER_USEWITH then | |
| 536 | + | hotkeyLabel:setText(tr('%s: (use object with crosshair)', hotkeyLabel.keyCombo)) | |
| 537 | + | hotkeyLabel:setColor(HotkeyColors.itemUseWith) | |
| 538 | + | elseif hotkeyLabel.itemId ~= nil then | |
| 539 | + | hotkeyLabel:setText(tr('%s: (use object)', hotkeyLabel.keyCombo)) | |
| 540 | + | hotkeyLabel:setColor(HotkeyColors.itemUse) | |
| 541 | + | else | |
| 542 | + | local text = hotkeyLabel.keyCombo .. ': ' | |
| 543 | + | if hotkeyLabel.value then | |
| 544 | + | text = text .. hotkeyLabel.value | |
| 545 | + | end | |
| 546 | + | hotkeyLabel:setText(text) | |
| 547 | + | if hotkeyLabel.autoSend then | |
| 548 | + | hotkeyLabel:setColor(HotkeyColors.autoSend) | |
| 549 | + | else | |
| 550 | + | hotkeyLabel:setColor(HotkeyColors.text) | |
| 551 | + | end | |
| 552 | + | end | |
| 553 | + | end | |
| 554 | + | ||
| 555 | + | function updateHotkeyForm(reset) | |
| 556 | + | configValueChanged = true | |
| 557 | + | if hotkeysWindow.action then | |
| 558 | + | if currentHotkeyLabel then | |
| 559 | + | hotkeysWindow.action:enable() | |
| 560 | + | if currentHotkeyLabel.action then | |
| 561 | + | hotkeysWindow.action:setCurrentIndex(translateActionToActionComboboxIndex(currentHotkeyLabel.action), true) | |
| 562 | + | else | |
| 563 | + | hotkeysWindow.action:setCurrentIndex(1, true) | |
| 564 | + | end | |
| 565 | + | else | |
| 566 | + | hotkeysWindow.action:disable() | |
| 567 | + | hotkeysWindow.action:setCurrentIndex(1, true) | |
| 568 | + | end | |
| 569 | + | end | |
| 570 | + | local hasCustomAction = hotkeysWindow.action and hotkeysWindow.action.currentIndex > 1 | |
| 571 | + | if currentHotkeyLabel and not hasCustomAction then | |
| 572 | + | removeHotkeyButton:enable() | |
| 573 | + | if currentHotkeyLabel.itemId ~= nil then | |
| 574 | + | hotkeyText:clearText() | |
| 575 | + | hotkeyText:disable() | |
| 576 | + | hotKeyTextLabel:disable() | |
| 577 | + | sendAutomatically:setChecked(false) | |
| 578 | + | sendAutomatically:disable() | |
| 579 | + | selectObjectButton:disable() | |
| 580 | + | clearObjectButton:enable() | |
| 581 | + | currentItemPreview:setItemId(currentHotkeyLabel.itemId) | |
| 582 | + | if currentHotkeyLabel.subType then | |
| 583 | + | currentItemPreview:setItemSubType(currentHotkeyLabel.subType) | |
| 584 | + | end | |
| 585 | + | if currentItemPreview:getItem():isMultiUse() then | |
| 586 | + | useOnSelf:enable() | |
| 587 | + | useOnTarget:enable() | |
| 588 | + | useWith:enable() | |
| 589 | + | if currentHotkeyLabel.useType == HOTKEY_MANAGER_USEONSELF then | |
| 590 | + | useRadioGroup:selectWidget(useOnSelf) | |
| 591 | + | elseif currentHotkeyLabel.useType == HOTKEY_MANAGER_USEONTARGET then | |
| 592 | + | useRadioGroup:selectWidget(useOnTarget) | |
| 593 | + | elseif currentHotkeyLabel.useType == HOTKEY_MANAGER_USEWITH then | |
| 594 | + | useRadioGroup:selectWidget(useWith) | |
| 595 | + | end | |
| 596 | + | else | |
| 597 | + | useOnSelf:disable() | |
| 598 | + | useOnTarget:disable() | |
| 599 | + | useWith:disable() | |
| 600 | + | useRadioGroup:clearSelected() | |
| 601 | + | end | |
| 602 | + | else | |
| 603 | + | useOnSelf:disable() | |
| 604 | + | useOnTarget:disable() | |
| 605 | + | useWith:disable() | |
| 606 | + | useRadioGroup:clearSelected() | |
| 607 | + | hotkeyText:enable() | |
| 608 | + | hotkeyText:focus() | |
| 609 | + | hotKeyTextLabel:enable() | |
| 610 | + | if reset then | |
| 611 | + | hotkeyText:setCursorPos(-1) | |
| 612 | + | end | |
| 613 | + | hotkeyText:setText(currentHotkeyLabel.value) | |
| 614 | + | sendAutomatically:setChecked(currentHotkeyLabel.autoSend) | |
| 615 | + | sendAutomatically:setEnabled(currentHotkeyLabel.value and #currentHotkeyLabel.value > 0) | |
| 616 | + | selectObjectButton:enable() | |
| 617 | + | clearObjectButton:disable() | |
| 618 | + | currentItemPreview:clearItem() | |
| 619 | + | end | |
| 620 | + | else | |
| 621 | + | removeHotkeyButton:disable() | |
| 622 | + | hotkeyText:disable() | |
| 623 | + | sendAutomatically:disable() | |
| 624 | + | selectObjectButton:disable() | |
| 625 | + | clearObjectButton:disable() | |
| 626 | + | useOnSelf:disable() | |
| 627 | + | useOnTarget:disable() | |
| 628 | + | useWith:disable() | |
| 629 | + | hotkeyText:clearText() | |
| 630 | + | useRadioGroup:clearSelected() | |
| 631 | + | sendAutomatically:setChecked(false) | |
| 632 | + | currentItemPreview:clearItem() | |
| 633 | + | end | |
| 634 | + | end | |
| 635 | + | ||
| 636 | + | function removeHotkey() | |
| 637 | + | if currentHotkeyLabel == nil then return end | |
| 638 | + | local gameRootPanel = modules.game_interface.getRootPanel() | |
| 639 | + | configValueChanged = true | |
| 640 | + | g_keyboard.unbindKeyPress(currentHotkeyLabel.keyCombo, boundCombosCallback[currentHotkeyLabel.keyCombo], gameRootPanel) | |
| 641 | + | boundCombosCallback[currentHotkeyLabel.keyCombo] = nil | |
| 642 | + | currentHotkeyLabel:destroy() | |
| 643 | + | currentHotkeyLabel = nil | |
| 644 | + | end | |
| 645 | + | ||
| 646 | + | function updateHotkeyAction() | |
| 647 | + | if not hotkeysManagerLoaded then return end | |
| 648 | + | if currentHotkeyLabel == nil then return end | |
| 649 | + | configValueChanged = true | |
| 650 | + | currentHotkeyLabel.action = translateActionComboboxIndexToAction(hotkeysWindow.action.currentIndex) | |
| 651 | + | updateHotkeyLabel(currentHotkeyLabel) | |
| 652 | + | updateHotkeyForm() | |
| 653 | + | end | |
| 654 | + | ||
| 655 | + | function onHotkeyTextChange(value) | |
| 656 | + | if not hotkeysManagerLoaded then return end | |
| 657 | + | if currentHotkeyLabel == nil then return end | |
| 658 | + | currentHotkeyLabel.value = value | |
| 659 | + | if value == '' then | |
| 660 | + | currentHotkeyLabel.autoSend = false | |
| 661 | + | end | |
| 662 | + | configValueChanged = true | |
| 663 | + | updateHotkeyLabel(currentHotkeyLabel) | |
| 664 | + | updateHotkeyForm() | |
| 665 | + | end | |
| 666 | + | ||
| 667 | + | function onSendAutomaticallyChange(autoSend) | |
| 668 | + | if not hotkeysManagerLoaded then return end | |
| 669 | + | if currentHotkeyLabel == nil then return end | |
| 670 | + | if not currentHotkeyLabel.value or #currentHotkeyLabel.value == 0 then return end | |
| 671 | + | configValueChanged = true | |
| 672 | + | currentHotkeyLabel.autoSend = autoSend | |
| 673 | + | updateHotkeyLabel(currentHotkeyLabel) | |
| 674 | + | updateHotkeyForm() | |
| 675 | + | end | |
| 676 | + | ||
| 677 | + | function onChangeUseType(useTypeWidget) | |
| 678 | + | if not hotkeysManagerLoaded then return end | |
| 679 | + | if currentHotkeyLabel == nil then return end | |
| 680 | + | configValueChanged = true | |
| 681 | + | if useTypeWidget == useOnSelf then | |
| 682 | + | currentHotkeyLabel.useType = HOTKEY_MANAGER_USEONSELF | |
| 683 | + | elseif useTypeWidget == useOnTarget then | |
| 684 | + | currentHotkeyLabel.useType = HOTKEY_MANAGER_USEONTARGET | |
| 685 | + | elseif useTypeWidget == useWith then | |
| 686 | + | currentHotkeyLabel.useType = HOTKEY_MANAGER_USEWITH | |
| 687 | + | else | |
| 688 | + | currentHotkeyLabel.useType = HOTKEY_MANAGER_USE | |
| 689 | + | end | |
| 690 | + | updateHotkeyLabel(currentHotkeyLabel) | |
| 691 | + | updateHotkeyForm() | |
| 692 | + | end | |
| 693 | + | ||
| 694 | + | function onSelectHotkeyLabel(hotkeyLabel) | |
| 695 | + | currentHotkeyLabel = hotkeyLabel | |
| 696 | + | updateHotkeyForm(true) | |
| 697 | + | end | |
| 698 | + | ||
| 699 | + | function hotkeyCapture(assignWindow, keyCode, keyboardModifiers) | |
| 700 | + | local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers) | |
| 701 | + | local comboPreview = assignWindow:getChildById('comboPreview') | |
| 702 | + | comboPreview:setText(tr('Current hotkey to add: %s', keyCombo)) | |
| 703 | + | comboPreview.keyCombo = keyCombo | |
| 704 | + | comboPreview:resizeToText() | |
| 705 | + | assignWindow:getChildById('addButton'):enable() | |
| 706 | + | return true | |
| 707 | + | end | |
| 708 | + | ||
| 709 | + | function hotkeyCaptureOk(assignWindow, keyCombo) | |
| 710 | + | addKeyCombo(keyCombo, nil, true) | |
| 711 | + | assignWindow:destroy() | |
| 712 | + | end |
| @@ -0,0 +1,9 @@ | |||
| 1 | + | Module | |
| 2 | + | name: game_hotkeys | |
| 3 | + | description: Manage client hotkeys | |
| 4 | + | author: andrefaramir, BeniS | |
| 5 | + | website: https://github.com/edubart/otclient | |
| 6 | + | sandboxed: true | |
| 7 | + | scripts: [ hotkeys_extra, hotkeys_manager ] | |
| 8 | + | @onLoad: init() | |
| 9 | + | @onUnload: terminate() |
| @@ -0,0 +1,268 @@ | |||
| 1 | + | HotkeyListLabel < UILabel | |
| 2 | + | font: verdana-11px-monochrome | |
| 3 | + | background-color: alpha | |
| 4 | + | text-offset: 2 0 | |
| 5 | + | focusable: true | |
| 6 | + | phantom: false | |
| 7 | + | ||
| 8 | + | $focus: | |
| 9 | + | background-color: #ffffff22 | |
| 10 | + | ||
| 11 | + | MainWindow | |
| 12 | + | id: hotkeysWindow | |
| 13 | + | !text: tr('Hotkeys') | |
| 14 | + | size: 370 475 | |
| 15 | + | ||
| 16 | + | @onEnter: modules.game_hotkeys.ok() | |
| 17 | + | @onEscape: modules.game_hotkeys.cancel() | |
| 18 | + | ||
| 19 | + | ComboBox | |
| 20 | + | id: configSelector | |
| 21 | + | anchors.left: parent.left | |
| 22 | + | anchors.right: parent.right | |
| 23 | + | anchors.top: parent.top | |
| 24 | + | menu-scroll: true | |
| 25 | + | menu-height: 125 | |
| 26 | + | menu-scroll-step: 25 | |
| 27 | + | text-offset: 5 2 | |
| 28 | + | @onOptionChange: modules.game_hotkeys.onConfigChange() | |
| 29 | + | @onSetup: | | |
| 30 | + | self:addOption(tr("Hotkeys config #1")) | |
| 31 | + | self:addOption(tr("Hotkeys config #2")) | |
| 32 | + | self:addOption(tr("Hotkeys config #3")) | |
| 33 | + | self:addOption(tr("Hotkeys config #4")) | |
| 34 | + | self:addOption(tr("Hotkeys config #5")) | |
| 35 | + | ||
| 36 | + | ||
| 37 | + | VerticalScrollBar | |
| 38 | + | id: currentHotkeysScrollBar | |
| 39 | + | height: 150 | |
| 40 | + | anchors.top: prev.bottom | |
| 41 | + | anchors.right: parent.right | |
| 42 | + | margin-top: 5 | |
| 43 | + | step: 14 | |
| 44 | + | pixels-scroll: true | |
| 45 | + | ||
| 46 | + | TextList | |
| 47 | + | id: currentHotkeys | |
| 48 | + | vertical-scrollbar: currentHotkeysScrollBar | |
| 49 | + | anchors.left: parent.left | |
| 50 | + | anchors.right: prev.left | |
| 51 | + | anchors.top: prev.top | |
| 52 | + | anchors.bottom: prev.bottom | |
| 53 | + | focusable: false | |
| 54 | + | ||
| 55 | + | Button | |
| 56 | + | id: resetButton | |
| 57 | + | width: 96 | |
| 58 | + | !text: tr('Reset All') | |
| 59 | + | anchors.left: parent.left | |
| 60 | + | anchors.top: next.top | |
| 61 | + | @onClick: modules.game_hotkeys.reset() | |
| 62 | + | margin-right: 10 | |
| 63 | + | ||
| 64 | + | Button | |
| 65 | + | id: addHotkeyButton | |
| 66 | + | !text: tr('Add') | |
| 67 | + | width: 64 | |
| 68 | + | anchors.right: next.left | |
| 69 | + | anchors.top: next.top | |
| 70 | + | margin-right: 5 | |
| 71 | + | @onClick: modules.game_hotkeys.addHotkey() | |
| 72 | + | ||
| 73 | + | Button | |
| 74 | + | id: removeHotkeyButton | |
| 75 | + | !text: tr('Remove') | |
| 76 | + | width: 64 | |
| 77 | + | enabled: false | |
| 78 | + | anchors.right: parent.right | |
| 79 | + | anchors.top: currentHotkeys.bottom | |
| 80 | + | margin-top: 8 | |
| 81 | + | @onClick: modules.game_hotkeys.removeHotkey() | |
| 82 | + | ||
| 83 | + | HorizontalSeparator | |
| 84 | + | anchors.left: parent.left | |
| 85 | + | anchors.right: parent.right | |
| 86 | + | anchors.top: prev.bottom | |
| 87 | + | margin-top: 5 | |
| 88 | + | ||
| 89 | + | Label | |
| 90 | + | anchors.left: parent.left | |
| 91 | + | anchors.top: prev.bottom | |
| 92 | + | margin-top: 10 | |
| 93 | + | !text: tr('Extra action:') | |
| 94 | + | ||
| 95 | + | ComboBox | |
| 96 | + | id: action | |
| 97 | + | anchors.left: prev.right | |
| 98 | + | anchors.right: parent.right | |
| 99 | + | anchors.top: prev.top | |
| 100 | + | margin-left: 5 | |
| 101 | + | margin-top: -4 | |
| 102 | + | enabled: false | |
| 103 | + | @onOptionChange: modules.game_hotkeys.updateHotkeyAction() | |
| 104 | + | ||
| 105 | + | HorizontalSeparator | |
| 106 | + | anchors.left: parent.left | |
| 107 | + | anchors.right: parent.right | |
| 108 | + | anchors.top: prev.bottom | |
| 109 | + | margin-top: 5 | |
| 110 | + | ||
| 111 | + | Label | |
| 112 | + | id: hotKeyTextLabel | |
| 113 | + | !text: tr('Edit hotkey text:') | |
| 114 | + | enable: false | |
| 115 | + | anchors.left: parent.left | |
| 116 | + | anchors.right: parent.right | |
| 117 | + | anchors.top: prev.bottom | |
| 118 | + | margin-top: 6 | |
| 119 | + | ||
| 120 | + | TextEdit | |
| 121 | + | id: hotkeyText | |
| 122 | + | enabled: false | |
| 123 | + | anchors.left: parent.left | |
| 124 | + | anchors.right: parent.right | |
| 125 | + | anchors.top: prev.bottom | |
| 126 | + | margin-top: 2 | |
| 127 | + | @onTextChange: modules.game_hotkeys.onHotkeyTextChange(self:getText()) | |
| 128 | + | ||
| 129 | + | CheckBox | |
| 130 | + | id: sendAutomatically | |
| 131 | + | !text: tr('Send automatically') | |
| 132 | + | anchors.left: parent.left | |
| 133 | + | anchors.right: parent.right | |
| 134 | + | anchors.top: prev.bottom | |
| 135 | + | enabled:false | |
| 136 | + | margin-top: 5 | |
| 137 | + | @onCheckChange: modules.game_hotkeys.onSendAutomaticallyChange(self:isChecked()) | |
| 138 | + | ||
| 139 | + | Item | |
| 140 | + | id: itemPreview | |
| 141 | + | anchors.left: parent.left | |
| 142 | + | anchors.top: prev.bottom | |
| 143 | + | margin-top: 10 | |
| 144 | + | virtual: true | |
| 145 | + | ||
| 146 | + | Button | |
| 147 | + | id: selectObjectButton | |
| 148 | + | !text: tr('Select object') | |
| 149 | + | width: 128 | |
| 150 | + | enabled: false | |
| 151 | + | anchors.left: prev.right | |
| 152 | + | anchors.top: prev.top | |
| 153 | + | margin-left: 10 | |
| 154 | + | @onClick: modules.game_hotkeys.startChooseItem() | |
| 155 | + | ||
| 156 | + | Button | |
| 157 | + | id: clearObjectButton | |
| 158 | + | !text: tr('Clear object') | |
| 159 | + | width: 128 | |
| 160 | + | enabled: false | |
| 161 | + | anchors.left: prev.left | |
| 162 | + | anchors.right: prev.right | |
| 163 | + | anchors.top: prev.bottom | |
| 164 | + | margin-top: 2 | |
| 165 | + | @onClick: modules.game_hotkeys.clearObject() | |
| 166 | + | ||
| 167 | + | ButtonBox | |
| 168 | + | id: useOnSelf | |
| 169 | + | !text: tr('Use on yourself') | |
| 170 | + | width: 128 | |
| 171 | + | enabled: false | |
| 172 | + | anchors.left: selectObjectButton.right | |
| 173 | + | anchors.right: parent.right | |
| 174 | + | anchors.top: selectObjectButton.top | |
| 175 | + | checked: false | |
| 176 | + | margin-left: 10 | |
| 177 | + | ||
| 178 | + | ButtonBox | |
| 179 | + | id: useOnTarget | |
| 180 | + | !text: tr('Use on target') | |
| 181 | + | width: 128 | |
| 182 | + | enabled: false | |
| 183 | + | anchors.left: prev.left | |
| 184 | + | anchors.right: parent.right | |
| 185 | + | anchors.top: prev.bottom | |
| 186 | + | checked: false | |
| 187 | + | margin-top: 2 | |
| 188 | + | ||
| 189 | + | ButtonBox | |
| 190 | + | id: useWith | |
| 191 | + | !text: tr('With crosshair') | |
| 192 | + | width: 128 | |
| 193 | + | enabled: false | |
| 194 | + | anchors.left: prev.left | |
| 195 | + | anchors.right: parent.right | |
| 196 | + | anchors.top: prev.bottom | |
| 197 | + | checked: false | |
| 198 | + | margin-top: 2 | |
| 199 | + | ||
| 200 | + | HorizontalSeparator | |
| 201 | + | id: separator | |
| 202 | + | anchors.left: parent.left | |
| 203 | + | anchors.right: parent.right | |
| 204 | + | anchors.bottom: next.top | |
| 205 | + | margin-bottom: 5 | |
| 206 | + | ||
| 207 | + | Button | |
| 208 | + | id: okButton | |
| 209 | + | !text: tr('Ok') | |
| 210 | + | width: 64 | |
| 211 | + | anchors.right: next.left | |
| 212 | + | anchors.bottom: parent.bottom | |
| 213 | + | @onClick: modules.game_hotkeys.ok() | |
| 214 | + | margin-right: 10 | |
| 215 | + | ||
| 216 | + | Button | |
| 217 | + | id: cancelButton | |
| 218 | + | !text: tr('Cancel') | |
| 219 | + | width: 64 | |
| 220 | + | anchors.right: parent.right | |
| 221 | + | anchors.bottom: parent.bottom | |
| 222 | + | @onClick: modules.game_hotkeys.cancel() | |
| 223 | + | ||
| 224 | + | HotkeyAssignWindow < MainWindow | |
| 225 | + | id: assignWindow | |
| 226 | + | !text: tr('Button Assign') | |
| 227 | + | size: 360 150 | |
| 228 | + | @onEscape: self:destroy() | |
| 229 | + | ||
| 230 | + | Label | |
| 231 | + | !text: tr('Please, press the key you wish to add onto your hotkeys manager') | |
| 232 | + | anchors.left: parent.left | |
| 233 | + | anchors.right: parent.right | |
| 234 | + | anchors.top: parent.top | |
| 235 | + | text-auto-resize: true | |
| 236 | + | text-align: left | |
| 237 | + | ||
| 238 | + | Label | |
| 239 | + | id: comboPreview | |
| 240 | + | !text: tr('Current hotkey to add: %s', 'none') | |
| 241 | + | anchors.horizontalCenter: parent.horizontalCenter | |
| 242 | + | anchors.top: prev.bottom | |
| 243 | + | margin-top: 10 | |
| 244 | + | text-auto-resize: true | |
| 245 | + | ||
| 246 | + | HorizontalSeparator | |
| 247 | + | id: separator | |
| 248 | + | anchors.left: parent.left | |
| 249 | + | anchors.right: parent.right | |
| 250 | + | anchors.bottom: next.top | |
| 251 | + | margin-bottom: 10 | |
| 252 | + | ||
| 253 | + | Button | |
| 254 | + | id: addButton | |
| 255 | + | !text: tr('Add') | |
| 256 | + | width: 64 | |
| 257 | + | anchors.right: next.left | |
| 258 | + | anchors.bottom: parent.bottom | |
| 259 | + | margin-right: 10 | |
| 260 | + | @onClick: modules.game_hotkeys.hotkeyCaptureOk(self:getParent(), self:getParent():getChildById('comboPreview').keyCombo) | |
| 261 | + | ||
| 262 | + | Button | |
| 263 | + | id: cancelButton | |
| 264 | + | !text: tr('Cancel') | |
| 265 | + | width: 64 | |
| 266 | + | anchors.right: parent.right | |
| 267 | + | anchors.bottom: parent.bottom | |
| 268 | + | @onClick: self:getParent():destroy() |
| @@ -0,0 +1,313 @@ | |||
| 1 | + | local imbuingWindow | |
| 2 | + | local bankGold = 0 | |
| 3 | + | local inventoryGold = 0 | |
| 4 | + | local itemImbuements = {} | |
| 5 | + | local emptyImbue | |
| 6 | + | local groupsCombo | |
| 7 | + | local imbueLevelsCombo | |
| 8 | + | local protectionBtn | |
| 9 | + | local clearImbue | |
| 10 | + | local selectedImbue | |
| 11 | + | local imbueItems = {} | |
| 12 | + | local protection = false | |
| 13 | + | local clearConfirmWindow | |
| 14 | + | local imbueConfirmWindow | |
| 15 | + | ||
| 16 | + | function init() | |
| 17 | + | connect(g_game, { | |
| 18 | + | onGameEnd = hide, | |
| 19 | + | onResourceBalance = onResourceBalance, | |
| 20 | + | onImbuementWindow = onImbuementWindow, | |
| 21 | + | onCloseImbuementWindow = onCloseImbuementWindow | |
| 22 | + | }) | |
| 23 | + | ||
| 24 | + | imbuingWindow = g_ui.displayUI('imbuing') | |
| 25 | + | emptyImbue = imbuingWindow.emptyImbue | |
| 26 | + | groupsCombo = emptyImbue.groups | |
| 27 | + | imbueLevelsCombo = emptyImbue.imbuement | |
| 28 | + | protectionBtn = emptyImbue.protection | |
| 29 | + | clearImbue = imbuingWindow.clearImbue | |
| 30 | + | imbuingWindow:hide() | |
| 31 | + | ||
| 32 | + | groupsCombo.onOptionChange = function(widget) | |
| 33 | + | imbueLevelsCombo:clear() | |
| 34 | + | if itemImbuements ~= nil then | |
| 35 | + | local selectedGroup = groupsCombo:getCurrentOption().text | |
| 36 | + | for _,imbuement in ipairs(itemImbuements) do | |
| 37 | + | if imbuement["group"] == selectedGroup then | |
| 38 | + | emptyImbue.imbuement:addOption(imbuement["name"]) | |
| 39 | + | end | |
| 40 | + | end | |
| 41 | + | imbueLevelsCombo.onOptionChange(imbueLevelsCombo) -- update options | |
| 42 | + | end | |
| 43 | + | end | |
| 44 | + | ||
| 45 | + | imbueLevelsCombo.onOptionChange = function(widget) | |
| 46 | + | setProtection(false) | |
| 47 | + | local selectedGroup = groupsCombo:getCurrentOption().text | |
| 48 | + | for _,imbuement in ipairs(itemImbuements) do | |
| 49 | + | if imbuement["group"] == selectedGroup then | |
| 50 | + | if #imbuement["sources"] == widget.currentIndex then | |
| 51 | + | selectedImbue = imbuement | |
| 52 | + | for i,source in ipairs(imbuement["sources"]) do | |
| 53 | + | for _,item in ipairs(imbueItems) do | |
| 54 | + | if item:getId() == source["item"]:getId() then | |
| 55 | + | if item:getCount() >= source["item"]:getCount() then | |
| 56 | + | emptyImbue.imbue:setImageSource("/images/game/imbuing/imbue_green") | |
| 57 | + | emptyImbue.imbue:setEnabled(true) | |
| 58 | + | emptyImbue.requiredItems:getChildByIndex(i).count:setColor("white") | |
| 59 | + | end | |
| 60 | + | if item:getCount() < source["item"]:getCount() then | |
| 61 | + | emptyImbue.imbue:setEnabled(false) | |
| 62 | + | emptyImbue.imbue:setImageSource("/images/game/imbuing/imbue_empty") | |
| 63 | + | emptyImbue.requiredItems:getChildByIndex(i).count:setColor("red") | |
| 64 | + | end | |
| 65 | + | emptyImbue.requiredItems:getChildByIndex(i).count:setText(item:getCount() .. "/" .. source["item"]:getCount()) | |
| 66 | + | end | |
| 67 | + | end | |
| 68 | + | emptyImbue.requiredItems:getChildByIndex(i).item:setItemId(source["item"]:getId()) | |
| 69 | + | emptyImbue.requiredItems:getChildByIndex(i).item:setTooltip("The imbuement requires " .. source["description"] .. ".") | |
| 70 | + | end | |
| 71 | + | for i = 3, widget.currentIndex + 1, -1 do | |
| 72 | + | emptyImbue.requiredItems:getChildByIndex(i).count:setText("") | |
| 73 | + | emptyImbue.requiredItems:getChildByIndex(i).item:setItemId(0) | |
| 74 | + | emptyImbue.requiredItems:getChildByIndex(i).item:setTooltip("") | |
| 75 | + | end | |
| 76 | + | emptyImbue.protectionCost:setText(imbuement["protectionCost"]) | |
| 77 | + | emptyImbue.cost:setText(imbuement["cost"]) | |
| 78 | + | if not protection and (bankGold + inventoryGold) < imbuement["cost"] then | |
| 79 | + | emptyImbue.imbue:setEnabled(false) | |
| 80 | + | emptyImbue.imbue:setImageSource("/images/game/imbuing/imbue_empty") | |
| 81 | + | emptyImbue.cost:setColor("red") | |
| 82 | + | end | |
| 83 | + | if not protection and (bankGold + inventoryGold) >= imbuement["cost"] then | |
| 84 | + | emptyImbue.cost:setColor("white") | |
| 85 | + | end | |
| 86 | + | if protection and (bankGold + inventoryGold) < (imbuement["cost"] + imbuement["protectionCost"]) then | |
| 87 | + | emptyImbue.imbue:setEnabled(false) | |
| 88 | + | emptyImbue.imbue:setImageSource("/images/game/imbuing/imbue_empty") | |
| 89 | + | emptyImbue.cost:setColor("red") | |
| 90 | + | end | |
| 91 | + | if protection and (bankGold + inventoryGold) >= (imbuement["cost"] + imbuement["protectionCost"]) then | |
| 92 | + | emptyImbue.cost:setColor("white") | |
| 93 | + | end | |
| 94 | + | emptyImbue.successRate:setText(imbuement["successRate"] .. "%") | |
| 95 | + | if selectedImbue["successRate"] > 50 then | |
| 96 | + | emptyImbue.successRate:setColor("white") | |
| 97 | + | else | |
| 98 | + | emptyImbue.successRate:setColor("red") | |
| 99 | + | end | |
| 100 | + | emptyImbue.description:setText(imbuement["description"]) | |
| 101 | + | end | |
| 102 | + | end | |
| 103 | + | end | |
| 104 | + | end | |
| 105 | + | ||
| 106 | + | protectionBtn.onClick = function() | |
| 107 | + | setProtection(not protection) | |
| 108 | + | end | |
| 109 | + | end | |
| 110 | + | ||
| 111 | + | function setProtection(value) | |
| 112 | + | protection = value | |
| 113 | + | if protection then | |
| 114 | + | emptyImbue.cost:setText(selectedImbue["cost"] + selectedImbue["protectionCost"]) | |
| 115 | + | emptyImbue.successRate:setText("100%") | |
| 116 | + | emptyImbue.successRate:setColor("green") | |
| 117 | + | protectionBtn:setImageClip(torect("66 0 66 66")) | |
| 118 | + | else | |
| 119 | + | if selectedImbue then | |
| 120 | + | emptyImbue.cost:setText(selectedImbue["cost"]) | |
| 121 | + | emptyImbue.successRate:setText(selectedImbue["successRate"] .. "%") | |
| 122 | + | if selectedImbue["successRate"] > 50 then | |
| 123 | + | emptyImbue.successRate:setColor("white") | |
| 124 | + | else | |
| 125 | + | emptyImbue.successRate:setColor("red") | |
| 126 | + | end | |
| 127 | + | end | |
| 128 | + | protectionBtn:setImageClip(torect("0 0 66 66")) | |
| 129 | + | end | |
| 130 | + | end | |
| 131 | + | ||
| 132 | + | function terminate() | |
| 133 | + | disconnect(g_game, { | |
| 134 | + | onGameEnd = hide, | |
| 135 | + | onResourceBalance = onResourceBalance, | |
| 136 | + | onImbuementWindow = onImbuementWindow, | |
| 137 | + | onCloseImbuementWindow = onCloseImbuementWindow | |
| 138 | + | }) | |
| 139 | + | ||
| 140 | + | imbuingWindow:destroy() | |
| 141 | + | end | |
| 142 | + | ||
| 143 | + | function resetSlots() | |
| 144 | + | emptyImbue:setVisible(false) | |
| 145 | + | clearImbue:setVisible(false) | |
| 146 | + | for i=1,3 do | |
| 147 | + | imbuingWindow.itemInfo.slots:getChildByIndex(i):setText("Slot " .. i) | |
| 148 | + | imbuingWindow.itemInfo.slots:getChildByIndex(i):setEnabled(false) | |
| 149 | + | imbuingWindow.itemInfo.slots:getChildByIndex(i):setTooltip("Items can have up to three imbuements slots. This slot is not available for this item.") | |
| 150 | + | imbuingWindow.itemInfo.slots:getChildByIndex(i).onClick = nil | |
| 151 | + | end | |
| 152 | + | end | |
| 153 | + | ||
| 154 | + | function selectSlot(widget, slotId, activeSlot) | |
| 155 | + | if activeSlot then | |
| 156 | + | emptyImbue:setVisible(false) | |
| 157 | + | widget:setText(activeSlot[1]["name"]) | |
| 158 | + | clearImbue.title:setText('Clear Imbuement "' .. activeSlot[1]["name"] .. '"') | |
| 159 | + | clearImbue.groups:clear() | |
| 160 | + | clearImbue.groups:addOption(activeSlot[1]["group"]) | |
| 161 | + | clearImbue.imbuement:clear() | |
| 162 | + | clearImbue.imbuement:addOption(activeSlot[1]["name"]) | |
| 163 | + | clearImbue.description:setText(activeSlot[1]["description"]) | |
| 164 | + | ||
| 165 | + | hours = string.format("%02.f", math.floor(activeSlot[2]/3600)) | |
| 166 | + | mins = string.format("%02.f", math.floor(activeSlot[2]/60 - (hours*60))) | |
| 167 | + | clearImbue.time.timeRemaining:setText(hours..":"..mins.."h") | |
| 168 | + | ||
| 169 | + | clearImbue.cost:setText(activeSlot[3]) | |
| 170 | + | if (bankGold + inventoryGold) < activeSlot[3] then | |
| 171 | + | emptyImbue.clear:setEnabled(false) | |
| 172 | + | emptyImbue.clear:setImageSource("/images/game/imbuing/imbue_empty") | |
| 173 | + | emptyImbue.cost:setColor("red") | |
| 174 | + | end | |
| 175 | + | ||
| 176 | + | local yesCallback = function() | |
| 177 | + | g_game.clearImbuement(slotId) | |
| 178 | + | widget:setText("Slot " .. (slotId + 1)) | |
| 179 | + | if clearConfirmWindow then | |
| 180 | + | clearConfirmWindow:destroy() | |
| 181 | + | clearConfirmWindow=nil | |
| 182 | + | end | |
| 183 | + | end | |
| 184 | + | local noCallback = function() | |
| 185 | + | imbuingWindow:show() | |
| 186 | + | if clearConfirmWindow then | |
| 187 | + | clearConfirmWindow:destroy() | |
| 188 | + | clearConfirmWindow=nil | |
| 189 | + | end | |
| 190 | + | end | |
| 191 | + | ||
| 192 | + | clearImbue.clear.onClick = function() | |
| 193 | + | imbuingWindow:hide() | |
| 194 | + | clearConfirmWindow = displayGeneralBox(tr('Confirm Clearing'), tr('Do you wish to spend ' .. activeSlot[3] .. ' gold coins to clear the imbuement "' .. activeSlot[1]["name"] .. '" from your item?'), { | |
| 195 | + | { text=tr('Yes'), callback=yesCallback }, | |
| 196 | + | { text=tr('No'), callback=noCallback }, | |
| 197 | + | anchor=AnchorHorizontalCenter}, yesCallback, noCallback) | |
| 198 | + | end | |
| 199 | + | ||
| 200 | + | clearImbue:setVisible(true) | |
| 201 | + | else | |
| 202 | + | emptyImbue:setVisible(true) | |
| 203 | + | clearImbue:setVisible(false) | |
| 204 | + | ||
| 205 | + | local yesCallback = function() | |
| 206 | + | g_game.applyImbuement(slotId, selectedImbue["id"], protection) | |
| 207 | + | if clearConfirmWindow then | |
| 208 | + | clearConfirmWindow:destroy() | |
| 209 | + | clearConfirmWindow=nil | |
| 210 | + | end | |
| 211 | + | widget:setText(selectedImbue["name"]) | |
| 212 | + | imbuingWindow:show() | |
| 213 | + | end | |
| 214 | + | local noCallback = function() | |
| 215 | + | imbuingWindow:show() | |
| 216 | + | if clearConfirmWindow then | |
| 217 | + | clearConfirmWindow:destroy() | |
| 218 | + | clearConfirmWindow=nil | |
| 219 | + | end | |
| 220 | + | end | |
| 221 | + | ||
| 222 | + | emptyImbue.imbue.onClick = function() | |
| 223 | + | imbuingWindow:hide() | |
| 224 | + | local cost = selectedImbue["cost"] | |
| 225 | + | local successRate = selectedImbue["successRate"] | |
| 226 | + | if protection then | |
| 227 | + | cost = cost + selectedImbue["protectionCost"] | |
| 228 | + | successRate = "100" | |
| 229 | + | end | |
| 230 | + | clearConfirmWindow = displayGeneralBox(tr('Confirm Imbuing Attempt'), 'You are about to imbue your item with "' .. selectedImbue["name"] .. '".\nYour chance to succeed is ' .. successRate .. '%. It will consume the required astral sources and '.. cost ..' gold coins.\nDo you wish to proceed?', { | |
| 231 | + | { text=tr('Yes'), callback=yesCallback }, | |
| 232 | + | { text=tr('No'), callback=noCallback }, | |
| 233 | + | anchor=AnchorHorizontalCenter}, yesCallback, noCallback) | |
| 234 | + | end | |
| 235 | + | end | |
| 236 | + | end | |
| 237 | + | ||
| 238 | + | function onImbuementWindow(itemId, slots, activeSlots, imbuements, needItems) | |
| 239 | + | if not itemId then | |
| 240 | + | return | |
| 241 | + | end | |
| 242 | + | resetSlots() | |
| 243 | + | imbueItems = table.copy(needItems) | |
| 244 | + | imbuingWindow.itemInfo.item:setItemId(itemId) | |
| 245 | + | ||
| 246 | + | for i=1, slots do | |
| 247 | + | local slot = imbuingWindow.itemInfo.slots:getChildByIndex(i) | |
| 248 | + | slot.onClick = function(widget) | |
| 249 | + | selectSlot(widget, i - 1) | |
| 250 | + | end | |
| 251 | + | slot:setTooltip("Use this slot to imbue your item. Depending on the item you can have up to three different imbuements.") | |
| 252 | + | slot:setEnabled(true) | |
| 253 | + | ||
| 254 | + | if slot:getId() == "slot0" then | |
| 255 | + | selectSlot(slot, i - 1) | |
| 256 | + | end | |
| 257 | + | end | |
| 258 | + | ||
| 259 | + | for i, slot in pairs(activeSlots) do | |
| 260 | + | local activeSlotBtn = imbuingWindow.itemInfo.slots:getChildById("slot" .. i) | |
| 261 | + | activeSlotBtn.onClick = function(widget) | |
| 262 | + | selectSlot(widget, i, slot) | |
| 263 | + | end | |
| 264 | + | if activeSlotBtn:getId() == "slot0" then | |
| 265 | + | selectSlot(activeSlotBtn, i, slot) | |
| 266 | + | end | |
| 267 | + | end | |
| 268 | + | ||
| 269 | + | if imbuements ~= nil then | |
| 270 | + | groupsCombo:clear() | |
| 271 | + | imbueLevelsCombo:clear() | |
| 272 | + | itemImbuements = table.copy(imbuements) | |
| 273 | + | for _,imbuement in ipairs(itemImbuements) do | |
| 274 | + | if not groupsCombo:isOption(imbuement["group"]) then | |
| 275 | + | groupsCombo:addOption(imbuement["group"]) | |
| 276 | + | end | |
| 277 | + | end | |
| 278 | + | end | |
| 279 | + | show() | |
| 280 | + | end | |
| 281 | + | ||
| 282 | + | function onResourceBalance(type, balance) | |
| 283 | + | if type == 0 then | |
| 284 | + | bankGold = balance | |
| 285 | + | elseif type == 1 then | |
| 286 | + | inventoryGold = balance | |
| 287 | + | end | |
| 288 | + | if type == 0 or type == 1 then | |
| 289 | + | imbuingWindow.balance:setText(tr("Balance") .. ":\n" .. (bankGold + inventoryGold)) | |
| 290 | + | end | |
| 291 | + | end | |
| 292 | + | ||
| 293 | + | function onCloseImbuementWindow() | |
| 294 | + | resetSlots() | |
| 295 | + | end | |
| 296 | + | ||
| 297 | + | function hide() | |
| 298 | + | g_game.closeImbuingWindow() | |
| 299 | + | imbuingWindow:hide() | |
| 300 | + | end | |
| 301 | + | ||
| 302 | + | function show() | |
| 303 | + | imbuingWindow:show() | |
| 304 | + | imbuingWindow:raise() | |
| 305 | + | imbuingWindow:focus() | |
| 306 | + | end | |
| 307 | + | ||
| 308 | + | function toggle() | |
| 309 | + | if imbuingWindow:isVisible() then | |
| 310 | + | return hide() | |
| 311 | + | end | |
| 312 | + | show() | |
| 313 | + | end |
| @@ -0,0 +1,9 @@ | |||
| 1 | + | Module | |
| 2 | + | name: game_imbuing | |
| 3 | + | description: imbuing | |
| 4 | + | author: Vincent#1766 on discord | |
| 5 | + | website: http://otclient.ovh | |
| 6 | + | sandboxed: true | |
| 7 | + | scripts: [ imbuing ] | |
| 8 | + | @onLoad: init() | |
| 9 | + | @onUnload: terminate() |
| @@ -0,0 +1,346 @@ | |||
| 1 | + | Slot < Button | |
| 2 | + | width: 70 | |
| 3 | + | height: 66 | |
| 4 | + | anchors.verticalCenter: parent.verticalCenter | |
| 5 | + | enabled: false | |
| 6 | + | text-wrap: true | |
| 7 | + | !tooltip: tr('Items can have up to three imbuements slots. This slot is not available for this item.') | |
| 8 | + | ||
| 9 | + | RequiredItem < Panel | |
| 10 | + | width: 66 | |
| 11 | + | height: 90 | |
| 12 | + | ||
| 13 | + | UIItem | |
| 14 | + | id: item | |
| 15 | + | height: 66 | |
| 16 | + | width: 66 | |
| 17 | + | anchors.left: parent.left | |
| 18 | + | anchors.top: parent.top | |
| 19 | + | ||
| 20 | + | FlatLabel | |
| 21 | + | id: count | |
| 22 | + | margin-top: 5 | |
| 23 | + | text-align: center | |
| 24 | + | anchors.left: prev.left | |
| 25 | + | anchors.right: prev.right | |
| 26 | + | anchors.top: prev.bottom | |
| 27 | + | ||
| 28 | + | ItemInformation < Panel | |
| 29 | + | height: 100 | |
| 30 | + | border: 1 black | |
| 31 | + | padding: 5 | |
| 32 | + | ||
| 33 | + | Label | |
| 34 | + | id: title | |
| 35 | + | anchors.top: parent.top | |
| 36 | + | anchors.left: parent.left | |
| 37 | + | anchors.right: parent.right | |
| 38 | + | text-align: center | |
| 39 | + | !text: tr("Item Information") | |
| 40 | + | ||
| 41 | + | UIItem | |
| 42 | + | id: item | |
| 43 | + | anchors.top: parent.top | |
| 44 | + | anchors.left: parent.left | |
| 45 | + | anchors.VerticalCenter: parent.VerticalCenter | |
| 46 | + | margin-left: 10 | |
| 47 | + | margin-top: 5 | |
| 48 | + | height: 64 | |
| 49 | + | width: 64 | |
| 50 | + | ||
| 51 | + | Panel | |
| 52 | + | id: slots | |
| 53 | + | width: 240 | |
| 54 | + | height: 66 | |
| 55 | + | margin-top: 5 | |
| 56 | + | anchors.VerticalCenter: parent.VerticalCenter | |
| 57 | + | anchors.top: prev.top | |
| 58 | + | anchors.right: parent.right | |
| 59 | + | ||
| 60 | + | Slot | |
| 61 | + | id: slot0 | |
| 62 | + | !text: tr("Slot 1") | |
| 63 | + | text-align: center | |
| 64 | + | anchors.left: parent.left | |
| 65 | + | ||
| 66 | + | Slot | |
| 67 | + | id: slot1 | |
| 68 | + | !text: tr("Slot 2") | |
| 69 | + | text-align: center | |
| 70 | + | margin-left: 10 | |
| 71 | + | anchors.left: prev.right | |
| 72 | + | ||
| 73 | + | Slot | |
| 74 | + | id: slot2 | |
| 75 | + | !text: tr("Slot 3") | |
| 76 | + | text-align: center | |
| 77 | + | margin-left: 10 | |
| 78 | + | anchors.left: prev.right | |
| 79 | + | ||
| 80 | + | Label | |
| 81 | + | id: selectSlot | |
| 82 | + | margin-right: 15 | |
| 83 | + | anchors.right: slots.left | |
| 84 | + | anchors.VerticalCenter: parent.VerticalCenter | |
| 85 | + | !text: tr("Select Slot:") | |
| 86 | + | ||
| 87 | + | EmptyImbue < Panel | |
| 88 | + | height: 240 | |
| 89 | + | border: 1 black | |
| 90 | + | padding: 5 | |
| 91 | + | ||
| 92 | + | Label | |
| 93 | + | id: title | |
| 94 | + | anchors.top: parent.top | |
| 95 | + | anchors.left: parent.left | |
| 96 | + | anchors.right: parent.right | |
| 97 | + | text-align: center | |
| 98 | + | !text: tr("Imbue Empty Slot") | |
| 99 | + | ||
| 100 | + | ComboBox | |
| 101 | + | id: groups | |
| 102 | + | anchors.top: title.bottom | |
| 103 | + | anchors.left: parent.left | |
| 104 | + | anchors.right: parent.HorizontalCenter | |
| 105 | + | margin-right: 3 | |
| 106 | + | margin-top: 5 | |
| 107 | + | isdisabled: true | |
| 108 | + | ||
| 109 | + | ComboBox | |
| 110 | + | id: imbuement | |
| 111 | + | anchors.top: prev.top | |
| 112 | + | anchors.left: groups.right | |
| 113 | + | anchors.right: parent.right | |
| 114 | + | margin-left: 3 | |
| 115 | + | ||
| 116 | + | Label | |
| 117 | + | id: description | |
| 118 | + | anchors.top: prev.bottom | |
| 119 | + | anchors.left: parent.left | |
| 120 | + | anchors.right: parent.right | |
| 121 | + | margin-top: 10 | |
| 122 | + | height: 85 | |
| 123 | + | ||
| 124 | + | Label | |
| 125 | + | id: info | |
| 126 | + | anchors.bottom: prev.bottom | |
| 127 | + | anchors.left: parent.left | |
| 128 | + | !text: tr('Requires the following astral sources:') | |
| 129 | + | ||
| 130 | + | Label | |
| 131 | + | id: successRate | |
| 132 | + | anchors.top: info.top | |
| 133 | + | anchors.right: parent.right | |
| 134 | + | width: 35 | |
| 135 | + | text-align: right | |
| 136 | + | ||
| 137 | + | Label | |
| 138 | + | id: successRateTitle | |
| 139 | + | anchors.top: info.top | |
| 140 | + | anchors.right: successRate.left | |
| 141 | + | margin-right: 15 | |
| 142 | + | !text: tr('Success Rate:') | |
| 143 | + | ||
| 144 | + | Panel | |
| 145 | + | id: requiredItems | |
| 146 | + | width: 210 | |
| 147 | + | height: 90 | |
| 148 | + | anchors.left: parent.left | |
| 149 | + | anchors.bottom: parent.bottom | |
| 150 | + | ||
| 151 | + | RequiredItem | |
| 152 | + | id: item1 | |
| 153 | + | anchors.left: parent.left | |
| 154 | + | ||
| 155 | + | RequiredItem | |
| 156 | + | id: item2 | |
| 157 | + | margin-left: 10 | |
| 158 | + | anchors.left: prev.right | |
| 159 | + | ||
| 160 | + | RequiredItem | |
| 161 | + | id: item3 | |
| 162 | + | margin-left: 10 | |
| 163 | + | anchors.left: prev.right | |
| 164 | + | ||
| 165 | + | UIButton | |
| 166 | + | id: protection | |
| 167 | + | width: 66 | |
| 168 | + | height: 66 | |
| 169 | + | anchors.top: prev.top | |
| 170 | + | anchors.left: prev.right | |
| 171 | + | image-source: /images/game/imbuing/100percent | |
| 172 | + | image-clip: 0 0 66 66 | |
| 173 | + | margin-left: 15 | |
| 174 | + | !tooltip: ("Bribe the fates! Click here to raise your chance to 100%. For guaranteed success use gold.") | |
| 175 | + | ||
| 176 | + | FlatLabel | |
| 177 | + | id: protectionCost | |
| 178 | + | margin-top: 5 | |
| 179 | + | text-align: center | |
| 180 | + | anchors.left: prev.left | |
| 181 | + | anchors.right: prev.right | |
| 182 | + | anchors.top: prev.bottom | |
| 183 | + | ||
| 184 | + | UIWidget | |
| 185 | + | id: horizontalArrow | |
| 186 | + | anchors.left: prev.right | |
| 187 | + | anchors.verticalCenter: requiredItems.verticalCenter | |
| 188 | + | margin-left: 45 | |
| 189 | + | width: 12 | |
| 190 | + | height: 21 | |
| 191 | + | image-source: /images/ui/arrow_horizontal | |
| 192 | + | image-rect: 0 0 12 21 | |
| 193 | + | image-clip: 12 0 12 21 | |
| 194 | + | phantom: false | |
| 195 | + | ||
| 196 | + | UIButton | |
| 197 | + | id: imbue | |
| 198 | + | width: 128 | |
| 199 | + | height: 66 | |
| 200 | + | anchors.top: requiredItems.top | |
| 201 | + | anchors.right: parent.right | |
| 202 | + | image-source: /images/game/imbuing/imbue_empty | |
| 203 | + | image-clip: 0 0 128 66 | |
| 204 | + | margin-left: 15 | |
| 205 | + | !tooltip: tr("Click here to carry out the selected imbuement. This will consume the required astral sources and gold.") | |
| 206 | + | ||
| 207 | + | $pressed: | |
| 208 | + | image-clip: 0 66 128 66 | |
| 209 | + | ||
| 210 | + | FlatLabel | |
| 211 | + | id: cost | |
| 212 | + | margin-top: 5 | |
| 213 | + | text-align: center | |
| 214 | + | anchors.left: prev.left | |
| 215 | + | anchors.right: prev.right | |
| 216 | + | anchors.top: prev.bottom | |
| 217 | + | ||
| 218 | + | ClearImbue < Panel | |
| 219 | + | height: 240 | |
| 220 | + | border: 1 black | |
| 221 | + | padding: 5 | |
| 222 | + | ||
| 223 | + | Label | |
| 224 | + | id: title | |
| 225 | + | anchors.top: parent.top | |
| 226 | + | anchors.left: parent.left | |
| 227 | + | anchors.right: parent.right | |
| 228 | + | text-align: center | |
| 229 | + | !text: tr("Clear Imbuement") | |
| 230 | + | ||
| 231 | + | ComboBox | |
| 232 | + | id: groups | |
| 233 | + | anchors.top: title.bottom | |
| 234 | + | anchors.left: parent.left | |
| 235 | + | anchors.right: parent.HorizontalCenter | |
| 236 | + | margin-right: 3 | |
| 237 | + | margin-top: 5 | |
| 238 | + | enabled: false | |
| 239 | + | ||
| 240 | + | ComboBox | |
| 241 | + | id: imbuement | |
| 242 | + | anchors.top: prev.top | |
| 243 | + | anchors.left: groups.right | |
| 244 | + | anchors.right: parent.right | |
| 245 | + | margin-left: 3 | |
| 246 | + | enabled: false | |
| 247 | + | ||
| 248 | + | Label | |
| 249 | + | id: description | |
| 250 | + | anchors.top: prev.bottom | |
| 251 | + | anchors.left: parent.left | |
| 252 | + | anchors.right: parent.right | |
| 253 | + | margin-top: 10 | |
| 254 | + | height: 85 | |
| 255 | + | ||
| 256 | + | Label | |
| 257 | + | id: info | |
| 258 | + | anchors.bottom: prev.bottom | |
| 259 | + | anchors.left: parent.left | |
| 260 | + | !text: tr('Time remaining:') | |
| 261 | + | ||
| 262 | + | Label | |
| 263 | + | id: clearImbuementTitle | |
| 264 | + | anchors.top: info.top | |
| 265 | + | anchors.right: parent.right | |
| 266 | + | !text: tr('Clear imbuement:') | |
| 267 | + | ||
| 268 | + | Panel | |
| 269 | + | id: time | |
| 270 | + | width: 210 | |
| 271 | + | height: 90 | |
| 272 | + | anchors.left: parent.left | |
| 273 | + | anchors.bottom: parent.bottom | |
| 274 | + | ||
| 275 | + | FlatLabel | |
| 276 | + | id: timeRemaining | |
| 277 | + | size: 86 25 | |
| 278 | + | margin-bottom: 20 | |
| 279 | + | text-align: center | |
| 280 | + | anchors.left: parent.left | |
| 281 | + | anchors.right: parent.right | |
| 282 | + | anchors.verticalCenter: parent.verticalCenter | |
| 283 | + | ||
| 284 | + | UIButton | |
| 285 | + | id: clear | |
| 286 | + | width: 128 | |
| 287 | + | height: 66 | |
| 288 | + | anchors.top: time.top | |
| 289 | + | anchors.right: parent.right | |
| 290 | + | image-source: /images/game/imbuing/clear | |
| 291 | + | image-clip: 0 0 128 66 | |
| 292 | + | margin-left: 15 | |
| 293 | + | !tooltip: tr("Your needs have changed? Click here to clear the imbuement from your item for a fee.") | |
| 294 | + | ||
| 295 | + | $pressed: | |
| 296 | + | image-clip: 0 66 128 66 | |
| 297 | + | ||
| 298 | + | FlatLabel | |
| 299 | + | id: cost | |
| 300 | + | margin-top: 5 | |
| 301 | + | text-align: center | |
| 302 | + | anchors.left: prev.left | |
| 303 | + | anchors.right: prev.right | |
| 304 | + | anchors.top: prev.bottom | |
| 305 | + | ||
| 306 | + | ||
| 307 | + | MainWindow | |
| 308 | + | id: imbuingWindow | |
| 309 | + | !text: tr('Imbue Item') | |
| 310 | + | size: 550 430 | |
| 311 | + | background-color: #AAAAAA | |
| 312 | + | @onEscape: modules.game_imbuing.hide() | |
| 313 | + | ||
| 314 | + | ItemInformation | |
| 315 | + | id: itemInfo | |
| 316 | + | anchors.left: parent.left | |
| 317 | + | anchors.top: parent.top | |
| 318 | + | anchors.right: parent.right | |
| 319 | + | ||
| 320 | + | EmptyImbue | |
| 321 | + | id: emptyImbue | |
| 322 | + | anchors.left: parent.left | |
| 323 | + | anchors.top: prev.bottom | |
| 324 | + | anchors.right: parent.right | |
| 325 | + | margin-top: 5 | |
| 326 | + | ||
| 327 | + | ClearImbue | |
| 328 | + | id: clearImbue | |
| 329 | + | anchors.left: parent.left | |
| 330 | + | anchors.top: emptyImbue.top | |
| 331 | + | anchors.right: parent.right | |
| 332 | + | ||
| 333 | + | Button | |
| 334 | + | id: close | |
| 335 | + | !text: tr('Close') | |
| 336 | + | width: 50 | |
| 337 | + | anchors.right: parent.right | |
| 338 | + | anchors.bottom: parent.bottom | |
| 339 | + | @onClick: modules.game_imbuing.hide() | |
| 340 | + | ||
| 341 | + | Label | |
| 342 | + | id: balance | |
| 343 | + | height: 25 | |
| 344 | + | anchors.right: prev.left | |
| 345 | + | anchors.left: parent.left | |
| 346 | + | anchors.bottom: parent.bottom |