diff --git a/modules/client_entergame/characterlist.otui b/modules/client_entergame/characterlist.otui index 745e819b..e93cb385 100644 --- a/modules/client_entergame/characterlist.otui +++ b/modules/client_entergame/characterlist.otui @@ -45,13 +45,17 @@ CharacterWidget < UIWidget MainWindow id: charactersWindow !text: tr('Character List') - size: 250 248 visible: false @onEnter: CharacterList.doLogin() @onEscape: CharacterList.hide(true) @onSetup: | g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self) g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self) + if g_game.getFeature(GamePreviewState) then + self:setSize({width = 350, height = 400}) + else + self:setSize({width = 250, height = 248}) + end TextList id: characters diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 9b64ddfe..f2ea913e 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -59,6 +59,12 @@ local function onCharacterList(protocol, characters, account, otui) loadBox:destroy() loadBox = nil + for _, characterInfo in pairs(characters) do + if characterInfo.previewState and characterInfo.previewState ~= PreviewState.Default then + characterInfo.worldName = characterInfo.worldName .. ', Preview' + end + end + CharacterList.create(characters, account, otui) CharacterList.show() @@ -105,7 +111,7 @@ function EnterGame.init() local port = g_settings.get('port') local autologin = g_settings.getBoolean('autologin') local clientVersion = g_settings.getInteger('client-version') - if clientVersion == 0 then clientVersion = 860 end + if clientVersion == 0 then clientVersion = 1071 end if port == nil or port == 0 then port = 7171 end diff --git a/modules/game_inventory/inventory.lua b/modules/game_inventory/inventory.lua index 89e8f4f9..9cafb6c1 100644 --- a/modules/game_inventory/inventory.lua +++ b/modules/game_inventory/inventory.lua @@ -17,7 +17,10 @@ inventoryButton = nil purseButton = nil function init() - connect(LocalPlayer, { onInventoryChange = onInventoryChange }) + connect(LocalPlayer, { + onInventoryChange = onInventoryChange, + onBlessingsChange = onBlessingsChange + }) connect(g_game, { onGameStart = refresh }) g_keyboard.bindKeyDown('Ctrl+I', toggle) @@ -43,7 +46,10 @@ function init() end function terminate() - disconnect(LocalPlayer, { onInventoryChange = onInventoryChange }) + disconnect(LocalPlayer, { + onInventoryChange = onInventoryChange, + onBlessingsChange = onBlessingsChange + }) disconnect(g_game, { onGameStart = refresh }) g_keyboard.unbindKeyDown('Ctrl+I') @@ -52,6 +58,21 @@ function terminate() inventoryButton:destroy() end +function toggleAdventurerStyle(hasBlessing) + for slot = InventorySlotFirst, InventorySlotLast do + local itemWidget = inventoryPanel:getChildById('slot' .. slot) + if itemWidget then + if hasBlessing then + itemWidget:setBorderWidth(1) + itemWidget:setBorderColor('#F7C80C') + else + itemWidget:setBorderWidth(0) + itemWidget:setBorderColor('white') + end + end + end +end + function refresh() local player = g_game.getLocalPlayer() for i = InventorySlotFirst, InventorySlotPurse do @@ -60,6 +81,7 @@ function refresh() else onInventoryChange(player, i, nil) end + toggleAdventurerStyle(Bit.hasBit(player:getBlessings(), Blessings.Adventurer)) end purseButton:setVisible(g_game.getFeature(GamePurseSlot)) @@ -99,3 +121,10 @@ function onInventoryChange(player, slot, item, oldItem) itemWidget:setItem(nil) end end + +function onBlessingsChange(player, blessings, oldBlessings) + local hasAdventurerBlessing = Bit.hasBit(blessings, Blessings.Adventurer) + if hasAdventurerBlessing ~= Bit.hasBit(oldBlessings, Blessings.Adventurer) then + toggleAdventurerStyle(hasAdventurerBlessing) + end +end \ No newline at end of file diff --git a/modules/gamelib/const.lua b/modules/gamelib/const.lua index 15f940fb..16f1cfaf 100644 --- a/modules/gamelib/const.lua +++ b/modules/gamelib/const.lua @@ -120,6 +120,13 @@ GameSpritesAlphaChannel = 56 GamePremiumExpiration = 57 GameBrowseField = 58 GameEnhancedAnimations = 59 +GameOGLInformation = 60 +GameMessageSizeCheck = 61 +GamePreviewState = 62 +GameLoginPacketEncryption = 63 +GameClientVersion = 64 +GameContentRevision = 65 +GameExperienceBonus = 66 TextColors = { red = '#f55e5e', --'#c83200' @@ -201,7 +208,7 @@ CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618" "88792221429527047321331896351555606801473202394175817" -- set to the latest Tibia.pic signature to make otclient compatible with official tibia -PIC_SIGNATURE = 0x53208400 +PIC_SIGNATURE = 0x542100C1 OsTypes = { Linux = 1, @@ -244,4 +251,20 @@ ExtendedIds = { NeedsUpdate = 7 } +PreviewState = { + Default = 0, + Inactive = 1, + Active = 2 +} + +Blessings = { + None = 0, + Adventurer = 1, + SpiritualShielding = 2, + EmbraceOfTibia = 4, + FireOfSuns = 8, + WisdomOfSolitude = 16, + SparkOfPhoenix = 32 +} + -- @} diff --git a/modules/gamelib/game.lua b/modules/gamelib/game.lua index 28a7bf24..cc283d38 100644 --- a/modules/gamelib/game.lua +++ b/modules/gamelib/game.lua @@ -53,38 +53,39 @@ end function g_game.getSupportedClients() return { - 740, 741, 750, 760, 770, 772, + 740, 741, 750, 760, 770, 772, 780, 781, 782, 790, 792, - 800, 810, 811, 820, 821, 822, - 830, 831, 840, 842, 850, 853, - 854, 855, 857, 860, 861, 862, + 800, 810, 811, 820, 821, 822, + 830, 831, 840, 842, 850, 853, + 854, 855, 857, 860, 861, 862, 870, 871, - 900, 910, 920, 931, 940, 943, - 944, 951, 952, 953, 954, 960, - 961, 963, 970, 971, 972, 973, - 980, 981, 982, 983, 984, 985, + 900, 910, 920, 931, 940, 943, + 944, 951, 952, 953, 954, 960, + 961, 963, 970, 971, 972, 973, + 980, 981, 982, 983, 984, 985, 986, - 1000, 1001, 1002, 1010, 1011, - 1012, 1013, 1020, 1021, 1022, - 1030, 1031, 1032, 1033, 1034, - 1035, 1036, 1037, 1038, 1039, + 1000, 1001, 1002, 1010, 1011, + 1012, 1013, 1020, 1021, 1022, + 1030, 1031, 1032, 1033, 1034, + 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, - 1058, 1059, 1060, 1061 + 1058, 1059, 1060, 1061, 1062, + 1063, 1064, 1070, 1071 } end -- The client version and protocol version where --- unsynchronized for some releases, not sure if this +-- unsynchronized for some releases, not sure if this -- will be the normal standard. --- Client Version: Publicly given version when +-- Client Version: Publicly given version when -- downloading Cipsoft client. --- Protocol Version: Previously was the same as +-- Protocol Version: Previously was the same as -- the client version, but was unsychronized in some -- releases, now it needs to be verified and added here -- if it does not match the client version. @@ -92,7 +93,7 @@ end -- Reason for defining both: The server now requires a -- Client version and Protocol version from the client. --- Important: Use getClientVersion for specific protocol +-- Important: Use getClientVersion for specific protocol -- features to ensure we are using the proper version. function g_game.getClientProtocolVersion(client) diff --git a/modules/gamelib/protocollogin.lua b/modules/gamelib/protocollogin.lua index 1aa6665d..e3d20630 100644 --- a/modules/gamelib/protocollogin.lua +++ b/modules/gamelib/protocollogin.lua @@ -32,23 +32,28 @@ function ProtocolLogin:sendLoginPacket() msg:addU16(g_game.getProtocolVersion()) - if g_game.getClientVersion() >= 980 then + if g_game.getFeature(GameClientVersion) then msg:addU32(g_game.getClientVersion()) end - msg:addU32(g_things.getDatSignature()) + if g_game.getFeature(GameContentRevision) then + msg:addU16(g_things.getContentRevision()) + msg:addU16(0) + else + msg:addU32(g_things.getDatSignature()) + end msg:addU32(g_sprites.getSprSignature()) msg:addU32(PIC_SIGNATURE) - if g_game.getClientVersion() >= 980 then - msg:addU8(0) -- clientType + if g_game.getFeature(GamePreviewState) then + msg:addU8(0) end local offset = msg:getMessageSize() - - if g_game.getClientVersion() >= 770 then + if g_game.getFeature(GameLoginPacketEncryption) then -- first RSA byte must be 0 msg:addU8(0) + -- xtea key self:generateXteaKey() local xteaKey = self:getXteaKey() @@ -74,16 +79,23 @@ function ProtocolLogin:sendLoginPacket() local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset) assert(paddingBytes >= 0) msg:addPaddingBytes(paddingBytes, 0) - if g_game.getClientVersion() >= 770 then + if g_game.getFeature(GameLoginPacketEncryption) then msg:encryptRsa() end + if g_game.getFeature(GameOGLInformation) then + msg:addU8(1) --unknown + msg:addU8(1) --unknown + msg:addString(g_graphics.getRenderer()) + msg:addString(g_graphics.getVersion()) + end + if g_game.getFeature(GameProtocolChecksum) then self:enableChecksum() end self:send(msg) - if g_game.getClientVersion() >= 770 then + if g_game.getFeature(GameLoginPacketEncryption) then self:enableXteaEncryption() end self:recv() @@ -141,7 +153,7 @@ function ProtocolLogin:parseCharacterList(msg) world.worldName = msg:getString() world.worldIp = msg:getString() world.worldPort = msg:getU16() - msg:getU8() -- unknow byte? + world.previewState = msg:getU8() worlds[worldId] = world end @@ -153,6 +165,7 @@ function ProtocolLogin:parseCharacterList(msg) character.worldName = worlds[worldId].worldName character.worldIp = worlds[worldId].worldIp character.worldPort = worlds[worldId].worldPort + character.previewState = worlds[worldId].previewState characters[i] = character end @@ -165,8 +178,8 @@ function ProtocolLogin:parseCharacterList(msg) character.worldIp = iptostring(msg:getU32()) character.worldPort = msg:getU16() - if g_game.getClientVersion() >= 980 then - character.unknown = msg:getU8() + if g_game.getFeature(GamePreviewState) then + character.previewState = msg:getU8() end characters[i] = character diff --git a/src/client/const.h b/src/client/const.h index b147f66b..dd36caf9 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -328,7 +328,8 @@ namespace Otc MessageRVRChannel = 46, MessageRVRAnswer = 47, MessageRVRContinue = 48, - LastMessage = 49, + MessageGameHighlight = 49, + LastMessage = 50, MessageInvalid = 255 }; @@ -390,6 +391,13 @@ namespace Otc GamePremiumExpiration = 57, GameBrowseField = 58, GameEnhancedAnimations = 59, + GameOGLInformation = 60, + GameMessageSizeCheck = 61, + GamePreviewState = 62, + GameLoginPacketEncryption = 63, + GameClientVersion = 64, + GameContentRevision = 65, + GameExperienceBonus = 66, LastGameFeature = 101 }; @@ -450,6 +458,16 @@ namespace Otc PhaseRandom = 254, PhaseAsync = 255 }; + + enum Blessings { + BlessingNone = 0, + BlessingAdventurer = 1, + BlessingSpiritualShielding = 1 << 1, + BlessingEmbraceOfTibia = 1 << 2, + BlessingFireOfSuns = 1 << 3, + BlessingWisdomOfSolitude = 1 << 4, + BlessingSparkOfPhoenix = 1 << 5 + }; } #endif diff --git a/src/client/game.cpp b/src/client/game.cpp index 385fad41..dde46699 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1425,7 +1425,7 @@ void Game::setProtocolVersion(int version) if(isOnline()) stdext::throw_exception("Unable to change protocol version while online"); - if(version != 0 && (version < 740 || version > 1051)) + if(version != 0 && (version < 740 || version > 1071)) stdext::throw_exception(stdext::format("Protocol version %d not supported", version)); m_protocolVersion = version; @@ -1443,7 +1443,7 @@ void Game::setClientVersion(int version) if(isOnline()) stdext::throw_exception("Unable to change client version while online"); - if(version != 0 && (version < 740 || version > 1051)) + if(version != 0 && (version < 740 || version > 1071)) stdext::throw_exception(stdext::format("Client version %d not supported", version)); m_features.reset(); @@ -1452,6 +1452,7 @@ void Game::setClientVersion(int version) if(version >= 770) { enableFeature(Otc::GameLooktypeU16); enableFeature(Otc::GameMessageStatements); + enableFeature(Otc::GameLoginPacketEncryption); } if(version >= 780) { @@ -1475,6 +1476,7 @@ void Game::setClientVersion(int version) if(version >= 841) { enableFeature(Otc::GameChallengeOnLogin); + enableFeature(Otc::GameMessageSizeCheck); } if(version >= 854) { @@ -1523,6 +1525,11 @@ void Game::setClientVersion(int version) enableFeature(Otc::GameAdditionalVipInfo); } + if(version >= 980) { + enableFeature(Otc::GamePreviewState); + enableFeature(Otc::GameClientVersion); + } + if(version >= 981) { enableFeature(Otc::GameLoginPending); enableFeature(Otc::GameNewSpeedLaw); @@ -1538,7 +1545,7 @@ void Game::setClientVersion(int version) enableFeature(Otc::GamePVPMode); } - if (version >= 1035) { + if(version >= 1035) { enableFeature(Otc::GameDoubleSkills); enableFeature(Otc::GameBaseSkillU16); } @@ -1556,6 +1563,18 @@ void Game::setClientVersion(int version) enableFeature(Otc::GameEnhancedAnimations); } + if(version >= 1054) { + enableFeature(Otc::GameExperienceBonus); + } + + if(version >= 1061) { + enableFeature(Otc::GameOGLInformation); + } + + if(version >= 1071) { + enableFeature(Otc::GameContentRevision); + } + m_clientVersion = version; g_lua.callGlobalField("g_game", "onClientVersionChange", version); diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index a09ceb15..d4cbc898 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -31,6 +31,7 @@ LocalPlayer::LocalPlayer() { m_states = 0; m_vocation = 0; + m_blessings = Otc::BlessingNone; m_walkLockExpiration = 0; m_skillsLevel.fill(-1); @@ -546,6 +547,16 @@ void LocalPlayer::setSpells(const std::vector& spells) } } +void LocalPlayer::setBlessings(int blessings) +{ + if(blessings != m_blessings) { + int oldBlessings = m_blessings; + m_blessings = blessings; + + callLuaField("onBlessingsChange", blessings, oldBlessings); + } +} + bool LocalPlayer::hasSight(const Position& pos) { return m_position.isInRange(pos, g_map.getAwareRange().left - 1, g_map.getAwareRange().top - 1); diff --git a/src/client/localplayer.h b/src/client/localplayer.h index cbc1832e..e839d819 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -63,6 +63,7 @@ public: void setRegenerationTime(double regenerationTime); void setOfflineTrainingTime(double offlineTrainingTime); void setSpells(const std::vector& spells); + void setBlessings(int blessings); int getStates() { return m_states; } int getSkillLevel(Otc::Skill skill) { return m_skillsLevel[skill]; } @@ -88,6 +89,7 @@ public: double getOfflineTrainingTime() { return m_offlineTrainingTime; } std::vector getSpells() { return m_spells; } ItemPtr getInventoryItem(Otc::InventorySlot inventory) { return m_inventoryItems[inventory]; } + int getBlessings() { return m_blessings; } bool hasSight(const Position& pos); bool isKnown() { return m_known; } @@ -144,6 +146,7 @@ private: int m_states; int m_vocation; + int m_blessings; double m_health; double m_maxHealth; diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index ebc29073..f8cdcccb 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -63,6 +63,7 @@ void Client::registerLuaFunctions() g_lua.bindSingletonFunction("g_things", "isDatLoaded", &ThingTypeManager::isDatLoaded, &g_things); g_lua.bindSingletonFunction("g_things", "isOtbLoaded", &ThingTypeManager::isOtbLoaded, &g_things); g_lua.bindSingletonFunction("g_things", "getDatSignature", &ThingTypeManager::getDatSignature, &g_things); + g_lua.bindSingletonFunction("g_things", "getContentRevision", &ThingTypeManager::getContentRevision, &g_things); g_lua.bindSingletonFunction("g_things", "getThingType", &ThingTypeManager::getThingType, &g_things); g_lua.bindSingletonFunction("g_things", "getItemType", &ThingTypeManager::getItemType, &g_things); g_lua.bindSingletonFunction("g_things", "getThingTypes", &ThingTypeManager::getThingTypes, &g_things); @@ -630,6 +631,7 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("getTotalCapacity", &LocalPlayer::getTotalCapacity); g_lua.bindClassMemberFunction("getInventoryItem", &LocalPlayer::getInventoryItem); g_lua.bindClassMemberFunction("getVocation", &LocalPlayer::getVocation); + g_lua.bindClassMemberFunction("getBlessings", &LocalPlayer::getBlessings); g_lua.bindClassMemberFunction("isPremium", &LocalPlayer::isPremium); g_lua.bindClassMemberFunction("isKnown", &LocalPlayer::isKnown); g_lua.bindClassMemberFunction("isPreWalking", &LocalPlayer::isPreWalking); diff --git a/src/client/protocolcodes.cpp b/src/client/protocolcodes.cpp index 6990985e..134e24d2 100644 --- a/src/client/protocolcodes.cpp +++ b/src/client/protocolcodes.cpp @@ -28,7 +28,52 @@ std::map messageModesMap; void buildMessageModesMap(int version) { messageModesMap.clear(); - if(version >= 1036) { + + if(version >= 1055) { // might be 1054 + messageModesMap[Otc::MessageNone] = 0; + messageModesMap[Otc::MessageSay] = 1; + messageModesMap[Otc::MessageWhisper] = 2; + messageModesMap[Otc::MessageYell] = 3; + messageModesMap[Otc::MessagePrivateFrom] = 4; + messageModesMap[Otc::MessagePrivateTo] = 5; + messageModesMap[Otc::MessageChannelManagement] = 6; + messageModesMap[Otc::MessageChannel] = 7; + messageModesMap[Otc::MessageChannelHighlight] = 8; + messageModesMap[Otc::MessageSpell] = 9; + //NpcFromStartBlock = 10 + messageModesMap[Otc::MessageNpcFrom] = 11; + messageModesMap[Otc::MessageNpcTo] = 12; + messageModesMap[Otc::MessageGamemasterBroadcast] = 13; + messageModesMap[Otc::MessageGamemasterChannel] = 14; + messageModesMap[Otc::MessageGamemasterPrivateFrom] = 15; + messageModesMap[Otc::MessageGamemasterPrivateTo] = 16; + messageModesMap[Otc::MessageLogin] = 17; + messageModesMap[Otc::MessageWarning] = 18; // Admin + messageModesMap[Otc::MessageGame] = 19; + messageModesMap[Otc::MessageGameHighlight] = 20; + messageModesMap[Otc::MessageFailure] = 21; + messageModesMap[Otc::MessageLook] = 22; + messageModesMap[Otc::MessageDamageDealed] = 23; + messageModesMap[Otc::MessageDamageReceived] = 24; + messageModesMap[Otc::MessageHeal] = 25; + messageModesMap[Otc::MessageExp] = 26; + messageModesMap[Otc::MessageDamageOthers] = 27; + messageModesMap[Otc::MessageHealOthers] = 28; + messageModesMap[Otc::MessageExpOthers] = 29; + messageModesMap[Otc::MessageStatus] = 30; + messageModesMap[Otc::MessageLoot] = 31; + messageModesMap[Otc::MessageTradeNpc] = 32; + messageModesMap[Otc::MessageGuild] = 33; + messageModesMap[Otc::MessagePartyManagement] = 34; + messageModesMap[Otc::MessageParty] = 35; + messageModesMap[Otc::MessageBarkLow] = 36; + messageModesMap[Otc::MessageBarkLoud] = 37; + messageModesMap[Otc::MessageReport] = 38; + messageModesMap[Otc::MessageHotkeyUse] = 39; + messageModesMap[Otc::MessageTutorialHint] = 40; + messageModesMap[Otc::MessageThankyou] = 41; + messageModesMap[Otc::MessageMarket] = 42; + } else if(version >= 1036) { for(int i = Otc::MessageNone; i <= Otc::MessageBeyondLast; ++i) { if(i >= Otc::MessageNpcTo) messageModesMap[i] = i + 1; diff --git a/src/client/protocolcodes.h b/src/client/protocolcodes.h index b4c0482b..69780587 100644 --- a/src/client/protocolcodes.h +++ b/src/client/protocolcodes.h @@ -107,6 +107,8 @@ namespace Proto { GameServerCreatureType = 149, GameServerEditText = 150, GameServerEditList = 151, + GameServerBlessings = 156, + GameServerPreset = 157, GameServerPremiumTrigger = 158, // 1038 GameServerPlayerDataBasic = 159, // 950 GameServerPlayerData = 160, @@ -130,6 +132,8 @@ namespace Proto { GameServerTextMessage = 180, GameServerCancelWalk = 181, GameServerWalkWait = 182, + GameServerUnjustifiedStats = 183, + GameServerPvpSituations = 184, GameServerFloorChangeUp = 190, GameServerFloorChangeDown = 191, GameServerChooseOutfit = 200, diff --git a/src/client/protocolgame.cpp b/src/client/protocolgame.cpp index c9d194af..0d3ba266 100644 --- a/src/client/protocolgame.cpp +++ b/src/client/protocolgame.cpp @@ -56,7 +56,7 @@ void ProtocolGame::onRecv(const InputMessagePtr& inputMessage) if(m_firstRecv) { m_firstRecv = false; - if(g_game.getClientVersion() >= 841) { // not sure since which version this is, but it seems to be after 8.40 + if(g_game.getFeature(Otc::GameMessageSizeCheck)) { int size = inputMessage->getU16(); if(size != inputMessage->getUnreadSize()) { g_logger.traceError("invalid message size"); diff --git a/src/client/protocolgame.h b/src/client/protocolgame.h index 34f31620..7be5b44c 100644 --- a/src/client/protocolgame.h +++ b/src/client/protocolgame.h @@ -128,6 +128,10 @@ public: void addPosition(const OutputMessagePtr& msg, const Position& position); private: + void parseBlessings(const InputMessagePtr& msg); + void parseUnjustifiedStats(const InputMessagePtr& msg); + void parsePvpSituations(const InputMessagePtr& msg); + void parsePreset(const InputMessagePtr& msg); void parseCreatureType(const InputMessagePtr& msg); void parsePlayerHelpers(const InputMessagePtr& msg); void parseMessage(const InputMessagePtr& msg); diff --git a/src/client/protocolgameparse.cpp b/src/client/protocolgameparse.cpp index 4a64249d..a07c766b 100644 --- a/src/client/protocolgameparse.cpp +++ b/src/client/protocolgameparse.cpp @@ -340,6 +340,19 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg) case Proto::GameServerCreatureType: parseCreatureType(msg); break; + // PROTOCOL>=1055 + case Proto::GameServerBlessings: + parseBlessings(msg); + break; + case Proto::GameServerUnjustifiedStats: + parseUnjustifiedStats(msg); + break; + case Proto::GameServerPvpSituations: + parsePvpSituations(msg); + break; + case Proto::GameServerPreset: + parsePreset(msg); + break; // otclient ONLY case Proto::GameServerExtendedOpcode: parseExtendedOpcode(msg); @@ -372,6 +385,9 @@ void ProtocolGame::parseLogin(const InputMessagePtr& msg) } bool canReportBugs = msg->getU8(); + msg->getU8(); // can change pvp framing option + msg->getU8(); // expert mode enabled + m_localPlayer->setId(playerId); g_game.setServerBeat(serverBeat); g_game.setCanReportBugs(canReportBugs); @@ -396,6 +412,34 @@ void ProtocolGame::parseEnterGame(const InputMessagePtr& msg) } } +void ProtocolGame::parseBlessings(const InputMessagePtr& msg) +{ + uint16 blessings = msg->getU16(); + m_localPlayer->setBlessings(blessings); +} + +void ProtocolGame::parsePreset(const InputMessagePtr& msg) +{ + uint16 preset = msg->getU32(); +} + +void ProtocolGame::parseUnjustifiedStats(const InputMessagePtr& msg) +{ + // Unjustified Kills display since 10.55 + msg->getU8(); + msg->getU8(); + msg->getU8(); + msg->getU8(); + msg->getU8(); + msg->getU8(); + msg->getU8(); +} + +void ProtocolGame::parsePvpSituations(const InputMessagePtr& msg) +{ + msg->getU8(); // amount of open pvp situations +} + void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg) { uint id = msg->getU32(); @@ -468,6 +512,7 @@ void ProtocolGame::parseChallenge(const InputMessagePtr& msg) { uint timestamp = msg->getU32(); uint8 random = msg->getU8(); + sendLoginPacket(timestamp, random); } @@ -1038,7 +1083,7 @@ void ProtocolGame::parsePlayerInfo(const InputMessagePtr& msg) bool premium = msg->getU8(); // premium int vocation = msg->getU8(); // vocation if(g_game.getFeature(Otc::GamePremiumExpiration)) - int premiumEx = msg->getU32(); // premium expiration + int premiumEx = msg->getU32(); // premium expiration used for premium advertisement int spellCount = msg->getU16(); std::vector spells; @@ -1081,6 +1126,10 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg) double level = msg->getU16(); double levelPercent = msg->getU8(); + + if(g_game.getFeature(Otc::GameExperienceBonus)) + double experienceBonus = msg->getDouble(); + double mana; double maxMana; @@ -1145,7 +1194,7 @@ void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg) int baseLevel; if(g_game.getFeature(Otc::GameSkillsBase)) - if (g_game.getFeature(Otc::GameBaseSkillU16)) + if(g_game.getFeature(Otc::GameBaseSkillU16)) baseLevel = msg->getU16(); else baseLevel = msg->getU8(); @@ -1699,7 +1748,7 @@ void ProtocolGame::parseChangeMapAwareRange(const InputMessagePtr& msg) void ProtocolGame::parseCreaturesMark(const InputMessagePtr& msg) { int len; - if (g_game.getClientVersion() >= 1035) { + if(g_game.getClientVersion() >= 1035) { len = 1; } else { len = msg->getU8(); diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index 88906f1d..a32ee396 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -56,17 +56,20 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando msg->addU16(g_game.getOs()); msg->addU16(g_game.getProtocolVersion()); - if(g_game.getClientVersion() >= 980) { + if(g_game.getFeature(Otc::GameClientVersion)) msg->addU32(g_game.getClientVersion()); - msg->addU8(0); // preview state - } - int offset = msg->getMessageSize(); + if(g_game.getFeature(Otc::GameContentRevision)) + msg->addU16(g_things.getContentRevision()); - msg->addU8(0); // first RSA byte must be 0 + if(g_game.getFeature(Otc::GamePreviewState)) + msg->addU8(0); + + int offset = msg->getMessageSize(); + // first RSA byte must be 0 + msg->addU8(0); - if(g_game.getClientVersion() >= 770) - { + if(g_game.getFeature(Otc::GameLoginPacketEncryption)) { // xtea key generateXteaKey(); msg->addU32(m_xteaKey[0]); @@ -99,7 +102,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando msg->addPaddingBytes(paddingBytes); // encrypt with RSA - if(g_game.getClientVersion() >= 770) + if(g_game.getFeature(Otc::GameLoginPacketEncryption)) msg->encryptRsa(); if(g_game.getFeature(Otc::GameProtocolChecksum)) @@ -107,7 +110,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando send(msg); - if(g_game.getClientVersion() >= 770) + if(g_game.getFeature(Otc::GameLoginPacketEncryption)) enableXteaEncryption(); } diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index 7e5f5de3..b0524e74 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -274,50 +274,59 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS stdext::throw_exception(stdext::format("corrupt data (id: %d, category: %d, count: %d, lastAttr: %d)", m_id, m_category, count, attr)); - uint8 width = fin->getU8(); - uint8 height = fin->getU8(); - m_size = Size(width, height); - if(width > 1 || height > 1) { - m_realSize = fin->getU8(); - m_exactSize = std::min(m_realSize, std::max(width * 32, height * 32)); - } - else - m_exactSize = 32; - - m_layers = fin->getU8(); - m_numPatternX = fin->getU8(); - m_numPatternY = fin->getU8(); - if(g_game.getClientVersion() >= 755) - m_numPatternZ = fin->getU8(); - else - m_numPatternZ = 1; - m_animationPhases = fin->getU8(); + uint8 frames = 1; + if(category == ThingCategoryCreature && g_game.getClientVersion() >= 1057) + frames = fin->getU8(); + + for(int i = 0; i < frames; ++i) { + uint8 frameGroup = FrameGroupDefault; + if(category == ThingCategoryCreature && g_game.getClientVersion() >= 1057) { + frameGroup = fin->getU8(); + } - if(g_game.getFeature(Otc::GameEnhancedAnimations)) { - if(m_animationPhases > 1) { - m_animation.async = fin->getU8() == 0; - m_animation.loopCount = fin->get32(); - m_animation.startIndex = fin->getU8(); + uint8 width = fin->getU8(); + uint8 height = fin->getU8(); + m_size = Size(width, height); + if(width > 1 || height > 1) { + m_realSize = fin->getU8(); + m_exactSize = std::min(m_realSize, std::max(width * 32, height * 32)); + } + else + m_exactSize = 32; - for(int i = 0; i < m_animationPhases; i++) { - int minDuration = fin->getU32(); - int maxDuration = fin->getU32(); + m_layers = fin->getU8(); + m_numPatternX = fin->getU8(); + m_numPatternY = fin->getU8(); + if(g_game.getClientVersion() >= 755) + m_numPatternZ = fin->getU8(); + else + m_numPatternZ = 1; + m_animationPhases = fin->getU8(); + + if(g_game.getFeature(Otc::GameEnhancedAnimations)) { + if(m_animationPhases > 1) { + m_animation.async = fin->getU8() == 0; + m_animation.loopCount = fin->get32(); + m_animation.startIndex = fin->getU8(); + + for (int i = 0; i < m_animationPhases; i++) { + int minDuration = fin->getU32(); + int maxDuration = fin->getU32(); - m_animation.frames.push_back(std::make_tuple(minDuration, maxDuration)); + m_animation.frames.push_back(std::make_tuple(minDuration, maxDuration)); + } } } - } - int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases; + int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases; - // if(totalSprites == 0) - // stdext::throw_exception("a thing type has no sprites"); - if(totalSprites > 4096) - stdext::throw_exception("a thing type has more than 4096 sprites"); + if(totalSprites > 4096) + stdext::throw_exception("a thing type has more than 4096 sprites"); - m_spritesIndex.resize(totalSprites); - for(int i = 0; i < totalSprites; i++) - m_spritesIndex[i] = g_game.getFeature(Otc::GameSpritesU32) ? fin->getU32() : fin->getU16(); + m_spritesIndex.resize(totalSprites); + for(int i = 0; i < totalSprites; i++) + m_spritesIndex[i] = g_game.getFeature(Otc::GameSpritesU32) ? fin->getU32() : fin->getU16(); + } m_textures.resize(m_animationPhases); m_texturesFramesRects.resize(m_animationPhases); diff --git a/src/client/thingtype.h b/src/client/thingtype.h index 10363587..11a85f20 100644 --- a/src/client/thingtype.h +++ b/src/client/thingtype.h @@ -32,6 +32,12 @@ #include #include +enum FrameGroup : uint8 { + FrameGroupIdle = 0, + FrameGroupMoving, + FrameGroupDefault = FrameGroupIdle +}; + enum ThingCategory : uint8 { ThingCategoryItem = 0, ThingCategoryCreature, diff --git a/src/client/thingtypemanager.cpp b/src/client/thingtypemanager.cpp index eaec7e82..a1dc5805 100644 --- a/src/client/thingtypemanager.cpp +++ b/src/client/thingtypemanager.cpp @@ -42,6 +42,7 @@ void ThingTypeManager::init() m_nullThingType = ThingTypePtr(new ThingType); m_nullItemType = ItemTypePtr(new ItemType); m_datSignature = 0; + m_contentRevision = 0; m_otbMinorVersion = 0; m_otbMajorVersion = 0; m_datLoaded = false; @@ -100,12 +101,14 @@ bool ThingTypeManager::loadDat(std::string file) { m_datLoaded = false; m_datSignature = 0; + m_contentRevision = 0; try { file = g_resources.guessFilePath(file, "dat"); FileStreamPtr fin = g_resources.openFile(file); m_datSignature = fin->getU32(); + m_contentRevision = static_cast(m_datSignature); for(int category = 0; category < ThingLastCategory; ++category) { int count = fin->getU16() + 1; diff --git a/src/client/thingtypemanager.h b/src/client/thingtypemanager.h index 641f784f..a9b42894 100644 --- a/src/client/thingtypemanager.h +++ b/src/client/thingtypemanager.h @@ -66,6 +66,7 @@ public: uint32 getDatSignature() { return m_datSignature; } uint32 getOtbMajorVersion() { return m_otbMajorVersion; } uint32 getOtbMinorVersion() { return m_otbMinorVersion; } + uint16 getContentRevision() { return m_contentRevision; } bool isDatLoaded() { return m_datLoaded; } bool isXmlLoaded() { return m_xmlLoaded; } @@ -89,6 +90,7 @@ private: uint32 m_otbMinorVersion; uint32 m_otbMajorVersion; uint32 m_datSignature; + uint16 m_contentRevision; }; extern ThingTypeManager g_things;