Version 0.999 BETA - a lot of bug fixes, improvments and more advanced bot

This commit is contained in:
OTCv8
2019-10-31 05:46:22 +01:00
parent 017fa290b4
commit 06b08af1df
36 changed files with 1513 additions and 415 deletions

View File

@@ -6,6 +6,9 @@ It's based on https://github.com/edubart/otclient and it's not backward compatib
# DISCORD # DISCORD
OTClientV8 discord channel: https://discord.gg/feySup6 (new, working link!) OTClientV8 discord channel: https://discord.gg/feySup6 (new, working link!)
# Forum
OTClientV8 forum: https://otland.net/forums/otclient.494/
# FEATURES # FEATURES
- Rewritten and optimized rendering (60 fps on 11 years old computer) - Rewritten and optimized rendering (60 fps on 11 years old computer)
- Better DirectX9 and DirectX11 support - Better DirectX9 and DirectX11 support
@@ -26,6 +29,7 @@ OTClientV8 discord channel: https://discord.gg/feySup6 (new, working link!)
- Updated and optimized battle list - Updated and optimized battle list
- Crosshair, floor fading, extra health/mana bars and panels - Crosshair, floor fading, extra health/mana bars and panels
- Removed a lot of useless and outdated things - Removed a lot of useless and outdated things
- Bot (https://github.com/OTCv8/otclientv8_bot)
- Support for proxies to lower latency and protect against DDoS (extra paid option) - Support for proxies to lower latency and protect against DDoS (extra paid option)
### And hundreds of smaller features, optimizations and bug fixes! ### And hundreds of smaller features, optimizations and bug fixes!
@@ -33,13 +37,8 @@ OTClientV8 discord channel: https://discord.gg/feySup6 (new, working link!)
### There's github repo of tfs 1.3 with otclientv8 features: https://github.com/OTCv8/otclientv8-tfs ### There's github repo of tfs 1.3 with otclientv8 features: https://github.com/OTCv8/otclientv8-tfs
# Facts
### It took almost 1000h to make this project
### OTClientV8 has been used by over 6000 unique players!
### You can check last active players on: http://otclient.ovh/clients.php
# Paid version # Paid version
The difference between paid version and this one is that the 1st one comes with c++ sources and has professional support. You may need c++ source if you want to add some more advanced modifications, better encryption, bot protection or some other things. The free version doesn't offer technical support, you need to follow tutorials and in case of any bug or problem you should submit an issue on github. Check http://otclient.ovh if you want more about paid version and other extra services. The difference between paid version and this one is that the 1st one comes with c++ sources and has better support. You may need c++ source if you want to add some more advanced modifications, better encryption, bot protection or some other things. The free version doesn't offer technical support, you need to follow tutorials and in case of any bug or problem you should submit an issue on github. Visit http://otclient.ovh if you want to know more about paid version and other extra services.
# Quick Start # Quick Start

View File

@@ -9,6 +9,7 @@ local render = nil
local atlas = nil local atlas = nil
local adaptiveRender = nil local adaptiveRender = nil
local slowMain = nil local slowMain = nil
local widgetsInfo = nil
local updateEvent = nil local updateEvent = nil
local monitorEvent = nil local monitorEvent = nil
@@ -36,6 +37,7 @@ function init()
atlas = statsWindow:recursiveGetChildById('atlas') atlas = statsWindow:recursiveGetChildById('atlas')
adaptiveRender = statsWindow:recursiveGetChildById('adaptiveRender') adaptiveRender = statsWindow:recursiveGetChildById('adaptiveRender')
slowMain = statsWindow:recursiveGetChildById('slowMain') slowMain = statsWindow:recursiveGetChildById('slowMain')
widgetsInfo = statsWindow:recursiveGetChildById('widgetsInfo')
lastSend = os.time() lastSend = os.time()
g_stats.resetSleepTime() g_stats.resetSleepTime()
@@ -143,6 +145,7 @@ function sendStats()
g_stats.clear(i - 1) g_stats.clear(i - 1)
g_stats.clearSlow(i - 1) g_stats.clearSlow(i - 1)
end end
data.widgets = g_stats.getWidgetsInfo(10, false)
data = json.encode(data) data = json.encode(data)
if Services.stats ~= nil and Services.stats:len() > 3 then if Services.stats ~= nil and Services.stats:len() > 3 then
g_http.post(Services.stats, data) g_http.post(Services.stats, data)
@@ -171,7 +174,8 @@ function update()
dispatcherStats:setText(g_stats.get(3, 5, true)) dispatcherStats:setText(g_stats.get(3, 5, true))
luaStats:setText(g_stats.get(4, 5, true)) luaStats:setText(g_stats.get(4, 5, true))
luaCallback:setText(g_stats.get(5, 5, true)) luaCallback:setText(g_stats.get(5, 5, true))
slowMain:setText(g_stats.getSlow(3, 10, 10, true) .. "\n\n\n" .. g_stats.getSlow(1, 20, 20, true)) slowMain:setText(g_stats.getSlow(3, 10, 10, true) .. "\n\n\n" .. g_stats.getSlow(1, 20, 20, true))
widgetsInfo:setText(g_stats.getWidgetsInfo(10, true))
if g_proxy then if g_proxy then
local text = "" local text = ""

View File

@@ -87,6 +87,13 @@ MainWindow
DebugText DebugText
id: luaCallback id: luaCallback
text: - text: -
DebugLabel
!text: tr('Widgets')
DebugText
id: widgetsInfo
text: -
DebugLabel DebugLabel
!text: tr('Slow main functions') !text: tr('Slow main functions')

View File

@@ -317,6 +317,12 @@ function UIMoveableTabBar:onStyleApply(styleName, styleNode)
end end
end end
function UIMoveableTabBar:clearTabs()
while #self.tabs > 0 do
self:removeTab(self.tabs[#self.tabs])
end
end
function UIMoveableTabBar:removeTab(tab) function UIMoveableTabBar:removeTab(tab)
local tabTables = {self.tabs, self.preTabs, self.postTabs} local tabTables = {self.tabs, self.preTabs, self.postTabs}
local index = nil local index = nil

View File

@@ -80,8 +80,10 @@ end
function UISpinBox:onStyleApply(styleName, styleNode) function UISpinBox:onStyleApply(styleName, styleNode)
for name, value in pairs(styleNode) do for name, value in pairs(styleNode) do
if name == 'maximum' then if name == 'maximum' then
self.maximum = value
addEvent(function() self:setMaximum(value) end) addEvent(function() self:setMaximum(value) end)
elseif name == 'minimum' then elseif name == 'minimum' then
self.minimum = value
addEvent(function() self:setMinimum(value) end) addEvent(function() self:setMinimum(value) end)
elseif name == 'mouse-scroll' then elseif name == 'mouse-scroll' then
addEvent(function() self:setMouseScroll(value) end) addEvent(function() self:setMouseScroll(value) end)
@@ -118,6 +120,9 @@ function UISpinBox:down()
end end
function UISpinBox:setValue(value, dontSignal) function UISpinBox:setValue(value, dontSignal)
if type(value) == "string" then
value = tonumber(value)
end
value = value or 0 value = value or 0
value = math.max(math.min(self.maximum, value), self.minimum) value = math.max(math.min(self.maximum, value), self.minimum)

View File

@@ -6,6 +6,7 @@ contentsPanel = nil
configWindow = nil configWindow = nil
configEditorText = nil configEditorText = nil
configList = nil configList = nil
botTabs = nil
botPanel = nil botPanel = nil
local botMessages = nil local botMessages = nil
local configCopy = "" local configCopy = ""
@@ -32,6 +33,17 @@ function init()
onKeyPress = botKeyPress }) onKeyPress = botKeyPress })
connect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing }) connect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing })
connect(Creature, {
onAppear = botCreatureAppear,
onDisappear =botCreatureDisappear,
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
connect(LocalPlayer, {
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
botConfigFile = g_configs.create("/bot.otml") botConfigFile = g_configs.create("/bot.otml")
local config = botConfigFile:get("config") local config = botConfigFile:get("config")
@@ -53,18 +65,20 @@ function init()
botWindow = g_ui.loadUI('bot', modules.game_interface.getRightPanel()) botWindow = g_ui.loadUI('bot', modules.game_interface.getRightPanel())
botWindow:setup() botWindow:setup()
contentsPanel = botWindow:getChildById('contentsPanel') contentsPanel = botWindow.contentsPanel
configList = contentsPanel:getChildById('config') configList = contentsPanel.config
enableButton = contentsPanel:getChildById('enableButton') enableButton = contentsPanel.enableButton
statusLabel = contentsPanel:getChildById('statusLabel') statusLabel = contentsPanel.statusLabel
botMessages = contentsPanel:getChildById('messages') botMessages = contentsPanel.messages
botPanel = contentsPanel:getChildById('botPanel') botTabs = contentsPanel.botTabs
botPanel = contentsPanel.botPanel
botTabs:setContentWidget(botPanel)
configWindow = g_ui.displayUI('config') configWindow = g_ui.displayUI('config')
configWindow:hide() configWindow:hide()
configEditorText = configWindow:getChildById('text') configEditorText = configWindow.text
configTab = configWindow:getChildById('configTab') configTab = configWindow.configTab
configTab.onTabChange = editorTabChanged configTab.onTabChange = editorTabChanged
@@ -91,8 +105,27 @@ function init()
end end
function saveConfig() function saveConfig()
botConfigFile:set("config", json.encode(botConfig)) local status, result = pcall(function()
botConfigFile:save() botConfigFile:set("config", json.encode(botConfig))
botConfigFile:save()
end)
if not status then
errorOccured = true
-- try to fix it
local extraInfo = ""
for i = 1, #botConfig.configs do
if botConfig.configs[i].storage then
local status, result = pcall(function() json.encode(botConfig.configs[i].storage) end)
if not status then
botConfig.configs[i].storage = nil
extraInfo = extraInfo .. "\nLocal storage of config " .. i .. " has been erased due to invalid data"
end
end
end
statusLabel:setText("Error while saving config and user storage:\n" .. result .. extraInfo .. "\n\n" .. "Try to restart bot")
return false
end
return true
end end
function terminate() function terminate()
@@ -107,6 +140,17 @@ function terminate()
disconnect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing }) disconnect(Tile, { onAddThing = botAddThing, onRemoveThing = botRemoveThing })
disconnect(Creature, {
onAppear = botCreatureAppear,
onDisappear =botCreatureDisappear,
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
disconnect(LocalPlayer, {
onPositionChange = botCreaturePositionChange,
onHealthPercentChange = botCraetureHealthPercentChange
})
removeEvent(executeEvent) removeEvent(executeEvent)
removeEvent(checkMsgsEvent) removeEvent(checkMsgsEvent)
@@ -259,13 +303,15 @@ end
function clearConfig() function clearConfig()
compiledConfig = nil compiledConfig = nil
botPanel:destroyChildren()
botTabs:clearTabs()
botTabs:setOn(false)
botMessages:destroyChildren() botMessages:destroyChildren()
botMessages:updateLayout() botMessages:updateLayout()
end end
function refreshConfig() function refreshConfig()
clearConfig()
configWindow:hide() configWindow:hide()
botConfig.selectedConfig = configList.currentIndex botConfig.selectedConfig = configList.currentIndex
@@ -273,8 +319,13 @@ function refreshConfig()
return return
end end
saveConfig() if not saveConfig() then
clearConfig()
return
end
clearConfig()
local config = botConfig.configs[configList.currentIndex] local config = botConfig.configs[configList.currentIndex]
if not config.storage then if not config.storage then
config.storage = {} config.storage = {}
@@ -286,7 +337,7 @@ function refreshConfig()
end end
errorOccured = false errorOccured = false
g_game.enableTileThingLuaCallback(false) g_game.enableTileThingLuaCallback(false)
local status, result = pcall(function() return executeBot(config.script, config.storage, botPanel, botMsgCallback) end) local status, result = pcall(function() return executeBot(config.script, config.storage, botTabs, botMsgCallback) end)
if not status then if not status then
errorOccured = true errorOccured = true
statusLabel:setText("Error: " .. tostring(result)) statusLabel:setText("Error: " .. tostring(result))
@@ -342,9 +393,8 @@ function checkMsgs()
end end
end end
function botKeyDown(widget, keyCode, keyboardModifiers) function safeBotCall(func)
if keyCode == KeyUnknown or compiledConfig == nil then return false end local status, result = pcall(func)
local status, result = pcall(function() compiledConfig.callbacks.onKeyDown(keyCode, keyboardModifiers) end)
if not status then if not status then
errorOccured = true errorOccured = true
statusLabel:setText("Error: " .. result) statusLabel:setText("Error: " .. result)
@@ -352,52 +402,55 @@ function botKeyDown(widget, keyCode, keyboardModifiers)
return false return false
end end
function botKeyDown(widget, keyCode, keyboardModifiers)
if compiledConfig == nil then return false end
if keyCode == KeyUnknown then return end
safeBotCall(function() compiledConfig.callbacks.onKeyDown(keyCode, keyboardModifiers) end)
end
function botKeyUp(widget, keyCode, keyboardModifiers) function botKeyUp(widget, keyCode, keyboardModifiers)
if keyCode == KeyUnknown or compiledConfig == nil then return false end if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onKeyUp(keyCode, keyboardModifiers) end) if keyCode == KeyUnknown then return end
if not status then safeBotCall(function() compiledConfig.callbacks.onKeyUp(keyCode, keyboardModifiers) end)
errorOccured = true
statusLabel:setText("Error: " .. result)
end
return false
end end
function botKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks) function botKeyPress(widget, keyCode, keyboardModifiers, autoRepeatTicks)
if keyCode == KeyUnknown or compiledConfig == nil then return false end if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks) end) if keyCode == KeyUnknown then return end
if not status then safeBotCall(function() compiledConfig.callbacks.onKeyPress(keyCode, keyboardModifiers, autoRepeatTicks) end)
errorOccured = true
statusLabel:setText("Error: " .. result)
end
return false
end end
function botOnTalk(name, level, mode, text, channelId, pos) function botOnTalk(name, level, mode, text, channelId, pos)
if compiledConfig == nil then return false end if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onTalk(name, level, mode, text, channelId, pos) end) safeBotCall(function() compiledConfig.callbacks.onTalk(name, level, mode, text, channelId, pos) end)
if not status then
errorOccured = true
statusLabel:setText("Error: " .. result)
end
return false
end end
function botAddThing(tile, thing, asd) function botAddThing(tile, thing)
if compiledConfig == nil then return false end if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onAddThing(tile, thing) end) safeBotCall(function() compiledConfig.callbacks.onAddThing(tile, thing) end)
if not status then
errorOccured = true
statusLabel:setText("Error: " .. result)
end
return false
end end
function botRemoveThing(tile, thing) function botRemoveThing(tile, thing)
if compiledConfig == nil then return false end if compiledConfig == nil then return false end
local status, result = pcall(function() compiledConfig.callbacks.onRemoveThing(tile, thing) end) safeBotCall(function() compiledConfig.callbacks.onRemoveThing(tile, thing) end)
if not status then end
errorOccured = true
statusLabel:setText("Error: " .. result) function botCreatureAppear(creature)
end if compiledConfig == nil then return false end
return false safeBotCall(function() compiledConfig.callbacks.onCreatureAppear(creature) end)
end end
function botCreatureDisappear(creature)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreatureDisappear(creature) end)
end
function botCreaturePositionChange(creature, newPos, oldPos)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreaturePositionChange(creature, newPos, oldPos) end)
end
function botCraetureHealthPercentChange(creature, healthPercent)
if compiledConfig == nil then return false end
safeBotCall(function() compiledConfig.callbacks.onCreatureHealthPercentChange(creature, healthPercent) end)
end

