Progress updating to cv981/pv973:

* Implemented the new client AND protocol version methods.
* Implemented the new speed laws added in cv980 (http://www.tibia.com/news/?subtopic=newsarchive&id=2251).
* Added more missing bytea to login packets (client version/type and some unknown bytes).
* Fixed the InputMessage::getDouble method.
* Cleaned up some of the const values.
* Started on the pending state features.

TODO:
* Pending game state feature.
* Ensure version compatibility hasn't been compromised.
This commit is contained in:
BeniS 2012-12-29 00:05:45 +13:00
parent 619285069c
commit 44e428bccb
29 changed files with 260 additions and 129 deletions

View File

@ -82,9 +82,9 @@ function Client.terminate()
g_settings.set('window-pos', g_window.getUnmaximizedPos()) g_settings.set('window-pos', g_window.getUnmaximizedPos())
g_settings.set('window-maximized', g_window.isMaximized()) g_settings.set('window-maximized', g_window.isMaximized())
local clientVersion = g_game.getClientVersion() local protocolVersion = g_game.getProtocolVersion()
if clientVersion ~= 0 then if protocolVersion ~= 0 then
g_settings.set('client-version', clientVersion) g_settings.set('protocol-version', protocolVersion)
end end
Client = nil Client = nil

View File

@ -52,6 +52,11 @@ local function onCharacterList(protocol, characters, account, otui)
end end
end end
local function onChangeProtocol(combobox, option)
local clients = g_game.getSupportedClients(option)
protocolBox:setTooltip("Supports Client" .. (#clients > 1 and "s" or "") .. ": " .. table.toString(clients))
end
-- public functions -- public functions
function EnterGame.init() function EnterGame.init()
enterGame = g_ui.displayUI('entergame.otui') enterGame = g_ui.displayUI('entergame.otui')
@ -69,7 +74,7 @@ function EnterGame.init()
local host = g_settings.get('host') local host = g_settings.get('host')
local port = g_settings.get('port') local port = g_settings.get('port')
local autologin = g_settings.getBoolean('autologin') local autologin = g_settings.getBoolean('autologin')
local clientVersion = g_settings.getInteger('client-version') local protocolVersion = g_settings.getInteger('protocol-version')
if port == nil or port == 0 then port = 7171 end if port == nil or port == 0 then port = 7171 end
@ -82,12 +87,13 @@ function EnterGame.init()
enterGame:getChildById('accountNameTextEdit'):focus() enterGame:getChildById('accountNameTextEdit'):focus()
protocolBox = enterGame:getChildById('protocolComboBox') protocolBox = enterGame:getChildById('protocolComboBox')
protocolBox.onOptionChange = onChangeProtocol
for _i, proto in pairs(g_game.getSupportedProtocols()) do for _i, proto in pairs(g_game.getSupportedProtocols()) do
protocolBox:addOption(proto) protocolBox:addOption(proto)
end end
if clientVersion then if protocolVersion then
protocolBox:setCurrentOption(clientVersion) protocolBox:setCurrentOption(protocolVersion)
end end
enterGame:hide() enterGame:hide()
@ -153,7 +159,8 @@ function EnterGame.doLogin()
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText() G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
G.host = enterGame:getChildById('serverHostTextEdit'):getText() G.host = enterGame:getChildById('serverHostTextEdit'):getText()
G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText()) G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
local clientVersion = tonumber(protocolBox:getText()) local protocolVersion = tonumber(protocolBox:getText())
local clientVersions = g_game.getSupportedClients(protocolVersion)
EnterGame.hide() EnterGame.hide()
if g_game.isOnline() then if g_game.isOnline() then
@ -178,7 +185,10 @@ function EnterGame.doLogin()
end }) end })
g_game.chooseRsa(G.host) g_game.chooseRsa(G.host)
g_game.setClientVersion(clientVersion) g_game.setProtocolVersion(protocolVersion)
if #clientVersions > 0 then
g_game.setClientVersion(clientVersions[#clientVersions])
end
if modules.game_tibiafiles.isLoaded() then if modules.game_tibiafiles.isLoaded() then
protocolLogin:login(G.host, G.port, G.account, G.password) protocolLogin:login(G.host, G.port, G.account, G.password)

View File

@ -64,7 +64,8 @@ function onConnect(protocol)
post = post .. '&world_name=' .. g_game.getWorldName() post = post .. '&world_name=' .. g_game.getWorldName()
post = post .. '&otserv_host=' .. G.host post = post .. '&otserv_host=' .. G.host
post = post .. '&otserv_port=' .. G.port post = post .. '&otserv_port=' .. G.port
post = post .. '&otserv_protocol=' .. g_game.getClientVersion() post = post .. '&otserv_protocol=' .. g_game.getProtocolVersion()
--post = post .. '&otserv_client=' .. g_game.getClientVersion()
post = post .. '&build_version=' .. g_app.getVersion() post = post .. '&build_version=' .. g_app.getVersion()
post = post .. '&build_revision=' .. g_app.getBuildRevision() post = post .. '&build_revision=' .. g_app.getBuildRevision()
post = post .. '&build_commit=' .. g_app.getBuildCommit() post = post .. '&build_commit=' .. g_app.getBuildCommit()

View File

@ -29,7 +29,7 @@ function string:trim()
end end
function string:explode(sep, limit) function string:explode(sep, limit)
if(type(sep) ~= 'string' or tostring(self):len() == 0 or sep:len() == 0) then if type(sep) ~= 'string' or tostring(self):len() == 0 or sep:len() == 0 then
return {} return {}
end end
@ -40,7 +40,7 @@ function string:explode(sep, limit)
pos = e + 1 pos = e + 1
i = i + 1 i = i + 1
if(limit ~= nil and i == limit) then if limit ~= nil and i == limit then
break break
end end
end end
@ -49,3 +49,4 @@ function string:explode(sep, limit)
table.insert(t, tmp) table.insert(t, tmp)
return t return t
end end

View File

@ -92,3 +92,18 @@ function table.empty(t)
end end
return true return true
end end
function table.toString(t)
local maxn = #t
local str = ""
for k,v in pairs(t) do
if k == maxn and k ~= 1 then
str = str .. " and " .. v
elseif maxn > 1 and k ~= 1 then
str = str .. ", " .. v
else
str = str .. " " .. v
end
end
return str
end

View File

@ -192,8 +192,8 @@ end
function addTab(name, focus) function addTab(name, focus)
local tab = getTab(name) local tab = getTab(name)
if(tab) then -- is channel already open if tab then -- is channel already open
if(not focus) then focus = true end if not focus then focus = true end
else else
tab = consoleTabBar:addTab(name) tab = consoleTabBar:addTab(name)
end end

View File

@ -39,14 +39,14 @@ end
-- parsing protocols -- parsing protocols
local function parseMarketEnter(msg) local function parseMarketEnter(msg)
local balance local balance
if(g_game.getClientVersion() >= 980) then if g_game.getProtocolVersion() >= 973 then
balance = msg:getU64() balance = msg:getU64()
else else
balance = msg:getU32() balance = msg:getU32()
end end
local vocation = -1 local vocation = -1
if g_game.getClientVersion() < 950 then if g_game.getProtocolVersion() < 950 then
vocation = msg:getU8() -- get vocation id vocation = msg:getU8() -- get vocation id
end end
local offers = msg:getU8() local offers = msg:getU8()

View File

@ -182,7 +182,7 @@ function addMapFlag(pos, icon, message, flagId, version)
return return
end end
version = version or g_game.getClientVersion() version = version or g_game.getProtocolVersion()
-- Check if flag is set for that position -- Check if flag is set for that position
for i = 1, flagsPanel:getChildCount() do for i = 1, flagsPanel:getChildCount() do
local flag = flagsPanel:getChildByIndex(i) local flag = flagsPanel:getChildByIndex(i)
@ -218,7 +218,7 @@ function getMapArea()
end end
function isFlagVisible(flag, firstPosition, lastPosition) function isFlagVisible(flag, firstPosition, lastPosition)
return flag.version == g_game.getClientVersion() and (minimapWidget:getZoom() >= 30 and minimapWidget:getZoom() <= 150) and flag.position.x >= firstPosition.x and flag.position.x <= lastPosition.x and flag.position.y >= firstPosition.y and flag.position.y <= lastPosition.y and flag.position.z == firstPosition.z return flag.version == g_game.getProtocolVersion() and (minimapWidget:getZoom() >= 30 and minimapWidget:getZoom() <= 150) and flag.position.x >= firstPosition.x and flag.position.x <= lastPosition.x and flag.position.y >= firstPosition.y and flag.position.y <= lastPosition.y and flag.position.z == firstPosition.z
end end
function updateMapFlag(id) function updateMapFlag(id)
@ -267,8 +267,8 @@ function offline()
end end
function loadMap() function loadMap()
local clientVersion = g_game.getClientVersion() local protocolVersion = g_game.getProtocolVersion()
local minimapFile = '/minimap_' .. clientVersion .. '.otcm' local minimapFile = '/minimap_' .. protocolVersion .. '.otcm'
if g_resources.fileExists(minimapFile) then if g_resources.fileExists(minimapFile) then
g_map.clean() g_map.clean()
g_map.loadOtcm(minimapFile) g_map.loadOtcm(minimapFile)
@ -276,8 +276,8 @@ function loadMap()
end end
function saveMap() function saveMap()
local clientVersion = g_game.getClientVersion() local protocolVersion = g_game.getProtocolVersion()
local minimapFile = '/minimap_' .. clientVersion .. '.otcm' local minimapFile = '/minimap_' .. protocolVersion .. '.otcm'
g_map.saveOtcm(minimapFile) g_map.saveOtcm(minimapFile)
end end

View File

@ -80,7 +80,7 @@ function getIconImageClip(id)
end end
function setOptions() function setOptions()
if g_game.getClientVersion() >= 950 then -- Vocation is only send in newer clients if g_game.getProtocolVersion() >= 950 then -- Vocation is only send in newer clients
spelllistWindow:getChildById('buttonFilterVocation'):setVisible(true) spelllistWindow:getChildById('buttonFilterVocation'):setVisible(true)
else else
spelllistWindow:getChildById('buttonFilterVocation'):setVisible(false) spelllistWindow:getChildById('buttonFilterVocation'):setVisible(false)

View File

@ -118,14 +118,14 @@ function onGameEditText(id, itemId, maxLength, text, writter, time)
end end
local newLineCount = string.count(textEdit:getText(), '\n') local newLineCount = string.count(textEdit:getText(), '\n')
if(newLineCount >= 9) then if newLineCount >= 9 then
textScroll:setMaximum(newLineCount-9) textScroll:setMaximum(newLineCount-9)
end end
local _prev, _next = 0, 11 local _prev, _next = 0, 11
local scrollOnValueChange = function(widget, value, delta) local scrollOnValueChange = function(widget, value, delta)
local line = getLineByCursorPos(textEdit:getText(), textEdit:getCursorPos(), newLineCount) local line = getLineByCursorPos(textEdit:getText(), textEdit:getCursorPos(), newLineCount)
if(delta > 0) then if delta > 0 then
textEdit:setCursorPos(getCursorPosByNewLine(textEdit:getText(), _next + delta - 1)) textEdit:setCursorPos(getCursorPosByNewLine(textEdit:getText(), _next + delta - 1))
if writeable then textEdit:setCursorPos(getCursorPosByNewLine(textEdit:getText(), line + delta)) end if writeable then textEdit:setCursorPos(getCursorPosByNewLine(textEdit:getText(), line + delta)) end
else else
@ -180,7 +180,7 @@ function onGameEditText(id, itemId, maxLength, text, writter, time)
return false return false
end end
if(not writeable) then if not writeable then
textEdit:setCursorPos(0) textEdit:setCursorPos(0)
textWindow.onKeyPress = onKeyPress -- textEdit won't receive focus textWindow.onKeyPress = onKeyPress -- textEdit won't receive focus
else else
@ -244,4 +244,4 @@ function onGameEditList(id, doorId, text)
okButton.onClick = doneFunc okButton.onClick = doneFunc
textWindow.onEnter = doneFunc textWindow.onEnter = doneFunc
textWindow.onEscape = destroy textWindow.onEscape = destroy
end end

View File

@ -2,11 +2,11 @@ filename = 'Tibia'
loaded = false loaded = false
function init() function init()
connect(g_game, { onClientVersionChange = load }) connect(g_game, { onProtocolVersionChange = load })
end end
function terminate() function terminate()
disconnect(g_game, { onClientVersionChange = load }) disconnect(g_game, { onProtocolVersionChange = load })
end end
function setFileName(name) function setFileName(name)
@ -18,7 +18,7 @@ function isLoaded()
end end
function load() function load()
local version = g_game.getClientVersion() local version = g_game.getProtocolVersion()
local datPath = resolvepath(version .. '/' .. filename .. '.dat') local datPath = resolvepath(version .. '/' .. filename .. '.dat')
local sprPath = resolvepath(version .. '/' .. filename .. '.spr') local sprPath = resolvepath(version .. '/' .. filename .. '.spr')
@ -36,8 +36,8 @@ function load()
local messageBox = displayErrorBox(tr('Error'), errorMessage) local messageBox = displayErrorBox(tr('Error'), errorMessage)
addEvent(function() messageBox:raise() messageBox:focus() end) addEvent(function() messageBox:raise() messageBox:focus() end)
disconnect(g_game, { onClientVersionChange = load }) disconnect(g_game, { onProtocolVersionChange = load })
g_game.setClientVersion(0) g_game.setprotocolVersion(0)
connect(g_game, { onClientVersionChange = load }) connect(g_game, { onProtocolVersionChange = load })
end end
end end

View File

@ -50,8 +50,16 @@ function g_game.getSupportedProtocols()
return { return {
810, 853, 854, 860, 861, 862, 870, 810, 853, 854, 860, 861, 862, 870,
910, 940, 944, 953, 954, 960, 961, 910, 940, 944, 953, 954, 960, 961,
963, 970, 971, 980, 981 963, 970, 971, 973
} }
end end
function g_game.getSupportedClients(protocol)
clients = {
[971] = {980},
[973] = {981}
}
return clients[protocol] or {protocol}
end
g_game.setRsa(OTSERV_RSA) g_game.setRsa(OTSERV_RSA)

View File

@ -28,7 +28,12 @@ function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create() local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount) msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOsType()) msg:addU16(g_game.getOsType())
msg:addU16(g_game.getClientVersion()) msg:addU16(g_game.getProtocolVersion())
if g_game.getProtocolVersion() >= 971 then
msg:addU32(g_game.getClientVersion())
msg:addU8(182) -- clientType
end
msg:addU32(g_things.getDatSignature()) msg:addU32(g_things.getDatSignature())
msg:addU32(g_sprites.getSprSignature()) msg:addU32(g_sprites.getSprSignature())
@ -113,6 +118,11 @@ function ProtocolLogin:parseCharacterList(msg)
character.worldIp = iptostring(msg:getU32()) character.worldIp = iptostring(msg:getU32())
character.worldPort = msg:getU16() character.worldPort = msg:getU16()
characters[i] = character characters[i] = character
-- ??
if g_game.getProtocolVersion() >= 971 then
msg:getU8()
end
end end
local account = {} local account = {}

