mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-10-20 06:33:26 +02:00
Version 0.999 BETA - a lot of bug fixes, improvments and more advanced bot
This commit is contained in:
101
modules/game_bot/functions/callbacks.lua
Normal file
101
modules/game_bot/functions/callbacks.lua
Normal file
@@ -0,0 +1,101 @@
|
||||
local context = G.botContext
|
||||
|
||||
-- callback(callbackType, callback)
|
||||
context.callback = function(callbackType, callback)
|
||||
if not context._callbacks[callbackType] then
|
||||
return error("Wrong callback type: " .. callbackType)
|
||||
end
|
||||
if callbackType == "onAddThing" or callbackType == "onRemoveThing" then
|
||||
g_game.enableTileThingLuaCallback(true)
|
||||
end
|
||||
local callbackData = {}
|
||||
table.insert(context._callbacks[callbackType], function(...)
|
||||
if not callbackData.delay or callbackData.delay < context.now then
|
||||
context._currentExecution = callbackData
|
||||
callback(...)
|
||||
context._currentExecution = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- onKeyDown(callback) -- callback = function(keys)
|
||||
context.onKeyDown = function(callback)
|
||||
return context.callback("onKeyDown", callback)
|
||||
end
|
||||
|
||||
-- onKeyPress(callback) -- callback = function(keys)
|
||||
context.onKeyPress = function(callback)
|
||||
return context.callback("onKeyPress", callback)
|
||||
end
|
||||
|
||||
-- onKeyUp(callback) -- callback = function(keys)
|
||||
context.onKeyUp = function(callback)
|
||||
return context.callback("onKeyUp", callback)
|
||||
end
|
||||
|
||||
-- onTalk(callback) -- callback = function(name, level, mode, text, channelId, pos)
|
||||
context.onTalk = function(callback)
|
||||
return context.callback("onTalk", callback)
|
||||
end
|
||||
|
||||
-- onAddThing(callback) -- callback = function(tile, thing)
|
||||
context.onAddThing = function(callback)
|
||||
return context.callback("onAddThing", callback)
|
||||
end
|
||||
|
||||
-- onRemoveThing(callback) -- callback = function(tile, thing)
|
||||
context.onRemoveThing = function(callback)
|
||||
return context.callback("onRemoveThing", callback)
|
||||
end
|
||||
|
||||
-- onCreatureAppear(callback) -- callback = function(creature)
|
||||
context.onCreatureAppear = function(callback)
|
||||
return context.callback("onCreatureAppear", callback)
|
||||
end
|
||||
|
||||
-- onCreatureDisappear(callback) -- callback = function(creature)
|
||||
context.onCreatureDisappear = function(callback)
|
||||
return context.callback("onCreatureDisappear", callback)
|
||||
end
|
||||
|
||||
-- onCreaturePositionChange(callback) -- callback = function(creature, newPos, oldPos)
|
||||
context.onCreaturePositionChange = function(callback)
|
||||
return context.callback("onCreaturePositionChange", callback)
|
||||
end
|
||||
|
||||
-- onCreatureHealthPercentChange(callback) -- callback = function(creature, healthPercent)
|
||||
context.onCreatureHealthPercentChange = function(callback)
|
||||
return context.callback("onCreatureHealthPercentChange", callback)
|
||||
end
|
||||
|
||||
|
||||
-- custom callbacks
|
||||
|
||||
-- listen(name, callback) -- callback = function(text, channelId, pos)
|
||||
context.listen = function(name, callback)
|
||||
if not name then return context.error("listen: invalid name") end
|
||||
name = name:lower()
|
||||
context.onTalk(function(name2, level, mode, text, channelId, pos)
|
||||
if name == name2:lower() then
|
||||
callback(text, channelId, pos)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- onPlayerPositionChange(callback) -- callback = function(newPos, oldPos)
|
||||
context.onPlayerPositionChange = function(callback)
|
||||
context.onCreaturePositionChange(function(creature, newPos, oldPos)
|
||||
if creature == context.player then
|
||||
callback(newPos, oldPos)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- onPlayerHealthChange(callback) -- callback = function(healthPercent)
|
||||
context.onPlayerHealthChange = function(callback)
|
||||
context.onCreatureHealthPercentChange(function(creature, healthPercent)
|
||||
if creature == context.player then
|
||||
callback(healthPercent)
|
||||
end
|
||||
end)
|
||||
end
|
136
modules/game_bot/functions/main.lua
Normal file
136
modules/game_bot/functions/main.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
local context = G.botContext
|
||||
|
||||
-- MAIN BOT FUNCTION
|
||||
-- macro(timeout, callback)
|
||||
-- macro(timeout, name, callback)
|
||||
-- macro(timeout, name, hotkey, callback)
|
||||
-- macro(timeout, name, hotkey, callback, parent)
|
||||
context.macro = function(timeout, name, hotkey, callback, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
|
||||
if type(timeout) ~= 'number' or timeout < 1 then
|
||||
error("Invalid timeout for macro: " .. tostring(timeout))
|
||||
end
|
||||
if type(name) == 'function' then
|
||||
callback = name
|
||||
name = ""
|
||||
hotkey = ""
|
||||
elseif type(hotkey) == 'function' then
|
||||
callback = hotkey
|
||||
hotkey = ""
|
||||
elseif type(callback) ~= 'function' then
|
||||
error("Invalid callback for macro: " .. tostring(callback))
|
||||
end
|
||||
if hotkey == nil then
|
||||
hotkey = ""
|
||||
end
|
||||
if type(name) ~= 'string' or type(hotkey) ~= 'string' then
|
||||
error("Invalid name or hotkey for macro")
|
||||
end
|
||||
if hotkey:len() > 0 then
|
||||
hotkey = retranslateKeyComboDesc(hotkey)
|
||||
end
|
||||
|
||||
local switch = nil
|
||||
if name:len() > 0 then
|
||||
if context.storage._macros[name] == nil then
|
||||
context.storage._macros[name] = true
|
||||
end
|
||||
switch = context._addMacroSwitch(name, hotkey, parent)
|
||||
end
|
||||
|
||||
table.insert(context._macros, {
|
||||
timeout = timeout,
|
||||
name = name,
|
||||
lastExecution = context.now,
|
||||
hotkey = hotkey,
|
||||
switch = switch
|
||||
})
|
||||
|
||||
local macroData = context._macros[#context._macros]
|
||||
macroData.callback = function()
|
||||
if not macroData.delay or macroData.delay < context.now then
|
||||
context._currentExecution = macroData
|
||||
callback()
|
||||
context._currentExecution = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return macroData
|
||||
end
|
||||
|
||||
-- hotkey(keys, callback)
|
||||
-- hotkey(keys, name, callback)
|
||||
-- hotkey(keys, name, callback, parent)
|
||||
context.hotkey = function(keys, name, callback, single, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
if type(name) == 'function' then
|
||||
callback = name
|
||||
name = ""
|
||||
end
|
||||
keys = retranslateKeyComboDesc(keys)
|
||||
if not keys or #keys == 0 then
|
||||
return context.error("Invalid hotkey keys " .. tostring(name))
|
||||
end
|
||||
if context._hotkeys[keys] then
|
||||
return context.error("Duplicated hotkey: " .. keys)
|
||||
end
|
||||
|
||||
local switch = nil
|
||||
if name:len() > 0 then
|
||||
switch = context._addHotkeySwitch(name, keys, parent)
|
||||
end
|
||||
|
||||
context._hotkeys[keys] = {
|
||||
name = name,
|
||||
lastExecution = context.now,
|
||||
switch = switch,
|
||||
single = single
|
||||
}
|
||||
|
||||
local hotkeyData = context._hotkeys[keys]
|
||||
hotkeyData.callback = function()
|
||||
if not hotkeyData.delay or hotkeyData.delay < context.now then
|
||||
context._currentExecution = hotkeyData
|
||||
callback()
|
||||
context._currentExecution = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return hotkeyData
|
||||
end
|
||||
|
||||
-- singlehotkey(keys, callback)
|
||||
-- singlehotkey(keys, name, callback)
|
||||
-- singlehotkey(keys, name, callback, parent)
|
||||
context.singlehotkey = function(keys, name, callback, parent)
|
||||
if type(name) == 'function' then
|
||||
callback = name
|
||||
name = ""
|
||||
end
|
||||
return context.hotkey(keys, name, callback, true, parent)
|
||||
end
|
||||
|
||||
-- schedule(timeout, callback)
|
||||
context.schedule = function(timeout, callback)
|
||||
local extecute_time = g_clock.millis() + timeout
|
||||
table.insert(context._scheduler, {
|
||||
execution = extecute_time,
|
||||
callback = callback
|
||||
})
|
||||
table.sort(context._scheduler, function(a, b) return a.execution < b.execution end)
|
||||
end
|
||||
|
||||
-- delay(duration) -- block execution of current macro/hotkey/callback for x milliseconds
|
||||
context.delay = function(duration)
|
||||
if not context._currentExecution then
|
||||
return context.error("Invalid usage of delay function, it should be used inside callbacks")
|
||||
end
|
||||
context._currentExecution.delay = context.now + duration
|
||||
end
|
39
modules/game_bot/functions/map.lua
Normal file
39
modules/game_bot/functions/map.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
local context = G.botContext
|
||||
|
||||
context.zoomIn = function() modules.game_interface.getMapPanel():zoomIn() end
|
||||
context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end
|
||||
|
||||
context.getSpectators = function(multifloor)
|
||||
if multifloor ~= true then
|
||||
multifloor = false
|
||||
end
|
||||
return g_map.getSpectators(context.player:getPosition(), multifloor)
|
||||
end
|
||||
|
||||
context.getCreatureByName = function(name, multifloor)
|
||||
if not name then return nil end
|
||||
name = name:lower()
|
||||
if multifloor ~= true then
|
||||
multifloor = false
|
||||
end
|
||||
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
|
||||
if spec:getName():lower() == name then
|
||||
return spec
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
context.getPlayerByName = function(name, multifloor)
|
||||
if not name then return nil end
|
||||
name = name:lower()
|
||||
if multifloor ~= true then
|
||||
multifloor = false
|
||||
end
|
||||
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
|
||||
if spec:isPlayer() and spec:getName():lower() == name then
|
||||
return spec
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
98
modules/game_bot/functions/player.lua
Normal file
98
modules/game_bot/functions/player.lua
Normal file
@@ -0,0 +1,98 @@
|
||||
local context = G.botContext
|
||||
|
||||
context.name = function() return context.player:getName() end
|
||||
|
||||
context.hp = function() return context.player:getHealth() end
|
||||
context.mana = function() return context.player:getMana() end
|
||||
context.hppercent = function() return context.player:getHealthPercent() end
|
||||
context.manapercent = function() if context.player:getMaxMana() <= 1 then return 100 else return math.floor(context.player:getMana() * 100 / context.player:getMaxMana()) end end
|
||||
context.maxhp = function() return context.player:getMaxHealth() end
|
||||
context.maxmana = function() return context.player:getMaxMana() end
|
||||
context.hpmax = function() return context.player:getMaxHealth() end
|
||||
context.manamax = function() return context.player:getMaxMana() end
|
||||
|
||||
context.cap = function() return context.player:getCapacity() end
|
||||
context.freecap = function() return context.player:getFreeCapacity() end
|
||||
context.maxcap = function() return context.player:getTotalCapacity() end
|
||||
context.capmax = function() return context.player:getTotalCapacity() end
|
||||
|
||||
context.exp = function() return context.player:getExperience() end
|
||||
context.lvl = function() return context.player:getLevel() end
|
||||
context.level = function() return context.player:getLevel() end
|
||||
|
||||
context.mlev = function() return context.player:getMagicLevel() end
|
||||
context.magic = function() return context.player:getMagicLevel() end
|
||||
context.mlevel = function() return context.player:getMagicLevel() end
|
||||
|
||||
context.soul = function() return context.player:getSoul() end
|
||||
context.stamina = function() return context.player:getStamina() end
|
||||
context.voc = function() return context.player:getVocation() end
|
||||
context.vocation = function() return context.player:getVocation() end
|
||||
|
||||
context.bless = function() return context.player:getBlessings() end
|
||||
context.blesses = function() return context.player:getBlessings() end
|
||||
context.blessings = function() return context.player:getBlessings() end
|
||||
|
||||
|
||||
context.pos = function() return context.player:getPosition() end
|
||||
context.posx = function() return context.player:getPosition().x end
|
||||
context.posy = function() return context.player:getPosition().y end
|
||||
context.posz = function() return context.player:getPosition().z end
|
||||
|
||||
context.direction = function() return context.player:getDirection() end
|
||||
context.speed = function() return context.player:getSpeed() end
|
||||
context.skull = function() return context.player:getSkull() end
|
||||
context.outfit = function() return context.player:getOutfit() end
|
||||
|
||||
context.setOutfit = function(outfit)
|
||||
modules.game_outfit.ignoreNextOutfitWindow = g_clock.millis()
|
||||
g_game.requestOutfit()
|
||||
context.schedule(100, function()
|
||||
g_game.changeOutfit(outfit)
|
||||
end)
|
||||
end
|
||||
context.changeOutfit = context.setOutfit
|
||||
context.setSpeed = function(value) context.player:setSpeed(value) end
|
||||
|
||||
context.autoWalk = function(destination) return context.player:autoWalk(destination) end
|
||||
context.walk = function(dir) return modules.game_walking.walk(dir) end
|
||||
context.turn = function(dir) return g_game.turn(dir) end
|
||||
|
||||
-- game releated
|
||||
context.say = g_game.talk
|
||||
context.talk = g_game.talk
|
||||
|
||||
context.saySpell = function(text, lastSpellTimeout)
|
||||
if context.lastSpell == nil then
|
||||
context.lastSpell = 0
|
||||
end
|
||||
if not lastSpellTimeout then
|
||||
lastSpellTimeout = 1000
|
||||
end
|
||||
if context.lastSpell + lastSpellTimeout > context.now then
|
||||
return false
|
||||
end
|
||||
context.say(text)
|
||||
context.lastSpell = context.now
|
||||
return true
|
||||
end
|
||||
|
||||
context.setSpellTimeout = function()
|
||||
context.lastSpell = context.now
|
||||
end
|
||||
|
||||
context.talkPrivate = g_game.talkPrivate
|
||||
context.sayPrivate = g_game.talkPrivate
|
||||
context.use = g_game.useInventoryItem
|
||||
context.usewith = g_game.useInventoryItemWith
|
||||
context.useWith = g_game.useInventoryItemWith
|
||||
context.findItem = g_game.findItemInContainers
|
||||
|
||||
context.attack = g_game.attack
|
||||
context.cancelAttack = g_game.cancelAttack
|
||||
context.follow = g_game.follow
|
||||
context.cancelFollow = g_game.cancelFollow
|
||||
context.cancelAttackAndFollow = g_game.cancelAttackAndFollow
|
||||
|
||||
context.logout = g_game.forceLogout
|
||||
context.ping = g_game.getPing
|
32
modules/game_bot/functions/player_conditions.lua
Normal file
32
modules/game_bot/functions/player_conditions.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
local context = G.botContext
|
||||
|
||||
for i, state in ipairs(PlayerStates) do
|
||||
context[state] = state
|
||||
end
|
||||
|
||||
context.hasCondition = function(condition) return bit.band(context.player:getStates(), condition) > 0 end
|
||||
|
||||
context.isPoisioned = function() return context.hasCondition(PlayerStates.Poison) end
|
||||
context.isBurnining = function() return context.hasCondition(PlayerStates.Burn) end
|
||||
context.isEnergized = function() return context.hasCondition(PlayerStates.Energy) end
|
||||
context.isDrunk = function() return context.hasCondition(PlayerStates.Drunk) end
|
||||
context.hasManaShield = function() return context.hasCondition(PlayerStates.ManaShield) end
|
||||
context.isParalyzed = function() return context.hasCondition(PlayerStates.Paralyze) end
|
||||
context.hasHaste = function() return context.hasCondition(PlayerStates.Haste) end
|
||||
context.hasSwords = function() return context.hasCondition(PlayerStates.Swords) end
|
||||
context.isInFight = function() return context.hasCondition(PlayerStates.Swords) end
|
||||
context.canLogout = function() return not context.hasCondition(PlayerStates.Swords) end
|
||||
context.isDrowning = function() return context.hasCondition(PlayerStates.Drowning) end
|
||||
context.isFreezing = function() return context.hasCondition(PlayerStates.Freezing) end
|
||||
context.isDazzled = function() return context.hasCondition(PlayerStates.Dazzled) end
|
||||
context.isCursed = function() return context.hasCondition(PlayerStates.Cursed) end
|
||||
context.hasPartyBuff = function() return context.hasCondition(PlayerStates.PartyBuff) end
|
||||
context.hasPzLock = function() return context.hasCondition(PlayerStates.PzBlock) end
|
||||
context.hasPzBlock = function() return context.hasCondition(PlayerStates.PzBlock) end
|
||||
context.isPzLocked = function() return context.hasCondition(PlayerStates.PzBlock) end
|
||||
context.isPzBlocked = function() return context.hasCondition(PlayerStates.PzBlock) end
|
||||
context.isInProtectionZone = function() return context.hasCondition(PlayerStates.Pz) end
|
||||
context.hasPz = function() return context.hasCondition(PlayerStates.Pz) end
|
||||
context.isInPz = function() return context.hasCondition(PlayerStates.Pz) end
|
||||
context.isBleeding = function() return context.hasCondition(PlayerStates.Bleeding) end
|
||||
context.isHungry = function() return context.hasCondition(PlayerStates.Hungry) end
|
33
modules/game_bot/functions/player_inventory.lua
Normal file
33
modules/game_bot/functions/player_inventory.lua
Normal file
@@ -0,0 +1,33 @@
|
||||
local context = G.botContext
|
||||
|
||||
context.SlotOther = InventorySlotOther
|
||||
context.SlotHead = InventorySlotHead
|
||||
context.SlotNeck = InventorySlotNeck
|
||||
context.SlotBack = InventorySlotBack
|
||||
context.SlotBody = InventorySlotBody
|
||||
context.SlotRight = InventorySlotRight
|
||||
context.SlotLeft = InventorySlotLeft
|
||||
context.SlotLeg = InventorySlotLeg
|
||||
context.SlotFeet = InventorySlotFeet
|
||||
context.SlotFinger = InventorySlotFinger
|
||||
context.SlotAmmo = InventorySlotAmmo
|
||||
context.SlotPurse = InventorySlotPurse
|
||||
|
||||
context.getInventoryItem = function(slot) return context.player:getInventoryItem(slot) end
|
||||
context.getSlot = context.getInventoryItem
|
||||
|
||||
context.getHead = function() return context.getInventoryItem(context.SlotHead) end
|
||||
context.getNeck = function() return context.getInventoryItem(context.SlotNeck) end
|
||||
context.getBack = function() return context.getInventoryItem(context.SlotBack) end
|
||||
context.getBody = function() return context.getInventoryItem(context.SlotBody) end
|
||||
context.getRight = function() return context.getInventoryItem(context.SlotRight) end
|
||||
context.getLeft = function() return context.getInventoryItem(context.SlotLeft) end
|
||||
context.getLeg = function() return context.getInventoryItem(context.SlotLeg) end
|
||||
context.getFeet = function() return context.getInventoryItem(context.SlotFeet) end
|
||||
context.getFinger = function() return context.getInventoryItem(context.SlotFinger) end
|
||||
context.getAmmo = function() return context.getInventoryItem(context.SlotAmmo) end
|
||||
context.getPurse = function() return context.getInventoryItem(context.SlotPurse) end
|
||||
|
||||
|
||||
context.getContainers = function() return g_game.getContainers() end
|
||||
context.getContainer = function(index) return g_game.getContainer(index) end
|
3
modules/game_bot/functions/test.lua
Normal file
3
modules/game_bot/functions/test.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
local context = G.botContext
|
||||
|
||||
context.test = function() return context.info("test") end
|
4
modules/game_bot/functions/tools.lua
Normal file
4
modules/game_bot/functions/tools.lua
Normal file
@@ -0,0 +1,4 @@
|
||||
local context = G.botContext
|
||||
|
||||
context.encode = function(data) return json.encode(data) end
|
||||
context.decode = function(text) local status, result = pcall(function() return json.decode(text) end) if status then return result end return {} end
|
95
modules/game_bot/functions/ui.lua
Normal file
95
modules/game_bot/functions/ui.lua
Normal file
@@ -0,0 +1,95 @@
|
||||
local context = G.botContext
|
||||
|
||||
context.setupUI = function(otml, parent)
|
||||
if parent == nil then
|
||||
parent = context.panel
|
||||
end
|
||||
local widget = g_ui.loadUIFromString(otml, parent)
|
||||
return widget
|
||||
end
|
||||
|
||||
context.addTab = function(name)
|
||||
context.tabs:setOn(true)
|
||||
return context.tabs:addTab(name, g_ui.createWidget('BotPanel')).tabPanel
|
||||
end
|
||||
|
||||
context.addSwitch = function(id, text, onClickCallback, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local switch = g_ui.createWidget('BotSwitch', parent)
|
||||
switch:setId(id)
|
||||
switch:setText(text)
|
||||
switch.onClick = onClickCallback
|
||||
return switch
|
||||
end
|
||||
|
||||
context.addButton = function(id, text, onClickCallback, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local button = g_ui.createWidget('BotButton', parent)
|
||||
button:setId(id)
|
||||
button:setText(text)
|
||||
button.onClick = onClickCallback
|
||||
return button
|
||||
end
|
||||
|
||||
context.addLabel = function(id, text, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local label = g_ui.createWidget('BotLabel', parent)
|
||||
label:setId(id)
|
||||
label:setText(text)
|
||||
return label
|
||||
end
|
||||
|
||||
context.addTextEdit = function(id, text, onTextChangeCallback, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local widget = g_ui.createWidget('BotTextEdit', parent)
|
||||
widget:setId(id)
|
||||
widget.onTextChange = onTextChangeCallback
|
||||
widget:setText(text)
|
||||
return widget
|
||||
end
|
||||
|
||||
context.addSeparator = function(id, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local separator = g_ui.createWidget('BotSeparator', parent)
|
||||
separator:setId(id)
|
||||
return separator
|
||||
end
|
||||
|
||||
context._addMacroSwitch = function(name, keys, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local text = name
|
||||
if keys:len() > 0 then
|
||||
text = name .. " [" .. keys .. "]"
|
||||
end
|
||||
local switch = context.addSwitch("macro_" .. #context._macros, text, function(widget)
|
||||
context.storage._macros[name] = not context.storage._macros[name]
|
||||
widget:setOn(context.storage._macros[name])
|
||||
end, parent)
|
||||
switch:setOn(context.storage._macros[name])
|
||||
return switch
|
||||
end
|
||||
|
||||
context._addHotkeySwitch = function(name, keys, parent)
|
||||
if not parent then
|
||||
parent = context.panel
|
||||
end
|
||||
local text = name
|
||||
if keys:len() > 0 then
|
||||
text = name .. " [" .. keys .. "]"
|
||||
end
|
||||
local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil, parent)
|
||||
switch:setOn(false)
|
||||
return switch
|
||||
end
|
Reference in New Issue
Block a user