View File

@@ -3,12 +3,14 @@ BotButton < Button
BotSwitch < Button BotSwitch < Button
margin-top: 2 margin-top: 2
height: 20
image-color: green image-color: green
$!on: $!on:
image-color: red image-color: red
BotLabel < Label BotLabel < Label
margin-top: 2 margin-top: 2
height: 20
text-auto-resize: true text-auto-resize: true
text-align: center text-align: center
text-wrap: true text-wrap: true
@@ -18,17 +20,20 @@ BotItem < UIItem
size: 32 32 size: 32 32
border: 1 black border: 1 black
&selectable: true &selectable: true
BotTextEdit < TextEdit
@onClick: modules.game_textedit.show(self)
text-align: center
multiline: false
BotSeparator < HorizontalSeparator BotSeparator < HorizontalSeparator
margin-top: 5 margin-top: 5
margin-bottom: 3 margin-bottom: 3
BotPanel < Panel BotPanel < Panel
margin-top: 2
layout: layout:
type: verticalBox type: verticalBox
fit-children: true
MiniWindow MiniWindow
id: botWindow id: botWindow
!text: tr('Bot') !text: tr('Bot')
@@ -99,12 +104,28 @@ MiniWindow
anchors.right: parent.right anchors.right: parent.right
anchors.top: prev.bottom anchors.top: prev.bottom
margin-top: 5 margin-top: 5
MoveableTabBar
id: botTabs
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
tab-spacing: 2
height: 20
movable: false
$on:
visible: true
margin-top: 2
$!on:
visible: false
margin-top: -20
Panel Panel
id: botPanel
margin-top: 2
anchors.top: prev.bottom anchors.top: prev.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
id: botPanel
layout:
type: verticalBox