View File

@ -89,7 +89,7 @@ std::string InputMessage::getString()
double InputMessage::getDouble() double InputMessage::getDouble()
{ {
uint8 precision = getU8(); uint8 precision = getU8();
uint32 v = getU32(); int32 v = getU32() - INT_MAX;
return (v / std::pow((float)10, precision)); return (v / std::pow((float)10, precision));
} }

View File

@ -348,49 +348,57 @@ namespace Otc
}; };
enum PathFindResult { enum PathFindResult {
PATHFIND_RESULT_OK = 0, PathFineResultOk = 0,
PATHFIND_RESULT_SAME_POSITION, PathFindResultSamePosition,
PATHFIND_RESULT_IMPOSSIBLE, PathFindResultImpossible,
PATHFIND_RESULT_TOO_FAR, PathFindResultTooFar,
PATHFIND_RESULT_NO_WAY PathFindResultNoWay
}; };
enum PathFindFlag { enum PathFindFlags {
PATHFIND_ALLOW_NULLTILES = 1, PathFindAllowNullTiles = 1,
PATHFIND_ALLOW_CREATURES = 2, PathFindAllowCreatures = 2,
PATHFIND_ALLOW_NONPATHABLE = 4, PathFindAllowNonPathable = 4,
PATHFIND_ALLOW_NONWALKABLE = 8 PathFindAllowNonWalkable = 8
}; };
enum AutomapFlags enum AutomapFlags
{ {
MAPMARK_TICK = 0, MapMarkTick = 0,
MAPMARK_QUESTION, MapMarkQuestion,
MAPMARK_EXCLAMATION, MapMarkExclamation,
MAPMARK_STAR, MapMarkStar,
MAPMARK_CROSS, MapMarkCross,
MAPMARK_TEMPLE, MapMarkTemple,
MAPMARK_KISS, MapMarkKiss,
MAPMARK_SHOVEL, MapMarkShovel,
MAPMARK_SWORD, MapMarkSword,
MAPMARK_FLAG, MapMarkFlag,
MAPMARK_LOCK, MapMarkLock,
MAPMARK_BAG, MapMarkBag,
MAPMARK_SKULL, MapMarkSkull,
MAPMARK_DOLLAR, MapMarkDollar,
MAPMARK_REDNORTH, MapMarkRedNorth,
MAPMARK_REDSOUTH, MapMarkRedSouth,
MAPMARK_REDEAST, MapMarkRedEast,
MAPMARK_REDWEST, MapMarkRedWest,
MAPMARK_GREENNORTH, MapMarkGreenNorth,
MAPMARK_GREENSOUTH MapMarkGreenSouth
}; };
enum VipState enum VipState
{ {
VIPSTATE_OFFLINE = 0, VipStateOffline = 0,
VIPSTATE_ONLINE = 1, VipStateOnline = 1,
VIPSTATE_PENDING = 2 VipStatePending = 2
};
enum SpeedFormula
{
SpeedFormulaA = 0,
SpeedFormulaB,
SpeedFormulaC,
LastSpeedFormula
}; };
} }

