Esempio n. 1
0
        private void ParseOpenContainer(Internal.CommunicationStream message)
        {
            byte   containerId     = message.ReadUnsignedByte();
            var    objectIcon      = ProtocolGameExtentions.ReadObjectInstance(message);
            string name            = message.ReadString();
            byte   nOfSlotsPerPage = message.ReadUnsignedByte(); // capacity of shown view
            bool   isSubContainer  = message.ReadBoolean();

            bool canUseDepotSearch    = false;
            bool isDragAndDropEnabled = true;
            bool isPaginationEnabled  = false;
            int  nOfTotalObjects;
            int  indexOfFirstObject = 0;
            int  nOfContentObjects; // objects in the current shown view //

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameContainerPagination))
            {
                if (OpenTibiaUnity.GameManager.ClientVersion >= 1220)
                {
                    canUseDepotSearch = message.ReadBoolean();
                }

                isDragAndDropEnabled = message.ReadBoolean();
                isPaginationEnabled  = message.ReadBoolean();
                nOfTotalObjects      = message.ReadUnsignedShort();
                indexOfFirstObject   = message.ReadUnsignedShort();
                nOfContentObjects    = message.ReadUnsignedByte();

                if (nOfContentObjects > nOfSlotsPerPage)
                {
                    throw new System.Exception("ProtocolGame.ParseOpenContainer: Number of content objects " + nOfContentObjects + " exceeds number of slots per page " + nOfSlotsPerPage);
                }

                if (nOfContentObjects > nOfTotalObjects)
                {
                    throw new System.Exception("ProtocolGame.ParseOpenContainer: Number of content objects " + nOfContentObjects + " exceeds number of total objects " + nOfTotalObjects);
                }
            }
            else
            {
                nOfContentObjects = message.ReadUnsignedByte();
                nOfTotalObjects   = nOfContentObjects;

                if (nOfContentObjects > nOfSlotsPerPage)
                {
                    throw new System.Exception("ProtocolGame.ParseOpenContainer: Number of content objects " + nOfContentObjects + " exceeds the capaciy " + nOfSlotsPerPage);
                }
            }

            var containerView = ContainerStorage.CreateContainerView(containerId, objectIcon, name, isSubContainer,
                                                                     isDragAndDropEnabled, isPaginationEnabled, nOfSlotsPerPage,
                                                                     nOfTotalObjects - nOfContentObjects, indexOfFirstObject, nOfContentObjects);

            for (int i = 0; i < nOfContentObjects; i++)
            {
                containerView.AddObject(indexOfFirstObject + i, ProtocolGameExtentions.ReadObjectInstance(message));
            }
        }
Esempio n. 2
0
 private void ParseSetStoreDeepLink(Internal.CommunicationStream message)
 {
     message.ReadUnsignedByte();
     if (OpenTibiaUnity.GameManager.ClientVersion >= 1200)
     {
         message.ReadUnsignedByte();
         message.ReadUnsignedByte();
     }
 }
Esempio n. 3
0
 private void ParsePreyPrices(Internal.CommunicationStream message)
 {
     message.ReadUnsignedInt(); // rerollPrice in gold
     if (OpenTibiaUnity.GameManager.ClientVersion >= 1190)
     {
         message.ReadUnsignedByte(); // unknown
         message.ReadUnsignedByte(); // selectCreatureDirectly in preyWildCards
     }
 }
Esempio n. 4
0
        private void ParseAmbientLight(Internal.CommunicationStream message)
        {
            int intensity = message.ReadUnsignedByte();
            int rawColor  = message.ReadUnsignedByte();

            var color = Colors.ColorFrom8Bit(rawColor);

            WorldMapStorage.SetAmbientLight(color, intensity);
        }
Esempio n. 5
0
        private void ParseBuddyState(Internal.CommunicationStream message)
        {
            uint creatureId = message.ReadUnsignedInt();
            byte state      = 1;

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameLoginPending))
            {
                state = message.ReadUnsignedByte();
            }
        }
Esempio n. 6
0
        private void ReadCyclopediaCharacterInfoRecentDeaths(Internal.CommunicationStream message)
        {
            ushort count = message.ReadUnsignedShort();

            for (int i = 0; i < count; i++)
            {
                uint   time  = message.ReadUnsignedInt();
                string cause = message.ReadString();
            }
        }
