2022-02-15 20:58:27 +00:00

624 lines
22 KiB
Lua

---@diagnostic disable: undefined-global
bottomActionPanel1 = nil
bottomActionPanel2 = nil
bottomActionPanel3 = nil
leftActionPanel1 = nil
leftActionPanel2 = nil
leftActionPanel3 = nil
local settings = {}
local hotkeyAssignWindow
local actionButtonsInPanel = 50
ActionTypes = {
USE = 0,
USE_SELF = 1,
USE_TARGET = 2,
USE_WITH = 3,
EQUIP = 4
}
ActionColors = {
empty = '#00000033',
text = '#00000033',
itemUse = '#8888FF88',
itemUseSelf = '#00FF0088',
itemUseTarget = '#FF000088',
itemUseWith = '#F5B32588',
itemEquip = '#FFFFFF88'
}
function init()
local bottomPanel = modules.game_interface.getBottomActionPanel()
local leftPanel = modules.game_interface.getLeftActionPanel()
local rightPanel = modules.game_interface.getRightActionPanel()
-- bottom
bottomActionPanel1 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(bottomActionPanel1, 1)
bottomActionPanel2 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(bottomActionPanel2, 1)
bottomActionPanel3 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(bottomActionPanel3, 1)
-- left
leftActionPanel1 = g_ui.loadUI('sideactionbar', leftPanel)
leftPanel:moveChildToIndex(leftActionPanel1, 1)
leftActionPanel2 = g_ui.loadUI('sideactionbar', leftPanel)
leftPanel:moveChildToIndex(leftActionPanel2, 1)
leftActionPanel3 = g_ui.loadUI('sideactionbar', leftPanel)
leftPanel:moveChildToIndex(leftActionPanel3, 1)
-- right
rightActionPanel1 = g_ui.loadUI('sideactionbar', rightPanel)
rightPanel:moveChildToIndex(rightActionPanel1, 1)
rightActionPanel2 = g_ui.loadUI('sideactionbar', rightPanel)
rightPanel:moveChildToIndex(rightActionPanel2, 1)
rightActionPanel3 = g_ui.loadUI('sideactionbar', rightPanel)
rightPanel:moveChildToIndex(rightActionPanel3, 1)
connect(g_game, {
onGameStart = online,
onGameEnd = offline,
onSpellGroupCooldown = onSpellGroupCooldown,
onSpellCooldown = onSpellCooldown
})
if g_game.isOnline() then
online()
end
end
function terminate()
disconnect(g_game, {
onGameStart = online,
onGameEnd = offline,
onSpellGroupCooldown = onSpellGroupCooldown,
onSpellCooldown = onSpellCooldown
})
-- remove hotkeys, also saves config
local panels = {
bottomActionPanel1,
bottomActionPanel2,
bottomActionPanel3,
leftActionPanel1,
leftActionPanel2,
leftActionPanel3,
rightActionPanel1,
rightActionPanel2,
rightActionPanel3,
}
for i, panel in ipairs(panels) do
if panel.tabBar:getChildCount() > 0 then
offline()
break
end
end
bottomActionPanel1:destroy()
bottomActionPanel2:destroy()
bottomActionPanel3:destroy()
leftActionPanel1:destroy()
leftActionPanel2:destroy()
leftActionPanel3:destroy()
rightActionPanel1:destroy()
rightActionPanel2:destroy()
rightActionPanel3:destroy()
end
function show()
if not g_game.isOnline() then return end
bottomActionPanel1:setOn(g_settings.getBoolean("actionbarBottom1", false))
bottomActionPanel2:setOn(g_settings.getBoolean("actionbarBottom2", false))
bottomActionPanel3:setOn(g_settings.getBoolean("actionbarBottom3", false))
leftActionPanel1:setOn(g_settings.getBoolean("actionbarLeft1", false))
leftActionPanel2:setOn(g_settings.getBoolean("actionbarLeft2", false))
leftActionPanel3:setOn(g_settings.getBoolean("actionbarLeft3", false))
rightActionPanel1:setOn(g_settings.getBoolean("actionbarRight1", false))
rightActionPanel2:setOn(g_settings.getBoolean("actionbarRight2", false))
rightActionPanel3:setOn(g_settings.getBoolean("actionbarRight3", false))
end
function hide()
bottomActionPanel1:setOn(false)
bottomActionPanel2:setOn(false)
bottomActionPanel3:setOn(false)
leftActionPanel1:setOn(false)
leftActionPanel2:setOn(false)
leftActionPanel3:setOn(false)
rightActionPanel1:setOn(false)
rightActionPanel2:setOn(false)
rightActionPanel3:setOn(false)
end
function switchMode(newMode)
if newMode then
bottomActionPanel1:setImageColor('#ffffff88')
bottomActionPanel2:setImageColor('#ffffff88')
bottomActionPanel3:setImageColor('#ffffff88')
leftActionPanel1:setImageColor('#ffffff88')
leftActionPanel2:setImageColor('#ffffff88')
leftActionPanel3:setImageColor('#ffffff88')
rightActionPanel1:setImageColor('#ffffff88')
rightActionPanel2:setImageColor('#ffffff88')
rightActionPanel3:setImageColor('#ffffff88')
else
bottomActionPanel1:setImageColor('white')
bottomActionPanel2:setImageColor('white')
bottomActionPanel3:setImageColor('white')
leftActionPanel1:setImageColor('white')
leftActionPanel2:setImageColor('white')
leftActionPanel3:setImageColor('white')
rightActionPanel1:setImageColor('white')
rightActionPanel2:setImageColor('white')
rightActionPanel3:setImageColor('white')
end
end
function online()
load()
setupActionPanel(1, bottomActionPanel1, true)
setupActionPanel(2, bottomActionPanel2, true)
setupActionPanel(3, bottomActionPanel3, true)
setupActionPanel(4, leftActionPanel1, false)
setupActionPanel(5, leftActionPanel2, false)
setupActionPanel(6, leftActionPanel3, false)
setupActionPanel(7, rightActionPanel1, false)
setupActionPanel(8, rightActionPanel2, false)
setupActionPanel(9, rightActionPanel3, false)
show()
end
function refresh(reloaded)
offline(reloaded)
online()
end
function offline(reloaded)
hide()
if hotkeyAssignWindow then
hotkeyAssignWindow:destroy()
hotkeyAssignWindow = nil
end
local gameRootPanel = modules.game_interface.getRootPanel()
for index, panel in ipairs({bottomActionPanel1, bottomActionPanel2, bottomActionPanel3,
leftActionPanel1, leftActionPanel2, leftActionPanel3,
rightActionPanel1, rightActionPanel2, rightActionPanel3}) do
settings[tostring(index)] = {}
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.config and child.config.item then
settings[tostring(index)][tostring(i)] = child.config
if type(child.config.hotkey) == 'string' and child.config.hotkey:len() > 0 then
g_keyboard.unbindKeyPress(child.config.hotkey, child.callback, gameRootPanel)
end
end
if child.cooldownEvent then
removeEvent(child.cooldownEvent)
end
end
panel.tabBar:destroyChildren()
end
if not reloaded then
save()
end
end
function setupActionPanel(index, panel, bottom)
local config = settings[tostring(index)] or {}
for i=1,actionButtonsInPanel do
local type = bottom and 'ActionButton' or 'SideActionButton'
local action = g_ui.createWidget(type, panel.tabBar)
action.config = config[tostring(i)] or {}
setupAction(action)
end
panel.nextButton.onClick = function()
panel.tabBar:moveChildToIndex(panel.tabBar:getFirstChild(), panel.tabBar:getChildCount())
end
panel.prevButton.onClick = function()
panel.tabBar:moveChildToIndex(panel.tabBar:getLastChild(), 1)
end
end
function setupAction(action)
local config = action.config
action.item:setShowCount(false)
action.onMouseRelease = actionOnMouseRelease
action.onTouchRelease = actionOnMouseRelease
action.callback = function(k, c, ticks)
local lockKeyboard = g_settings.getBoolean('actionbarLock', false)
local chatMode = not modules.game_walking.wsadWalking
if not lockKeyboard or not chatMode then
print('lock', lockKeyboard)
print('chatMode', chatMode)
executeAction(action, ticks)
end
end
action.item.onItemChange = nil -- disable callbacks for setup
if config then
if type(config.text) == 'number' then
config.text = tostring(config.text)
end
if type(config.hotkey) == 'number' then
config.hotkey = tostring(config.hotkey)
end
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.bindKeyPress(config.hotkey, action.callback, gameRootPanel)
local text = config.hotkey
-- formatting similar to cip Tibia 12
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
action.hotkeyLabel:setText(text)
else
action.hotkeyLabel:setText("")
end
action.text:setImageSource("")
action.cooldownTill = 0
action.cooldownStart = 0
if type(config.text) == 'string' and config.text:len() > 0 then
action.text:setText(config.text)
action.item:setBorderColor(ActionColors.text)
action.item:setOn(true) -- removes background
action.item:setItemId(0)
if Spells then
local spell, profile = Spells.getSpellByWords(config.text:lower())
action.spell = spell
if action.spell and action.spell.icon and profile then
action.text:setImageSource(SpelllistSettings[profile].iconFile)
action.text:setImageClip(Spells.getImageClip(SpellIcons[action.spell.icon][1], profile))
action.text:setText("")
end
end
else
action.text:setText("")
action.spell = nil
if type(config.item) == 'number' and config.item > 100 then
action.item:setOn(true)
action.item:setItemId(config.item)
action.item:setItemCount(config.count or 1)
setupActionType(action, config.actionType)
else
action.item:setItemId(0)
action.item:setOn(false)
action.item:setBorderColor(ActionColors.empty)
end
end
end
action.item.onItemChange = actionOnItemChange
end
function setupActionType(action, actionType)
local item = action.item:getItem()
if action.item:getItem():isMultiUse() then
if not actionType or actionType <= ActionTypes.USE then
actionType = ActionTypes.USE_WITH
end
elseif g_game.getClientVersion() >= 910 then
if actionType ~= ActionTypes.USE and actionType ~= ActionTypes.EQUIP then
actionType = ActionTypes.USE
end
else
actionType = ActionTypes.USE
end
action.config.actionType = actionType
if action.config.actionType == ActionTypes.USE then
action.item:setBorderColor(ActionColors.itemUse)
elseif action.config.actionType == ActionTypes.USE_SELF then
action.item:setBorderColor(ActionColors.itemUseSelf)
elseif action.config.actionType == ActionTypes.USE_TARGET then
action.item:setBorderColor(ActionColors.itemUseTarget)
elseif action.config.actionType == ActionTypes.USE_WITH then
action.item:setBorderColor(ActionColors.itemUseWith)
elseif action.config.actionType == ActionTypes.EQUIP then
action.item:setBorderColor(ActionColors.itemEquip)
end
end
function updateAction(action, newConfig)
local config = action.config
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.unbindKeyPress(config.hotkey, action.callback, gameRootPanel)
end
for key, val in pairs(newConfig) do
action.config[key] = val
end
setupAction(action)
end
function actionOnMouseRelease(action, mousePosition, mouseButton)
if mouseButton == MouseTouch then return end
if mouseButton == MouseRightButton or not action.item:isOn() then
local menu = g_ui.createWidget('PopupMenu')
menu:setGameMenu(true)
if action.item:getItemId() > 0 then
if action.item:getItem():isMultiUse() then
menu:addOption(tr('Use on yourself'), function() return setupActionType(action, ActionTypes.USE_SELF) end)
menu:addOption(tr('Use on target'), function() return setupActionType(action, ActionTypes.USE_TARGET) end)
menu:addOption(tr('With crosshair'), function() return setupActionType(action, ActionTypes.USE_WITH) end)
end
if g_game.getClientVersion() >= 910 then
if not action.item:getItem():isMultiUse() then
menu:addOption(tr('Use'), function() return setupActionType(action, ActionTypes.USE) end)
end
menu:addOption(tr('Equip'), function() return setupActionType(action, ActionTypes.EQUIP) end)
end
else
menu:addOption(tr('Select item'), function() return modules.game_itemselector.show(action.item) end)
end
menu:addSeparator()
menu:addOption(tr('Set text'), function()
modules.client_textedit.singlelineEditor(action.config.text or "", function(newText)
updateAction(action, {text=newText, item=0})
end)
end)
menu:addOption(tr('Set hotkey'), function()
if hotkeyAssignWindow then
hotkeyAssignWindow:destroy()
end
local assignWindow = g_ui.createWidget('ActionAssignWindow', rootWidget)
assignWindow:grabKeyboard()
assignWindow.comboPreview.keyCombo = ''
assignWindow.onKeyDown = function(assignWindow, keyCode, keyboardModifiers)
local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers)
assignWindow.comboPreview:setText(tr('Current action hotkey: %s', keyCombo))
assignWindow.comboPreview.keyCombo = keyCombo
assignWindow.comboPreview:resizeToText()
return true
end
assignWindow.onDestroy = function(widget)
if widget == hotkeyAssignWindow then
hotkeyAssignWindow = nil
end
end
assignWindow.addButton.onClick = function()
local text = tostring(assignWindow.comboPreview.keyCombo)
updateAction(action, {hotkey=text})
assignWindow:destroy()
end
hotkeyAssignWindow = assignWindow
end)
menu:addSeparator()
menu:addOption(tr('Clear'), function()
updateAction(action, {hotkey="", text="", item=0, count=1})
end)
menu:display(mousePosition)
return true
elseif mouseButton == MouseLeftButton or mouseButton == MouseTouch2 or mouseButton == MouseTouch3 then
action.callback()
return true
end
return false
end
function actionOnItemChange(widget)
updateAction(widget:getParent(), {text="", item=widget:getItemId(), count=widget:getItemCountOrSubType()})
end
function onSpellCooldown(iconId, duration)
for index, panel in ipairs({bottomActionPanel1, bottomActionPanel2, bottomActionPanel3,
leftActionPanel1, leftActionPanel2, leftActionPanel3,
rightActionPanel1, rightActionPanel2, rightActionPanel3}) do
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.spell and child.spell.id == iconId then
startCooldown(child, duration)
end
end
end
end
function onSpellGroupCooldown(groupId, duration)
for index, panel in ipairs({bottomActionPanel1, bottomActionPanel2, bottomActionPanel3,
leftActionPanel1, leftActionPanel2, leftActionPanel3,
rightActionPanel1, rightActionPanel2, rightActionPanel3}) do
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.spell and child.spell.group then
for group, dur in pairs(child.spell.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 <= 30 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
action.cooldown:setText(formattedText)
action.cooldown:setPercent(100 - math.floor(100 * timeleft / duration))
action.cooldownEvent = scheduleEvent(function() updateCooldown(action) end, 30)
end
function executeAction(action, ticks)
if not action.config then return end
if type(ticks) ~= 'number' then ticks = 0 end
local actionDelay = 100
if ticks == 0 then
actionDelay = 200 -- for first use
elseif action.actionDelayTo ~= nil and g_clock.millis() < action.actionDelayTo then
return
end
local actionType = action.config.actionType
if type(action.config.text) == 'string' and action.config.text:len() > 0 then
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
if modules.game_interface.isChatVisible() then
modules.game_console.sendMessage(action.config.text)
else
g_game.talk(action.config.text)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif action.item:getItemId() > 0 then
if actionType == ActionTypes.USE then
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.use(item)
end
else
g_game.useInventoryItem(action.item:getItemId())
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_SELF then
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.useWith(item, g_game.getLocalPlayer())
end
else
g_game.useInventoryItemWith(action.item:getItemId(), g_game.getLocalPlayer(), action.item:getItemSubType() or -1)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_TARGET then
local attackingCreature = g_game.getAttackingCreature()
if not attackingCreature then
local item = Item.create(action.item:getItemId())
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if not tmpItem then return end
item = tmpItem
end
modules.game_interface.startUseWith(item, action.item:getItemSubType() or - 1)
return
end
if not attackingCreature:getTile() then return end
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.useWith(item, attackingCreature, action.item:getItemSubType() or -1)
end
else
g_game.useInventoryItemWith(action.item:getItemId(), attackingCreature, action.item:getItemSubType() or -1)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_WITH then
local item = Item.create(action.item:getItemId())
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if not tmpItem then return true end
item = tmpItem
end
modules.game_interface.startUseWith(item, action.item:getItemSubType() or - 1)
elseif actionType == ActionTypes.EQUIP then
if g_game.getClientVersion() >= 910 then
local item = Item.create(action.item:getItemId())
g_game.equipItem(item)
action.actionDelayTo = g_clock.millis() + actionDelay
end
end
end
end
function save()
local settingsFile = modules.client_profiles.getSettingsFilePath("actionbar.json")
local status, result = pcall(function() return json.encode(settings, 2) end)
if not status then
return onError(
"Error while saving top bar settings. Data won't be saved. Details: " ..
result)
end
if result:len() > 100 * 1024 * 1024 then
return onError(
"Something went wrong, file is above 100MB, won't be saved")
end
g_resources.writeFileContents(settingsFile, result)
end
function load()
local settingsFile = modules.client_profiles.getSettingsFilePath("actionbar.json")
if g_resources.fileExists(settingsFile) then
local status, result = pcall(function()
return json.decode(g_resources.readFileContents(settingsFile))
end)
if not status then
return onError(
"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