mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-04-29 18:59:20 +02:00
Version 2.5.3 - http://otclient.net/showthread.php?tid=275
This commit is contained in:
parent
a65844f182
commit
929ab400ed
2
init.lua
2
init.lua
@ -1,6 +1,6 @@
|
||||
-- CONFIG
|
||||
APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata
|
||||
APP_VERSION = 1340 -- client version for updater and login to identify outdated client
|
||||
APP_VERSION = 1342 -- client version for updater and login to identify outdated client
|
||||
DEFAULT_LAYOUT = "retro" -- on android it's forced to "mobile", check code bellow
|
||||
|
||||
-- If you don't use updater or other service, set it to updater = ""
|
||||
|
@ -125,11 +125,13 @@ end
|
||||
|
||||
function onGameConnectionError(message, code)
|
||||
CharacterList.destroyLoadBox()
|
||||
local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
|
||||
errorBox = displayErrorBox(tr("Connection Error"), text)
|
||||
errorBox.onOk = function()
|
||||
errorBox = nil
|
||||
CharacterList.showAgain()
|
||||
if (not g_game.isOnline() or code ~= 2) and not errorBox then -- code 2 is normal disconnect, end of file
|
||||
local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
|
||||
errorBox = displayErrorBox(tr("Connection Error"), text)
|
||||
errorBox.onOk = function()
|
||||
errorBox = nil
|
||||
CharacterList.showAgain()
|
||||
end
|
||||
end
|
||||
scheduleAutoReconnect()
|
||||
end
|
||||
@ -144,8 +146,8 @@ function onGameUpdateNeeded(signature)
|
||||
end
|
||||
|
||||
function onGameEnd()
|
||||
CharacterList.showAgain()
|
||||
scheduleAutoReconnect()
|
||||
CharacterList.showAgain()
|
||||
end
|
||||
|
||||
function onLogout()
|
||||
@ -163,7 +165,7 @@ function scheduleAutoReconnect()
|
||||
end
|
||||
|
||||
function executeAutoReconnect()
|
||||
if not autoReconnectButton or not autoReconnectButton:isOn() then
|
||||
if not autoReconnectButton or not autoReconnectButton:isOn() or g_game.isOnline() then
|
||||
return
|
||||
end
|
||||
if errorBox then
|
||||
|
@ -63,6 +63,12 @@ local function onUpdateNeeded(protocol, signature)
|
||||
return EnterGame.onError(tr('Your client needs updating, try redownloading it.'))
|
||||
end
|
||||
|
||||
local function onProxyList(protocol, proxies)
|
||||
for _, proxy in ipairs(proxies) do
|
||||
g_proxy.addProxy(proxy["host"], proxy["port"], proxy["priority"])
|
||||
end
|
||||
end
|
||||
|
||||
local function parseFeatures(features)
|
||||
for feature_id, value in pairs(features) do
|
||||
if value == "1" or value == "true" or value == true then
|
||||
@ -468,6 +474,7 @@ function EnterGame.doLogin()
|
||||
protocolLogin.onSessionKey = onSessionKey
|
||||
protocolLogin.onCharacterList = onCharacterList
|
||||
protocolLogin.onUpdateNeeded = onUpdateNeeded
|
||||
protocolLogin.onProxyList = onProxyList
|
||||
|
||||
EnterGame.hide()
|
||||
loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to login server...'))
|
||||
|
@ -13,6 +13,7 @@ prevCreature = nil
|
||||
|
||||
battleButtons = {}
|
||||
local ageNumber = 1
|
||||
local ages = {}
|
||||
|
||||
function init()
|
||||
g_ui.importStyle('battlebutton')
|
||||
@ -239,8 +240,12 @@ function checkCreatures()
|
||||
local creatures = {}
|
||||
for _, creature in ipairs(spectators) do
|
||||
if doCreatureFitFilters(creature) and #creatures < maxCreatures then
|
||||
if not creature.age then
|
||||
creature.age = ageNumber
|
||||
if not ages[creature:getId()] then
|
||||
if ageNumber > 10000 then
|
||||
ageNumber = 1
|
||||
ages = {}
|
||||
end
|
||||
ages[creature:getId()] = ageNumber
|
||||
ageNumber = ageNumber + 1
|
||||
end
|
||||
table.insert(creatures, creature)
|
||||
@ -324,23 +329,23 @@ function sortCreatures(creatures)
|
||||
local playerPos = player:getPosition()
|
||||
table.sort(creatures, function(a, b)
|
||||
if getDistanceBetween(playerPos, a:getPosition()) == getDistanceBetween(playerPos, b:getPosition()) then
|
||||
return a.age > b.age
|
||||
return ages[a:getId()] > ages[b:getId()]
|
||||
end
|
||||
return getDistanceBetween(playerPos, a:getPosition()) > getDistanceBetween(playerPos, b:getPosition())
|
||||
end)
|
||||
elseif getSortType() == 'health' then
|
||||
table.sort(creatures, function(a, b)
|
||||
if a:getHealthPercent() == b:getHealthPercent() then
|
||||
return a.age > b.age
|
||||
return ages[a:getId()] > ages[b:getId()]
|
||||
end
|
||||
return a:getHealthPercent() > b:getHealthPercent()
|
||||
end)
|
||||
elseif getSortType() == 'age' then
|
||||
table.sort(creatures, function(a, b) return a.age > b.age end)
|
||||
table.sort(creatures, function(a, b) return ages[a:getId()] > ages[b:getId()] end)
|
||||
else -- name
|
||||
table.sort(creatures, function(a, b)
|
||||
if a:getName():lower() == b:getName():lower() then
|
||||
return a.age > b.age
|
||||
return ages[a:getId()] > ages[b:getId()]
|
||||
end
|
||||
return a:getName():lower() > b:getName():lower()
|
||||
end)
|
||||
|
@ -17,6 +17,8 @@ local enableButton = nil
|
||||
local executeEvent = nil
|
||||
local statusLabel = nil
|
||||
|
||||
local configManagerUrl = "http://otclient.ovh/configs.php"
|
||||
|
||||
function init()
|
||||
dofile("executor")
|
||||
|
||||
@ -263,6 +265,13 @@ function onError(message)
|
||||
end
|
||||
|
||||
function edit()
|
||||
local configs = g_resources.listDirectoryFiles("/bot", false, false)
|
||||
editWindow.manager.upload.config:clearOptions()
|
||||
for i=1,#configs do
|
||||
editWindow.manager.upload.config:addOption(configs[i])
|
||||
end
|
||||
editWindow.manager.download.config:setText("")
|
||||
|
||||
editWindow:show()
|
||||
editWindow:focus()
|
||||
editWindow:raise()
|
||||
@ -306,6 +315,102 @@ function createDefaultConfigs()
|
||||
end
|
||||
end
|
||||
|
||||
function uploadConfig()
|
||||
local config = editWindow.manager.upload.config:getCurrentOption().text
|
||||
local archive = compressConfig(config)
|
||||
if not archive then
|
||||
return displayErrorBox(tr("Config upload failed"), tr("Config %s is invalid (can't be compressed)", config))
|
||||
end
|
||||
if archive:len() > 64 * 1024 then
|
||||
return displayErrorBox(tr("Config upload failed"), tr("Config %s is too big, maximum size is 64KB. Now it has %s KB.", config, math.floor(archive / 1024)))
|
||||
end
|
||||
|
||||
local infoBox = displayInfoBox(tr("Uploading config"), tr("Uploading config %s. Please wait.", config))
|
||||
|
||||
HTTP.postJSON(configManagerUrl .. "?config=" .. config:gsub("%s+", "_"), archive, function(data, err)
|
||||
if infoBox then
|
||||
infoBox:destroy()
|
||||
end
|
||||
if err or data["error"] then
|
||||
return displayErrorBox(tr("Config upload failed"), tr("Error while upload config %s:\n%s", config, err or data["error"]))
|
||||
end
|
||||
displayInfoBox(tr("Succesful config upload"), tr("Config %s has been uploaded.\n%s", config, data["message"]))
|
||||
end)
|
||||
end
|
||||
|
||||
function downloadConfig()
|
||||
local hash = editWindow.manager.download.config:getText()
|
||||
if hash:len() == 0 then
|
||||
return displayErrorBox(tr("Config download error"), tr("Enter correct config hash"))
|
||||
end
|
||||
local infoBox = displayInfoBox(tr("Downloading config"), tr("Downloading config with hash %s. Please wait.", hash))
|
||||
HTTP.download(configManagerUrl .. "?hash=" .. hash, hash .. ".zip", function(path, checksum, err)
|
||||
if infoBox then
|
||||
infoBox:destroy()
|
||||
end
|
||||
if err then
|
||||
return displayErrorBox(tr("Config download error"), tr("Config with hash %s cannot be downloaded", hash))
|
||||
end
|
||||
modules.client_textedit.show("", {
|
||||
title="Enter name for downloaded config",
|
||||
description="Config with hash " .. hash .. " has been downloaded. Enter name for new config.\nWarning: if config with same name already exist, it will be overwritten!",
|
||||
width=500
|
||||
}, function(configName)
|
||||
decompressConfig(configName, "/downloads/" .. path)
|
||||
refresh()
|
||||
edit()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function compressConfig(configName)
|
||||
if not g_resources.directoryExists("/bot/" .. configName) then
|
||||
return onError("Config " .. configName .. " doesn't exist")
|
||||
end
|
||||
local forArchive = {}
|
||||
for _, file in ipairs(g_resources.listDirectoryFiles("/bot/" .. configName)) do
|
||||
local fullPath = "/bot/" .. configName .. "/" .. file
|
||||
if g_resources.fileExists(fullPath) then -- regular file
|
||||
forArchive[file] = g_resources.readFileContents(fullPath)
|
||||
else -- dir
|
||||
for __, file2 in ipairs(g_resources.listDirectoryFiles(fullPath)) do
|
||||
local fullPath2 = fullPath .. "/" .. file2
|
||||
if g_resources.fileExists(fullPath2) then -- regular file
|
||||
forArchive[file .. "/" .. file2] = g_resources.readFileContents(fullPath2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return g_resources.createArchive(forArchive)
|
||||
end
|
||||
|
||||
function decompressConfig(configName, archive)
|
||||
if g_resources.directoryExists("/bot/" .. configName) then
|
||||
g_resources.deleteFile("/bot/" .. configName) -- also delete dirs
|
||||
end
|
||||
local files = g_resources.decompressArchive(archive)
|
||||
g_resources.makeDir("/bot/" .. configName)
|
||||
if not g_resources.directoryExists("/bot/" .. configName) then
|
||||
return onError("Can't create /bot/" .. configName .. " directory in " .. g_resources.getWriteDir())
|
||||
end
|
||||
|
||||
for file, contents in pairs(files) do
|
||||
local split = file:split("/")
|
||||
split[#split] = nil -- remove file name
|
||||
local dirPath = "/bot/" .. configName
|
||||
for _, s in ipairs(split) do
|
||||
dirPath = dirPath .. "/" .. s
|
||||
if not g_resources.directoryExists(dirPath) then
|
||||
g_resources.makeDir(dirPath)
|
||||
if not g_resources.directoryExists(dirPath) then
|
||||
return onError("Can't create " .. dirPath .. " directory in " .. g_resources.getWriteDir())
|
||||
end
|
||||
end
|
||||
end
|
||||
g_resources.writeFileContents("/bot/" .. configName .. file, contents)
|
||||
end
|
||||
end
|
||||
|
||||
-- Executor
|
||||
function message(category, msg)
|
||||
local widget = g_ui.createWidget('BotLabel', botMessages)
|
||||
|
@ -1,85 +1,210 @@
|
||||
MainWindow
|
||||
id: editWindow
|
||||
size: 550 580
|
||||
!text: tr("Config editor")
|
||||
!text: tr("Config editor & manager")
|
||||
@onEscape: self:hide()
|
||||
@onEnter: self:hide()
|
||||
size: 550 570
|
||||
$mobile:
|
||||
size: 550 240
|
||||
|
||||
Label
|
||||
Panel
|
||||
id: manager
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Bot configs are stored in:")
|
||||
height: 152
|
||||
|
||||
Label
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Config Manager\nYou can use config manager to share configs between different machines, especially smartphones. After you configure your config, you can upload it, then you'll get unique hash code which you can use on diffent machinge (for eg. mobile phone) to download it.")
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 3
|
||||
height: 2
|
||||
|
||||
Panel
|
||||
id: upload
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
margin-top: 3
|
||||
|
||||
TextEdit
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: prev.bottom
|
||||
height: 20
|
||||
width: 400
|
||||
margin-top: 5
|
||||
editable: false
|
||||
!text: g_resources.getWriteDir() .. "bot"
|
||||
text-align: center
|
||||
Label
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Upload config")
|
||||
|
||||
Button
|
||||
id: documentationButton
|
||||
!text: tr('Click here to open bot directory')
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 5
|
||||
width: 250
|
||||
@onClick: g_platform.openDir(g_resources.getWriteDir() .. "bot")
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 7
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Select config to upload")
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
ComboBox
|
||||
id: config
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 4
|
||||
margin-left: 20
|
||||
margin-right: 20
|
||||
text-offset: 3 0
|
||||
|
||||
Button
|
||||
id: submit
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
!text: tr('Upload config')
|
||||
margin-top: 4
|
||||
margin-left: 40
|
||||
margin-right: 40
|
||||
@onClick: modules.game_bot.uploadConfig()
|
||||
|
||||
Panel
|
||||
id: download
|
||||
anchors.top: prev.top
|
||||
anchors.left: parent.horizontalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Label
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Download config")
|
||||
|
||||
Label
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 7
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Enter config hash code")
|
||||
|
||||
TextEdit
|
||||
id: config
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 4
|
||||
margin-left: 20
|
||||
margin-right: 20
|
||||
|
||||
Button
|
||||
id: submit
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
!text: tr('Download config')
|
||||
margin-top: 4
|
||||
margin-left: 40
|
||||
margin-right: 40
|
||||
@onClick: modules.game_bot.downloadConfig()
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Every directory in bot directory is treated as different config.\nTo create new config just create new directory.")
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 175
|
||||
image-source: configs.png
|
||||
image-fixed-ratio: true
|
||||
image-size: 500 175
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
margin-top: 3
|
||||
height: 2
|
||||
|
||||
Panel
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Inside config directory put .lua and .otui files.\nEvery file will be loaded and executed in alphabetical order, .otui first and then .lua.")
|
||||
|
||||
Label
|
||||
anchors.right: parent.right
|
||||
margin-top: 5
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 150
|
||||
image-source: scripts.png
|
||||
image-fixed-ratio: true
|
||||
image-size: 500 150
|
||||
height: 330
|
||||
$mobile:
|
||||
visible: false
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("To reload configs just press On and Off in bot window.\nTo learn more about bot click Tutorials button.")
|
||||
Label
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Bot configs are stored in:")
|
||||
|
||||
TextEdit
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: prev.bottom
|
||||
height: 20
|
||||
width: 400
|
||||
margin-top: 5
|
||||
editable: false
|
||||
!text: g_resources.getWriteDir() .. "bot"
|
||||
text-align: center
|
||||
|
||||
Button
|
||||
id: documentationButton
|
||||
!text: tr('Click here to open bot directory')
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 5
|
||||
width: 250
|
||||
@onClick: g_platform.openDir(g_resources.getWriteDir() .. "bot")
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Every directory in bot directory is treated as different config.\nTo create new config just create new directory.")
|
||||
|
||||
Label
|
||||
margin-top: 5
|
||||
anchors.top: prev.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: 175
|
||||
image-source: configs.png
|
||||
image-fixed-ratio: true
|
||||
image-size: 500 175
|
||||
|
||||
Label
|
||||
margin-top: 3
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("Inside config directory put .lua and .otui files.\nEvery file will be loaded and executed in alphabetical order, .otui first and then .lua.")
|
||||
|
||||
Label
|
||||
margin-top: 3
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-align: center
|
||||
text-wrap: true
|
||||
!text: tr("To reload configs just press On and Off in bot window.\nTo learn more about bot click Tutorials button.")
|
||||
|
||||
Button
|
||||
!text: tr('Documentation')
|
||||
|
@ -86,17 +86,22 @@ function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, relo
|
||||
context.getDistanceBetween = function(p1, p2)
|
||||
return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
|
||||
end
|
||||
context.isMobile = g_app.isMobile
|
||||
context.getVersion = g_app.getVersion
|
||||
|
||||
-- classes
|
||||
context.g_resources = g_resources
|
||||
context.g_game = g_game
|
||||
context.g_map = g_map
|
||||
context.g_ui = g_ui
|
||||
context.g_platform = g_platform
|
||||
context.g_sounds = g_sounds
|
||||
context.g_window = g_window
|
||||
context.g_mouse = g_mouse
|
||||
context.g_things = g_things
|
||||
context.g_platform = {
|
||||
openUrl = g_platform.openUrl,
|
||||
openDir = g_platform.openDir,
|
||||
}
|
||||
|
||||
context.Item = Item
|
||||
context.Creature = Creature
|
||||
|
@ -8,17 +8,25 @@ context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end
|
||||
context.getSpectators = function(param1, param2)
|
||||
--[[
|
||||
if param1 is table (position) then it's used for central position, then param2 is used as param1
|
||||
if param1 is creature, then creature position and direction of creature is used, then param2 is used as param1
|
||||
if param1 is true/false then it's used for multifloor, example: getSpectators(true)
|
||||
if param1 is string then it's used for getSpectatorsByPattern
|
||||
]]--
|
||||
local pos = context.player:getPosition()
|
||||
local direction = context.player:getDirection()
|
||||
if type(param1) == 'table' then
|
||||
pos = param1
|
||||
direction = 8 -- invalid direction
|
||||
param1 = param2
|
||||
end
|
||||
if type(param1) == 'userdata' then
|
||||
pos = param1:getPosition()
|
||||
direction = param1:getDirection()
|
||||
param1 = param2
|
||||
end
|
||||
|
||||
if type(param1) == 'string' then
|
||||
return g_map.getSpectatorsByPattern(pos, param1)
|
||||
return g_map.getSpectatorsByPattern(pos, param1, direction)
|
||||
end
|
||||
|
||||
local multifloor = false
|
||||
@ -221,4 +229,13 @@ end
|
||||
context.getTileUnderCursor = function()
|
||||
if not modules.game_interface.gameMapPanel.mousePos then return end
|
||||
return modules.game_interface.gameMapPanel:getTile(modules.game_interface.gameMapPanel.mousePos)
|
||||
end
|
||||
|
||||
context.canShoot = function(pos, distance)
|
||||
if not distance then distance = 5 end
|
||||
local tile = g_map.getTile(pos, distance)
|
||||
if tile then
|
||||
return tile:canShoot(distance)
|
||||
end
|
||||
return false
|
||||
end
|
@ -227,6 +227,7 @@ function accept()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
g_game.changeOutfit(outfit)
|
||||
destroy()
|
||||
end
|
||||
|
@ -5,3 +5,5 @@ end
|
||||
|
||||
function terminate()
|
||||
end
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@ LoginServerUpdateNeeded = 30
|
||||
LoginServerSessionKey = 40
|
||||
LoginServerCharacterList = 100
|
||||
LoginServerExtendedCharacterList = 101
|
||||
LoginServerProxyList = 110
|
||||
|
||||
-- Since 10.76
|
||||
LoginServerRetry = 10
|
||||
@ -182,9 +183,19 @@ function ProtocolLogin:onRecv(msg)
|
||||
self:parseExtendedCharacterList(msg)
|
||||
elseif opcode == LoginServerUpdate then
|
||||
local signature = msg:getString()
|
||||
signalcall(self.onUpdateNeeded, self, signature)
|
||||
signalcall(self.onUpdateNeeded, self, signature)
|
||||
elseif opcode == LoginServerSessionKey then
|
||||
self:parseSessionKey(msg)
|
||||
elseif opcode == LoginServerProxyList then
|
||||
local proxies = {}
|
||||
local proxiesCount = msg:getU8()
|
||||
for i=1, proxiesCount do
|
||||
local host = msg:getString()
|
||||
local port = msg:getU16()
|
||||
local priority = msg:getU16()
|
||||
table.insert(proxies, {host=host, port=port, priority=priority})
|
||||
end
|
||||
signalcall(self.onProxyList, self, proxies)
|
||||
else
|
||||
self:parseOpcode(opcode, msg)
|
||||
end
|
||||
|
BIN
otclient_dx.exe
BIN
otclient_dx.exe
Binary file not shown.
BIN
otclient_gl.exe
BIN
otclient_gl.exe
Binary file not shown.
BIN
otclient_linux
BIN
otclient_linux
Binary file not shown.
BIN
otclientv8.apk
BIN
otclientv8.apk
Binary file not shown.
BIN
pdb/pdb.7z
BIN
pdb/pdb.7z
Binary file not shown.
@ -169,6 +169,8 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_map", "getMinimapColor", &Map::getMinimapColor, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "isPatchable", &Map::isPatchable, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "isWalkable", &Map::isWalkable, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "checkSightLine", &Map::checkSightLine, &g_map);
|
||||
g_lua.bindSingletonFunction("g_map", "isSightClear", &Map::isSightClear, &g_map);
|
||||
|
||||
g_lua.registerSingletonClass("g_minimap");
|
||||
g_lua.bindSingletonFunction("g_minimap", "clean", &Minimap::clean, &g_minimap);
|
||||
@ -523,6 +525,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<Creature>("isDead", &Creature::isDead);
|
||||
g_lua.bindClassMemberFunction<Creature>("isRemoved", &Creature::isRemoved);
|
||||
g_lua.bindClassMemberFunction<Creature>("canBeSeen", &Creature::canBeSeen);
|
||||
g_lua.bindClassMemberFunction<Creature>("canShoot", &Creature::canShoot);
|
||||
g_lua.bindClassMemberFunction<Creature>("jump", &Creature::jump);
|
||||
g_lua.bindClassMemberFunction<Creature>("getPrewalkingPosition", &Creature::getPrewalkingPosition);
|
||||
g_lua.bindClassMemberFunction<Creature>("setInformationColor", &Creature::setInformationColor);
|
||||
@ -772,6 +775,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<Tile>("isFullGround", &Tile::isFullGround);
|
||||
g_lua.bindClassMemberFunction<Tile>("isFullyOpaque", &Tile::isFullyOpaque);
|
||||
g_lua.bindClassMemberFunction<Tile>("isLookPossible", &Tile::isLookPossible);
|
||||
g_lua.bindClassMemberFunction<Tile>("isBlockingProjectile", &Tile::isBlockingProjectile);
|
||||
g_lua.bindClassMemberFunction<Tile>("hasCreature", &Tile::hasCreature);
|
||||
g_lua.bindClassMemberFunction<Tile>("hasBlockingCreature", &Tile::hasBlockingCreature);
|
||||
g_lua.bindClassMemberFunction<Tile>("isEmpty", &Tile::isEmpty);
|
||||
@ -789,6 +793,7 @@ void Client::registerLuaFunctions()
|
||||
g_lua.bindClassMemberFunction<Tile>("getElevation", &Tile::getElevation);
|
||||
g_lua.bindClassMemberFunction<Tile>("hasElevation", &Tile::hasElevation);
|
||||
g_lua.bindClassMemberFunction<Tile>("isBlocking", &Tile::isBlocking);
|
||||
g_lua.bindClassMemberFunction<Tile>("canShoot", &Tile::canShoot);
|
||||
// for bot
|
||||
g_lua.bindClassMemberFunction<Tile>("setText", &Tile::setText);
|
||||
g_lua.bindClassMemberFunction<Tile>("getText", &Tile::getText);
|
||||
|
@ -967,3 +967,118 @@ bool Map::isWalkable(const Position& pos, bool ignoreCreatures)
|
||||
const MinimapTile& mtile = g_minimap.getTile(pos);
|
||||
return !mtile.hasFlag(MinimapTileNotPathable);
|
||||
}
|
||||
|
||||
std::vector<CreaturePtr> Map::getSpectatorsByPattern(const Position& centerPos, const std::string& pattern, Otc::Direction direction)
|
||||
{
|
||||
std::vector<bool> finalPattern(pattern.size(), false);
|
||||
std::vector<CreaturePtr> creatures;
|
||||
int width = 0, height = 0, lineLength = 0, p = 0;
|
||||
for (auto& c : pattern) {
|
||||
lineLength += 1;
|
||||
if (c == '0' || c == '-') {
|
||||
p += 1;
|
||||
} else if (c == '1' || c == '+') {
|
||||
finalPattern[p++] = true;
|
||||
} else if (c == 'N' || c == 'n') {
|
||||
finalPattern[p++] = direction == Otc::North;
|
||||
} else if (c == 'E' || c == 'e') {
|
||||
finalPattern[p++] = direction == Otc::East;
|
||||
} else if (c == 'W' || c == 'w') {
|
||||
finalPattern[p++] = direction == Otc::West;
|
||||
} else if (c == 'S' || c == 's') {
|
||||
finalPattern[p++] = direction == Otc::South;
|
||||
} else {
|
||||
lineLength -= 1;
|
||||
if (lineLength > 1) {
|
||||
if (width == 0)
|
||||
width = lineLength;
|
||||
if (width != lineLength) {
|
||||
g_logger.error(stdext::format("Invalid pattern for getSpectatorsByPattern: %s", pattern));
|
||||
return creatures;
|
||||
}
|
||||
height += 1;
|
||||
lineLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lineLength > 0) {
|
||||
if (width == 0)
|
||||
width = lineLength;
|
||||
if (width != lineLength) {
|
||||
g_logger.error(stdext::format("Invalid pattern for getSpectatorsByPattern: %s", pattern));
|
||||
return creatures;
|
||||
}
|
||||
height += 1;
|
||||
}
|
||||
if (width % 2 != 1 || height % 2 != 1) {
|
||||
g_logger.error(stdext::format("Invalid pattern for getSpectatorsByPattern, width and height should be odd (height: %i width: %i)", height, width));
|
||||
return creatures;
|
||||
}
|
||||
|
||||
p = 0;
|
||||
for (int y = centerPos.y - height / 2, endy = centerPos.y + height / 2; y <= endy; ++y) {
|
||||
for (int x = centerPos.x - width / 2, endx = centerPos.x + width / 2; x <= endx; ++x) {
|
||||
if (!finalPattern[p++])
|
||||
continue;
|
||||
TilePtr tile = getTile(Position(x, y, centerPos.z));
|
||||
if (!tile)
|
||||
continue;
|
||||
auto tileCreatures = tile->getCreatures();
|
||||
creatures.insert(creatures.end(), tileCreatures.rbegin(), tileCreatures.rend());
|
||||
}
|
||||
}
|
||||
return creatures;
|
||||
}
|
||||
|
||||
bool Map::isSightClear(const Position& fromPos, const Position& toPos)
|
||||
{
|
||||
if (fromPos == toPos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Position start(fromPos.z > toPos.z ? toPos : fromPos);
|
||||
Position destination(fromPos.z > toPos.z ? fromPos : toPos);
|
||||
|
||||
const int8_t mx = start.x < destination.x ? 1 : start.x == destination.x ? 0 : -1;
|
||||
const int8_t my = start.y < destination.y ? 1 : start.y == destination.y ? 0 : -1;
|
||||
|
||||
int32_t A = destination.y - start.y;
|
||||
int32_t B = start.x - destination.x;
|
||||
int32_t C = -(A * destination.x + B * destination.y);
|
||||
|
||||
while (start.x != destination.x || start.y != destination.y) {
|
||||
int32_t move_hor = std::abs(A * (start.x + mx) + B * (start.y) + C);
|
||||
int32_t move_ver = std::abs(A * (start.x) + B * (start.y + my) + C);
|
||||
int32_t move_cross = std::abs(A * (start.x + mx) + B * (start.y + my) + C);
|
||||
|
||||
if (start.y != destination.y && (start.x == destination.x || move_hor > move_ver || move_hor > move_cross)) {
|
||||
start.y += my;
|
||||
}
|
||||
|
||||
if (start.x != destination.x && (start.y == destination.y || move_ver > move_hor || move_ver > move_cross)) {
|
||||
start.x += mx;
|
||||
}
|
||||
|
||||
auto tile = getTile(Position(start.x, start.y, start.z));
|
||||
if (tile && tile->isBlockingProjectile()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (start.z != destination.z) {
|
||||
auto tile = getTile(Position(start.x, start.y, start.z));
|
||||
if (tile && tile->getThingCount() > 0) {
|
||||
return false;
|
||||
}
|
||||
start.z++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::checkSightLine(const Position& fromPos, const Position& toPos)
|
||||
{
|
||||
if (fromPos.z != toPos.z)
|
||||
return false;
|
||||
return checkSightLine(fromPos, toPos) || checkSightLine(toPos, fromPos);
|
||||
}
|
@ -109,13 +109,15 @@ void Application::registerLuaFunctions()
|
||||
|
||||
// Platform
|
||||
g_lua.registerSingletonClass("g_platform");
|
||||
#ifdef UNSAFE_LUA_FUNCTIONS
|
||||
g_lua.bindSingletonFunction("g_platform", "spawnProcess", &Platform::spawnProcess, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "getProcessId", &Platform::getProcessId, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "isProcessRunning", &Platform::isProcessRunning, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "copyFile", &Platform::copyFile, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "fileExists", &Platform::fileExists, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "removeFile", &Platform::removeFile, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "killProcess", &Platform::killProcess, &g_platform);
|
||||
#endif
|
||||
g_lua.bindSingletonFunction("g_platform", "getProcessId", &Platform::getProcessId, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "isProcessRunning", &Platform::isProcessRunning, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "getTempPath", &Platform::getTempPath, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "openUrl", &Platform::openUrl, &g_platform);
|
||||
g_lua.bindSingletonFunction("g_platform", "openDir", &Platform::openDir, &g_platform);
|
||||
@ -226,6 +228,7 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_http", "wsSend", &Http::wsSend, &g_http);
|
||||
g_lua.bindSingletonFunction("g_http", "wsClose", &Http::wsClose, &g_http);
|
||||
g_lua.bindSingletonFunction("g_http", "cancel", &Http::cancel, &g_http);
|
||||
g_lua.bindSingletonFunction("g_http", "setUserAgent", &Http::setUserAgent, &g_http);
|
||||
|
||||
g_lua.registerSingletonClass("g_atlas");
|
||||
g_lua.bindSingletonFunction("g_atlas", "getStats", &Atlas::getStats, &g_atlas);
|
||||
@ -273,6 +276,8 @@ void Application::registerLuaFunctions()
|
||||
g_lua.bindSingletonFunction("g_resources", "selfChecksum", &ResourceManager::selfChecksum, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "updateData", &ResourceManager::updateData, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "updateExecutable", &ResourceManager::updateExecutable, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "createArchive", &ResourceManager::createArchive, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "decompressArchive", &ResourceManager::decompressArchive, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "setLayout", &ResourceManager::setLayout, &g_resources);
|
||||
g_lua.bindSingletonFunction("g_resources", "getLayout", &ResourceManager::getLayout, &g_resources);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user