Esempio n. 7
0
        private void ParseMarketStatistics(Internal.CommunicationStream message)
        {
            int count = message.ReadUnsignedShort();

            for (int i = 0; i < count; i++)
            {
                ushort objectId    = message.ReadUnsignedShort();
                uint   objectPrice = message.ReadUnsignedInt();
            }
        }
Esempio n. 8
0
 private void ParseUnjustifiedPoints(Internal.CommunicationStream message)
 {
     message.ReadUnsignedByte(); // dailyProgress
     message.ReadUnsignedByte(); // dailyRemaining
     message.ReadUnsignedByte(); // weeklyProgress
     message.ReadUnsignedByte(); // weeklyRemaining
     message.ReadUnsignedByte(); // monthlyProgress
     message.ReadUnsignedByte(); // monthlyRemaining
     message.ReadUnsignedByte(); // skullDuration
 }
Esempio n. 9
0
        private void ParsePremiumTrigger(Internal.CommunicationStream message)
        {
            int triggers = message.ReadUnsignedByte();

            for (int i = 0; i < triggers; i++)
            {
                message.ReadUnsignedByte(); // trigger
                // TODO
            }
        }
Esempio n. 10
0
        private void ParseGraphicalEffects(Internal.CommunicationStream message)
        {
            var initialPosition = message.ReadPosition();
            int modifier        = message.ReadUnsignedByte();

            Appearances.AppearanceInstance effect = null;
            byte effectId = 0;

            var    fromPosition    = initialPosition;
            ushort unclampedOffset = 0;

            while (modifier != 0)
            {
                if (modifier == 1)
                {
                    unclampedOffset = message.ReadUnsignedShort();
                    int offset = unclampedOffset % 256;
                    fromPosition.x += offset % Constants.MapSizeX;
                    fromPosition.y += offset / Constants.MapSizeX;
                }

                // the effect is far away from the initial position
                while (fromPosition.x - initialPosition.x >= Constants.MapSizeX)
                {
                    fromPosition.x -= Constants.MapSizeX;
                    fromPosition.y++;
                }

                if ((unclampedOffset >= 1024 && modifier == 1) || (modifier & 3) == 0)
                {
                    effectId = message.ReadUnsignedByte();
                    int deltaX     = message.ReadSignedByte();
                    int deltaY     = message.ReadSignedByte();
                    var toPosition = new Vector3Int(fromPosition.x + deltaX, fromPosition.y + deltaY, fromPosition.z);
                    effect = AppearanceStorage.CreateMissileInstance(effectId, fromPosition, toPosition);
                }
                else if (unclampedOffset >= 768 || modifier == 3)
                {
                    effectId = message.ReadUnsignedByte();
                    effect   = AppearanceStorage.CreateEffectInstance(effectId);
                }
                else
                {
                    throw new System.NotImplementedException();
                }

                if (!effect)
                {
                    throw new System.Exception("ProtocolGame.ParseGraphicalEffects: Unknown effect: " + effectId);
                }

                WorldMapStorage.AppendEffect(fromPosition, effect);
                modifier = message.ReadUnsignedByte();
            }
        }
Esempio n. 11
0
        private void ParseStoreOffers(Internal.CommunicationStream message)
        {
            var    gameManager  = OpenTibiaUnity.GameManager;
            string categoryName = message.ReadString();

            if (gameManager.ClientVersion >= 1180)
            {
                uint selectedOfferId = message.ReadUnsignedInt();
                var  sortType        = message.ReadEnum <StoreOfferSortType>();
                int  filterCount     = message.ReadUnsignedByte();
                for (int i = 0; i < filterCount; i++)
                {
                    string filter = message.ReadString();
                }

                if (gameManager.ClientVersion >= 1185)
                {
                    // if a filter is not included, then if "Show all" is not selected
                    // the offer will be hidden until then..
                    // if the offer has no filter, then this value won't affect it

                    int shownFiltersCount = message.ReadUnsignedShort();
                    for (int i = 0; i < shownFiltersCount; i++)
                    {
                        int filterIndex = message.ReadUnsignedByte();
                    }
                }
            }

            var storeCategory = OpenTibiaUnity.StoreStorage.FindCategory(categoryName);

            int offerCount = message.ReadUnsignedShort();

            for (int i = 0; i < offerCount; i++)
            {
                var offer = ReadStoreOffer(message);

                // server may be sending information about non-existant category
                if (storeCategory != null)
                {
                    storeCategory.AddOffer(offer);
                }
            }

            if (gameManager.ClientVersion >= 1180 && categoryName == Constants.StoreHomeCategoryName)
            {
                byte featuredOfferCount = message.ReadUnsignedByte();
                for (int i = 0; i < featuredOfferCount; i++)
                {
                    var storeFeaturedOffer = ReadStoreFeaturedOffer(message);
                }

                byte unknown = message.ReadUnsignedByte();
            }
        }