View File

@ -57,6 +57,7 @@ Creature::Creature() : Thing()
m_nameCache.setFont(g_fonts.getFont("verdana-11px-rounded")); m_nameCache.setFont(g_fonts.getFont("verdana-11px-rounded"));
m_nameCache.setAlign(Fw::AlignTopCenter); m_nameCache.setAlign(Fw::AlignTopCenter);
m_footStep = 0; m_footStep = 0;
m_speedFormula.fill(-1);
} }
void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView) void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView)
@ -615,6 +616,19 @@ void Creature::setEmblemTexture(const std::string& filename)
m_emblemTexture = g_textures.getTexture(filename); m_emblemTexture = g_textures.getTexture(filename);
} }
void Creature::setSpeedFormula(double speedA, double speedB, double speedC)
{
m_speedFormula[Otc::SpeedFormulaA] = speedA;
m_speedFormula[Otc::SpeedFormulaB] = speedB;
m_speedFormula[Otc::SpeedFormulaC] = speedC;
}
bool Creature::hasSpeedFormula()
{
return m_speedFormula[Otc::SpeedFormulaA] != -1 && m_speedFormula[Otc::SpeedFormulaB] != -1
&& m_speedFormula[Otc::SpeedFormulaC] != -1;
}
void Creature::addTimedSquare(uint8 color) void Creature::addTimedSquare(uint8 color)
{ {
m_showTimedSquare = true; m_showTimedSquare = true;
@ -658,28 +672,43 @@ Point Creature::getDrawOffset()
int Creature::getStepDuration() int Creature::getStepDuration()
{ {
int speed = m_speed * 2;
int groundSpeed = 0; int groundSpeed = 0;
Position tilePos = m_lastStepToPosition; Position tilePos = m_lastStepToPosition;
if(!tilePos.isValid()) if(!tilePos.isValid())
tilePos = m_position; tilePos = m_position;
const TilePtr& tile = g_map.getTile(tilePos); const TilePtr& tile = g_map.getTile(tilePos);
if(tile) if(tile) {
groundSpeed = tile->getGroundSpeed(); groundSpeed = tile->getGroundSpeed();
if(groundSpeed == 0)
groundSpeed = 150;
}
int interval = 1000; int interval = 1000;
if(groundSpeed > 0 && m_speed > 0) if(groundSpeed > 0 && speed > 0)
interval = (1000 * groundSpeed) / m_speed; interval = 1000 * groundSpeed;
if(g_game.getClientVersion() >= 900) if(g_game.getFeature(Otc::GameNewSpeedLaw) && hasSpeedFormula()) {
int formulatedSpeed = 1;
if(speed > -m_speedFormula[Otc::SpeedFormulaB]) {
formulatedSpeed = std::max(1, (int)floor((m_speedFormula[Otc::SpeedFormulaA] * log((speed / 2)
+ m_speedFormula[Otc::SpeedFormulaB]) + m_speedFormula[Otc::SpeedFormulaC]) + 0.5));
}
interval = std::floor(interval / (double)formulatedSpeed);
}
else
interval /= speed;
if(g_game.getProtocolVersion() >= 900)
interval = (interval / g_game.getServerBeat()) * g_game.getServerBeat(); interval = (interval / g_game.getServerBeat()) * g_game.getServerBeat();
interval = std::max(interval, g_game.getServerBeat());
if(m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast || if(m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast) m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast)
interval *= 3; interval *= 3;
interval = std::max(interval, g_game.getServerBeat());
return interval; return interval;
} }

View File

@ -44,7 +44,6 @@ public:
Creature(); Creature();
virtual void draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView = nullptr); virtual void draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView = nullptr);
void internalDrawOutfit(Point dest, float scaleFactor, bool animateWalk, bool animateIdle, Otc::Direction direction, LightView *lightView = nullptr); void internalDrawOutfit(Point dest, float scaleFactor, bool animateWalk, bool animateIdle, Otc::Direction direction, LightView *lightView = nullptr);
@ -65,6 +64,7 @@ public:
void setShieldTexture(const std::string& filename, bool blink); void setShieldTexture(const std::string& filename, bool blink);
void setEmblemTexture(const std::string& filename); void setEmblemTexture(const std::string& filename);
void setPassable(bool passable) { m_passable = passable; } void setPassable(bool passable) { m_passable = passable; }
void setSpeedFormula(double speedA, double speedB, double speedC);
void addTimedSquare(uint8 color); void addTimedSquare(uint8 color);
void removeTimedSquare() { m_showTimedSquare = false; } void removeTimedSquare() { m_showTimedSquare = false; }
@ -89,6 +89,9 @@ public:
Position getLastStepFromPosition() { return m_lastStepFromPosition; } Position getLastStepFromPosition() { return m_lastStepFromPosition; }
Position getLastStepToPosition() { return m_lastStepToPosition; } Position getLastStepToPosition() { return m_lastStepToPosition; }
float getStepProgress() { return m_walkTimer.ticksElapsed() / getStepDuration(); } float getStepProgress() { return m_walkTimer.ticksElapsed() / getStepDuration(); }
double getSpeedFormula(Otc::SpeedFormula formula) { return m_speedFormula[formula]; }
bool hasSpeedFormula();
std::array<double, Otc::LastSpeedFormula> getSpeedFormulaArray() { return m_speedFormula; }
virtual Point getDisplacement(); virtual Point getDisplacement();
virtual int getDisplacementX(); virtual int getDisplacementX();
virtual int getDisplacementY(); virtual int getDisplacementY();
@ -148,6 +151,8 @@ protected:
CachedText m_nameCache; CachedText m_nameCache;
Color m_informationColor; Color m_informationColor;
std::array<double, Otc::LastSpeedFormula> m_speedFormula;
// walk related // walk related
int m_walkAnimationPhase; int m_walkAnimationPhase;
int m_walkedPixels; int m_walkedPixels;

