mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-04-29 18:59:20 +02:00
1178 lines
34 KiB
Lua
1178 lines
34 KiB
Lua
local actionBars = {}
|
|
local settings = {}
|
|
local settingsFile = ""
|
|
local cachedSettings = nil
|
|
local window = nil
|
|
local mouseGrabberWidget = nil
|
|
|
|
local TYPE = {
|
|
BLANK = 0,
|
|
TEXT = 1,
|
|
SPELL = 2,
|
|
ITEM = 3
|
|
}
|
|
|
|
local ACTION = {
|
|
BLANK = 0,
|
|
EQUIP = 1,
|
|
USE = 2,
|
|
USE_SELF = 3,
|
|
USE_TARGET = 4,
|
|
USE_CROSS = 5
|
|
}
|
|
|
|
-- servers may have different id's, change if not working properly (only for protocols 910+)
|
|
local function translateVocation(id)
|
|
if id == 1 or id == 11 then
|
|
return 8 -- ek
|
|
elseif id == 2 or id == 12 then
|
|
return 7 -- rp
|
|
elseif id == 3 or id == 13 then
|
|
return 5 -- ms
|
|
elseif id == 4 or id == 14 then
|
|
return 6 -- ed
|
|
end
|
|
end
|
|
|
|
local function isSpell(text) -- returns bool or table (spelldata, param text)
|
|
text = text:lower():trim()
|
|
|
|
for spellName, spellData in pairs(SpellInfo['Default']) do
|
|
local words = spellData.words
|
|
local param = spellData.parameter
|
|
local data = spellData
|
|
data.spellName = spellName
|
|
|
|
if not param then
|
|
if words == text then
|
|
return {data=data}
|
|
end
|
|
else
|
|
if text:find(words) then
|
|
text = text:gsub(words, ""):trim()
|
|
text = text:gsub('"', "")
|
|
text = text:gsub("'", "")
|
|
return {data=data, param=text}
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function init()
|
|
connect(g_game, {
|
|
onGameStart = online,
|
|
onGameEnd = offline,
|
|
onSpellGroupCooldown = onSpellGroupCooldown,
|
|
onSpellCooldown = onSpellCooldown
|
|
})
|
|
|
|
if g_game.isOnline() then
|
|
online()
|
|
end
|
|
|
|
-- taken from game_hotkeys
|
|
mouseGrabberWidget = g_ui.createWidget('UIWidget')
|
|
mouseGrabberWidget:setVisible(false)
|
|
mouseGrabberWidget:setFocusable(false)
|
|
mouseGrabberWidget.onMouseRelease = onDropActionButton
|
|
end
|
|
|
|
function terminate()
|
|
disconnect(g_game, {
|
|
onGameStart = online,
|
|
onGameEnd = offline,
|
|
onSpellGroupCooldown = onSpellGroupCooldown,
|
|
onSpellCooldown = onSpellCooldown
|
|
})
|
|
end
|
|
|
|
function createActionBars()
|
|
local bottomPanel = modules.game_interface.getBottomActionPanel()
|
|
local leftPanel = modules.game_interface.getLeftActionPanel()
|
|
local rightPanel = modules.game_interface.getRightActionPanel()
|
|
|
|
-- 1-3: bottom
|
|
-- 4-6: left
|
|
-- 7-9: right
|
|
for i=1,9 do
|
|
local parent
|
|
local index
|
|
local layout
|
|
|
|
if i <= 3 then
|
|
parent = bottomPanel
|
|
index = i
|
|
layout = 'actionbar'
|
|
elseif i <= 6 then
|
|
parent = leftPanel
|
|
index = i - 3
|
|
layout = 'sideactionbar'
|
|
else
|
|
parent = rightPanel
|
|
index = i - 6
|
|
layout = 'sideactionbar'
|
|
end
|
|
|
|
actionBars[i] = g_ui.loadUI(layout, parent)
|
|
actionBars[i]:setId("actionbar."..i)
|
|
actionBars[i].n = i
|
|
parent:moveChildToIndex(actionBars[i], index)
|
|
end
|
|
end
|
|
|
|
function offline()
|
|
-- save settings to json
|
|
save()
|
|
|
|
-- destroy windows
|
|
destroyAssignWindows()
|
|
mouseGrabberWidget:destroy()
|
|
|
|
-- remove binds
|
|
for index, actionbar in ipairs(actionBars) do
|
|
if actionbar.tabBar then
|
|
for i, actionButton in ipairs(actionbar.tabBar:getChildren()) do
|
|
local callback = actionButton.callback
|
|
local hotkey = actionButton.hotkey and actionButton.hotkey:len() > 0 and actionButton.hotkey or false
|
|
|
|
if callback and hotkey then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(hotkey, callback, gameRootPanel)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- destroy actionbars
|
|
for i, panel in ipairs(actionBars) do
|
|
panel:destroy()
|
|
end
|
|
end
|
|
|
|
function online()
|
|
settingsFile = modules.client_profiles.getSettingsFilePath("actionbar_v2.json")
|
|
-- load settings
|
|
load()
|
|
|
|
-- create actionbars
|
|
createActionBars()
|
|
|
|
-- show & setup actionbars
|
|
show()
|
|
|
|
destroyAssignWindows()
|
|
end
|
|
|
|
function show()
|
|
for i=1,#actionBars do
|
|
local actionbar = actionBars[i]
|
|
local enabled = g_settings.getBoolean("actionbar"..i, false)
|
|
|
|
actionbar:setOn(enabled)
|
|
setupActionBar(i)
|
|
end
|
|
end
|
|
|
|
function refresh()
|
|
-- first save
|
|
save()
|
|
|
|
-- recheck file
|
|
settingsFile = modules.client_profiles.getSettingsFilePath("actionbar_v2.json")
|
|
|
|
-- load settings
|
|
load()
|
|
|
|
-- setup actionbars
|
|
show()
|
|
|
|
destroyAssignWindows()
|
|
end
|
|
|
|
function translateHotkeyDesc(text)
|
|
-- formatting similar to cip Tibia 12
|
|
if not text then
|
|
return ""
|
|
end
|
|
|
|
local values = {
|
|
{"Shift", "S"},
|
|
{"Ctrl", "C"},
|
|
{"+", ""},
|
|
{"PageUp", "PgUp"},
|
|
{"PageDown", "PgDown"},
|
|
{"Enter", "Return"},
|
|
{"Insert", "Ins"},
|
|
{"Delete", "Del"},
|
|
{"Escape", "Esc"}
|
|
}
|
|
|
|
for i, v in pairs(values) do
|
|
text = text:gsub(v[1], v[2])
|
|
end
|
|
|
|
if text:len() > 6 then
|
|
text = text:sub(text:len()-3,text:len())
|
|
text = "..."..text
|
|
end
|
|
|
|
return text
|
|
end
|
|
|
|
function destroyAssignWindows()
|
|
local windows = {
|
|
'assignItemWindow',
|
|
'assignSpellWindow',
|
|
'assignTextWindow',
|
|
'assignHotkeyWindow'
|
|
}
|
|
|
|
local rootWidget = g_ui.getRootWidget()
|
|
for i, id in ipairs(windows) do
|
|
local widget = rootWidget[id]
|
|
|
|
if widget then
|
|
widget:destroy()
|
|
end
|
|
end
|
|
end
|
|
|
|
function changeLockState(widget)
|
|
local actionbar = widget:getParent():getParent()
|
|
|
|
widget:setOn(not widget:isOn())
|
|
widget.image:setOn(widget:isOn())
|
|
actionbar.locked = not widget:isOn()
|
|
|
|
settings[actionbar:getId()] = not widget:isOn() or nil
|
|
end
|
|
|
|
function moveActionButtons(widget)
|
|
local dir = widget:getId()
|
|
local actionBar = widget:getParent():getParent()
|
|
local scroll = actionBar.actionScroll
|
|
local buttons = {actionBar.prevPanel.prev, actionBar.prevPanel.first, actionBar.nextPanel.next, actionBar.nextPanel.last}
|
|
|
|
if dir == "next" then
|
|
scroll:increment(37)
|
|
elseif dir == "last" then
|
|
scroll:setValue(scroll:getMaximum())
|
|
elseif dir == "prev" then
|
|
scroll:decrement(37)
|
|
else
|
|
scroll:setValue(scroll:getMinimum())
|
|
end
|
|
|
|
local prevEnabled = scroll:getValue() > 0
|
|
local nextEnabled = scroll:getValue() < scroll:getMaximum()
|
|
|
|
buttons[1]:setOn(prevEnabled)
|
|
buttons[2]:setOn(prevEnabled)
|
|
buttons[3]:setOn(nextEnabled)
|
|
buttons[4]:setOn(nextEnabled)
|
|
buttons[1].image:setOn(prevEnabled)
|
|
buttons[2].image:setOn(prevEnabled)
|
|
buttons[3].image:setOn(nextEnabled)
|
|
buttons[4].image:setOn(nextEnabled)
|
|
end
|
|
|
|
function onDropActionButton(self, mousePosition, mouseButton)
|
|
if not g_ui.isMouseGrabbed() then return end
|
|
|
|
local clickedWidget = modules.game_interface.getRootPanel():recursiveGetChildByPos(mousePosition, false)
|
|
if clickedWidget and clickedWidget:getParent() and clickedWidget:getParent():getStyleName():find('ActionButton') then
|
|
if cachedSettings then
|
|
clickedWidget = clickedWidget:getParent()
|
|
if clickedWidget ~= cachedSettings.widget then
|
|
local clickedHotkey = clickedWidget.hotkey
|
|
local cachedHotkey = cachedSettings.widget.hotkey
|
|
|
|
settings[cachedSettings.id] = settings[clickedWidget:getId()]
|
|
settings[clickedWidget:getId()] = cachedSettings.data
|
|
|
|
local clickedTill = clickedWidget.cooldownTill or 0
|
|
local clickedStart = clickedWidget.cooldownStart or 0
|
|
local cachedTill = cachedSettings.widget.cooldownTill or 0
|
|
local cachedStart = cachedSettings.widget.cooldownStart or 0
|
|
|
|
cachedSettings.widget.cooldownTill = clickedTill
|
|
cachedSettings.widget.cooldownStart = clickedStart
|
|
clickedWidget.cooldownTill = cachedTill
|
|
clickedWidget.cooldownStart = cachedStart
|
|
|
|
-- hotkeys remain unchanged
|
|
settings[cachedSettings.id] = settings[cachedSettings.id] or {}
|
|
settings[cachedSettings.id].hotkey = cachedHotkey
|
|
settings[clickedWidget:getId()] = settings[clickedWidget:getId()] or {}
|
|
settings[clickedWidget:getId()].hotkey = clickedHotkey
|
|
|
|
updateCooldown(clickedWidget)
|
|
updateCooldown(cachedSettings.widget)
|
|
setupButton(cachedSettings.widget)
|
|
setupButton(clickedWidget)
|
|
end
|
|
end
|
|
end
|
|
|
|
cachedSettings.widget.item:setBorderColor('#00000000')
|
|
cachedSettings = nil
|
|
g_mouse.popCursor('target')
|
|
self:ungrabMouse()
|
|
end
|
|
|
|
function setupActionBar(n)
|
|
local actionbar = actionBars[n]
|
|
local visible = actionbar:isVisible()
|
|
locked = settings[actionbar:getId()]
|
|
actionbar.tabBar.onMouseWheel = nil -- disable scroll wheel
|
|
|
|
actionbar.locked = locked
|
|
actionbar.nextPanel.lock:setOn(not locked)
|
|
actionbar.nextPanel.lock.image:setOn(not locked)
|
|
|
|
if not visible then
|
|
return actionbar.tabBar:destroyChildren() -- will hopefully lower stress
|
|
else
|
|
actionbar.tabBar:destroyChildren()
|
|
for i=1,50 do
|
|
local layout = n < 4 and 'ActionButton' or 'SideActionButton'
|
|
local widget = g_ui.createWidget(layout, actionbar.tabBar)
|
|
widget:setId(actionbar.n.."."..i)
|
|
|
|
setupButton(widget)
|
|
end
|
|
end
|
|
end
|
|
|
|
function setupButton(widget)
|
|
local id = widget:getId()
|
|
local config = settings[id]
|
|
local actionbar = widget:getParent():getParent()
|
|
|
|
-- disable count
|
|
widget.item:setShowCount(false)
|
|
|
|
-- remove callback to avoid recurrency
|
|
widget.item.onItemChange = nil
|
|
|
|
-- clear settings
|
|
widget.type = TYPE.BLANK
|
|
widget.text:setText("")
|
|
widget.parameterText:setText("")
|
|
if widget.item:getItemId() ~= 0 then
|
|
widget.item:setItemId(0)
|
|
end
|
|
widget.item:setOn(false)
|
|
widget.autoSay = nil
|
|
widget.action = ACTION.BLANK
|
|
widget.spellData = nil
|
|
widget.item:setItemVisible(true)
|
|
widget.text:setImageSource('')
|
|
widget.hotkey = config and config.hotkey or ""
|
|
widget.callback = nil
|
|
|
|
-- add new settings
|
|
if config and config.type then
|
|
widget.item:setOn(true)
|
|
widget.type = config.type
|
|
widget.text:setText(config.sayText or "")
|
|
if widget.item:getItemId() ~= (config.itemId and config.itemId > 100 and config.itemId or 0) then
|
|
widget.item:setItem(Item.create(config.itemId, 50))
|
|
end
|
|
widget.sayText = config.sayText
|
|
widget.autoSay = config.autoSay
|
|
widget.action = config.action
|
|
widget.spellData = config.spellData
|
|
if config.type ~= 0 and config.type ~= 3 then
|
|
widget.item:setItemVisible(false)
|
|
end
|
|
end
|
|
|
|
-- callback
|
|
setupAction(widget)
|
|
|
|
--hotkey
|
|
widget.hotkeyLabel:setText(translateHotkeyDesc(widget.hotkey))
|
|
|
|
if widget.spellData then
|
|
--image
|
|
widget.text:setImageSource(widget.spellData.source)
|
|
widget.text:setImageClip(widget.spellData.clip)
|
|
|
|
--param
|
|
local param = widget.spellData.param
|
|
if param and param:len() > 6 then
|
|
param = param:sub(1,5) .. "..."
|
|
end
|
|
widget.parameterText:setText(param or "")
|
|
else
|
|
widget.text:setImageSource('')
|
|
end
|
|
|
|
widget.item.onDragEnter = function(self)
|
|
if g_ui.isMouseGrabbed() or actionbar.locked then return end
|
|
mouseGrabberWidget:grabMouse()
|
|
g_mouse.pushCursor('target')
|
|
|
|
self:setBorderColor('#FFFFFF')
|
|
cachedSettings = {id=widget:getId(), data=settings[widget:getId()], widget=widget}
|
|
end
|
|
|
|
-- popupmenu & execute action
|
|
widget.onMouseRelease = function(widget, mousePos, mouseButton)
|
|
if mouseButton == MouseRightButton then
|
|
|
|
local menu = g_ui.createWidget('PopupMenu')
|
|
menu:setGameMenu(true)
|
|
menu:addOption(widget.spellId and tr('Edit Spell') or tr('Assign Spell'), function() assignSpell(widget) end)
|
|
menu:addOption(widget.item:getItemId() > 100 and tr('Edit Object') or tr('Assign Object'), function() assignItem(widget) end)
|
|
menu:addOption(widget.text:getText():len() > 0 and tr('Edit Text') or tr('Assign Text'), function() assignText(widget) end)
|
|
menu:addOption(widget.hotkey and tr('Edit Hotkey') or tr('Assign Hotkey'), function() assignHotkey(widget) end)
|
|
|
|
if widget.type > 0 then
|
|
menu:addSeparator()
|
|
menu:addOption(tr('Clear Action'), function() resetSlot(widget) end)
|
|
end
|
|
menu:display(mousePos)
|
|
elseif mouseButton == MouseLeftButton and widget.callback then
|
|
widget.callback()
|
|
end
|
|
end
|
|
|
|
widget.item.onItemChange = function(widget)
|
|
widget:setOn(true)
|
|
assignItem(widget:getParent())
|
|
end
|
|
|
|
-- tooltip
|
|
local itemAction
|
|
if widget.type == TYPE.ITEM then
|
|
if widget.action == ACTION.EQUIP then
|
|
itemAction = "Equip/Unequip this object"
|
|
elseif widget.action == ACTION.USE then
|
|
itemAction = "Use this object"
|
|
elseif widget.action == ACTION.USE_SELF then
|
|
itemAction = "Use this object on Yourself"
|
|
elseif widget.action == ACTION.USE_TARGET then
|
|
itemAction = "Use this object on Attack Target"
|
|
elseif widget.action == ACTION.USE_CROSS then
|
|
itemAction = "Use this object with Crosshair"
|
|
end
|
|
end
|
|
|
|
local actionDesc
|
|
local spellData = widget.spellData
|
|
if widget.type == TYPE.BLANK then
|
|
actionDesc = "None"
|
|
elseif widget.type == TYPE.TEXT then
|
|
actionDesc = 'Say: "'..widget.text:getText()..'"\n'
|
|
actionDesc = actionDesc.. "Auto sent: " .. (widget.autoSay and "Yes" or "No")
|
|
elseif widget.type == TYPE.SPELL then
|
|
local paramText
|
|
if spellData.param and spellData.param:len() > 0 then
|
|
paramText = ' "'.. spellData.param ..'"'
|
|
else
|
|
paramText = ""
|
|
end
|
|
actionDesc = "Cast " .. spellData.name .."\n"
|
|
actionDesc = actionDesc.. "Formula: ".. spellData.words .. paramText.."\n"
|
|
actionDesc = actionDesc.. "Cooldown: "..spellData.cd.."s\n"
|
|
actionDesc = actionDesc.. "Mana: "..spellData.mana
|
|
elseif widget.type == TYPE.ITEM then
|
|
actionDesc = itemAction
|
|
end
|
|
|
|
local hotkeyDesc = widget.hotkey and widget.hotkey:len() > 0 and widget.hotkey or "None"
|
|
local tooltip = "Action Button "..id
|
|
tooltip = tooltip.."\n\n\tAction: ".. actionDesc
|
|
tooltip = tooltip.."\nHotkeys: ".. hotkeyDesc
|
|
|
|
widget.item:setTooltip(tooltip)
|
|
end
|
|
|
|
function resetSlot(widget)
|
|
local hotkey = settings[widget:getId()] and settings[widget:getId()].hotkey or nil
|
|
if hotkey and hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(widget.hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
|
|
if hotkey then
|
|
settings[widget:getId()] = {hotkey=hotkey}
|
|
else
|
|
settings[widget:getId()] = nil
|
|
end
|
|
|
|
setupButton(widget)
|
|
end
|
|
|
|
function assignItem(widget)
|
|
destroyAssignWindows()
|
|
local radio = UIRadioGroup.create()
|
|
local item = widget.item:getItem()
|
|
local id = widget.item:getItemId()
|
|
|
|
-- check if item wasn't cleared
|
|
if id == 0 and widget.item:isOn() then
|
|
return resetSlot(widget)
|
|
end
|
|
|
|
-- create window
|
|
window = g_ui.loadUI('object', g_ui.getRootWidget())
|
|
window:show()
|
|
window:raise()
|
|
window:focus()
|
|
|
|
-- basics
|
|
window:setText("Assign Object to Action Button "..widget:getId())
|
|
window:setId("assignItemWindow")
|
|
|
|
-- select item
|
|
window.select.onClick = function()
|
|
modules.game_itemselector.show(widget.item)
|
|
end
|
|
|
|
-- checks
|
|
window.item:setShowCount(false)
|
|
window.item.onItemChange = function(widget)
|
|
local item = window.item:getItem()
|
|
|
|
if item then
|
|
for i, child in ipairs(window.checks:getChildren()) do
|
|
-- add to radio grouo
|
|
radio:addWidget(child)
|
|
|
|
-- enabled
|
|
if item:getId() < 100 then
|
|
child:setEnabled(false)
|
|
elseif i < 4 then
|
|
local check = item:isMultiUse()
|
|
child:setEnabled(check)
|
|
if check then
|
|
radio:selectWidget(child)
|
|
end
|
|
elseif g_game.getClientVersion() >= 910 then
|
|
if i == 4 then
|
|
-- local check = item:getClothSlot() > 0
|
|
local check = true -- unfortunately two handed weapons returns cloth 0, so we have to disable this check
|
|
child:setEnabled(check)
|
|
if check then
|
|
radio:selectWidget(child)
|
|
end
|
|
else
|
|
child:setEnabled(true)
|
|
radio:selectWidget(child)
|
|
end
|
|
else
|
|
child:setVisible(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- validation
|
|
window.buttonOk:setEnabled(item and item:getId() > 100)
|
|
window.buttonApply:setEnabled(item and item:getId() > 100)
|
|
end
|
|
|
|
window.item:setItemId(id)
|
|
|
|
-- select current action, if exists
|
|
local actionType = widget.action or 0
|
|
if actionType > ACTION.BLANK then
|
|
local id
|
|
if actionType == ACTION.USE_SELF then
|
|
id = "useSelf"
|
|
elseif actionType == ACTION.USE_TARGET then
|
|
id = "useTarget"
|
|
elseif actionType == ACTION.USE_CROSS then
|
|
id = "useCross"
|
|
elseif actionType == ACTION.EQUIP then
|
|
id = "equip"
|
|
elseif actionType == ACTION.USE then
|
|
id = "use"
|
|
end
|
|
|
|
for i, child in ipairs(radio.widgets) do
|
|
local childId = child:getId()
|
|
if childId == id then
|
|
radio:selectWidget(child)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- functions
|
|
local okFunc = function(destroy)
|
|
local hotkey = settings[widget:getId()] and settings[widget:getId()].hotkey
|
|
if hotkey and hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(widget.hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
|
|
settings[widget:getId()] = {hotkey=hotkey}
|
|
settings[widget:getId()].itemId = window.item:getItemId()
|
|
settings[widget:getId()].type = TYPE.ITEM
|
|
|
|
local selected = radio:getSelectedWidget():getId()
|
|
if selected == "useSelf" then
|
|
settings[widget:getId()].action = ACTION.USE_SELF
|
|
elseif selected == "useTarget" then
|
|
settings[widget:getId()].action = ACTION.USE_TARGET
|
|
elseif selected == "useCross" then
|
|
settings[widget:getId()].action = ACTION.USE_CROSS
|
|
elseif selected == "equip" then
|
|
settings[widget:getId()].action = ACTION.EQUIP
|
|
else
|
|
settings[widget:getId()].action = ACTION.USE
|
|
end
|
|
|
|
if destroy then
|
|
window:destroy()
|
|
radio:destroy()
|
|
end
|
|
setupButton(widget)
|
|
end
|
|
|
|
local cancelFunc = function()
|
|
setupButton(widget)
|
|
window:destroy()
|
|
radio:destroy()
|
|
end
|
|
|
|
-- callbacks
|
|
window.buttonOk.onClick = function() okFunc(true) end
|
|
window.onEnter = function() okFunc(true) end
|
|
window.buttonApply.onClick = function() okFunc(false) end
|
|
window.buttonClose.onClick = cancelFunc
|
|
window.onEscape = cancelFunc
|
|
|
|
local actionbar = widget:getParent():getParent()
|
|
if actionbar.locked then
|
|
cancelFunc()
|
|
end
|
|
end
|
|
|
|
function assignText(widget)
|
|
destroyAssignWindows()
|
|
|
|
-- create window
|
|
window = g_ui.loadUI('text', g_ui.getRootWidget())
|
|
window:show()
|
|
window:raise()
|
|
window:focus()
|
|
|
|
window.text.onTextChange = function(self, text)
|
|
window.buttonOk:setEnabled(text:len() > 0)
|
|
window.buttonApply:setEnabled(text:len() > 0)
|
|
end
|
|
|
|
-- copy settings from current widget
|
|
window.text:setText(widget.text:getText())
|
|
if widget.type > 0 then
|
|
window.checkPanel.tick:setChecked(widget.autoSay)
|
|
end
|
|
|
|
-- functions
|
|
local okFunc = function(destroy)
|
|
local autoSay = window.checkPanel.tick:isChecked()
|
|
local text = window.text:getText()
|
|
|
|
local hotkey = settings[widget:getId()] and settings[widget:getId()].hotkey
|
|
if hotkey and hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
|
|
settings[widget:getId()] = {hotkey=hotkey}
|
|
|
|
local spell = isSpell(text)
|
|
if spell then -- entered text is spell
|
|
local paramText = spell.param
|
|
local spellData = spell.data
|
|
local newGroup = {}
|
|
for groupId, duration in pairs(spellData.group) do
|
|
table.insert(newGroup, groupId)
|
|
end
|
|
spellData.group = newGroup
|
|
|
|
|
|
settings[widget:getId()].type = TYPE.SPELL
|
|
settings[widget:getId()].spellData = {
|
|
words = spellData.words,
|
|
cd = spellData.exhaustion/1000,
|
|
mana = spellData.mana,
|
|
source = SpelllistSettings['Default'].iconFile,
|
|
clip = Spells.getImageClip(SpellIcons[spellData.icon][1], 'Default'),
|
|
name = spellData.spellName,
|
|
param = paramText,
|
|
group = spellData.group,
|
|
id = spellData.id
|
|
}
|
|
else -- is just text
|
|
settings[widget:getId()].sayText = text
|
|
settings[widget:getId()].type = TYPE.TEXT
|
|
settings[widget:getId()].autoSay = autoSay
|
|
end
|
|
|
|
if destroy then
|
|
window:destroy()
|
|
end
|
|
setupButton(widget)
|
|
end
|
|
local cancelFunc = function()
|
|
window:destroy()
|
|
setupButton(widget)
|
|
end
|
|
|
|
-- buttons
|
|
window.buttonOk.onClick = function() okFunc(true) end
|
|
window.buttonApply.onClick = function() okFunc(false) end
|
|
window.buttonClose.onClick = cancelFunc
|
|
window.onEscape = cancelFunc
|
|
window.onEnter = function() okFunc(true) end
|
|
|
|
local actionbar = widget:getParent():getParent()
|
|
if actionbar.locked then
|
|
cancelFunc()
|
|
end
|
|
end
|
|
|
|
function assignSpell(widget)
|
|
destroyAssignWindows()
|
|
local radio = UIRadioGroup.create()
|
|
|
|
-- create window
|
|
window = g_ui.loadUI('spell', g_ui.getRootWidget())
|
|
window:show()
|
|
window:raise()
|
|
window:focus()
|
|
|
|
window:setText("Assign Spell to Action Button "..widget:getId())
|
|
|
|
-- old tibia does not send vocation
|
|
if g_game.getClientVersion() < 910 then
|
|
window.checkPanel:setVisible(false)
|
|
end
|
|
|
|
local spells = modules.gamelib.SpellInfo['Default']
|
|
for spellName, spellData in pairs(spells) do
|
|
local widget = g_ui.createWidget('SpellPreview', window.spellList)
|
|
-- radio
|
|
radio:addWidget(widget)
|
|
local newGroup = {}
|
|
for groupId, duration in pairs(spellData.group) do
|
|
table.insert(newGroup, groupId)
|
|
end
|
|
spellData.group = newGroup
|
|
|
|
widget:setId(spellData.id)
|
|
widget:setText(spellName.."\n"..spellData.words)
|
|
widget.voc = spellData.vocations
|
|
widget.param = spellData.parameter
|
|
widget.source = SpelllistSettings['Default'].iconFile
|
|
widget.clip = Spells.getImageClip(SpellIcons[spellData.icon][1], 'Default')
|
|
widget.image:setImageSource(widget.source)
|
|
widget.image:setImageClip(widget.clip)
|
|
|
|
widget.spellData = {
|
|
words = spellData.words,
|
|
cd = spellData.exhaustion/1000,
|
|
mana = spellData.mana,
|
|
source = widget.source,
|
|
clip = widget.clip,
|
|
name = spellName,
|
|
param = spellData.parameter,
|
|
group = spellData.group,
|
|
id = spellData.id
|
|
}
|
|
end
|
|
|
|
-- sort alphabetically
|
|
local widgets = window.spellList:getChildren()
|
|
table.sort(widgets, function(a, b) return a.spellData.name < b.spellData.name end)
|
|
for i, widget in ipairs(widgets) do
|
|
window.spellList:moveChildToIndex(widget, i)
|
|
end
|
|
|
|
local function filterByVocation(a, filter)
|
|
-- disable callback
|
|
window.spellList.onChildFocusChange = nil
|
|
|
|
local widgets = window.spellList:getChildren()
|
|
local vocation = translateVocation(g_game.getLocalPlayer():getVocation())
|
|
|
|
-- visible
|
|
local fistVisible = nil
|
|
for i, widget in ipairs(widgets) do
|
|
local viable = not filter or table.find(widget.voc, vocation) and true or false
|
|
widget:setVisible(viable)
|
|
if viable and not fistVisible then
|
|
fistVisible = widget
|
|
radio:selectWidget(fistVisible)
|
|
end
|
|
end
|
|
|
|
-- select current one if exist
|
|
local currentSpell = widget.spellData and widget.spellData.id or 0
|
|
if currentSpell > 0 then
|
|
for i, child in ipairs(radio.widgets) do
|
|
local childId = child.spellData.id
|
|
|
|
if childId == currentSpell then
|
|
child:setVisible(true)
|
|
window.spellList:ensureChildVisible(child)
|
|
radio:selectWidget(child)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- callback
|
|
radio.onSelectionChange = function(widget, selected)
|
|
if selected then
|
|
local name = selected:getText()
|
|
local source = selected.source
|
|
local clip = selected.clip
|
|
local param = selected.param
|
|
|
|
-- preview
|
|
window.preview:setText(name)
|
|
window.preview.image:setImageSource(source)
|
|
window.preview.image:setImageClip(clip)
|
|
|
|
-- param
|
|
window.paramLabel:setOn(param)
|
|
window.paramText:setEnabled(param)
|
|
window.spellList:ensureChildVisible(widget)
|
|
end
|
|
end
|
|
|
|
window.checkPanel.tick.onCheckChange = filterByVocation
|
|
filterByVocation(nil, true)
|
|
|
|
local okFunc = function(destroy)
|
|
local selected = radio:getSelectedWidget()
|
|
local paramWidgetText = window.paramText:getText()
|
|
if not selected then return end
|
|
|
|
selected.spellData.param = paramWidgetText
|
|
|
|
local hotkey = settings[widget:getId()] and settings[widget:getId()].hotkey
|
|
if hotkey and hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(widget.hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
|
|
settings[widget:getId()] = {hotkey=hotkey}
|
|
settings[widget:getId()].spellData = selected.spellData
|
|
settings[widget:getId()].type = TYPE.SPELL
|
|
|
|
if destroy then
|
|
window:destroy()
|
|
end
|
|
setupButton(widget)
|
|
end
|
|
local cancelFunc = function()
|
|
window:destroy()
|
|
setupButton(widget)
|
|
end
|
|
|
|
window.buttonOk.onClick = function() okFunc(true) end
|
|
window.buttonApply.onClick = function() okFunc(false) end
|
|
window.buttonClose.onClick = cancelFunc
|
|
window.onEscape = cancelFunc
|
|
window.onEnter = function() okFunc(true) end
|
|
|
|
local actionbar = widget:getParent():getParent()
|
|
if actionbar.locked then
|
|
cancelFunc()
|
|
end
|
|
end
|
|
|
|
function assignHotkey(widget)
|
|
destroyAssignWindows()
|
|
|
|
-- create window
|
|
window = g_ui.loadUI('hotkey', g_ui.getRootWidget())
|
|
window:show()
|
|
window:raise()
|
|
window:focus()
|
|
|
|
local barN = widget:getParent():getParent().n
|
|
local barDesc
|
|
if barN < 4 then
|
|
barDesc = "Bottom"
|
|
elseif barN < 7 then
|
|
barDesc = "Left"
|
|
else
|
|
barDesc = "Right"
|
|
end
|
|
|
|
-- things
|
|
barDesc = barDesc.." Action Bar: Action Button "..widget:getId()
|
|
window:setText('Edit Hotkey for "'..barDesc)
|
|
window.desc:setText(window.desc:getText()..barDesc..'"')
|
|
window.display:setText(widget.hotkey or "")
|
|
|
|
-- hotkey
|
|
window:grabKeyboard()
|
|
window.onKeyDown = function(window, keyCode, keyboardModifiers)
|
|
local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers)
|
|
window.display:setText(keyCombo)
|
|
return true
|
|
end
|
|
|
|
local okFunc = function()
|
|
local hotkey = window.display:getText()
|
|
|
|
if settings[widget:getId()].hotkey and settings[widget:getId()].hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(widget.hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
settings[widget:getId()] = settings[widget:getId()] or {}
|
|
settings[widget:getId()].hotkey = hotkey
|
|
|
|
window:destroy()
|
|
setupButton(widget)
|
|
end
|
|
local clearFunc = function()
|
|
window.display:setText('')
|
|
local hotkey = window.display:getText()
|
|
|
|
if settings[widget:getId()].hotkey and settings[widget:getId()].hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.unbindKeyPress(widget.hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
settings[widget:getId()] = settings[widget:getId()] or {}
|
|
settings[widget:getId()].hotkey = hotkey
|
|
|
|
window:destroy()
|
|
setupButton(widget)
|
|
end
|
|
local closeFunc = function()
|
|
window:destroy()
|
|
setupButton(widget)
|
|
end
|
|
|
|
window.buttonOk.onClick = okFunc
|
|
window.buttonClear.onClick = clearFunc
|
|
window.buttonClose.onClick = closeFunc
|
|
|
|
local actionbar = widget:getParent():getParent()
|
|
if actionbar.locked then
|
|
cancelFunc()
|
|
end
|
|
end
|
|
|
|
function setupAction(widget)
|
|
if widget.type == TYPE.BLANK then
|
|
return
|
|
end
|
|
if widget.type == TYPE.TEXT then
|
|
widget.callback = function()
|
|
if modules.game_interface.isChatVisible() then
|
|
if widget.autoSay then
|
|
modules.game_console.sendMessage(widget.sayText)
|
|
else
|
|
modules.game_console.setTextEditText(widget.sayText)
|
|
end
|
|
elseif widget.autoSay then
|
|
g_game.talk(widget.sayText)
|
|
end
|
|
end
|
|
elseif widget.type == TYPE.SPELL then
|
|
widget.callback = function()
|
|
if g_app.isMobile() then -- turn to direction of targer
|
|
local target = g_game.getAttackingCreature()
|
|
if target then
|
|
local pos = g_game.getLocalPlayer():getPosition()
|
|
local tpos = target:getPosition()
|
|
if pos and tpos then
|
|
local offx = tpos.x - pos.x
|
|
local offy = tpos.y - pos.y
|
|
if offy < 0 and offx <= 0 and math.abs(offx) < math.abs(offy) then
|
|
g_game.turn(Directions.North)
|
|
elseif offy > 0 and offx >= 0 and math.abs(offx) < math.abs(offy) then
|
|
g_game.turn(Directions.South)
|
|
elseif offx < 0 and offy <= 0 and math.abs(offx) > math.abs(offy) then
|
|
g_game.turn(Directions.West)
|
|
elseif offx > 0 and offy >= 0 and math.abs(offx) > math.abs(offy) then
|
|
g_game.turn(Directions.East)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local paramText
|
|
if widget.spellData.param and widget.spellData.param:len() > 0 then
|
|
paramText = ' "'.. widget.spellData.param ..'"'
|
|
else
|
|
paramText = ""
|
|
end
|
|
g_game.talk(widget.spellData.words..paramText)
|
|
end
|
|
elseif widget.type == TYPE.ITEM then
|
|
widget.callback = function()
|
|
if widget.action == ACTION.BLANK then
|
|
return
|
|
elseif widget.action == ACTION.EQUIP then
|
|
if g_game.getClientVersion() >= 910 then
|
|
local item = Item.create(widget.item:getItemId())
|
|
return g_game.equipItem(item)
|
|
end
|
|
elseif widget.action == ACTION.USE then
|
|
if g_game.getClientVersion() < 780 then
|
|
local item = g_game.findPlayerItem(widget.item:getItemId(), widget.item:getItemSubType() or -1)
|
|
if item then
|
|
g_game.use(item)
|
|
end
|
|
else
|
|
g_game.useInventoryItem(widget.item:getItemId())
|
|
end
|
|
elseif widget.action == ACTION.USE_SELF then
|
|
if g_game.getClientVersion() < 780 then
|
|
local item = g_game.findPlayerItem(widget.item:getItemId(), widget.item:getItemSubType() or -1)
|
|
if item then
|
|
g_game.useWith(item, g_game.getLocalPlayer())
|
|
end
|
|
else
|
|
g_game.useInventoryItemWith(widget.item:getItemId(), g_game.getLocalPlayer(), widget.item:getItemSubType() or -1)
|
|
end
|
|
elseif widget.action == ACTION.USE_TARGET then
|
|
local attackingCreature = g_game.getAttackingCreature()
|
|
if not attackingCreature then
|
|
local item = Item.create(widget.item:getItemId())
|
|
if g_game.getClientVersion() < 780 then
|
|
local tmpItem = g_game.findPlayerItem(widget.item:getItemId(), widget.item:getItemSubType() or -1)
|
|
if not tmpItem then return end
|
|
item = tmpItem
|
|
end
|
|
modules.game_interface.startUseWith(item, widget.item:getItemSubType() or - 1)
|
|
return
|
|
end
|
|
if not attackingCreature:getTile() then return end
|
|
if g_game.getClientVersion() < 780 then
|
|
local item = g_game.findPlayerItem(widget.item:getItemId(), widget.item:getItemSubType() or -1)
|
|
if item then
|
|
g_game.useWith(item, attackingCreature, widget.item:getItemSubType() or -1)
|
|
end
|
|
else
|
|
g_game.useInventoryItemWith(widget.item:getItemId(), attackingCreature, widget.item:getItemSubType() or -1)
|
|
end
|
|
elseif widget.action == ACTION.USE_CROSS then
|
|
local item = Item.create(widget.item:getItemId())
|
|
if g_game.getClientVersion() < 780 then
|
|
local tmpItem = g_game.findPlayerItem(widget.item:getItemId(), widget.item:getItemSubType() or -1)
|
|
if not tmpItem then return true end
|
|
item = tmpItem
|
|
end
|
|
modules.game_interface.startUseWith(item, widget.item:getItemSubType() or - 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
if widget.hotkey and widget.hotkey:len() > 0 and widget.callback then
|
|
local gameRootPanel = modules.game_interface.getRootPanel()
|
|
g_keyboard.bindKeyPress(widget.hotkey, widget.callback, gameRootPanel)
|
|
end
|
|
end
|
|
|
|
function onSpellCooldown(iconId, duration)
|
|
for index, actionbar in ipairs(actionBars) do
|
|
for i, child in ipairs(actionbar.tabBar:getChildren()) do
|
|
if child.type == 2 and child.spellData.id == iconId then
|
|
startCooldown(child, duration)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function onSpellGroupCooldown(groupId, duration)
|
|
for index, actionbar in ipairs(actionBars) do
|
|
for i, child in ipairs(actionbar.tabBar:getChildren()) do
|
|
if child.type == 2 and child.spellData.group then
|
|
for i, group in ipairs(child.spellData.group) do
|
|
if groupId == group then
|
|
startCooldown(child, duration)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function startCooldown(action, duration)
|
|
if type(action.cooldownTill) == 'number' and action.cooldownTill > g_clock.millis() + duration then
|
|
return -- already has cooldown with greater duration
|
|
end
|
|
action.cooldownStart = g_clock.millis()
|
|
action.cooldownTill = g_clock.millis() + duration
|
|
updateCooldown(action)
|
|
end
|
|
|
|
function updateCooldown(action)
|
|
if not action or not action.cooldownTill then return end
|
|
local timeleft = action.cooldownTill - g_clock.millis()
|
|
if timeleft <= 50 then
|
|
action.cooldown:setPercent(100)
|
|
action.cooldownEvent = nil
|
|
action.cooldown:setText("")
|
|
return
|
|
end
|
|
local duration = action.cooldownTill - action.cooldownStart
|
|
local formattedText
|
|
if timeleft > 60000 then
|
|
formattedText = math.floor(timeleft / 60000) .. "m"
|
|
else
|
|
formattedText = timeleft/1000
|
|
formattedText = math.floor(formattedText * 10) / 10
|
|
formattedText = math.floor(formattedText) .. "." .. math.floor(formattedText * 10) % 10
|
|
end
|
|
|
|
local retry
|
|
if timeleft > 60000 then
|
|
retry = math.min(math.floor(timeleft * 0.1), 60 * 1000) -- max 1min
|
|
retry = math.max(retry, 100) -- min 100
|
|
elseif timeleft > 1000 then
|
|
retry = 100
|
|
else
|
|
retry = 30
|
|
end
|
|
action.cooldown:setText(formattedText)
|
|
action.cooldown:setPercent(100 - math.floor(100 * timeleft / duration))
|
|
action.cooldownEvent = scheduleEvent(function() updateCooldown(action) end, retry)
|
|
end
|
|
|
|
function save()
|
|
local status, result = pcall(function() return json.encode(settings, 2) end)
|
|
if not status then
|
|
return g_logger.error(
|
|
"Error while saving top bar settings. Data won't be saved. Details: " ..
|
|
result)
|
|
end
|
|
|
|
if result:len() > 100 * 1024 * 1024 then
|
|
return g_logger.error(
|
|
"Something went wrong, file is above 100MB, won't be saved")
|
|
end
|
|
|
|
g_resources.writeFileContents(settingsFile, result)
|
|
end
|
|
|
|
function load()
|
|
if g_resources.fileExists(settingsFile) then
|
|
local status, result = pcall(function()
|
|
return json.decode(g_resources.readFileContents(settingsFile))
|
|
end)
|
|
if not status then
|
|
return g_logger.error(
|
|
"Error while reading top bar settings file. To fix this problem you can delete storage.json. Details: " ..
|
|
result)
|
|
end
|
|
settings = result
|
|
else
|
|
settings = {}
|
|
end
|
|
end |