Esempio n. 12
0
        public void ParseKillTracking(Internal.CommunicationStream message)
        {
            string name      = message.ReadString();
            var    outfit    = ProtocolGameExtentions.ReadCreatureOutfit(message);
            int    lootCount = message.ReadUnsignedByte();

            for (int i = 0; i < lootCount; i++)
            {
                var @object = ProtocolGameExtentions.ReadObjectInstance(message);
            }
        }
Esempio n. 13
0
        private void ParseObjectInfo(Internal.CommunicationStream message)
        {
            int objectCount = message.ReadUnsignedByte();

            for (int i = 0; i < objectCount; i++)
            {
                ushort objectId = message.ReadUnsignedShort();
                byte   data     = message.ReadUnsignedByte();
                string name     = message.ReadString();
            }
        }
Esempio n. 14
0
        private int ReadField(Internal.CommunicationStream message, int x, int y, int z)
        {
            var mapPosition      = new UnityEngine.Vector3Int(x, y, z);
            var absolutePosition = WorldMapStorage.ToAbsolute(mapPosition);

            int  typeOrId;
            int  stackPos  = 0;
            bool gotEffect = false;

            while (true)
            {
                typeOrId = message.ReadUnsignedShort();
                if (typeOrId >= 65280)
                {
                    break;
                }

                if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameEnvironmentEffect) && !gotEffect)
                {
                    var effectObject = AppearanceStorage.CreateEnvironmentalEffect((uint)typeOrId);
                    WorldMapStorage.SetEnvironmentalEffect(mapPosition, effectObject);
                    gotEffect = true;
                    continue;
                }

                if (typeOrId == AppearanceInstance.UnknownCreature || typeOrId == AppearanceInstance.OutdatedCreature ||
                    typeOrId == AppearanceInstance.Creature)
                {
                    var creature = ReadCreatureInstance(message, typeOrId, absolutePosition);

                    var @object = AppearanceStorage.CreateObjectInstance(AppearanceInstance.Creature, creature.Id);
                    if (stackPos < Constants.MapSizeW)
                    {
                        WorldMapStorage.AppendObject(mapPosition, @object);
                    }
                }
                else
                {
                    var @object = ReadObjectInstance(message, typeOrId);
                    if (stackPos < Constants.MapSizeW)
                    {
                        WorldMapStorage.AppendObject(mapPosition, @object);
                    }
                    else
                    {
                        throw new System.Exception("ProtocolGameUtility.ReadField: Expected creatures but received regular object.");
                    }
                }

                stackPos++;
            }

            return(typeOrId - 65280);
        }
Esempio n. 15
0
 private StoreOffer ReadStoreOffer(Internal.CommunicationStream message)
 {
     if (OpenTibiaUnity.GameManager.ClientVersion >= 1180)
     {
         return(ReadExtendedStoreOffer(message));
     }
     else
     {
         return(ReadLegacyStoreOffer(message));
     }
 }
Esempio n. 16
0
        private void ParseUpdateLootContainers(Internal.CommunicationStream message)
        {
            byte unknown = message.ReadUnsignedByte();
            int  count   = message.ReadUnsignedByte();

            for (int i = 0; i < count; i++)
            {
                var    type     = message.ReadEnum <ObjectCategory>();
                ushort objectId = message.ReadUnsignedShort();
            }
        }
Esempio n. 17
0
        private void ReadCyclopediaCharacterInfoRecentPvpKills(Internal.CommunicationStream message)
        {
            ushort count = message.ReadUnsignedShort();

            for (int i = 0; i < count; i++)
            {
                uint   time        = message.ReadUnsignedInt();
                string description = message.ReadString();
                var    status      = message.ReadEnum <CyclopediaPvpKillStatus>();
            }
        }