View File

@ -39,7 +39,7 @@ Game g_game;
Game::Game() Game::Game()
{ {
resetGameStates(); resetGameStates();
m_clientVersion = 0; m_protocolVersion = 0;
} }
void Game::terminate() void Game::terminate()
@ -118,6 +118,18 @@ void Game::processLoginWait(const std::string& message, int time)
g_lua.callGlobalField("g_game", "onLoginWait", message, time); g_lua.callGlobalField("g_game", "onLoginWait", message, time);
} }
void Game::processPendingGame()
{
m_localPlayer->setPendingGame(true);
g_lua.callGlobalField("g_game", "onPendingGame");
}
void Game::processEnterGame()
{
m_localPlayer->setPendingGame(false);
g_lua.callGlobalField("g_game", "onEnterGame");
}
void Game::processGameStart() void Game::processGameStart()
{ {
m_online = true; m_online = true;
@ -432,7 +444,7 @@ void Game::loginWorld(const std::string& account, const std::string& password, c
if(m_protocolGame || isOnline()) if(m_protocolGame || isOnline())
stdext::throw_exception("Unable to login into a world while already online or logging."); stdext::throw_exception("Unable to login into a world while already online or logging.");
if(m_clientVersion == 0) if(m_protocolVersion == 0)
stdext::throw_exception("Must set a valid game protocol version before logging."); stdext::throw_exception("Must set a valid game protocol version before logging.");
// reset the new game state // reset the new game state
@ -642,7 +654,7 @@ void Game::look(const ThingPtr& thing)
if(!canPerformGameAction() || !thing) if(!canPerformGameAction() || !thing)
return; return;
if(thing->isCreature() && m_clientVersion >= 961) if(thing->isCreature() && m_protocolVersion >= 961)
m_protocolGame->sendLookCreature(thing->getId()); m_protocolGame->sendLookCreature(thing->getId());
else else
m_protocolGame->sendLook(thing->getPosition(), thing->getId(), thing->getStackpos()); m_protocolGame->sendLook(thing->getPosition(), thing->getId(), thing->getStackpos());
@ -715,7 +727,7 @@ void Game::useWith(const ItemPtr& item, const ThingPtr& toThing)
if(!pos.isValid()) // virtual item if(!pos.isValid()) // virtual item
pos = Position(0xFFFF, 0, 0); // means that is a item in inventory pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
if(toThing->isCreature() && g_game.getClientVersion() >= 860) if(toThing->isCreature() && g_game.getProtocolVersion() >= 860)
m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackpos(), toThing->getId()); m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackpos(), toThing->getId());
else else
m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackpos(), toThing->getPosition(), toThing->getId(), toThing->getStackpos()); m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackpos(), toThing->getPosition(), toThing->getId(), toThing->getStackpos());
@ -785,7 +797,7 @@ void Game::attack(CreaturePtr creature)
setAttackingCreature(creature); setAttackingCreature(creature);
if(m_clientVersion >= 963) { if(m_protocolVersion >= 963) {
if(creature) if(creature)
m_seq = creature->getId(); m_seq = creature->getId();
} else } else
@ -808,7 +820,7 @@ void Game::follow(CreaturePtr creature)
setFollowingCreature(creature); setFollowingCreature(creature);
if(m_clientVersion >= 963) { if(m_protocolVersion >= 963) {
if(creature) if(creature)
m_seq = creature->getId(); m_seq = creature->getId();
} else } else
@ -1173,15 +1185,15 @@ bool Game::canPerformGameAction()
return m_online && m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection(); return m_online && m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection();
} }
void Game::setClientVersion(int version) void Game::setProtocolVersion(int version)
{ {
if(m_clientVersion == version) if(m_protocolVersion == version)
return; return;
if(isOnline()) if(isOnline())
stdext::throw_exception("Unable to change client version while online"); stdext::throw_exception("Unable to change protocol version while online");
if(version != 0 && (version < 810 || version > 981)) if(version != 0 && (version < 810 || version > 973))
stdext::throw_exception(stdext::format("Protocol version %d not supported", version)); stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_features.reset(); m_features.reset();
@ -1233,15 +1245,31 @@ void Game::setClientVersion(int version)
enableFeature(Otc::GameOfflineTrainingTime); enableFeature(Otc::GameOfflineTrainingTime);
} }
if(version >= 980) { if(version >= 973) {
enableFeature(Otc::GameLoginPending); enableFeature(Otc::GameLoginPending);
enableFeature(Otc::GameNewSpeedLaw); enableFeature(Otc::GameNewSpeedLaw);
} }
m_clientVersion = version; m_protocolVersion = version;
Proto::buildMessageModesMap(version); Proto::buildMessageModesMap(version);
g_lua.callGlobalField("g_game", "onProtocolVersionChange", version);
}
void Game::setClientVersion(int version)
{
if(m_clientVersion == version)
return;
if(isOnline())
stdext::throw_exception("Unable to change client version while online");
if(version != 0 && (version < 981 || version > 981))
stdext::throw_exception(stdext::format("Client version %d not supported", version));
m_clientVersion = version;
g_lua.callGlobalField("g_game", "onClientVersionChange", version); g_lua.callGlobalField("g_game", "onClientVersionChange", version);
} }

