From c795eb91ab61344091c64a9a5b2d95f236f7a9ec Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Thu, 26 Jul 2012 03:10:28 -0300 Subject: [PATCH] More multiprotocol support --- modules/client_entergame/characterlist.lua | 2 +- modules/client_entergame/entergame.lua | 3 +- modules/corelib/util.lua | 23 +- modules/game_battle/battle.lua | 4 +- .../game_combatcontrols/combatcontrols.lua | 4 - .../game_combatcontrols/combatcontrols.otui | 1 - modules/game_containers/containers.lua | 4 +- modules/game_healthinfo/healthinfo.lua | 2 +- modules/game_interface/gameinterface.lua | 81 +++--- modules/game_inventory/inventory.lua | 5 +- modules/game_market/marketprotocol.lua | 8 +- modules/game_ruleviolation/ruleviolation.lua | 1 + modules/game_ruleviolation/ruleviolation.otui | 5 +- modules/game_textmessage/protocol.lua | 2 +- modules/game_tibiafiles/tibiafiles.lua | 18 +- modules/gamelib/const.lua | 3 + modules/gamelib/player.lua | 1 + modules/gamelib/protocollogin.lua | 81 +++--- .../graphics/painterogl2_shadersources.h | 4 +- src/framework/luafunctions.cpp | 1 + src/framework/sound/soundmanager.h | 2 +- src/framework/ui/uianchorlayout.h | 2 +- src/framework/ui/uiwidget.cpp | 12 + src/framework/ui/uiwidget.h | 1 + src/otclient/creature.h | 2 +- src/otclient/game.cpp | 45 ++- src/otclient/game.h | 16 +- src/otclient/item.cpp | 4 +- src/otclient/luafunctions.cpp | 4 +- src/otclient/map.cpp | 2 +- src/otclient/map.h | 2 +- src/otclient/otclient.cpp | 2 +- src/otclient/protocolcodes.h | 3 +- src/otclient/protocolgame.cpp | 3 +- src/otclient/protocolgame.h | 16 +- src/otclient/protocolgameparse.cpp | 257 ++++++++++-------- src/otclient/protocolgamesend.cpp | 16 +- src/otclient/spritemanager.cpp | 8 +- src/otclient/spritemanager.h | 2 +- src/otclient/thing.cpp | 2 +- src/otclient/thingtypemanager.cpp | 29 +- src/otclient/thingtypemanager.h | 2 +- src/otclient/tile.cpp | 13 +- 43 files changed, 389 insertions(+), 309 deletions(-) diff --git a/modules/client_entergame/characterlist.lua b/modules/client_entergame/characterlist.lua index 33ca2fe2..b314c411 100644 --- a/modules/client_entergame/characterlist.lua +++ b/modules/client_entergame/characterlist.lua @@ -92,7 +92,7 @@ end local function onLoginWait(message, time) CharacterList.destroyLoadBox() - waitingWindow = g_ui.loadUI('waitinglist.otui') + waitingWindow = g_ui.displayUI('waitinglist.otui') local label = waitingWindow:getChildById('infoLabel') label:setText(message) diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 2524d4ed..09ef796e 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -168,7 +168,8 @@ function EnterGame.doLogin() end }) g_game.chooseRsa(G.host) - g_game.setProtocolVersion(protocol) + g_game.setClientVersion(protocol) + modules.game_tibiafiles.load() protocolLogin:login(G.host, G.port, G.account, G.password) end diff --git a/modules/corelib/util.lua b/modules/corelib/util.lua index 26e7f7a3..26fc6920 100644 --- a/modules/corelib/util.lua +++ b/modules/corelib/util.lua @@ -72,7 +72,14 @@ function connect(object, arg1, arg2, arg3) end end -function disconnect(object, signalsAndSlots) +function disconnect(object, arg1, arg2) + local signalsAndSlots + if type(arg1) == 'string' then + signalsAndSlots = { [arg1] = arg2 } + else + signalsAndSlots = arg1 + end + for signal,slot in pairs(signalsAndSlots) do if not object[signal] then elseif type(object[signal]) == 'function' then @@ -231,11 +238,19 @@ end function signalcall(param, ...) if type(param) == 'function' then - return param(...) + local status, ret = pcall(param, ...) + if status then + return ret + else + perror(ret) + end elseif type(param) == 'table' then for k,v in pairs(param) do - if v(...) then - return true + local status, ret = pcall(v, ...) + if status then + if ret then return true end + else + perror(ret) end end elseif func ~= nil then diff --git a/modules/game_battle/battle.lua b/modules/game_battle/battle.lua index 461c6463..da9d7e6e 100644 --- a/modules/game_battle/battle.lua +++ b/modules/game_battle/battle.lua @@ -91,7 +91,7 @@ end function addAllCreatures() local spectators = {} local player = g_game.getLocalPlayer() - if player then + if g_game.isOnline() then creatures = g_map.getSpectators(player:getPosition(), false) for i, creature in ipairs(creatures) do if creature ~= player and doCreatureFitFilters(creature) then @@ -129,7 +129,7 @@ end function checkCreatures(forceRecheck) local player = g_game.getLocalPlayer() - if player then + if g_game.isOnline() then local spectators = {} -- reloading list of spectators diff --git a/modules/game_combatcontrols/combatcontrols.lua b/modules/game_combatcontrols/combatcontrols.lua index abf441e6..ce0800a4 100644 --- a/modules/game_combatcontrols/combatcontrols.lua +++ b/modules/game_combatcontrols/combatcontrols.lua @@ -135,10 +135,6 @@ function toggle() end end -function onMiniWindowClose() - combatControlsButton:setOn(false) -end - function onSetFightMode(self, selectedFightButton) if selectedFightButton == nil then return end local buttonId = selectedFightButton:getId() diff --git a/modules/game_combatcontrols/combatcontrols.otui b/modules/game_combatcontrols/combatcontrols.otui index ccfed135..f78cade1 100644 --- a/modules/game_combatcontrols/combatcontrols.otui +++ b/modules/game_combatcontrols/combatcontrols.otui @@ -27,7 +27,6 @@ MiniWindow !text: tr('Combat Controls') icon: combatcontrols.png height: 48 - @onClose: CombatControls.onMiniWindowClose() &save: true MiniWindowContents diff --git a/modules/game_containers/containers.lua b/modules/game_containers/containers.lua index aa1b3a89..b2dc0481 100644 --- a/modules/game_containers/containers.lua +++ b/modules/game_containers/containers.lua @@ -37,14 +37,14 @@ function clean() end end -local function refreshContainerItems(container) +function refreshContainerItems(container) for slot=0,container:getCapacity()-1 do local itemWidget = container.itemsPanel:getChildById('item' .. slot) itemWidget:setItem(container:getItem(slot)) end end -local function onContainerOpen(container, previousContainer) +function onContainerOpen(container, previousContainer) local containerWindow if previousContainer then containerWindow = previousContainer.window diff --git a/modules/game_healthinfo/healthinfo.lua b/modules/game_healthinfo/healthinfo.lua index fd5d4e36..d36a2f54 100644 --- a/modules/game_healthinfo/healthinfo.lua +++ b/modules/game_healthinfo/healthinfo.lua @@ -2,7 +2,7 @@ Icons = {} Icons[1] = { tooltip = tr('You are poisoned'), path = '/game_healthinfo/icons/poisoned.png', id = 'condition_poisoned' } Icons[2] = { tooltip = tr('You are burning'), path = '/game_healthinfo/icons/burning.png', id = 'condition_burning' } Icons[4] = { tooltip = tr('You are electrified'), path = '/game_healthinfo/icons/electrified.png', id = 'condition_electrified' } -Icons[8] = { tooltip = tr('You are freezing'), path = '/game_healthinfo/icons/drunk.png', id = 'condition_drunk' } +Icons[8] = { tooltip = tr('You are drunk'), path = '/game_healthinfo/icons/drunk.png', id = 'condition_drunk' } Icons[16] = { tooltip = tr('You are protected by a magic shield'), path = '/game_healthinfo/icons/magic_shield.png', id = 'condition_magic_shield' } Icons[32] = { tooltip = tr('You are paralysed'), path = '/game_healthinfo/icons/slowed.png', id = 'condition_slowed' } Icons[64] = { tooltip = tr('You are hasted'), path = '/game_healthinfo/icons/haste.png', id = 'condition_haste' } diff --git a/modules/game_interface/gameinterface.lua b/modules/game_interface/gameinterface.lua index f5c7c6c6..6b50614a 100644 --- a/modules/game_interface/gameinterface.lua +++ b/modules/game_interface/gameinterface.lua @@ -238,6 +238,7 @@ function startTradeWith(thing) end function createThingMenu(menuPosition, lookThing, useThing, creatureThing) + if not g_game.isOnline() then return end local menu = g_ui.createWidget('PopupMenu') if lookThing then @@ -297,52 +298,50 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing) else local localPlayer = g_game.getLocalPlayer() - if localPlayer then - if g_game.getAttackingCreature() ~= creatureThing then - menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end) - else - menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end) - end + if g_game.getAttackingCreature() ~= creatureThing then + menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end) + else + menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end) + end - if g_game.getFollowingCreature() ~= creatureThing then - menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end) - else - menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end) + if g_game.getFollowingCreature() ~= creatureThing then + menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end) + else + menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end) + end + + if creatureThing:asPlayer() then + menu:addSeparator() + local creatureName = creatureThing:getName() + menu:addOption(tr('Message to %s', creatureName), function() g_game.openPrivateChannel(creatureName) end) + if modules.game_console.getOwnPrivateTab() then + menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end) + menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end) -- [TODO] must be removed after message's popup labels been implemented + end + if (not Player:hasVip(creatureName)) then + menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end) end - if creatureThing:asPlayer() then - menu:addSeparator() - local creatureName = creatureThing:getName() - menu:addOption(tr('Message to %s', creatureName), function() g_game.openPrivateChannel(creatureName) end) - if modules.game_console.getOwnPrivateTab() then - menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end) - menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end) -- [TODO] must be removed after message's popup labels been implemented + local localPlayerShield = localPlayer:asCreature():getShield() + local creatureShield = creatureThing:getShield() + + if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then + if creatureShield == ShieldWhiteYellow then + menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(creatureThing:getId()) end) + else + menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end) end - if (not Player:hasVip(creatureName)) then - menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end) + elseif localPlayerShield == ShieldWhiteYellow then + if creatureShield == ShieldWhiteBlue then + menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end) end - - local localPlayerShield = localPlayer:asCreature():getShield() - local creatureShield = creatureThing:getShield() - - if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then - if creatureShield == ShieldWhiteYellow then - menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(creatureThing:getId()) end) - else - menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end) - end - elseif localPlayerShield == ShieldWhiteYellow then - if creatureShield == ShieldWhiteBlue then - menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end) - end - elseif localPlayerShield == ShieldYellow or localPlayerShield == ShieldYellowSharedExp or localPlayerShield == ShieldYellowNoSharedExpBlink or localPlayerShield == ShieldYellowNoSharedExp then - if creatureShield == ShieldWhiteBlue then - menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end) - elseif creatureShield == ShieldBlue or creatureShield == ShieldBlueSharedExp or creatureShield == ShieldBlueNoSharedExpBlink or creatureShield == ShieldBlueNoSharedExp then - menu:addOption(tr('Pass Leadership to %s', creatureThing:getName()), function() g_game.partyPassLeadership(creatureThing:getId()) end) - else - menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end) - end + elseif localPlayerShield == ShieldYellow or localPlayerShield == ShieldYellowSharedExp or localPlayerShield == ShieldYellowNoSharedExpBlink or localPlayerShield == ShieldYellowNoSharedExp then + if creatureShield == ShieldWhiteBlue then + menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end) + elseif creatureShield == ShieldBlue or creatureShield == ShieldBlueSharedExp or creatureShield == ShieldBlueNoSharedExpBlink or creatureShield == ShieldBlueNoSharedExp then + menu:addOption(tr('Pass Leadership to %s', creatureThing:getName()), function() g_game.partyPassLeadership(creatureThing:getId()) end) + else + menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end) end end end diff --git a/modules/game_inventory/inventory.lua b/modules/game_inventory/inventory.lua index 32f6fd6a..533958ab 100644 --- a/modules/game_inventory/inventory.lua +++ b/modules/game_inventory/inventory.lua @@ -43,7 +43,7 @@ end function refresh() local player = g_game.getLocalPlayer() for i=InventorySlotFirst,InventorySlotLast do - if player then + if g_game.isOnline() then onInventoryChange(player, i, player:getInventoryItem(i)) else onInventoryChange(player, i, nil) @@ -67,8 +67,9 @@ end -- hooked events function onInventoryChange(player, slot, item, oldItem) + if slot >= InventorySlotPurse then return end local itemWidget = inventoryPanel:getChildById('slot' .. slot) - if(item) then + if item then itemWidget:setStyle('Item') itemWidget:setItem(item) else diff --git a/modules/game_market/marketprotocol.lua b/modules/game_market/marketprotocol.lua index c297a116..1479a35e 100644 --- a/modules/game_market/marketprotocol.lua +++ b/modules/game_market/marketprotocol.lua @@ -38,7 +38,7 @@ end local function parseMarketEnter(msg) local balance = msg:getU32() local vocation = -1 - if g_game.getProtocolVersion() < 950 then + if g_game.getClientVersion() < 950 then vocation = msg:getU8() -- get vocation id end local offers = msg:getU8() @@ -74,7 +74,8 @@ local function parseMarketDetail(msg) end local purchaseStats = {} - if msg:getU8() == 0x01 then + local count = msg:getU8() + for i=1,count do local transactions = msg:getU32() -- transaction count local totalPrice = msg:getU32() -- total price local highestPrice = msg:getU32() -- highest price @@ -84,7 +85,8 @@ local function parseMarketDetail(msg) end local saleStats = {} - if msg:getU8() == 0x01 then + count = msg:getU8() + for i=1,count do local transactions = msg:getU32() -- transaction count local totalPrice = msg:getU32() -- total price local highestPrice = msg:getU32() -- highest price diff --git a/modules/game_ruleviolation/ruleviolation.lua b/modules/game_ruleviolation/ruleviolation.lua index bf022ded..b2a80b76 100644 --- a/modules/game_ruleviolation/ruleviolation.lua +++ b/modules/game_ruleviolation/ruleviolation.lua @@ -67,6 +67,7 @@ function loadReasons() local actions = g_game.getGMActions() for reason, actionFlags in pairs(actions) do local label = g_ui.createWidget('RVListLabel', reasonsTextList) + label.onFocusChange = onSelectReason label:setText(rvreasons[reason]) label.reasonId = reason label.actionFlags = actionFlags diff --git a/modules/game_ruleviolation/ruleviolation.otui b/modules/game_ruleviolation/ruleviolation.otui index 4760b755..3a014ac6 100644 --- a/modules/game_ruleviolation/ruleviolation.otui +++ b/modules/game_ruleviolation/ruleviolation.otui @@ -2,7 +2,6 @@ RVListLabel < Label background-color: alpha text-offset: 2 0 focusable: true - @onFocusChange: function (self, focused) RuleViolation.onSelectReason(self, focused) end $focus: background-color: #ffffff22 @@ -109,7 +108,7 @@ MainWindow width: 64 anchors.right: parent.right anchors.bottom: parent.bottom - @onClick: RuleViolation.hide() + @onClick: hide() Button !text: tr('Ok') @@ -117,4 +116,4 @@ MainWindow margin-right: 5 anchors.right: prev.left anchors.bottom: parent.bottom - @onClick: RuleViolation.report() \ No newline at end of file + @onClick: report() \ No newline at end of file diff --git a/modules/game_textmessage/protocol.lua b/modules/game_textmessage/protocol.lua index b8287b13..29061cef 100644 --- a/modules/game_textmessage/protocol.lua +++ b/modules/game_textmessage/protocol.lua @@ -47,7 +47,7 @@ end function parseTextMessage(msg) local msgtype = msg:getU8() local text = msg:getString() - msgtype = getMessageTypes(g_game.getProtocolVersion())[msgtype] + msgtype = getMessageTypes(g_game.getClientVersion())[msgtype] signalcall(g_game.onTextMessage, msgtype, text) end diff --git a/modules/game_tibiafiles/tibiafiles.lua b/modules/game_tibiafiles/tibiafiles.lua index f891ed51..2fcddefc 100644 --- a/modules/game_tibiafiles/tibiafiles.lua +++ b/modules/game_tibiafiles/tibiafiles.lua @@ -1,11 +1,17 @@ function init() - if not g_things.loadDat('/game_tibiafiles/Tibia.dat') then - fatal(tr("Unable to load dat file, please place a valid Tibia dat in modules/game_tibiafiles/Tibia.dat")) - end - if not g_sprites.loadSpr('/game_tibiafiles/Tibia.spr') then - fatal(tr("Unable to load spr file, please place a valid Tibia spr in modules/game_tibiafiles/Tibia.spr")) + if g_game.getClientVersion() ~= 0 then + load() end end -function terminate() +function load() + local version = g_game.getClientVersion() + local datPath = resolvepath(version .. '/Tibia.dat') + local sprPath = resolvepath(version .. '/Tibia.spr') + if not g_things.loadDat(datPath) then + fatal(tr("Unable to load dat file, please place a valid dat in '%s'", datPath)) + end + if not g_sprites.loadSpr(sprPath) then + fatal(tr("Unable to load spr file, please place a valid spr in '%s'", sprPath)) + end end diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index b67c8fe3..c0d8d7a3 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -71,6 +71,9 @@ CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618" "2907336840325241747827401343576296990629870233111328210165697754" .. "88792221429527047321331896351555606801473202394175817" +-- set to the latest Tibia.pic signature to make otclient compatible with official tibia +PIC_SIGNATURE = 1337606793 + OsTypes = { Linux = 1, Windows = 2, diff --git a/modules/gamelib/player.lua b/modules/gamelib/player.lua index d5538f49..8e324566 100644 --- a/modules/gamelib/player.lua +++ b/modules/gamelib/player.lua @@ -11,6 +11,7 @@ InventorySlotLeg = 7 InventorySlotFeet = 8 InventorySlotFinger = 9 InventorySlotAmmo = 10 +InventorySlotPurse = 11 InventorySlotFirst = 1 InventorySlotLast = 10 diff --git a/modules/gamelib/protocollogin.lua b/modules/gamelib/protocollogin.lua index 2dd65ab4..535a52d9 100644 --- a/modules/gamelib/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -1,21 +1,37 @@ -- @docclass ProtocolLogin = extends(Protocol) --- set to the latest Tibia.pic signature to make otclient compatible with official tibia -local PIC_SIGNATURE = 1337606793 - LoginServerError = 10 LoginServerMotd = 20 LoginServerUpdateNeeded = 30 LoginServerCharacterList = 100 +function ProtocolLogin:login(host, port, accountName, accountPassword) + if string.len(accountName) == 0 or string.len(accountPassword) == 0 then + signalcall(self.onError, self, tr("You must enter an account name and password.")) + return + end + if string.len(host) == 0 or port == nil or port == 0 then + signalcall(self.onError, self, tr("You must enter a valid server address and port.")) + return + end + + self.accountName = accountName + self.accountPassword = accountPassword + self.connectCallback = sendLoginPacket + + self:connect(host, port) +end --- private functions -local function sendLoginPacket(protocol) +function ProtocolLogin:cancelLogin() + self:disconnect() +end + +function ProtocolLogin:sendLoginPacket() local msg = OutputMessage.create() msg:addU8(ClientOpcodes.ClientEnterAccount) msg:addU16(g_game.getOsType()) - msg:addU16(g_game.getProtocolVersion()) + msg:addU16(g_game.getClientVersion()) msg:addU32(g_things.getDatSignature()) msg:addU32(g_sprites.getSprSignature()) @@ -26,8 +42,8 @@ local function sendLoginPacket(protocol) paddingBytes = paddingBytes - 1 -- xtea key - protocol:generateXteaKey() - local xteaKey = protocol:getXteaKey() + self:generateXteaKey() + local xteaKey = self:getXteaKey() msg:addU32(xteaKey[1]) msg:addU32(xteaKey[2]) msg:addU32(xteaKey[3]) @@ -35,30 +51,29 @@ local function sendLoginPacket(protocol) paddingBytes = paddingBytes - 16 if g_game.getFeature(GameProtocolChecksum) then - protocol:enableChecksum() + self:enableChecksum() end if g_game.getFeature(GameAccountNames) then - msg:addString(protocol.accountName) - msg:addString(protocol.accountPassword) - paddingBytes = paddingBytes - (4 + string.len(protocol.accountName) + string.len(protocol.accountPassword)) + msg:addString(self.accountName) + msg:addString(self.accountPassword) + paddingBytes = paddingBytes - (4 + string.len(self.accountName) + string.len(self.accountPassword)) else - msg:addU32(tonumber(protocol.accountName)) - msg:addString(protocol.accountPassword) - paddingBytes = paddingBytes - (6 + string.len(protocol.accountPassword)) + msg:addU32(tonumber(self.accountName)) + msg:addString(self.accountPassword) + paddingBytes = paddingBytes - (6 + string.len(self.accountPassword)) end msg:addPaddingBytes(paddingBytes, 0) msg:encryptRsa(128, g_game.getRsa()) - protocol:send(msg) - protocol:enableXteaEncryption() - protocol:recv() + self:send(msg) + self:enableXteaEncryption() + self:recv() end --- events function ProtocolLogin:onConnect() - self:connectCallback(self) + self:sendLoginPacket() end function ProtocolLogin:onRecv(msg) @@ -79,32 +94,6 @@ function ProtocolLogin:onRecv(msg) self:disconnect() end --- public functions -function ProtocolLogin.create() - return ProtocolLogin.internalCreate() -end - -function ProtocolLogin:login(host, port, accountName, accountPassword) - if string.len(accountName) == 0 or string.len(accountPassword) == 0 then - signalcall(self.onError, self, tr("You must enter an account name and password.")) - return - end - if string.len(host) == 0 or port == nil or port == 0 then - signalcall(self.onError, self, tr("You must enter a valid server address and port.")) - return - end - - self.accountName = accountName - self.accountPassword = accountPassword - self.connectCallback = sendLoginPacket - - self:connect(host, port) -end - -function ProtocolLogin:cancelLogin() - self:disconnect() -end - function ProtocolLogin:parseError(msg) local errorMessage = msg:getString() signalcall(self.onError, self, errorMessage) diff --git a/src/framework/graphics/painterogl2_shadersources.h b/src/framework/graphics/painterogl2_shadersources.h index fccb7b57..b6898c4a 100644 --- a/src/framework/graphics/painterogl2_shadersources.h +++ b/src/framework/graphics/painterogl2_shadersources.h @@ -37,14 +37,14 @@ static const std::string glslMainWithTexCoordsVertexShader = "\n\ void main()\n\ {\n\ gl_Position = calculatePosition();\n\ - v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1)).xy;\n\ + v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;\n\ }\n"; static std::string glslPositionOnlyVertexShader = "\n\ attribute highp vec2 a_Vertex;\n\ uniform highp mat3 u_ProjectionMatrix;\n\ highp vec4 calculatePosition() {\n\ - return vec4(u_ProjectionMatrix * vec3(a_Vertex.xy, 1), 1);\n\ + return vec4(u_ProjectionMatrix * vec3(a_Vertex.xy, 1.0), 1.0);\n\ }\n"; static const std::string glslMainFragmentShader = "\n\ diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index a1191865..6597f0f5 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -336,6 +336,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("getChildByIndex", &UIWidget::getChildByIndex); g_lua.bindClassMemberFunction("recursiveGetChildById", &UIWidget::recursiveGetChildById); g_lua.bindClassMemberFunction("recursiveGetChildByPos", &UIWidget::recursiveGetChildByPos); + g_lua.bindClassMemberFunction("recursiveGetChildren", &UIWidget::recursiveGetChildren); g_lua.bindClassMemberFunction("recursiveGetChildrenByPos", &UIWidget::recursiveGetChildrenByPos); g_lua.bindClassMemberFunction("recursiveGetChildrenByMarginPos", &UIWidget::recursiveGetChildrenByMarginPos); g_lua.bindClassMemberFunction("backwardsGetWidgetById", &UIWidget::backwardsGetWidgetById); diff --git a/src/framework/sound/soundmanager.h b/src/framework/sound/soundmanager.h index d897424e..85c8faff 100644 --- a/src/framework/sound/soundmanager.h +++ b/src/framework/sound/soundmanager.h @@ -56,7 +56,7 @@ private: SoundSourcePtr createSoundSource(const std::string& filename); uint loadFileIntoBuffer(const SoundFilePtr& soundFile); - std::map m_buffers; + std::unordered_map m_buffers; std::vector m_sources; SoundSourcePtr m_musicSource; ALCdevice *m_device; diff --git a/src/framework/ui/uianchorlayout.h b/src/framework/ui/uianchorlayout.h index 35118973..522273c0 100644 --- a/src/framework/ui/uianchorlayout.h +++ b/src/framework/ui/uianchorlayout.h @@ -81,7 +81,7 @@ protected: private: bool updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup, UIWidgetPtr first = nullptr); - std::map m_anchorsGroups; + std::unordered_map m_anchorsGroups; }; #endif diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 0a7165a1..9897c15a 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -1130,6 +1130,18 @@ UIWidgetPtr UIWidget::recursiveGetChildByPos(const Point& childPos, bool wantsPh return nullptr; } +UIWidgetList UIWidget::recursiveGetChildren() +{ + UIWidgetList children; + for(const UIWidgetPtr& child : m_children) { + UIWidgetList subChildren = child->recursiveGetChildren(); + if(!subChildren.empty()) + children.insert(children.end(), subChildren.begin(), subChildren.end()); + children.push_back(child); + } + return children; +} + UIWidgetList UIWidget::recursiveGetChildrenByPos(const Point& childPos) { UIWidgetList children; diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index fe7889ce..de4e5f93 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -147,6 +147,7 @@ public: UIWidgetPtr getChildByIndex(int index); UIWidgetPtr recursiveGetChildById(const std::string& id); UIWidgetPtr recursiveGetChildByPos(const Point& childPos, bool wantsPhantom); + UIWidgetList recursiveGetChildren(); UIWidgetList recursiveGetChildrenByPos(const Point& childPos); UIWidgetList recursiveGetChildrenByMarginPos(const Point& childPos); UIWidgetPtr backwardsGetWidgetById(const std::string& id); diff --git a/src/otclient/creature.h b/src/otclient/creature.h index 7930d0aa..c127b439 100644 --- a/src/otclient/creature.h +++ b/src/otclient/creature.h @@ -79,7 +79,7 @@ public: uint8 getSkull() { return m_skull; } uint8 getShield() { return m_shield; } uint8 getEmblem() { return m_emblem; } - bool getPassable() { return m_passable; } + bool isPassable() { return m_passable; } Point getDrawOffset(); Point getWalkOffset() { return m_walkOffset; } diff --git a/src/otclient/game.cpp b/src/otclient/game.cpp index 5ef4e920..7a5c5dca 100644 --- a/src/otclient/game.cpp +++ b/src/otclient/game.cpp @@ -39,15 +39,16 @@ Game::Game() { resetGameStates(); m_protocolVersion = 0; - setProtocolVersion(PROTOCOL); } void Game::resetGameStates() { + m_online = false; m_denyBotCall = false; m_dead = false; m_mounted = false; m_serverBeat = 50; + m_seq = 0; m_canReportBugs = false; m_fightMode = Otc::FightBalanced; m_chaseMode = Otc::DontChase; @@ -104,15 +105,9 @@ void Game::processLoginWait(const std::string& message, int time) g_lua.callGlobalField("g_game", "onLoginWait", message, time); } -void Game::processGameStart(const LocalPlayerPtr& localPlayer, int serverBeat, bool canReportBugs) +void Game::processGameStart() { - // reset the new game state - resetGameStates(); - - m_localPlayer = localPlayer; - m_localPlayer->setName(m_characterName); - m_serverBeat = serverBeat; - m_canReportBugs = canReportBugs; + m_online = true; // synchronize fight modes with the server m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight); @@ -401,9 +396,9 @@ void Game::processQuestLine(int questId, const std::vectorsetName(m_characterName); + m_protocolGame = ProtocolGamePtr(new ProtocolGame); m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName); m_characterName = characterName; @@ -505,15 +506,10 @@ void Game::autoWalk(const std::vector& dirs) Otc::Direction direction = dirs.front(); TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); - if(toTile && toTile->isWalkable()) + if(toTile && toTile->isWalkable() && !m_localPlayer->isAutoWalking()) m_localPlayer->preWalk(direction); - forceWalk(direction); - - std::vector nextDirs = dirs; - nextDirs.erase(nextDirs.begin()); - if(nextDirs.size() > 0) - m_protocolGame->sendAutoWalk(nextDirs); + m_protocolGame->sendAutoWalk(dirs); g_lua.callGlobalField("g_game", "onAutoWalk", direction); } @@ -737,7 +733,7 @@ void Game::attack(const CreaturePtr& creature) cancelFollow(); setAttackingCreature(creature); - m_protocolGame->sendAttack(creature ? creature->getId() : 0); + m_protocolGame->sendAttack(creature ? creature->getId() : 0, ++m_seq); } void Game::follow(const CreaturePtr& creature) @@ -751,7 +747,7 @@ void Game::follow(const CreaturePtr& creature) cancelAttack(); setFollowingCreature(creature); - m_protocolGame->sendFollow(creature ? creature->getId() : 0); + m_protocolGame->sendFollow(creature ? creature->getId() : 0, ++m_seq); } void Game::cancelAttackAndFollow() @@ -885,7 +881,7 @@ void Game::partyShareExperience(bool active) { if(!canPerformGameAction()) return; - m_protocolGame->sendShareExperience(active, 0); + m_protocolGame->sendShareExperience(active); } void Game::requestOutfit() @@ -1084,15 +1080,16 @@ bool Game::checkBotProtection() bool Game::canPerformGameAction() { // we can only perform game actions if we meet these conditions: + // - the game is online // - the local player exists // - the local player is not dead // - we have a game protocol // - the game protocol is connected // - its not a bot action - return 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::setProtocolVersion(int version) +void Game::setClientVersion(int version) { if(isOnline()) stdext::throw_exception("Unable to change client version while online"); @@ -1147,6 +1144,8 @@ void Game::setProtocolVersion(int version) } m_protocolVersion = version; + + g_lua.callGlobalField("g_game", "onClientVersionChange", version); } void Game::setAttackingCreature(const CreaturePtr& creature) diff --git a/src/otclient/game.h b/src/otclient/game.h index e82a50a6..5cd5ce62 100644 --- a/src/otclient/game.h +++ b/src/otclient/game.h @@ -48,7 +48,7 @@ protected: void processLoginAdvice(const std::string& message); void processLoginWait(const std::string& message, int time); - void processGameStart(const LocalPlayerPtr& localPlayer, int serverBeat, bool canReportBugs); + void processGameStart(); void processGameEnd(); void processDeath(int penality); @@ -56,7 +56,7 @@ protected: void processInventoryChange(int slot, const ItemPtr& item); void processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos); void processCreatureTeleport(const CreaturePtr& creature); - void processAttackCancel(); + void processAttackCancel(uint seq); void processWalkCancel(Otc::Direction direction); // message related @@ -232,17 +232,16 @@ public: void setFeature(Otc::GameFeature feature) { m_features.set(feature, false); } bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); } - void setProtocolVersion(int version); - int getProtocolVersion() { return m_protocolVersion; } + void setClientVersion(int version); + int getClientVersion() { return m_protocolVersion; } void setRSA(const std::string& rsa); std::string getRSA() { return m_rsa; } bool canPerformGameAction(); - bool canReportBugs() { return m_canReportBugs; } bool checkBotProtection(); - bool isOnline() { return !!m_localPlayer; } + bool isOnline() { return m_online; } bool isDead() { return m_dead; } bool isAttacking() { return !!m_attackingCreature; } bool isFollowing() { return !!m_followingCreature; } @@ -253,7 +252,10 @@ public: std::map getVips() { return m_vips; } CreaturePtr getAttackingCreature() { return m_attackingCreature; } CreaturePtr getFollowingCreature() { return m_followingCreature; } + void setServerBeat(int beat) { m_serverBeat = beat; } int getServerBeat() { return m_serverBeat; } + void setCanReportBugs(bool enable) { m_canReportBugs = enable; } + bool canReportBugs() { return m_canReportBugs; } LocalPlayerPtr getLocalPlayer() { return m_localPlayer; } ProtocolGamePtr getProtocolGame() { return m_protocolGame; } std::string getCharacterName() { return m_characterName; } @@ -275,10 +277,12 @@ private: std::map m_containers; std::map m_vips; + bool m_online; bool m_denyBotCall; bool m_dead; bool m_mounted; int m_serverBeat; + uint m_seq; Otc::FightModes m_fightMode; Otc::ChaseModes m_chaseMode; bool m_safeFight; diff --git a/src/otclient/item.cpp b/src/otclient/item.cpp index 825fc650..de3b5678 100644 --- a/src/otclient/item.cpp +++ b/src/otclient/item.cpp @@ -187,8 +187,8 @@ void Item::setOtbId(uint16 id) { if(!g_things.isValidOtbId(id)) id = 0; - auto otbType = g_things.getItemType(id); - m_id = otbType->getClientId(); + auto itemType = g_things.getItemType(id); + m_id = itemType->getClientId(); m_otbId = id; } diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index 45b811e3..83cc5251 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -194,8 +194,8 @@ void OTClient::registerLuaFunctions() 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", "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", "setClientVersion", &Game::setClientVersion, &g_game); g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game); g_lua.bindSingletonFunction("g_game", "getWorldName", &Game::getWorldName, &g_game); g_lua.bindSingletonFunction("g_game", "getGMActions", &Game::getGMActions, &g_game); diff --git a/src/otclient/map.cpp b/src/otclient/map.cpp index e059b7a6..b03006c0 100644 --- a/src/otclient/map.cpp +++ b/src/otclient/map.cpp @@ -518,7 +518,7 @@ void Map::saveOtcm(const std::string& fileName) // version 1 header fin->addString("OTCM 1.0"); // map description fin->addU32(g_things.getDatSignature()); - fin->addU16(g_game.getProtocolVersion()); + fin->addU16(g_game.getClientVersion()); fin->addString(g_game.getWorldName()); // go back and rewrite where the map data starts diff --git a/src/otclient/map.h b/src/otclient/map.h index a505dae4..10b2cd7a 100644 --- a/src/otclient/map.h +++ b/src/otclient/map.h @@ -175,7 +175,7 @@ public: private: TileMap m_tiles; - std::map m_knownCreatures; + std::unordered_map m_knownCreatures; std::array, Otc::MAX_Z+1> m_floorMissiles; std::vector m_animatedTexts; std::vector m_staticTexts; diff --git a/src/otclient/otclient.cpp b/src/otclient/otclient.cpp index 2b04d1c3..b5dced86 100644 --- a/src/otclient/otclient.cpp +++ b/src/otclient/otclient.cpp @@ -81,6 +81,6 @@ void OTClient::terminate() { g_map.terminate(); g_things.terminate(); - g_sprites.termiante(); + g_sprites.terminate(); g_shaders.terminate(); } diff --git a/src/otclient/protocolcodes.h b/src/otclient/protocolcodes.h index f2b2dfbb..a0b3afa7 100644 --- a/src/otclient/protocolcodes.h +++ b/src/otclient/protocolcodes.h @@ -135,7 +135,8 @@ namespace Proto { GameServerMarketEnter = 246, // 944 GameServerMarketLeave = 247, // 944 GameServerMarketDetail = 248, // 944 - GameServerMarketBrowse = 249 // 944 + GameServerMarketBrowse = 249, // 944 + GameServerShowModalDialog = 250 // 960 }; enum ClientOpcodes { diff --git a/src/otclient/protocolgame.cpp b/src/otclient/protocolgame.cpp index 306ede37..4befc7a8 100644 --- a/src/otclient/protocolgame.cpp +++ b/src/otclient/protocolgame.cpp @@ -44,8 +44,7 @@ void ProtocolGame::onConnect() { Protocol::onConnect(); - // must create local player before parsing anything - m_localPlayer = LocalPlayerPtr(new LocalPlayer); + m_localPlayer = g_game.getLocalPlayer(); if(g_game.getFeature(Otc::GameProtocolChecksum)) enableChecksum(); diff --git a/src/otclient/protocolgame.h b/src/otclient/protocolgame.h index b0e9fc30..111919e3 100644 --- a/src/otclient/protocolgame.h +++ b/src/otclient/protocolgame.h @@ -79,14 +79,14 @@ public: void sendOpenPrivateChannel(const std::string& receiver); void sendCloseNpcChannel(); void sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight); - void sendAttack(uint creatureId); - void sendFollow(uint creatureId); + void sendAttack(uint creatureId, uint seq); + void sendFollow(uint creatureId, uint seq); void sendInviteToParty(uint creatureId); void sendJoinParty(uint creatureId); void sendRevokeInvitation(uint creatureId); void sendPassLeadership(uint creatureId); void sendLeaveParty(); - void sendShareExperience(bool active, int unknown); + void sendShareExperience(bool active); void sendOpenOwnChannel(); void sendInviteToOwnChannel(const std::string& name); void sendExcludeFromOwnChannel(const std::string& name); @@ -166,9 +166,9 @@ private: void parsePlayerSkills(const InputMessagePtr& msg); void parsePlayerState(const InputMessagePtr& msg); void parsePlayerCancelAttack(const InputMessagePtr& msg); - void parseSpellDelay(const InputMessagePtr& msg); - void parseSpellGroupDelay(const InputMessagePtr& msg); - void parseMultiUseDelay(const InputMessagePtr& msg); + void parseSpellCooldown(const InputMessagePtr& msg); + void parseSpellGroupCooldown(const InputMessagePtr& msg); + void parseMultiUseCooldown(const InputMessagePtr& msg); void parseCreatureSpeak(const InputMessagePtr& msg); void parseChannelList(const InputMessagePtr& msg); void parseOpenChannel(const InputMessagePtr& msg); @@ -198,15 +198,17 @@ private: void parseChannelEvent(const InputMessagePtr& msg); void parseItemInfo(const InputMessagePtr& msg); void parsePlayerInventory(const InputMessagePtr& msg); + void parseShowModalDialog(const InputMessagePtr& msg); void parseExtendedOpcode(const InputMessagePtr& msg); public: void setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height); int setFloorDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height, int offset, int skip); - void setTileDescription(const InputMessagePtr& msg, Position position); + int setTileDescription(const InputMessagePtr& msg, Position position); Outfit getOutfit(const InputMessagePtr& msg); ThingPtr getThing(const InputMessagePtr& msg); + ThingPtr getMappedThing(const InputMessagePtr& msg); CreaturePtr getCreature(const InputMessagePtr& msg, int type = 0); ItemPtr getItem(const InputMessagePtr& msg, int id = 0); Position getPosition(const InputMessagePtr& msg); diff --git a/src/otclient/protocolgameparse.cpp b/src/otclient/protocolgameparse.cpp index 25070c3f..d78333b0 100644 --- a/src/otclient/protocolgameparse.cpp +++ b/src/otclient/protocolgameparse.cpp @@ -35,13 +35,18 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) { - int opcode = 0; - int prevOpcode = 0; + int opcode = -1; + int prevOpcode = -1; try { while(!msg->eof()) { opcode = msg->getU8(); + if(!m_gameInitialized && opcode >= Proto::GameServerFirstGameOpcode) { + g_game.processGameStart(); + m_gameInitialized = true; + } + // try to parse in lua first int readPos = msg->getReadPos(); if(callLuaField("onOpcode", opcode, msg)) @@ -49,9 +54,6 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) else msg->setReadPos(readPos); // restore read pos - if(!m_gameInitialized && opcode > Proto::GameServerFirstGameOpcode) - g_logger.warning("received a game opcode from the server, but the game is not initialized yet, this is a server side bug"); - switch(opcode) { case Proto::GameServerInitGame: parseInitGame(msg); @@ -70,8 +72,8 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) break; case Proto::GameServerPing: case Proto::GameServerPingBack: - if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) || - (opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953)) + if((opcode == Proto::GameServerPing && g_game.getClientVersion() >= 953) || + (opcode == Proto::GameServerPingBack && g_game.getClientVersion() < 953)) parsePingBack(msg); else parsePing(msg); @@ -279,13 +281,13 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) break; // PROTOCOL>=870 case Proto::GameServerSpellDelay: - parseSpellDelay(msg); + parseSpellCooldown(msg); break; case Proto::GameServerSpellGroupDelay: - parseSpellGroupDelay(msg); + parseSpellGroupCooldown(msg); break; case Proto::GameServerMultiUseDelay: - parseMultiUseDelay(msg); + parseMultiUseCooldown(msg); break; // PROTOCOL>=910 case Proto::GameServerChannelEvent: @@ -306,7 +308,7 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) parseExtendedOpcode(msg); break; default: - stdext::throw_exception("unknown opcode"); + stdext::throw_exception(stdext::format("unhandled opcode %d", (int)opcode)); break; } prevOpcode = opcode; @@ -319,13 +321,13 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) void ProtocolGame::parseInitGame(const InputMessagePtr& msg) { - m_gameInitialized = true; uint playerId = msg->getU32(); int serverBeat = msg->getU16(); bool canReportBugs = msg->getU8(); m_localPlayer->setId(playerId); - g_game.processGameStart(m_localPlayer, serverBeat, canReportBugs); + g_game.setServerBeat(serverBeat); + g_game.setCanReportBugs(canReportBugs); } void ProtocolGame::parseGMActions(const InputMessagePtr& msg) @@ -334,9 +336,9 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg) int numViolationReasons; - if(g_game.getProtocolVersion() >= 860) + if(g_game.getClientVersion() >= 860) numViolationReasons = 20; - else if(g_game.getProtocolVersion() >= 854) + else if(g_game.getClientVersion() >= 854) numViolationReasons = 19; else numViolationReasons = 32; @@ -454,7 +456,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg) Position pos = getPosition(msg); int stackPos = -1; - if(g_game.getProtocolVersion() >= 854) + if(g_game.getClientVersion() >= 854) stackPos = msg->getU8(); ThingPtr thing = getThing(msg); @@ -463,54 +465,54 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg) void ProtocolGame::parseTileTransformThing(const InputMessagePtr& msg) { - Position pos = getPosition(msg); - int stackPos = msg->getU8(); + ThingPtr thing = getMappedThing(msg); + ThingPtr newThing = getThing(msg); - ThingPtr thing = getThing(msg); - if(thing) { - if(!g_map.removeThingByPos(pos, stackPos)) - g_logger.traceError("could not remove thing"); - g_map.addThing(thing, pos, stackPos); + if(!thing) { + g_logger.traceError("no thing"); + return; } + + Position pos = thing->getPosition(); + int stackpos = thing->getStackpos(); + assert(thing->getStackPriority() == newThing->getStackPriority()); + + if(!g_map.removeThing(thing)) { + g_logger.traceError("unable to remove thing"); + return; + } + + g_map.addThing(newThing, pos, stackpos); } void ProtocolGame::parseTileRemoveThing(const InputMessagePtr& msg) { - Position pos = getPosition(msg); - int stackPos = msg->getU8(); + ThingPtr thing = getMappedThing(msg); + if(!thing) { + g_logger.traceError("no thing"); + return; + } - if(!g_map.removeThingByPos(pos, stackPos)) - g_logger.traceError("could not remove thing"); + g_map.removeThing(thing); } void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg) { - Position oldPos = getPosition(msg); - int oldStackpos = msg->getU8(); + ThingPtr thing = getMappedThing(msg); Position newPos = getPosition(msg); - ThingPtr thing = g_map.getThing(oldPos, oldStackpos); - if(!thing) { - g_logger.traceError("could not get thing"); + if(!thing || !thing->isCreature()) { + g_logger.traceError("no creature found to move"); return; } - CreaturePtr creature = thing->asCreature(); - if(!creature) { - g_logger.traceError("thing is not a creature"); - return; - } - - // update map tiles - if(!g_map.removeThing(thing)) - g_logger.traceError("could not remove thing"); int stackPos = -2; - - // older protocols stores creatures in reverse order - if(!g_game.getProtocolVersion() >= 854) + // newer protocols stores creatures in reverse order + if(!g_game.getClientVersion() >= 854) stackPos = -1; + g_map.removeThing(thing); g_map.addThing(thing, newPos, stackPos); } @@ -887,7 +889,7 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg) m_localPlayer->setStamina(stamina); m_localPlayer->setSoul(soul); - if(g_game.getProtocolVersion() >= 910) + if(g_game.getClientVersion() >= 910) m_localPlayer->setSpeed(msg->getU16()); if(g_game.getFeature(Otc::GamePlayerRegenerationTime)) @@ -918,29 +920,28 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg) void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg) { - if(g_game.getProtocolVersion() >= 860) - msg->getU32(); // unknown + uint seq = 0; + if(g_game.getClientVersion() >= 860) + seq = msg->getU32(); - g_game.processAttackCancel(); + g_game.processAttackCancel(seq); } -void ProtocolGame::parseSpellDelay(const InputMessagePtr& msg) +void ProtocolGame::parseSpellCooldown(const InputMessagePtr& msg) { msg->getU16(); // spell id - msg->getU16(); // cooldown - msg->getU8(); // unknown + msg->getU32(); // cooldown } -void ProtocolGame::parseSpellGroupDelay(const InputMessagePtr& msg) +void ProtocolGame::parseSpellGroupCooldown(const InputMessagePtr& msg) { - msg->getU16(); // spell id - msg->getU16(); // cooldown - msg->getU8(); // unknown + msg->getU8(); // group id + msg->getU32(); // cooldown } -void ProtocolGame::parseMultiUseDelay(const InputMessagePtr& msg) +void ProtocolGame::parseMultiUseCooldown(const InputMessagePtr& msg) { - //TODO + msg->getU32(); // cooldown } void ProtocolGame::parseCreatureSpeak(const InputMessagePtr& msg) @@ -1225,18 +1226,36 @@ void ProtocolGame::parseChannelEvent(const InputMessagePtr& msg) void ProtocolGame::parseItemInfo(const InputMessagePtr& msg) { - /*int count = msg.getU16() - 1; - for(int i = 0; i < count; i++) - { - int unknown1 = msg->getU8(); - int unknown2 = msg->getU16(); - std::string unknown3 = msg->getString(); - }*/ + int size = msg->getU8(); + for(int i=0;igetU16(); // id + msg->getU8(); // subtype + msg->getString(); // description + } } void ProtocolGame::parsePlayerInventory(const InputMessagePtr& msg) { - //TODO + int size = msg->getU16(); + for(int i=0;igetU16(); // id + msg->getU8(); // subtype + msg->getU16(); // count + } +} + +void ProtocolGame::parseShowModalDialog(const InputMessagePtr& msg) +{ + msg->getU32(); // id + msg->getString(); // title + msg->getString(); // message + int size = msg->getU8(); + for(int i=0;igetString(); // button name + msg->getU8(); // button value + } + msg->getU8(); // default escape button + msg->getU8(); // default enter button } void ProtocolGame::parseExtendedOpcode(const InputMessagePtr& msg) @@ -1275,51 +1294,41 @@ int ProtocolGame::setFloorDescription(const InputMessagePtr& msg, int x, int y, for(int nx = 0; nx < width; nx++) { for(int ny = 0; ny < height; ny++) { Position tilePos(x + nx + offset, y + ny + offset, z); - - // clean pre stored tiles - g_map.cleanTile(tilePos); - - if(skip == 0) { - int tileOpt = msg->peekU16(); - if(tileOpt >= 0xFF00) - skip = (msg->getU16() & 0xFF); - else { - setTileDescription(msg, tilePos); - skip = (msg->getU16() & 0xFF); - } - } - else + if(skip == 0) + skip = setTileDescription(msg, tilePos); + else { + g_map.cleanTile(tilePos); skip--; + } } } return skip; } -void ProtocolGame::setTileDescription(const InputMessagePtr& msg, Position position) +int ProtocolGame::setTileDescription(const InputMessagePtr& msg, Position position) { - if(g_game.getFeature(Otc::GameEnvironmentEffect)) - msg->getU16(); // environment effect - - g_map.cleanTile(position); - int stackPos = 0; - while(true) { - int inspectItemId = msg->peekU16(); - if(inspectItemId >= 0xFF00) { - return; - } - else { - if(stackPos >= 10) - g_logger.traceError(stdext::format("too many things, stackpos=%d, pos=%s", stackPos, stdext::to_string(position))); + bool gotEffect = 0; + for(int stackPos=0;stackPos<256;stackPos++) { + if(msg->peekU16() >= 0xff00) + return msg->getU16() & 0xff; - ThingPtr thing = getThing(msg); - g_map.addThing(thing, position, -2); + if(g_game.getFeature(Otc::GameEnvironmentEffect) && !gotEffect) { + msg->getU16(); // environment effect + gotEffect = true; + continue; } - stackPos++; + + if(stackPos > 10) + g_logger.traceError(stdext::format("too many things, pos=%s, stackpos=%d", stdext::to_string(position), stackPos)); + + ThingPtr thing = getThing(msg); + g_map.addThing(thing, position, -2); } -} + return 0; +} Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg) { Outfit outfit; @@ -1376,6 +1385,31 @@ ThingPtr ProtocolGame::getThing(const InputMessagePtr& msg) return thing; } +ThingPtr ProtocolGame::getMappedThing(const InputMessagePtr& msg) +{ + ThingPtr thing; + uint16 x = msg->getU16(); + + if(x != 0xffff) { + Position pos; + pos.x = x; + pos.y = msg->getU16(); + pos.z = msg->getU8(); + uint8 stackpos = msg->getU8(); + assert(stackpos != 255); + thing = g_map.getThing(pos, stackpos); + if(!thing) + g_logger.traceError(stdext::format("no thing at pos:%s, stackpos:%d", stdext::to_string(pos), stackpos)); + } else { + uint32 id = msg->getU32(); + thing = g_map.getCreatureById(id); + if(!thing) + g_logger.traceError(stdext::format("no creature with id %u", id)); + } + + return thing; +} + CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) { if(type == 0) @@ -1397,7 +1431,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) uint id = msg->getU32(); int creatureType; - if(g_game.getProtocolVersion() >= 910) + if(g_game.getClientVersion() >= 910) creatureType = msg->getU8(); else { if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId) @@ -1416,8 +1450,13 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) if(id == m_localPlayer->getId()) creature = m_localPlayer; - else if(creatureType == Proto::CreatureTypePlayer) - creature = PlayerPtr(new Player); + else if(creatureType == Proto::CreatureTypePlayer) { + // fixes a bug server side bug where GameInit is not sent and local player id is unknown + if(m_localPlayer->getId() == 0 && name == m_localPlayer->getName()) + creature = m_localPlayer; + else + creature = PlayerPtr(new Player); + } else if(creatureType == Proto::CreatureTypeMonster) creature = MonsterPtr(new Monster); else if(creatureType == Proto::CreatureTypeNpc) @@ -1447,13 +1486,13 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) // emblem is sent only when the creature is not known int emblem = -1; - bool passable = false; + bool unpass = true; if(g_game.getFeature(Otc::GameCreatureEmblems) && !known) emblem = msg->getU8(); - if(g_game.getProtocolVersion() >= 854) - passable = (msg->getU8() == 0); + if(g_game.getClientVersion() >= 854) + unpass = msg->getU8(); if(creature) { creature->setHealthPercent(healthPercent); @@ -1465,7 +1504,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) creature->setShield(shield); if(emblem != -1) creature->setEmblem(emblem); - creature->setPassable(passable); + creature->setPassable(!unpass); if(creature == m_localPlayer && !m_localPlayer->isKnown()) m_localPlayer->setKnown(true); @@ -1481,11 +1520,11 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type) if(creature) creature->turn(direction); - if(g_game.getProtocolVersion() >= 953) { - bool passable = msg->getU8(); + if(g_game.getClientVersion() >= 953) { + bool unpass = msg->getU8(); if(creature) - creature->setPassable(passable); + creature->setPassable(!unpass); } } else { @@ -1502,7 +1541,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id) ItemPtr item = Item::create(id); if(item->getId() == 0) - stdext::throw_exception("unable to create item with invalid id 0"); + stdext::throw_exception(stdext::format("unable to create item with invalid id %d", id)); if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable()) item->setCountOrSubType(msg->getU8()); @@ -1511,7 +1550,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id) if(item->getAnimationPhases() > 1) { // 0xfe => random phase // 0xff => async? - msg->getU8(); + msg->getU8(); // phase } } diff --git a/src/otclient/protocolgamesend.cpp b/src/otclient/protocolgamesend.cpp index f10f9ff7..11f6a0a6 100644 --- a/src/otclient/protocolgamesend.cpp +++ b/src/otclient/protocolgamesend.cpp @@ -51,7 +51,7 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando msg->addU8(Proto::ClientEnterGame); msg->addU16(g_lua.callGlobalField("g_game", "getOs")); - msg->addU16(g_game.getProtocolVersion()); + msg->addU16(g_game.getClientVersion()); int paddingBytes = 128; msg->addU8(0); // first RSA byte must be 0 @@ -509,21 +509,21 @@ void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseMod send(msg); } -void ProtocolGame::sendAttack(uint creatureId) +void ProtocolGame::sendAttack(uint creatureId, uint seq) { OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientAttack); msg->addU32(creatureId); - msg->addU32(0); - msg->addU32(0); + msg->addU32(seq); send(msg); } -void ProtocolGame::sendFollow(uint creatureId) +void ProtocolGame::sendFollow(uint creatureId, uint seq) { OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientFollow); msg->addU32(creatureId); + msg->addU32(seq); send(msg); } @@ -566,14 +566,14 @@ void ProtocolGame::sendLeaveParty() send(msg); } -void ProtocolGame::sendShareExperience(bool active, int unknown) +void ProtocolGame::sendShareExperience(bool active) { OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientShareExperience); msg->addU8(active ? 0x01 : 0x00); - if(g_game.getProtocolVersion() < 910) - msg->addU8(unknown); + if(g_game.getClientVersion() < 910) + msg->addU8(0); send(msg); } diff --git a/src/otclient/spritemanager.cpp b/src/otclient/spritemanager.cpp index ddcea00b..a3a6184e 100644 --- a/src/otclient/spritemanager.cpp +++ b/src/otclient/spritemanager.cpp @@ -34,18 +34,18 @@ SpriteManager::SpriteManager() m_signature = 0; } -void SpriteManager::termiante() +void SpriteManager::terminate() { unload(); } bool SpriteManager::loadSpr(const std::string& file) { + m_spritesCount = 0; + m_signature = 0; + m_loaded = false; try { m_spritesFile = g_resources.openFile(file); - if(!m_spritesFile) - return false; - // cache file buffer to avoid lags from hard drive m_spritesFile->cache(); diff --git a/src/otclient/spritemanager.h b/src/otclient/spritemanager.h index f14b88a6..68e1be72 100644 --- a/src/otclient/spritemanager.h +++ b/src/otclient/spritemanager.h @@ -32,7 +32,7 @@ class SpriteManager public: SpriteManager(); - void termiante(); + void terminate(); bool loadSpr(const std::string& file); void unload(); diff --git a/src/otclient/thing.cpp b/src/otclient/thing.cpp index 310e87b2..1081789c 100644 --- a/src/otclient/thing.cpp +++ b/src/otclient/thing.cpp @@ -66,7 +66,7 @@ ContainerPtr Thing::getParentContainer() int Thing::getStackpos() { if(m_position.x == 65535 && asItem()) // is inside a container - return 0; + return m_position.z; else if(const TilePtr& tile = getTile()) return tile->getThingStackpos(asThing()); else { diff --git a/src/otclient/thingtypemanager.cpp b/src/otclient/thingtypemanager.cpp index e408bc3b..065cbd91 100644 --- a/src/otclient/thingtypemanager.cpp +++ b/src/otclient/thingtypemanager.cpp @@ -59,16 +59,17 @@ void ThingTypeManager::terminate() bool ThingTypeManager::loadDat(const std::string& file) { + m_datLoaded = false; + m_datSignature = 0; try { FileStreamPtr fin = g_resources.openFile(file); - if(!fin) - stdext::throw_exception("unable to open file"); m_datSignature = fin->getU32(); int numThings[ThingLastCategory]; for(int category = 0; category < ThingLastCategory; ++category) { int count = fin->getU16() + 1; + m_thingTypes[category].clear(); m_thingTypes[category].resize(count, m_nullThingType); } @@ -114,9 +115,9 @@ void ThingTypeManager::loadOtb(const std::string& file) m_itemTypes.resize(root->getChildren().size(), m_nullItemType); for(const BinaryTreePtr& node : root->getChildren()) { - ItemTypePtr otbType(new ItemType); - otbType->unserialize(node); - addItemType(otbType); + ItemTypePtr itemType(new ItemType); + itemType->unserialize(node); + addItemType(itemType); } m_otbLoaded = true; @@ -171,25 +172,25 @@ void ThingTypeManager::parseItemType(uint16 id, TiXmlElement* elem) addItemType(newType); } - ItemTypePtr otbType = getItemType(serverId); - otbType->setName(elem->Attribute("name")); + ItemTypePtr itemType = getItemType(serverId); + itemType->setName(elem->Attribute("name")); for(TiXmlElement* attrib = elem->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) { if(attrib->ValueStr() != "attribute") break; if(attrib->Attribute("key") == "description") { - otbType->setDesc(attrib->Attribute("value")); + itemType->setDesc(attrib->Attribute("value")); break; } } } -void ThingTypeManager::addItemType(const ItemTypePtr& otbType) +void ThingTypeManager::addItemType(const ItemTypePtr& itemType) { - uint16 id = otbType->getServerId(); + uint16 id = itemType->getServerId(); if(m_itemTypes.size() <= id) m_itemTypes.resize(id+1, m_nullItemType); - m_itemTypes[id] = otbType; + m_itemTypes[id] = itemType; } const ItemTypePtr& ThingTypeManager::findOtbForClientId(uint16 id) @@ -197,9 +198,9 @@ const ItemTypePtr& ThingTypeManager::findOtbForClientId(uint16 id) if(m_itemTypes.empty()) return m_nullItemType; - for(const ItemTypePtr& otbType : m_itemTypes) { - if(otbType->getClientId() == id) - return otbType; + for(const ItemTypePtr& itemType : m_itemTypes) { + if(itemType->getClientId() == id) + return itemType; } return m_nullItemType; diff --git a/src/otclient/thingtypemanager.h b/src/otclient/thingtypemanager.h index 717e49b5..d57c3f23 100644 --- a/src/otclient/thingtypemanager.h +++ b/src/otclient/thingtypemanager.h @@ -40,7 +40,7 @@ public: void loadXml(const std::string& file); void parseItemType(uint16 id, TiXmlElement *elem); - void addItemType(const ItemTypePtr& otbType); + void addItemType(const ItemTypePtr& itemType); const ItemTypePtr& findOtbForClientId(uint16 id); const ThingTypePtr& getNullThingType() { return m_nullThingType; } diff --git a/src/otclient/tile.cpp b/src/otclient/tile.cpp index e42f6ff9..7ea34841 100644 --- a/src/otclient/tile.cpp +++ b/src/otclient/tile.cpp @@ -392,7 +392,7 @@ bool Tile::isWalkable() return false; if(CreaturePtr creature = thing->asCreature()) { - if(!creature->getPassable()) + if(!creature->isPassable()) return false; } } @@ -405,7 +405,7 @@ bool Tile::isPathable() return false; for(const ThingPtr& thing : m_things) { - if(thing->isNotPathable()) + if(thing->isNotPathable() || thing->isCreature()) return false; } @@ -507,4 +507,13 @@ void Tile::update() if(c != 0) m_minimapColorByte = c; } + + // check stack priorities + // this code exists to find stackpos bugs faster + int lastPriority = 0; + for(const ThingPtr& thing : m_things) { + int priority = thing->getStackPriority(); + assert(lastPriority <= priority); + lastPriority = priority; + } }