diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index bd1d233..ad013c0 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -19,7 +19,7 @@ local serverSelector local clientVersionSelector local serverHostTextEdit local rememberPasswordBox -local protos = {"740", "760", "772", "800", "810", "854", "860", "1090", "1096", "1099"} +local protos = {"740", "760", "772", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099"} -- private functions diff --git a/modules/client_options/game.otui b/modules/client_options/game.otui index f902bfd..3eb821c 100644 --- a/modules/client_options/game.otui +++ b/modules/client_options/game.otui @@ -28,7 +28,7 @@ Panel OptionCheckBox id: realDirection - !text: tr('Show real direction') + !text: tr('Show real direction (ignore walk direction)') !tooltip: tr('Shows real creature direction while it\'s walking') Label diff --git a/modules/game_bot/bot.lua b/modules/game_bot/bot.lua index deac035..e8d22fa 100644 --- a/modules/game_bot/bot.lua +++ b/modules/game_bot/bot.lua @@ -17,7 +17,7 @@ local errorOccured = false local statusLabel = nil local compiledConfig = nil local configTab = nil -local tabs = {"macros", "hotkeys", "callbacks", "other"} +local tabs = {"main", "macros", "hotkeys", "callbacks", "other"} local mainTab = nil local activeTab = nil local editorText = {"", ""} @@ -31,6 +31,8 @@ function init() connect(rootWidget, { onKeyDown = botKeyDown, onKeyUp = botKeyUp, onKeyPress = botKeyPress }) + + connect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing }) botConfigFile = g_configs.create("/bot.otml") local config = botConfigFile:get("config") @@ -103,6 +105,8 @@ function terminate() onKeyPress = botKeyPress }) disconnect(g_game, { onGameStart = online, onGameEnd = offline, onTalk = botOnTalk}) + + disconnect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing }) removeEvent(executeEvent) removeEvent(checkMsgsEvent) @@ -130,7 +134,7 @@ function online() botButton:show() updateEnabled() if botConfig.enabled then - refreshConfig() + scheduleEvent(refreshConfig, 1) else clearConfig() end @@ -263,6 +267,7 @@ function refreshConfig() return end errorOccured = false + g_game.enableTileThingLuaCallback(false) local status, result = pcall(function() return executeBot(config.script, config.storage, botPanel, botMsgCallback) end) if not status then errorOccured = true @@ -274,7 +279,7 @@ function refreshConfig() end function executeConfig() - executeEvent = scheduleEvent(executeConfig, 20) + executeEvent = scheduleEvent(executeConfig, 25) if compiledConfig == nil then return end @@ -359,12 +364,22 @@ function botOnTalk(name, level, mode, text, channelId, pos) return false end -function botOnGet(oprationId, url, err, data) +function botAddThing(tile, thing, asd) if compiledConfig == nil then return false end - local status, result = pcall(function() compiledConfig.callbacks.onGet(oprationId, url, err, data) end) + local status, result = pcall(function() compiledConfig.callbacks.onAddThing(tile, thing) end) if not status then errorOccured = true statusLabel:setText(tr("Error: " .. result)) end - return false + return false +end + +function botRemoveThing(tile, thing) + if compiledConfig == nil then return false end + local status, result = pcall(function() compiledConfig.callbacks.onRemoveThing(tile, thing) end) + if not status then + errorOccured = true + statusLabel:setText(tr("Error: " .. result)) + end + return false end \ No newline at end of file diff --git a/modules/game_bot/defaultconfig.lua b/modules/game_bot/defaultconfig.lua index 663b628..e4a8017 100644 --- a/modules/game_bot/defaultconfig.lua +++ b/modules/game_bot/defaultconfig.lua @@ -3,34 +3,71 @@ botDefaultConfig = { {name = "Example", script = [[ --#Example config +--#main +local widget = setupUI(%[%[ +Panel + id: redPanel + background: red + margin-top: 10 + margin-bottom: 10 + height: 100 + + Label + anchors.fill: parent + text: custom ui, otml based + text-align: center +%]%]) + --#macros macro(5000, "macro send link", "f5", function() g_game.talk("macro test - https://github.com/OTCv8/otclient_bot") + g_game.talk("bot is hiding 50% of effects as example, say exevo gran mas vis") end) macro(1000, "flag tiles", function() local staticText = StaticText.create() staticText:addMessage("t", 9, "xDDD") local tile = player:getTile() - tile:clearTexts() - tile:addText(staticText) - for i = 1, 10 do - schedule(1000 * i, function() - staticText:setText(i) - end) - end - schedule(11000, function() - tile:clearTexts() - end) + tile:setText("Hello =)", "red") end) +macro(25, "auto healing", function() + if hppercent() < 80 then + say("exura") + delay(1000) -- not calling this macro for next 1s + end +end) addSeparator("spe0") --#hotkeys hotkey('y', 'test hotkey', function() g_game.talk('hotkey elo') end) +singlehotkey('x', 'single hotkey', function() g_game.talk('single hotkey') end) + +singlehotkey('=', "Zoom in map", function () zoomIn() end) +singlehotkey('-', "Zoom out map", function () zoomOut() end) --#callbacks +onAddThing(function(tile, thing) + if thing:isItem() and thing:getId() == 2129 then + local pos = tile:getPosition().x .. "," .. tile:getPosition().y .. "," .. tile:getPosition().z + if not storage[pos] or storage[pos] < now then + storage[pos] = now + 20000 + end + tile:setTimer(storage[pos] - now) + end +end) + +-- hide 50% of effects +onAddThing(function(tile, thing) + if thing:isEffect() and math.random(1, 2) == 1 then + thing:hide() + end +end) + +listen(player:getName(), function(text) + info("you said: " .. text) +end) --#other addLabel("label1", "Test label 1") @@ -50,10 +87,7 @@ HTTP.getJSON("https://api.ipify.org/?format=json", function(data, err) end info("HTTP: My IP is: " .. tostring(data['ip'])) end) - -info("Bot started") - - + ]]}, {}, {}, {}, {} }, diff --git a/modules/game_bot/executor.lua b/modules/game_bot/executor.lua index 3a53a64..856073a 100644 --- a/modules/game_bot/executor.lua +++ b/modules/game_bot/executor.lua @@ -1,24 +1,26 @@ -function executeBot(config, storage, panel, msg) +function executeBot(config, storage, panel, msgCallback) local context = {} context.panel = panel context.storage = storage - if context.storage.macros == nil then - context.storage.macros = {} -- active macros + if context.storage._macros == nil then + context.storage._macros = {} -- active macros end -- - context.macros = {} - context.hotkeys = {} - context.scheduler = {} - context.callbacks = { + context._macros = {} + context._hotkeys = {} + context._scheduler = {} + context._callbacks = { onKeyDown = {}, onKeyUp = {}, onKeyPress = {}, onTalk = {}, + onAddThing = {}, + onRemoveThing = {} } context.ui = {} - -- basic functions + -- basic functions & classes context.print = print context.pairs = pairs context.ipairs = ipairs @@ -29,30 +31,33 @@ function executeBot(config, storage, panel, msg) context.tr = tr context.json = json context.regexMatch = regexMatch - - -- game functions - context.say = g_game.talk - context.talk = g_game.talk - context.talkPrivate = context.talkPrivate - context.sayPrivate = context.talkPrivate - context.use = g_game.useInventoryItem - context.usewith = g_game.useInventoryItemWith - context.useWith = g_game.useInventoryItemWith - context.findItem = g_game.findItemInContainers -- classes context.g_game = g_game context.g_map = g_map + context.g_ui = g_ui context.StaticText = StaticText + context.Position = Position context.HTTP = HTTP -- log functions - context.info = function(text) return msg("info", text) end - context.warn = function(text) return msg("warn", text) end - context.error = function(text) return msg("error", text) end + context.info = function(text) return msgCallback("info", tostring(text)) end + context.warn = function(text) return msgCallback("warn", tostring(text)) end + context.error = function(text) return msgCallback("error", tostring(text)) end context.warning = context.warn -- UI + context.setupUI = function(otml, parent) + if parent == nil then + parent = context.panel + end + local widget = g_ui.loadUIFromString(otml, parent) + if parent == context.panel and widget:getId() then + context.ui[widget:getId()] = widget + end + return widget + end + context.addSwitch = function(id, text, onClickCallback) local switch = g_ui.createWidget('BotSwitch', context.panel) switch:setId(id) @@ -86,25 +91,25 @@ function executeBot(config, storage, panel, msg) return separator end - context.addMacroSwitch = function(name, keys) + context._addMacroSwitch = function(name, keys) 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]) + 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) - switch:setOn(context.storage.macros[name]) + switch:setOn(context.storage._macros[name]) return switch end - context.addHotkeySwitch = function(name, keys) + context._addHotkeySwitch = function(name, keys) local text = name if keys:len() > 0 then text = name .. " [" .. keys .. "]" end - local switch = context.addSwitch("hotkey_" .. #context.hotkeys, text, nil) + local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil) switch:setOn(false) return switch end @@ -136,13 +141,13 @@ function executeBot(config, storage, panel, msg) local switch = nil if name:len() > 0 then - if context.storage.macros[name] == nil then - context.storage.macros[name] = true + if context.storage._macros[name] == nil then + context.storage._macros[name] = true end - switch = context.addMacroSwitch(name, hotkey) + switch = context._addMacroSwitch(name, hotkey) end - table.insert(context.macros, { + table.insert(context._macros, { timeout = timeout, name = name, callback = callback, @@ -160,33 +165,139 @@ function executeBot(config, storage, panel, msg) 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) - end + switch = context._addHotkeySwitch(name, keys) + end - context.hotkeys[keys] = { + context._hotkeys[keys] = { name = name, callback = callback, lastExecution = context.now, - switch = switch + switch = switch, + single = false } end + -- singlehotkey(keys, callback) + -- singlehotkey(keys, name, callback) + context.singlehotkey = function(keys, name, callback) + 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) + end + + context._hotkeys[keys] = { + name = name, + callback = callback, + lastExecution = context.now, + switch = switch, + single = true + } + end + -- schedule(timeout, callback) context.schedule = function(timeout, callback) local extecute_time = g_clock.millis() + timeout - table.insert(context.scheduler, { + table.insert(context._scheduler, { execution = extecute_time, callback = callback }) - table.sort(context.scheduler, function(a, b) return a.execution < b.execution end) + table.sort(context._scheduler, function(a, b) return a.execution < b.execution end) + end + + -- 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 + + -- listen(name, callback) -- callback = function(text, channelId, pos) + context.listen = function(name, callback) + name = name:lower() + context.onTalk(function(name2, level, mode, text, channelId, pos) + if name == name2:lower() then + callback(text, channelId, pos) + end + end) + end + + -- delay(duration) + 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 -- init context context.now = g_clock.millis() context.time = g_clock.millis() context.player = g_game.getLocalPlayer() + + require("functions.lua") + setupFunctions(context) -- run script assert(load(config, nil, nil, context))() @@ -196,51 +307,89 @@ function executeBot(config, storage, panel, msg) context.now = g_clock.millis() context.time = g_clock.millis() - for i, macro in ipairs(context.macros) do - if macro.lastExecution + macro.timeout <= context.now and (macro.name == nil or macro.name:len() < 1 or context.storage.macros[macro.name]) then - macro.lastExecution = context.now - macro.callback() + for i, macro in ipairs(context._macros) do + if macro.lastExecution + macro.timeout <= context.now and (macro.name == nil or macro.name:len() < 1 or context.storage._macros[macro.name]) then + if not macro.delay or macro.delay < context.now then + macro.lastExecution = context.now + context._currentExecution = context._macros[i] + macro.callback() + context._currentExecution = nil + end end end - while #context.scheduler > 0 and context.scheduler[1].execution <= g_clock.millis() do - context.scheduler[1].callback() - table.remove(context.scheduler, 1) + while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do + context._scheduler[1].callback() + table.remove(context._scheduler, 1) end end, callbacks = { onKeyDown = function(keyCode, keyboardModifiers) local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers) - for i, macro in ipairs(context.macros) do + for i, macro in ipairs(context._macros) do if macro.switch and macro.hotkey == keyDesc then macro.switch:onClick() end end - local hotkey = context.hotkeys[keyDesc] - if hotkey and hotkey.switch then - hotkey.switch:setOn(true) + local hotkey = context._hotkeys[keyDesc] + if hotkey then + if hotkey.single then + if not hotkey.delay or hotkey.delay < context.now then + hotkey.lastExecution = context.now + context._currentExecution = hotkey + hotkey.callback() + context._currentExecution = nil + end + end + if hotkey.switch then + hotkey.switch:setOn(true) + end + end + for i, callback in ipairs(context._callbacks.onKeyDown) do + callback(keyDesc) end - end, onKeyUp = function(keyCode, keyboardModifiers) local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers) - local hotkey = context.hotkeys[keyDesc] - if hotkey and hotkey.switch then - hotkey.switch:setOn(false) + local hotkey = context._hotkeys[keyDesc] + if hotkey then + if hotkey.switch then + hotkey.switch:setOn(false) + end + end + for i, callback in ipairs(context._callbacks.onKeyUp) do + callback(keyDesc) end - end, onKeyPress = function(keyCode, keyboardModifiers, autoRepeatTicks) local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers) - local hotkey = context.hotkeys[keyDesc] - if hotkey then - hotkey.lastExecution = context.now - hotkey.callback() + local hotkey = context._hotkeys[keyDesc] + if hotkey and not hotkey.single then + if not hotkey.delay or hotkey.delay < context.now then + hotkey.lastExecution = context.now + context._currentExecution = hotkey + hotkey.callback() + context._currentExecution = nil + end + end + for i, callback in ipairs(context._callbacks.onKeyPress) do + callback(keyDesc, autoRepeatTicks) end - end, onTalk = function(name, level, mode, text, channelId, pos) - + for i, callback in ipairs(context._callbacks.onTalk) do + callback(name, level, mode, text, channelId, pos) + end + end, + onAddThing = function(tile, thing) + for i, callback in ipairs(context._callbacks.onAddThing) do + callback(tile, thing) + end + end, + onRemoveThing = function(tile, thing) + for i, callback in ipairs(context._callbacks.onRemoveThing) do + callback(tile, thing) + end end } } diff --git a/modules/game_bot/functions.lua b/modules/game_bot/functions.lua new file mode 100644 index 0000000..03851e4 --- /dev/null +++ b/modules/game_bot/functions.lua @@ -0,0 +1,80 @@ +-- In this file are declared extra functions and variables for bot +-- It has been made to make most common functions shorter, for eg. hp() instead of player:getHealth() + +function setupFunctions(context) + -- player releated + 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() return context.player:getManaPercent() 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.autoWalk = function(destination) return context.player:autoWalk(destination) end + context.walk = function(dir) return modules.game_walking.walk(dir) end + + -- game releated + context.say = g_game.talk + context.talk = g_game.talk + 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 + + -- map releated + context.zoomIn = function() modules.game_interface.getMapPanel():zoomIn() end + context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end + + -- tools + 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 +end \ No newline at end of file diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index e68c501..c1cab29 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -532,7 +532,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing) if g_game.getFeature(GameBot) and useThing then menu:addSeparator() - menu:addOption(tr("ID: " .. useThing:getId())) + menu:addOption("ID: " .. useThing:getId()) end menu:display(menuPosition) diff --git a/modules/game_walking/walking.lua b/modules/game_walking/walking.lua index 66dd64b..273e472 100644 --- a/modules/game_walking/walking.lua +++ b/modules/game_walking/walking.lua @@ -360,6 +360,7 @@ function walk(dir) lastWalkDir = dir lastWalk = g_clock.millis() + return true end function turn(dir, repeated) diff --git a/otclient_dx.exe b/otclient_dx.exe index 38fbb92..92e6e5d 100644 Binary files a/otclient_dx.exe and b/otclient_dx.exe differ diff --git a/otclient_gl.exe b/otclient_gl.exe index 95e4af2..ef00001 100644 Binary files a/otclient_gl.exe and b/otclient_gl.exe differ diff --git a/pdb/otclient_dx.pdb b/pdb/otclient_dx.pdb index e3da30f..9e68c12 100644 Binary files a/pdb/otclient_dx.pdb and b/pdb/otclient_dx.pdb differ diff --git a/pdb/otclient_gl.pdb b/pdb/otclient_gl.pdb index 7708130..7aacbad 100644 Binary files a/pdb/otclient_gl.pdb and b/pdb/otclient_gl.pdb differ