Esempio n. 18
0
        private void ParsePlayerInventory(Internal.CommunicationStream message)
        {
            //List<KeyValuePair<ushort, ushort>> items = new List<KeyValuePair<ushort, ushort>>();
            ushort size = message.ReadUnsignedShort();

            for (int i = 0; i < size; i++)
            {
                ushort id      = message.ReadUnsignedShort();
                byte   subType = message.ReadUnsignedByte();
                ushort count   = message.ReadUnsignedShort();
            }
        }
Esempio n. 19
0
        private void ParseMapLeftRow(Internal.CommunicationStream message)
        {
            UnityEngine.Vector3Int position = WorldMapStorage.Position;
            position.x--;

            WorldMapStorage.Position = position;
            MiniMapStorage.Position  = position;
            WorldMapStorage.ScrollMap(1, 0);
            WorldMapStorage.InvalidateOnscreenMessages();
            ProtocolGameExtentions.ReadArea(message, 0, 0, 0, Constants.MapSizeY - 1);
            WorldMapStorage.CacheRefresh = true;
        }
Esempio n. 20
0
        private void ParseNPCOffer(Internal.CommunicationStream message)
        {
            string npcName = null;

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameNameOnNpcTrade))
            {
                npcName = message.ReadString();
            }

            var buyObjects  = new List <Trade.TradeObjectRef>();
            var sellObjects = new List <Trade.TradeObjectRef>();

            ushort currencyObjectId = 0;

            if (OpenTibiaUnity.GameManager.ClientVersion >= 1203)
            {
                currencyObjectId = message.ReadUnsignedShort();
            }

            int listCount;

            if (OpenTibiaUnity.GameManager.ClientVersion >= 900)
            {
                listCount = message.ReadUnsignedShort();
            }
            else
            {
                listCount = message.ReadUnsignedByte();
            }

            for (int i = 0; i < listCount; i++)
            {
                ushort objectId   = message.ReadUnsignedShort();
                ushort objectData = message.ReadUnsignedByte();

                string name      = message.ReadString();
                uint   weight    = message.ReadUnsignedInt();
                uint   buyPrice  = message.ReadUnsignedInt();
                uint   sellPrice = message.ReadUnsignedInt();

                if (buyPrice > 0)
                {
                    buyObjects.Add(new Trade.TradeObjectRef(objectId, objectData, name, buyPrice, weight));
                }

                if (sellPrice > 0)
                {
                    sellObjects.Add(new Trade.TradeObjectRef(objectId, objectData, name, sellPrice, weight));
                }
            }

            OpenTibiaUnity.GameManager.onRequestNPCTrade.Invoke(npcName, buyObjects, sellObjects);
        }
Esempio n. 21
0
        private void ParseChannels(Internal.CommunicationStream message)
        {
            int count = message.ReadUnsignedByte();
            List <Chat.Channel> channels = new List <Chat.Channel>();

            for (int i = 0; i < count; i++)
            {
                int    id   = message.ReadUnsignedShort();
                string name = message.ReadString();
                channels.Add(new Chat.Channel(id, name, MessageModeType.None));
            }
        }
Esempio n. 22
0
        private void ParseMapBottomRow(Internal.CommunicationStream message)
        {
            UnityEngine.Vector3Int position = WorldMapStorage.Position;
            position.y++;

            WorldMapStorage.Position = position;
            MiniMapStorage.Position  = position;
            WorldMapStorage.ScrollMap(0, -1);
            WorldMapStorage.InvalidateOnscreenMessages();
            ReadArea(message, 0, Constants.MapSizeY - 1, Constants.MapSizeX - 1, Constants.MapSizeY - 1);
            WorldMapStorage.CacheRefresh = true;
        }