View File

@@ -1,82 +1,52 @@
botDefaultConfig = { botDefaultConfig = {
configs = { configs = {
{name = "Example", script = [=[ {name = "Example", script = [=[
--#Example --Example
info("Tested on 10.99")
--#main --#main
local widget = setupUI([[
Panel Panels.Haste()
id: redPanel Panels.ManaShield()
background: red Panels.Health()
margin-top: 10 Panels.HealthItem()
margin-bottom: 10 Panels.ManaItem()
height: 100 Panels.ManaItem()
Panels.AntiParalyze()
Label
anchors.fill: parent local tab2 = addTab("Another Tab")
text: custom ui, otml based addButton("button1", "test button on 2nd tab", nil, tab2)
text-align: center
]]) local tab3 = addTab("3th tab")
addLabel("label1", "Label on 3th tab", tab3)
Panels.Turning(tab3)
--#macros --#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 helloLabel = addLabel("helloLabel", "", tab2)
player:getTile():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")
macro(1000, "example macro (time)", nil, function()
helloLabel:setText("Time from start: " .. now)
end, tab2)
--#hotkeys --#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) hotkey("f5", "example hotkey", function()
singlehotkey('-', "Zoom out map", function () zoomOut() end) info("Wow, you clicked f5 hotkey")
--#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) end)
-- hide 50% of effects singlehotkey("f6", "example hotkey2", function()
onAddThing(function(tile, thing) info("Wow, you clicked f6 singlehotkey")
if thing:isEffect() and math.random(1, 2) == 1 then end)
thing:hide() --#callbacks
end
local positionLabel = addLabel("positionLabel", "")
onPlayerPositionChange(function()
positionLabel:setText("Pos: " .. posx() .. "," .. posy() .. "," .. posz())
end) end)
listen(player:getName(), function(text) listen(player:getName(), function(text)
info("you said: " .. text) info("you said: " .. text)
end) end)
--#other --#other
addLabel("label1", "Test label 1")
addSeparator("sep1")
addLabel("label2", "Test label 2")
storage.clicks = 0
addButton("button1", "Click me", function()
storage.clicks = storage.clicks + 1
ui.button1:setText("Clicks: " .. storage.clicks)
end)
HTTP.getJSON("https://api.ipify.org/?format=json", function(data, err) HTTP.getJSON("https://api.ipify.org/?format=json", function(data, err)
if err then if err then
@@ -85,6 +55,7 @@ HTTP.getJSON("https://api.ipify.org/?format=json", function(data, err)
end end
info("HTTP: My IP is: " .. tostring(data['ip'])) info("HTTP: My IP is: " .. tostring(data['ip']))
end) end)
]=]}, ]=]},

View File

@@ -1,6 +1,8 @@
function executeBot(config, storage, panel, msgCallback) function executeBot(config, storage, tabs, msgCallback)
local context = {} local context = {}
context.panel = panel context.tabs = tabs
context.panel = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel
context.storage = storage context.storage = storage
if context.storage._macros == nil then if context.storage._macros == nil then
context.storage._macros = {} -- active macros context.storage._macros = {} -- active macros
@@ -16,9 +18,12 @@ function executeBot(config, storage, panel, msgCallback)
onKeyPress = {}, onKeyPress = {},
onTalk = {}, onTalk = {},
onAddThing = {}, onAddThing = {},
onRemoveThing = {} onRemoveThing = {},
onCreatureAppear = {},
onCreatureDisappear = {},
onCreaturePositionChange = {},
onCreatureHealthPercentChange = {}
} }
context.ui = {}
-- basic functions & classes -- basic functions & classes
context.print = print context.print = print
@@ -44,265 +49,20 @@ function executeBot(config, storage, panel, msgCallback)
context.info = function(text) return msgCallback("info", tostring(text)) end context.info = function(text) return msgCallback("info", tostring(text)) end
context.warn = function(text) return msgCallback("warn", tostring(text)) end context.warn = function(text) return msgCallback("warn", tostring(text)) end
context.error = function(text) return msgCallback("error", tostring(text)) end context.error = function(text) return msgCallback("error", tostring(text)) end
context.warning = context.warn 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)
switch:setText(text)
switch.onClick = onClickCallback
context.ui[id] = switch
return switch
end
context.addButton = function(id, text, onClickCallback)
local button = g_ui.createWidget('BotButton', context.panel)
button:setId(id)
button:setText(text)
button.onClick = onClickCallback
context.ui[id] = button
return button
end
context.addLabel = function(id, text)
local label = g_ui.createWidget('BotLabel', context.panel)
label:setId(id)
label:setText(text)
context.ui[id] = label
return label
end
context.addSeparator = function(id)
local separator = g_ui.createWidget('BotSeparator', context.panel)
separator:setId(id)
context.ui[id] = separator
return separator
end
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])
end)
switch:setOn(context.storage._macros[name])
return switch
end
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)
switch:setOn(false)
return switch
end
-- MAIN BOT FUNCTION
-- macro(timeout, callback)
-- macro(timeout, name, callback)
-- macro(timeout, name, hotkey, callback)
context.macro = function(timeout, name, hotkey, callback)
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 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)
end
table.insert(context._macros, {
timeout = timeout,
name = name,
callback = callback,
lastExecution = context.now,
hotkey = hotkey,
switch = switch
})
return context._macros[#context._macros]
end
-- hotkey(keys, callback)
-- hotkey(keys, name, callback)
context.hotkey = 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 = false
}
return context._hotkeys[keys]
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
}
return context._hotkeys[keys]
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
-- 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)
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
-- 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
-- init context -- init context
context.now = g_clock.millis() context.now = g_clock.millis()
context.time = g_clock.millis() context.time = g_clock.millis()
context.player = g_game.getLocalPlayer() context.player = g_game.getLocalPlayer()
require("functions.lua") -- init functions
setupFunctions(context) G.botContext = context
dofiles("functions")
context.Panels = {}
dofiles("panels")
G.botContext = nil
-- run script -- run script
assert(load(config, nil, nil, context))() assert(load(config, nil, nil, context))()
@@ -313,11 +73,8 @@ function executeBot(config, storage, panel, msgCallback)
for i, macro in ipairs(context._macros) do 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 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 if macro.callback() then
macro.lastExecution = context.now macro.lastExecution = context.now
context._currentExecution = context._macros[i]
macro.callback()
context._currentExecution = nil
end end
end end
end end
@@ -338,11 +95,8 @@ function executeBot(config, storage, panel, msgCallback)
local hotkey = context._hotkeys[keyDesc] local hotkey = context._hotkeys[keyDesc]
if hotkey then if hotkey then
if hotkey.single then if hotkey.single then
if not hotkey.delay or hotkey.delay < context.now then if hotkey.callback() then
hotkey.lastExecution = context.now hotkey.lastExecution = context.now
context._currentExecution = hotkey
hotkey.callback()
context._currentExecution = nil
end end
end end
if hotkey.switch then if hotkey.switch then
@@ -369,11 +123,8 @@ function executeBot(config, storage, panel, msgCallback)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers) local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc] local hotkey = context._hotkeys[keyDesc]
if hotkey and not hotkey.single then if hotkey and not hotkey.single then
if not hotkey.delay or hotkey.delay < context.now then if hotkey.callback() then
hotkey.lastExecution = context.now hotkey.lastExecution = context.now
context._currentExecution = hotkey
hotkey.callback()
context._currentExecution = nil
end end
end end
for i, callback in ipairs(context._callbacks.onKeyPress) do for i, callback in ipairs(context._callbacks.onKeyPress) do
@@ -394,6 +145,26 @@ function executeBot(config, storage, panel, msgCallback)
for i, callback in ipairs(context._callbacks.onRemoveThing) do for i, callback in ipairs(context._callbacks.onRemoveThing) do
callback(tile, thing) callback(tile, thing)
end end
end,
onCreatureAppear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureAppear) do
callback(creature)
end
end,
onCreatureDisappear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureDisappear) do
callback(creature)
end
end,
onCreaturePositionChange = function(creature, newPos, oldPos)
for i, callback in ipairs(context._callbacks.onCreaturePositionChange) do
callback(creature, newPos, oldPos)
end
end,
onCreatureHealthPercentChange = function(creature, healthPercent)
for i, callback in ipairs(context._callbacks.onCreatureHealthPercentChange) do
callback(creature, healthPercent)
end
end end
} }
} }

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,3 @@
local context = G.botContext
context.test = function() return context.info("test") end