View File

@ -59,6 +59,9 @@ protected:
void processLoginAdvice(const std::string& message); void processLoginAdvice(const std::string& message);
void processLoginWait(const std::string& message, int time); void processLoginWait(const std::string& message, int time);
void processPendingGame();
void processEnterGame();
void processGameStart(); void processGameStart();
void processGameEnd(); void processGameEnd();
void processDeath(int penality); void processDeath(int penality);
@ -248,6 +251,9 @@ public:
void setFeature(Otc::GameFeature feature, bool enabled) { m_features.set(feature, enabled); } void setFeature(Otc::GameFeature feature, bool enabled) { m_features.set(feature, enabled); }
bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); } bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); }
void setProtocolVersion(int version);
int getProtocolVersion() { return m_protocolVersion; }
void setClientVersion(int version); void setClientVersion(int version);
int getClientVersion() { return m_clientVersion; } int getClientVersion() { return m_clientVersion; }
@ -308,6 +314,7 @@ private:
std::string m_worldName; std::string m_worldName;
std::bitset<Otc::LastGameFeature> m_features; std::bitset<Otc::LastGameFeature> m_features;
ScheduledEventPtr m_pingEvent; ScheduledEventPtr m_pingEvent;
int m_protocolVersion;
int m_clientVersion; int m_clientVersion;
}; };