Esempio n. 23
0
        private ObjectInstance ReadObjectInstance(Internal.CommunicationStream message, int id = -1)
        {
            if (id == -1)
            {
                id = message.ReadUnsignedShort();
            }

            if (id == 0)
            {
                return(null);
            }
            else if (id <= AppearanceInstance.Creature)
            {
                throw new System.Exception("ProtocolGameUtility.ReadObjectInstance: Invalid type (id = " + id + ")");
            }

            var @object = AppearanceStorage.CreateObjectInstance((uint)id, 0);

            if (!@object)
            {
                throw new System.Exception("ProtocolGameUtility.ReadObjectInstance: Invalid instance with id " + id);
            }

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameObjectMarks))
            {
                @object.Marks.SetMark(MarkType.Permenant, message.ReadUnsignedByte());
            }

            if (@object.Type.IsStackable || @object.Type.IsFluidContainer || @object.Type.IsSplash)
            {
                @object.Data = message.ReadUnsignedByte();
            }

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameItemAnimationPhase))
            {
                if (@object.Type.FrameGroups[(int)Protobuf.Shared.FrameGroupType.Idle].SpriteInfo.IsAnimation)
                {
                    int phase = message.ReadUnsignedByte();
                    @object.Phase = phase == 0 ? Constants.PhaseAutomatic : phase;
                }
            }

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameQuickLoot) && @object.Type.IsContainer)
            {
                bool assignedToQuickLoot = message.ReadBoolean();
                if (assignedToQuickLoot)
                {
                    uint lootContainers = message.ReadUnsignedInt(); // 1 << ObjectCategory | ....
                }
            }

            return(@object);
        }
Esempio n. 24
0
        private void ParseSupplyStash(Internal.CommunicationStream message)
        {
            int availableItems = message.ReadUnsignedShort();

            for (int i = 0; i < availableItems; i++)
            {
                ushort objectID    = message.ReadUnsignedShort();
                uint   objectCount = message.ReadUnsignedInt();
            }

            int freeSlots = message.ReadUnsignedShort();
        }
Esempio n. 25
0
        public StoreOpenParameters ReadStoreOpenParameters(Internal.CommunicationStream message)
        {
            var openAction = message.ReadEnum <StoreOpenParameterAction>();

            Store.OpenParameters.IStoreOpenParamater openParam = null;
            switch (openAction)
            {
            case StoreOpenParameterAction.Invalid: {
                break;
            }

            case StoreOpenParameterAction.CategoryType: {
                var categoryType = message.ReadEnum <StoreCategoryType>();
                openParam = new Store.OpenParameters.StoreCategoryTypeOpenParamater(categoryType);
                break;
            }

            case StoreOpenParameterAction.CategoryAndFilter: {
                var categoryAndFilter = ReadStoreCategoryAndFilter(message);
                openParam = new Store.OpenParameters.StoreCategoryAndFilterOpenParamater(categoryAndFilter);
                break;
            }

            case StoreOpenParameterAction.OfferType: {
                var offerType = message.ReadEnum <StoreOfferType>();
                openParam = new Store.OpenParameters.StoreOfferTypeOpenParamater(offerType);
                break;
            }

            case StoreOpenParameterAction.OfferId: {
                var offerId = message.ReadUnsignedInt();
                openParam = new Store.OpenParameters.StoreOfferIdOpenParamater(offerId);
                break;
            }

            case StoreOpenParameterAction.CategoryName: {
                var categoryName = message.ReadString();
                openParam = new Store.OpenParameters.StoreCategoryNameOpenParamater(categoryName);
                break;
            }
            }

            // enum too, 0, 1, 2, 3
            message.ReadUnsignedByte();

            /**
             * 0: default
             * 1: home
             * // 2, 3?
             */
            message.ReadBoolean(); // 0, 1, 2, 3 (enum)
            return(new StoreOpenParameters(openAction, openParam));
        }
Esempio n. 26
0
        private void ParseDailyRewardHistory(Internal.CommunicationStream message)
        {
            int count = message.ReadUnsignedByte();

            for (int i = 0; i < count; i++)
            {
                var timestamp = message.ReadUnsignedInt(); // timestamp
                message.ReadUnsignedByte();                // some state (0: none, 1: active[green])
                message.ReadString();                      // description
                message.ReadUnsignedShort();               // streak
            }
        }
Esempio n. 27
0
        private void ParseMonsterCyclopedia(Internal.CommunicationStream message)
        {
            int count = message.ReadUnsignedShort();

            for (int i = 0; i < count; i++)
            {
                string classification = message.ReadString();

                uint total = message.ReadUnsignedShort();
                uint known = message.ReadUnsignedShort();
            }
        }