View 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

View 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

View File

@@ -0,0 +1,326 @@
local context = G.botContext
local Panels = context.Panels
Panels.Haste = function(parent)
context.macro(500, "Auto Haste", nil, function()
if not context.hasHaste() and context.storage.autoHasteText:len() > 0 then
if context.saySpell(context.storage.autoHasteText, 2500) then
context.delay(5000)
end
end
end, parent)
context.addTextEdit("autoHasteText", context.storage.autoHasteText or "utani hur", function(widget, text)
context.storage.autoHasteText = text
end, parent)
end
Panels.ManaShield = function(parent)
context.macro(500, "Auto ManaShield", nil, function()
if not context.hasManaShield() then
if context.saySpell("utamo vita", 1000) then
context.delay(5000)
end
end
end, parent)
end
Panels.AntiParalyze = function(parent)
context.macro(500, "Anti Paralyze", nil, function()
if not context.isParalyzed() and context.storage.autoHasteText:len() > 0 then
if context.saySpell(context.storage.autoAntiParalyzeText, 2500) then
context.delay(5000)
end
end
end, parent)
context.addTextEdit("autoHasteText", context.storage.autoAntiParalyzeText or "utani hur", function(widget, text)
context.storage.autoAntiParalyzeText = text
end, parent)
end
Panels.Health = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = context.setupUI([[
Panel
height: 70
margin-top: 2
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: Auto Healing
text-align: center
Label
id: label
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin: 0 5 0 5
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: label.left
anchors.right: label.horizontalCenter
anchors.top: label.bottom
margin-top: 5
margin-right: 2
minimum: 0
maximum: 100
step: 1
HorizontalScrollBar
id: scroll2
anchors.left: label.horizontalCenter
anchors.right: label.right
anchors.top: label.bottom
margin-top: 5
margin-left: 2
minimum: 0
maximum: 100
step: 1
BotTextEdit
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: scroll1.bottom
]], parent)
ui:setId(panelName)
ui.text.onTextChange = function(widget, text)
context.storage["healthText" .. panelId] = text
end
ui.text:setText(context.storage["healthText" .. panelId] or "exura")
local updateText = function()
ui.label:setText("" .. (context.storage["healthPercentMin" .. panelId] or "") .. "% <= hp <= " .. (context.storage["healthPercentMax" .. panelId] or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage["healthPercentMin" .. panelId] = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage["healthPercentMax" .. panelId] = value
updateText()
end
ui.scroll1:setValue(context.storage["healthPercentMin" .. panelId] or 20)
ui.scroll2:setValue(context.storage["healthPercentMax" .. panelId] or 60)
context.macro(25, function()
if context.storage["healthText" .. panelId]:len() > 0 and context.storage["healthPercentMin" .. panelId] <= context.hppercent() and context.hppercent() <= context.storage["healthPercentMax" .. panelId] then
if context.saySpell(context.storage["healthText" .. panelId], 500) then
context.delay(200)
end
end
end)
end
Panels.HealthItem = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = context.setupUI([[
Panel
height: 55
margin-top: 2
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: Auto Healing
text-align: center
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
Label
id: label
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin: 0 5 0 5
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: label.left
anchors.right: label.horizontalCenter
anchors.top: label.bottom
margin-top: 5
margin-right: 2
minimum: 0
maximum: 100
step: 1
HorizontalScrollBar
id: scroll2
anchors.left: label.horizontalCenter
anchors.right: label.right
anchors.top: label.bottom
margin-top: 5
margin-left: 2
minimum: 0
maximum: 100
step: 1
]], parent)
ui:setId(panelName)
ui.item.onItemChange = function(widget)
cp("item change")
context.storage["healthItem" .. panelId] = widget:getItemId()
end
ui.item:setItemId(context.storage["healthItem" .. panelId] or 266)
local updateText = function()
ui.label:setText("" .. (context.storage["healthItemPercentMin" .. panelId] or "") .. "% <= hp <= " .. (context.storage["healthItemPercentMax" .. panelId] or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage["healthItemPercentMin" .. panelId] = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage["healthItemPercentMax" .. panelId] = value
updateText()
end
ui.scroll1:setValue(context.storage["healthItemPercentMin" .. panelId] or 20)
ui.scroll2:setValue(context.storage["healthItemPercentMax" .. panelId] or 60)
context.macro(25, function()
if context.storage["healthItem" .. panelId] > 0 and context.storage["healthItemPercentMin" .. panelId] <= context.hppercent() and context.hppercent() <= context.storage["healthItemPercentMax" .. panelId] then
context.useWith(context.storage["healthItem" .. panelId], context.player)
context.delay(300)
end
end)
end
Panels.Mana = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoManaItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = context.setupUI([[
Panel
height: 55
margin-top: 2
Label
id: info
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text: Auto Mana
text-align: center
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
Label
id: label
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin: 0 5 0 5
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: label.left
anchors.right: label.horizontalCenter
anchors.top: label.bottom
margin-top: 5
margin-right: 2
minimum: 0
maximum: 100
step: 1
HorizontalScrollBar
id: scroll2
anchors.left: label.horizontalCenter
anchors.right: label.right
anchors.top: label.bottom
margin-top: 5
margin-left: 2
minimum: 0
maximum: 100
step: 1
]], parent)
ui:setId(panelName)
ui.item.onItemChange = function(widget)
context.storage["manaItem" .. panelId] = widget:getItemId()
end
ui.item:setItemId(context.storage["manaItem" .. panelId] or 268)
local updateText = function()
ui.label:setText("" .. (context.storage["manaItemPercentMin" .. panelId] or "") .. "% <= mana <= " .. (context.storage["manaItemPercentMax" .. panelId] or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage["manaItemPercentMin" .. panelId] = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage["manaItemPercentMax" .. panelId] = value
updateText()
end
ui.scroll1:setValue(context.storage["manaItemPercentMin" .. panelId] or 20)
ui.scroll2:setValue(context.storage["manaItemPercentMax" .. panelId] or 60)
context.macro(25, function()
if context.storage["manaItem" .. panelId] > 0 and context.storage["manaItemPercentMin" .. panelId] <= context.manapercent() and context.manapercent() <= context.storage["manaItemPercentMax" .. panelId] then
context.useWith(context.storage["manaItem" .. panelId], context.player)
context.delay(300)
end
end)
end
Panels.ManaItem = Panels.Mana
Panels.Turning = function(parent)
context.macro(1000, "Turning / AntiIdle", nil, function()
context.turn(math.random(1, 4))
end, parent)
end
Panels.AntiIdle = Panels.Turning

View File

@@ -619,22 +619,36 @@ function addTabText(text, speaktype, tab, creatureName)
local panel = consoleTabBar:getTabPanel(tab) local panel = consoleTabBar:getTabPanel(tab)
local consoleBuffer = panel:getChildById('consoleBuffer') local consoleBuffer = panel:getChildById('consoleBuffer')
local label = g_ui.createWidget('ConsoleLabel', consoleBuffer)
local label = nil
if consoleBuffer:getChildCount() > MAX_LINES then
label = consoleBuffer:getFirstChild()
consoleBuffer:moveChildToIndex(label, consoleBuffer:getChildCount())
end
if not label then
label = g_ui.createWidget('ConsoleLabel', consoleBuffer)
end
label:setId('consoleLabel' .. consoleBuffer:getChildCount()) label:setId('consoleLabel' .. consoleBuffer:getChildCount())
label:setText(text) label:setText(text)
label:setColor(speaktype.color) label:setColor(speaktype.color)
consoleTabBar:blinkTab(tab) consoleTabBar:blinkTab(tab)
-- Overlay for consoleBuffer which shows highlighted words only -- Overlay for consoleBuffer which shows highlighted words only
local labelHighlight = label:getChildById("consoleLabelHighlight")
if labelHighlight then
labelHighlight:setText("")
end
if speaktype.npcChat and (g_game.getCharacterName() ~= creatureName or g_game.getCharacterName() == 'Account Manager') then if speaktype.npcChat and (g_game.getCharacterName() ~= creatureName or g_game.getCharacterName() == 'Account Manager') then
local highlightData = getHighlightedText(text) local highlightData = getHighlightedText(text)
if #highlightData > 0 then if #highlightData > 0 then
local labelHighlight = g_ui.createWidget('ConsolePhantomLabel', label) if not labelHighlight then
labelHighlight:fill('parent') labelHighlight = g_ui.createWidget('ConsolePhantomLabel', label)
labelHighlight:fill('parent')
labelHighlight:setId('consoleLabelHighlight' .. consoleBuffer:getChildCount()) labelHighlight:setId('consoleLabelHighlight')
labelHighlight:setColor("#1f9ffe") labelHighlight:setColor("#1f9ffe")
end
-- Remove the curly braces -- Remove the curly braces
for i = 1, #highlightData / 3 do for i = 1, #highlightData / 3 do
@@ -768,12 +782,6 @@ function addTabText(text, speaktype, tab, creatureName)
return true return true
end end
if consoleBuffer:getChildCount() > MAX_LINES then
local child = consoleBuffer:getFirstChild()
clearSelection(consoleBuffer)
child:destroy()
end
end end
function removeTabLabelByName(tab, name) function removeTabLabelByName(tab, name)
@@ -1499,6 +1507,12 @@ function online()
defaultTab = addTab(tr('Default'), true) defaultTab = addTab(tr('Default'), true)
serverTab = addTab(tr('Server Log'), false) serverTab = addTab(tr('Server Log'), false)
if g_game.getClientVersion() >= 820 then
local tab = addTab("NPCs", false)
tab.npcChat = true
end
if g_game.getClientVersion() < 862 then if g_game.getClientVersion() < 862 then
local gameRootPanel = modules.game_interface.getRootPanel() local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.bindKeyDown('Ctrl+R', openPlayerReportRuleViolationWindow, gameRootPanel) g_keyboard.bindKeyDown('Ctrl+R', openPlayerReportRuleViolationWindow, gameRootPanel)

View File

@@ -12,6 +12,7 @@ function updateFeatures(version)
-- you can add custom features here, list of them in modules\gamelib\const.lua -- you can add custom features here, list of them in modules\gamelib\const.lua
g_game.enableFeature(GameBot) g_game.enableFeature(GameBot)
--g_game.enableFeature(GameMinimapLimitedToSingleFloor) --g_game.enableFeature(GameMinimapLimitedToSingleFloor)
--g_game.enableFeature(GameSpritesAlphaChannel)
if(version >= 770) then if(version >= 770) then
g_game.enableFeature(GameLooktypeU16); g_game.enableFeature(GameLooktypeU16);

View File

@@ -402,7 +402,7 @@ end
function prepareKeyCombo(keyCombo, repeated) function prepareKeyCombo(keyCombo, repeated)
local hotKey = hotkeyList[keyCombo] local hotKey = hotkeyList[keyCombo]
if keyCombo:lower():find("ctrl") or not hotKey or (hotKey.itemId == nil and (not hotKey.value or #hotKey.value == 0)) then if (keyCombo:lower():find("ctrl") and not hotKey) or (hotKey and hotKey.itemId == nil and (not hotKey.value or #hotKey.value == 0)) then
keyCombo = keyCombo:gsub("Ctrl%+", "") keyCombo = keyCombo:gsub("Ctrl%+", "")
keyCombo = keyCombo:gsub("ctrl%+", "") keyCombo = keyCombo:gsub("ctrl%+", "")
hotKey = hotkeyList[keyCombo] hotKey = hotkeyList[keyCombo]

View File

@@ -531,9 +531,13 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
end end
end end
if g_game.getFeature(GameBot) and useThing then if g_game.getFeature(GameBot) and useThing and useThing:isItem() then
menu:addSeparator() menu:addSeparator()
menu:addOption("ID: " .. useThing:getId()) if useThing:getSubType() > 1 then
menu:addOption("ID: " .. useThing:getId() .. " SubType: " .. useThing:getSubType())
else
menu:addOption("ID: " .. useThing:getId())
end
end end
menu:display(menuPosition) menu:display(menuPosition)

View File

@@ -4,7 +4,7 @@ Module
author: OTClient team author: OTClient team
website: https://github.com/edubart/otclient website: https://github.com/edubart/otclient
sandboxed: true sandboxed: true
scripts: [ widgets/uigamemap, widgets/uiitem, gameinterface ] scripts: [ widgets/uigamemap, gameinterface ]
load-later: load-later:
- game_hotkeys - game_hotkeys
- game_questlog - game_questlog
@@ -32,6 +32,8 @@ Module
- game_unjustifiedpoints - game_unjustifiedpoints
- game_walking - game_walking
- game_shop - game_shop
- game_itemselector
- game_textedit
- game_bot - game_bot
@onLoad: init() @onLoad: init()
@onUnload: terminate() @onUnload: terminate()

View File

@@ -200,6 +200,7 @@ function refresh()
if player then if player then
onSoulChange(player, player:getSoul()) onSoulChange(player, player:getSoul())
onFreeCapacityChange(player, player:getFreeCapacity()) onFreeCapacityChange(player, player:getFreeCapacity())
onStatesChange(player, player:getStates(), 0)
end end
--purseButton:setVisible(g_game.getFeature(GamePurseSlot)) --purseButton:setVisible(g_game.getFeature(GamePurseSlot))

View File

@@ -0,0 +1,73 @@
local activeWindow
function init()
g_ui.importStyle('itemselector')
connect(g_game, { onGameEnd = destroyWindow })
end
function terminate()
disconnect(g_game, { onGameEnd = destroyWindow })
destroyWindow()
end
function destroyWindow()
if activeWindow then
activeWindow:destroy()
activeWindow = nil
end
end
function show(itemWidget)
if not itemWidget then
return
end
if activeWindow then
destroyWindow()
end
local window = g_ui.createWidget('ItemSelectorWindow', rootWidget)
local destroy = function()
window:destroy()
if window == activeWindow then
activeWindow = nil
end
end
local doneFunc = function()
itemWidget:setItemId(window.item:getItemId())
itemWidget:setItemCount(window.item:getItemCount())
if itemWidget.onItemChange then
itemWidget:onItemChange()
end
destroy()
end
window.okButton.onClick = doneFunc
window.cancelButton.onClick = destroy
window.onEnter = doneFunc
window.onEscape = destroy
window.item:setItemId(itemWidget:getItemId())
window.item:setItemCount(itemWidget:getItemCount())
window.itemId:setValue(itemWidget:getItemId())
if itemWidget:getItemCount() > 1 then
window.itemCount:setValue(itemWidget:getItemCount())
end
window.itemId.onValueChange = function(widget, value)
window.item:setItemId(value)
end
window.itemCount.onValueChange = function(widget, value)
window.item:setItemCount(value)
end
activeWindow = window
activeWindow:raise()
activeWindow:focus()
end
function hide()
destroyWindow()
end

View File

@@ -0,0 +1,10 @@
Module
name: game_itemselector
description: Allow to select item
author: OTClientV8
website: https://github.com/OTCv8/otclientv8
sandboxed: true
dependencies: [ game_interface ]
scripts: [ itemselector ]
@onLoad: init()
@onUnload: terminate()

View File

@@ -0,0 +1,65 @@
ItemSelectorWindow < MainWindow
id: itemSelector
size: 260 120
!text: tr("Select item")
Item
id: item
virtual: true
size: 32 32
margin-top: 10
anchors.top: parent.top
anchors.left: parent.left
SpinBox
id: itemId
anchors.top: parent.top
anchors.left: prev.right
margin-top: 15
margin-left: 5
padding-left: 5
width: 70
minimum: 1
maximum: 999999999
focusable: true
Label
anchors.top: parent.top
anchors.left: prev.left
anchors.right: prev.right
text-align: center
!text: tr("Item ID")
SpinBox
id: itemCount
anchors.top: parent.top
anchors.left: prev.right
margin-top: 15
margin-left: 5
padding-left: 5
width: 120
minimum: 1
maximum: 100
focusable: true
Label
anchors.top: parent.top
anchors.left: prev.left
anchors.right: prev.right
text-align: center
!text: tr("Count / SubType")
Button
id: okButton
!text: tr('Ok')
anchors.bottom: parent.bottom
anchors.right: next.left
margin-right: 10
width: 60
Button
id: cancelButton
!text: tr('Cancel')
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 60

View File

@@ -0,0 +1,56 @@
local activeWindow
function init()
g_ui.importStyle('textedit')
connect(g_game, { onGameEnd = destroyWindow })
end
function terminate()
disconnect(g_game, { onGameEnd = destroyWindow })
destroyWindow()
end
function destroyWindow()
if activeWindow then
activeWindow:destroy()
activeWindow = nil
end
end
function show(widget)
if not widget then
return
end
if activeWindow then
destroyWindow()
end
local window = g_ui.createWidget('TextEditWindow', rootWidget)
local destroy = function()
window:destroy()
if window == activeWindow then
activeWindow = nil
end
end
local doneFunc = function()
widget:setText(window.text:getText())
destroy()
end
window.okButton.onClick = doneFunc
window.cancelButton.onClick = destroy
window.onEnter = doneFunc
window.onEscape = destroy
window.text:setText(widget:getText())
activeWindow = window
activeWindow:raise()
activeWindow:focus()
end
function hide()
destroyWindow()
end

View File

@@ -0,0 +1,10 @@
Module
name: game_textedit
description: Allow to edit text
author: OTClientV8
website: https://github.com/OTCv8/otclientv8
sandboxed: true
dependencies: [ game_interface ]
scripts: [ textedit ]
@onLoad: init()
@onUnload: terminate()

View File

@@ -0,0 +1,25 @@
TextEditWindow < MainWindow
id: textedit
size: 260 105
!text: tr("Edit text")
TextEdit
id: text
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Button
id: okButton
!text: tr('Ok')
anchors.bottom: parent.bottom
anchors.right: next.left
margin-right: 10
width: 60
Button
id: cancelButton
!text: tr('Cancel')
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 60

View File

@@ -0,0 +1,133 @@
function UIItem:onDragEnter(mousePos)
if self:isVirtual() then return false end
local item = self:getItem()
if not item then return false end
self:setBorderWidth(1)
self.currentDragThing = item
g_mouse.pushCursor('target')
return true
end
function UIItem:onDragLeave(droppedWidget, mousePos)
if self:isVirtual() then return false end
self.currentDragThing = nil
g_mouse.popCursor('target')
self:setBorderWidth(0)
self.hoveredWho = nil
return true
end
function UIItem:onDrop(widget, mousePos, forced)
if not self:canAcceptDrop(widget, mousePos) and not forced then return false end
local item = widget.currentDragThing
if not item or not item:isItem() then return false end
if self.selectable then
self:setItemId(item:getId())
self:setItemCount(item:getCount())
if item:getSubType() > 1 then
self:setItemSubType(item:getSubType())
end
if self.onItemChange then
self:onItemChange()
end
return
end
local toPos = self.position
local itemPos = item:getPosition()
if itemPos.x == toPos.x and itemPos.y == toPos.y and itemPos.z == toPos.z then return false end
if item:getCount() > 1 then
modules.game_interface.moveStackableItem(item, toPos)
else
g_game.move(item, toPos, 1)
end
self:setBorderWidth(0)
return true
end
function UIItem:onDestroy()
if self == g_ui.getDraggingWidget() and self.hoveredWho then
self.hoveredWho:setBorderWidth(0)
end
if self.hoveredWho then
self.hoveredWho = nil
end
end
function UIItem:onHoverChange(hovered)
UIWidget.onHoverChange(self, hovered)
if self:isVirtual() or not self:isDraggable() then return end
local draggingWidget = g_ui.getDraggingWidget()
if draggingWidget and self ~= draggingWidget then
local gotMap = draggingWidget:getClassName() == 'UIGameMap'
local gotItem = draggingWidget:getClassName() == 'UIItem' and not draggingWidget:isVirtual()
if hovered and (gotItem or gotMap) then
self:setBorderWidth(1)
draggingWidget.hoveredWho = self
else
self:setBorderWidth(0)
draggingWidget.hoveredWho = nil
end
end
end
function UIItem:onMouseRelease(mousePosition, mouseButton)
if self.cancelNextRelease then
self.cancelNextRelease = false
return true
end
if self:isVirtual() then return false end
local item = self:getItem()
if not item or not self:containsPoint(mousePosition) then return false end
if modules.client_options.getOption('classicControl') and
((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton) or
(g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then
g_game.look(item)
self.cancelNextRelease = true
return true
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, nil) then
return true
end
return false
end
function UIItem:canAcceptDrop(widget, mousePos)
if not self.selectable and (self:isVirtual() or not self:isDraggable()) then return false end
if not widget or not widget.currentDragThing then return false end
local children = rootWidget:recursiveGetChildrenByPos(mousePos)
for i=1,#children do
local child = children[i]
if child == self then
return true
elseif not child:isPhantom() then
return false
end
end
error('Widget ' .. self:getId() .. ' not in drop list.')
return false
end
function UIItem:onClick(mousePos)
if not self.selectable then
return
end
if modules.game_itemselector then
modules.game_itemselector.show(self)
end
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.