View File

@ -216,7 +216,7 @@ int Item::getSubType()
{ {
if(isSplash() || isFluidContainer()) if(isSplash() || isFluidContainer())
return m_countOrSubType; return m_countOrSubType;
if(g_game.getClientVersion() >= 900) if(g_game.getProtocolVersion() >= 900)
return 0; return 0;
return 1; return 1;
} }

View File

@ -28,12 +28,6 @@
LocalPlayer::LocalPlayer() LocalPlayer::LocalPlayer()
{ {
m_preWalking = false;
m_lastPrewalkDone = true;
m_autoWalking = false;
m_known = false;
m_premium = false;
m_states = 0; m_states = 0;
m_vocation = 0; m_vocation = 0;
m_walkLockExpiration = 0; m_walkLockExpiration = 0;

View File

@ -53,6 +53,7 @@ public:
void setSoul(double soul); void setSoul(double soul);
void setStamina(double stamina); void setStamina(double stamina);
void setKnown(bool known) { m_known = known; } void setKnown(bool known) { m_known = known; }
void setPendingGame(bool pending) { m_pending = pending; }
void setInventoryItem(Otc::InventorySlot inventory, const ItemPtr& item); void setInventoryItem(Otc::InventorySlot inventory, const ItemPtr& item);
void setVocation(int vocation); void setVocation(int vocation);
void setPremium(bool premium); void setPremium(bool premium);
@ -92,6 +93,7 @@ public:
bool isPreWalking() { return m_preWalking; } bool isPreWalking() { return m_preWalking; }
bool isAutoWalking() { return m_autoWalking; } bool isAutoWalking() { return m_autoWalking; }
bool isPremium() { return m_premium; } bool isPremium() { return m_premium; }
bool isPendingGame() { return m_pending; }
LocalPlayerPtr asLocalPlayer() { return static_self_cast<LocalPlayer>(); } LocalPlayerPtr asLocalPlayer() { return static_self_cast<LocalPlayer>(); }
bool isLocalPlayer() { return true; } bool isLocalPlayer() { return true; }
@ -113,27 +115,30 @@ protected:
private: private:
// walk related // walk related
bool m_preWalking;
bool m_lastPrewalkDone;
bool m_autoWalking;
bool m_premium;
Position m_lastPrewalkDestionation;
ItemPtr m_inventoryItems[Otc::LastInventorySlot];
ScheduledEventPtr m_autoWalkEndEvent;
stdext::boolean<false> m_waitingWalkPong;
Timer m_walkPingTimer; Timer m_walkPingTimer;
Timer m_idleTimer; Position m_lastPrewalkDestionation;
ScheduledEventPtr m_autoWalkEndEvent;
ticks_t m_walkLockExpiration;
int m_lastWalkPing; int m_lastWalkPing;
stdext::boolean<false> m_preWalking;
stdext::boolean<true> m_lastPrewalkDone;
stdext::boolean<false> m_autoWalking;
stdext::boolean<false> m_waitingWalkPong;
stdext::boolean<false> m_premium;
stdext::boolean<false> m_known;
stdext::boolean<false> m_pending;
ItemPtr m_inventoryItems[Otc::LastInventorySlot];
Timer m_idleTimer;
std::array<int, Otc::LastSkill> m_skillsLevel; std::array<int, Otc::LastSkill> m_skillsLevel;
std::array<int, Otc::LastSkill> m_skillsBaseLevel; std::array<int, Otc::LastSkill> m_skillsBaseLevel;
std::array<int, Otc::LastSkill> m_skillsLevelPercent; std::array<int, Otc::LastSkill> m_skillsLevelPercent;
std::vector<int> m_spells; std::vector<int> m_spells;
bool m_known;
int m_states; int m_states;
int m_vocation; int m_vocation;
ticks_t m_walkLockExpiration;
double m_health; double m_health;
double m_maxHealth; double m_maxHealth;

View File

@ -220,6 +220,8 @@ void OTClient::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "getServerBeat", &Game::getServerBeat, &g_game); g_lua.bindSingletonFunction("g_game", "getServerBeat", &Game::getServerBeat, &g_game);
g_lua.bindSingletonFunction("g_game", "getLocalPlayer", &Game::getLocalPlayer, &g_game); g_lua.bindSingletonFunction("g_game", "getLocalPlayer", &Game::getLocalPlayer, &g_game);
g_lua.bindSingletonFunction("g_game", "getProtocolGame", &Game::getProtocolGame, &g_game); g_lua.bindSingletonFunction("g_game", "getProtocolGame", &Game::getProtocolGame, &g_game);
g_lua.bindSingletonFunction("g_game", "getProtocolVersion", &Game::getProtocolVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game); g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "setClientVersion", &Game::setClientVersion, &g_game); g_lua.bindSingletonFunction("g_game", "setClientVersion", &Game::setClientVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game); g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game);