Esempio n. 28
0
        private Creatures.Skill ReadSkill(Internal.CommunicationStream message, bool special = false)
        {
            int level;

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameDoubleSkills))
            {
                level = message.ReadUnsignedShort();
            }
            else
            {
                level = message.ReadUnsignedByte();
            }

            int baseLevel;

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameSkillsBase))
            {
                if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameBaseSkillU16))
                {
                    baseLevel = message.ReadUnsignedShort();
                }
                else
                {
                    baseLevel = message.ReadUnsignedByte();
                }
            }
            else
            {
                baseLevel = level;
            }

            if (!special && OpenTibiaUnity.GameManager.GetFeature(GameFeature.GamePercentSkillU16))
            {
                ushort loyaltyBonus = message.ReadUnsignedShort();
            }

            float percentage = 0.0f;

            if (!special)
            {
                if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GamePercentSkillU16))
                {
                    percentage = message.ReadUnsignedShort() / 100f;
                }
                else
                {
                    percentage = message.ReadUnsignedByte();
                }
            }

            return(new Creatures.Skill(level, baseLevel, percentage));
        }
Esempio n. 29
0
        private int ReadArea(Internal.CommunicationStream message, int startx, int starty, int endx, int endy)
        {
            UnityEngine.Vector3Int position = WorldMapStorage.Position;

            int z, endz, zstep;

            if (position.z <= Constants.GroundLayer)
            {
                z     = 0;
                endz  = Constants.GroundLayer + 1;
                zstep = 1;
            }
            else
            {
                z     = 2 * Constants.UndergroundLayer;
                endz  = System.Math.Max(-1, position.z - Constants.MapMaxZ + 1);
                zstep = -1;
            }

            int skip = 0;

            for (; z != endz; z += zstep)
            {
                for (int x = startx; x <= endx; x++)
                {
                    for (int y = starty; y <= endy; y++)
                    {
                        if (skip > 0)
                        {
                            skip--;
                        }
                        else
                        {
                            skip = ReadField(message, x, y, z);
                        }

                        UnityEngine.Vector3Int mapPosition      = new UnityEngine.Vector3Int(x, y, z);
                        UnityEngine.Vector3Int absolutePosition = WorldMapStorage.ToAbsolute(mapPosition);

                        if (absolutePosition.z == MiniMapStorage.PositionZ)
                        {
                            WorldMapStorage.UpdateMiniMap(mapPosition);
                            uint color = WorldMapStorage.GetMiniMapColour(mapPosition);
                            int  cost  = WorldMapStorage.GetMiniMapCost(mapPosition);
                            MiniMapStorage.UpdateField(absolutePosition, color, cost, false);
                        }
                    }
                }
            }

            return(skip);
        }
Esempio n. 30
0
        private void ParsePlayerSkills(Internal.CommunicationStream message)
        {
            // magic level is being parsed
            if (OpenTibiaUnity.GameManager.ClientVersion >= 1200)
            {
                var skillStruct = ReadSkill(message);
                Player.SetSkill(SkillType.MagLevel, skillStruct.Level, skillStruct.BaseLevel, skillStruct.Percentage);
            }

            var skills = new SkillType[] {
                SkillType.Fist,
                SkillType.Club,
                SkillType.Sword,
                SkillType.Axe,
                SkillType.Distance,
                SkillType.Shield,
                SkillType.Fishing
            };

            foreach (var skill in skills)
            {
                var skillStruct = ReadSkill(message);
                Player.SetSkill(skill, skillStruct.Level, skillStruct.BaseLevel, skillStruct.Percentage);
            }

            if (OpenTibiaUnity.GameManager.GetFeature(GameFeature.GameAdditionalSkills))
            {
                SkillType[] specialSkills = new SkillType[] {
                    SkillType.CriticalHitChance,
                    SkillType.CriticalHitDamage,
                    SkillType.LifeLeechChance,
                    SkillType.LifeLeechAmount,
                    SkillType.ManaLeechChance,
                    SkillType.ManaLeechAmount
                };

                foreach (var skill in specialSkills)
                {
                    var skillStruct = ReadSkill(message, true);
                    Player.SetSkill(skill, skillStruct.Level, skillStruct.BaseLevel);
                }
            }

            // todo: find if this is capacity
            if (OpenTibiaUnity.GameManager.ClientVersion >= 1150)
            {
                int totalCapacity = message.ReadInt();
                int baseCapacity  = message.ReadInt();

                Player.SetSkill(SkillType.Capacity, totalCapacity, baseCapacity);
            }
        }