View File

@ -459,20 +459,20 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
std::vector<Otc::Direction>& dirs = std::get<0>(ret); std::vector<Otc::Direction>& dirs = std::get<0>(ret);
Otc::PathFindResult& result = std::get<1>(ret); Otc::PathFindResult& result = std::get<1>(ret);
result = Otc::PATHFIND_RESULT_NO_WAY; result = Otc::PathFindResultNoWay;
if(startPos == goalPos) { if(startPos == goalPos) {
result = Otc::PATHFIND_RESULT_SAME_POSITION; result = Otc::PathFindResultSamePosition;
return ret; return ret;
} }
if(startPos.z != goalPos.z) { if(startPos.z != goalPos.z) {
result = Otc::PATHFIND_RESULT_IMPOSSIBLE; result = Otc::PathFindResultImpossible;
return ret; return ret;
} }
if(startPos.distance(goalPos) > maxSteps) { if(startPos.distance(goalPos) > maxSteps) {
result = Otc::PATHFIND_RESULT_TOO_FAR; result = Otc::PathFindResultTooFar;
return ret; return ret;
} }
@ -486,7 +486,7 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
while(currentNode) { while(currentNode) {
// too far // too far
if(currentNode->steps >= maxSteps) { if(currentNode->steps >= maxSteps) {
result = Otc::PATHFIND_RESULT_TOO_FAR; result = Otc::PathFindResultTooFar;
break; break;
} }
@ -507,14 +507,14 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
const TilePtr& tile = getTile(neighborPos); const TilePtr& tile = getTile(neighborPos);
if(neighborPos != goalPos) { if(neighborPos != goalPos) {
if(!(flags & Otc::PATHFIND_ALLOW_NULLTILES) && !tile) if(!(flags & Otc::PathFindAllowNullTiles) && !tile)
continue; continue;
if(tile) { if(tile) {
if(!(flags & Otc::PATHFIND_ALLOW_CREATURES) && tile->hasCreature()) if(!(flags & Otc::PathFindAllowCreatures) && tile->hasCreature())
continue; continue;
if(!(flags & Otc::PATHFIND_ALLOW_NONPATHABLE) && !tile->isPathable()) if(!(flags & Otc::PathFindAllowNonPathable) && !tile->isPathable())
continue; continue;
if(!(flags & Otc::PATHFIND_ALLOW_NONWALKABLE) && !tile->isWalkable()) if(!(flags & Otc::PathFindAllowNonWalkable) && !tile->isWalkable())
continue; continue;
} }
} }
@ -568,7 +568,7 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
} }
dirs.pop_back(); dirs.pop_back();
std::reverse(dirs.begin(), dirs.end()); std::reverse(dirs.begin(), dirs.end());
result = Otc::PATHFIND_RESULT_OK; result = Otc::PathFineResultOk;
} }
for(auto it : nodes) for(auto it : nodes)

View File

@ -473,7 +473,7 @@ void Map::saveOtcm(const std::string& fileName)
// version 1 header // version 1 header
fin->addString("OTCM 1.0"); // map description fin->addString("OTCM 1.0"); // map description
fin->addU32(g_things.getDatSignature()); fin->addU32(g_things.getDatSignature());
fin->addU16(g_game.getClientVersion()); fin->addU16(g_game.getProtocolVersion());
fin->addString(g_game.getWorldName()); fin->addString(g_game.getWorldName());
// go back and rewrite where the map data starts // go back and rewrite where the map data starts

View File

@ -56,7 +56,7 @@ void ProtocolGame::onRecv(const InputMessagePtr& inputMessage)
if(m_firstRecv) { if(m_firstRecv) {
m_firstRecv = false; m_firstRecv = false;
if(g_game.getClientVersion() > 810) { if(g_game.getProtocolVersion() > 810) {
int size = inputMessage->getU16(); int size = inputMessage->getU16();
if(size != inputMessage->getUnreadSize()) { if(size != inputMessage->getUnreadSize()) {
g_logger.traceError("invalid message size"); g_logger.traceError("invalid message size");

View File

@ -342,6 +342,7 @@ void ProtocolGame::parseInitGame(const InputMessagePtr& msg)
double speedA = msg->getDouble(); double speedA = msg->getDouble();
double speedB = msg->getDouble(); double speedB = msg->getDouble();
double speedC = msg->getDouble(); double speedC = msg->getDouble();
m_localPlayer->setSpeedFormula(speedA, speedB, speedC);
} }
bool canReportBugs = msg->getU8(); bool canReportBugs = msg->getU8();
@ -353,11 +354,13 @@ void ProtocolGame::parseInitGame(const InputMessagePtr& msg)
void ProtocolGame::parsePendingGame(const InputMessagePtr& msg) void ProtocolGame::parsePendingGame(const InputMessagePtr& msg)
{ {
//set player to pending game state //set player to pending game state
g_game.processPendingGame();
} }
void ProtocolGame::parseEnterGame(const InputMessagePtr& msg) void ProtocolGame::parseEnterGame(const InputMessagePtr& msg)
{ {
//set player to entered game state //set player to entered game state
g_game.processEnterGame();
} }
void ProtocolGame::parseGMActions(const InputMessagePtr& msg) void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
@ -366,7 +369,7 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
int numViolationReasons; int numViolationReasons;
if(g_game.getClientVersion() >= 854) if(g_game.getProtocolVersion() >= 854)
numViolationReasons = 20; numViolationReasons = 20;
else else
numViolationReasons = 32; numViolationReasons = 32;
@ -489,7 +492,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
Position pos = getPosition(msg); Position pos = getPosition(msg);
int stackPos = -1; int stackPos = -1;
if(g_game.getClientVersion() >= 854) if(g_game.getProtocolVersion() >= 854)
stackPos = msg->getU8(); stackPos = msg->getU8();
ThingPtr thing = getThing(msg); ThingPtr thing = getThing(msg);
@ -617,7 +620,7 @@ void ProtocolGame::parseOpenNpcTrade(const InputMessagePtr& msg)
int listCount; int listCount;
if(g_game.getClientVersion() >= 900) if(g_game.getProtocolVersion() >= 900)
listCount = msg->getU16(); listCount = msg->getU16();
else else
listCount = msg->getU8(); listCount = msg->getU8();
@ -644,7 +647,7 @@ void ProtocolGame::parsePlayerGoods(const InputMessagePtr& msg)
std::vector<std::tuple<ItemPtr, int>> goods; std::vector<std::tuple<ItemPtr, int>> goods;
int money; int money;
if(g_game.getClientVersion() >= 980) if(g_game.getProtocolVersion() >= 973)
money = msg->getU64(); money = msg->getU64();
else else
money = msg->getU32(); money = msg->getU32();
@ -993,7 +996,7 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg) void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
{ {
uint seq = 0; uint seq = 0;
if(g_game.getClientVersion() >= 860) if(g_game.getProtocolVersion() >= 860)
seq = msg->getU32(); seq = msg->getU32();
g_game.processAttackCancel(seq); g_game.processAttackCancel(seq);
@ -1295,7 +1298,7 @@ void ProtocolGame::parseVipAdd(const InputMessagePtr& msg)
id = msg->getU32(); id = msg->getU32();
name = g_game.formatCreatureName(msg->getString()); name = g_game.formatCreatureName(msg->getString());
if(g_game.getClientVersion() >= 963) { if(g_game.getProtocolVersion() >= 963) {
desc = msg->getString(); desc = msg->getString();
markId = msg->getU32(); markId = msg->getU32();
notifyLogin = msg->getU8(); notifyLogin = msg->getU8();
@ -1614,7 +1617,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
uint id = msg->getU32(); uint id = msg->getU32();
int creatureType; int creatureType;
if(g_game.getClientVersion() >= 910) if(g_game.getProtocolVersion() >= 910)
creatureType = msg->getU8(); creatureType = msg->getU8();
else { else {
if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId) if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId)
@ -1670,7 +1673,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
if(g_game.getFeature(Otc::GameCreatureEmblems) && !known) if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
emblem = msg->getU8(); emblem = msg->getU8();
if(g_game.getClientVersion() >= 854) if(g_game.getProtocolVersion() >= 854)
unpass = msg->getU8(); unpass = msg->getU8();
if(creature) { if(creature) {
@ -1699,7 +1702,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
if(creature) if(creature)
creature->turn(direction); creature->turn(direction);
if(g_game.getClientVersion() >= 953) { if(g_game.getProtocolVersion() >= 953) {
bool unpass = msg->getU8(); bool unpass = msg->getU8();
if(creature) if(creature)

View File

@ -51,7 +51,12 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
msg->addU8(Proto::ClientEnterGame); msg->addU8(Proto::ClientEnterGame);
msg->addU16(g_lua.callGlobalField<int>("g_game", "getOsType")); msg->addU16(g_lua.callGlobalField<int>("g_game", "getOsType"));
msg->addU16(g_game.getClientVersion()); msg->addU16(g_game.getProtocolVersion());
if(g_game.getProtocolVersion() >= 971) {
msg->addU32(g_game.getClientVersion());
msg->addU8(0); // clientType
}
int paddingBytes = 128; int paddingBytes = 128;
msg->addU8(0); // first RSA byte must be 0 msg->addU8(0); // first RSA byte must be 0
@ -588,7 +593,7 @@ void ProtocolGame::sendShareExperience(bool active)
msg->addU8(Proto::ClientShareExperience); msg->addU8(Proto::ClientShareExperience);
msg->addU8(active ? 0x01 : 0x00); msg->addU8(active ? 0x01 : 0x00);
if(g_game.getClientVersion() < 910) if(g_game.getProtocolVersion() < 910)
msg->addU8(0); msg->addU8(0);
send(msg); send(msg);

View File

@ -189,7 +189,7 @@ void Tile::addThing(const ThingPtr& thing, int stackPos)
append = (priority <= 3); append = (priority <= 3);
// newer protocols does not store creatures in reverse order // newer protocols does not store creatures in reverse order
if(g_game.getClientVersion() >= 854 && priority == 4) if(g_game.getProtocolVersion() >= 854 && priority == 4)
append = !append; append = !append;
} }