Refactor logic

FeroxRev [2016-07-21 07:02:21]
Refactor logic
Filename
PokemonGo.RocketAPI.Console/PokemonGo.RocketAPI.Console.csproj
PokemonGo.RocketAPI.Console/Program.cs
PokemonGo.RocketAPI.Logic/Inventory.cs
PokemonGo.RocketAPI.Logic/Logic.cs
PokemonGo.RocketAPI.Logic/PokemonGo.RocketAPI.Logic.csproj
PokemonGo.RocketAPI.Logic/Utils/StringUtils.cs
PokemonGo.RocketAPI/GeneratedCode/Payloads.cs
PokemonGo.RocketAPI/Proto/AllEnum.proto
PokemonGo.RocketAPI/Proto/Payloads.proto
diff --git a/PokemonGo.RocketAPI.Console/PokemonGo.RocketAPI.Console.csproj b/PokemonGo.RocketAPI.Console/PokemonGo.RocketAPI.Console.csproj
index 09946cf..df6438b 100644
--- a/PokemonGo.RocketAPI.Console/PokemonGo.RocketAPI.Console.csproj
+++ b/PokemonGo.RocketAPI.Console/PokemonGo.RocketAPI.Console.csproj
@@ -56,6 +56,10 @@
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\PokemonGo.RocketAPI.Logic\PokemonGo.RocketAPI.Logic.csproj">
+      <Project>{0739E40D-C589-4AEB-93E5-EE8CD6773C60}</Project>
+      <Name>PokemonGo.RocketAPI.Logic</Name>
+    </ProjectReference>
     <ProjectReference Include="..\PokemonGo.RocketAPI\PokemonGo.RocketAPI.csproj">
       <Project>{05D2DA44-1B8E-4CF7-94ED-4D52451CD095}</Project>
       <Name>PokemonGo.RocketAPI</Name>
diff --git a/PokemonGo.RocketAPI.Console/Program.cs b/PokemonGo.RocketAPI.Console/Program.cs
index 47ba1ca..df89e6e 100644
--- a/PokemonGo.RocketAPI.Console/Program.cs
+++ b/PokemonGo.RocketAPI.Console/Program.cs
@@ -17,16 +17,14 @@ namespace PokemonGo.RocketAPI.Console
 {
     class Program
     {
-
-        static readonly ISettings ClientSettings = new Settings();
-
+
         static void Main(string[] args)
         {
             Task.Run(() =>
             {
                 try
                 {
-                    Execute();
+                    new Logic.Logic(new Settings()).Execute();
                 }
                 catch (PtcOfflineException)
                 {
@@ -40,242 +38,6 @@ namespace PokemonGo.RocketAPI.Console
              System.Console.ReadLine();
         }

-        static async void Execute()
-        {
-            var client = new Client(ClientSettings);
-
-            if (ClientSettings.AuthType == AuthType.Ptc)
-                await client.DoPtcLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
-            else if (ClientSettings.AuthType == AuthType.Google)
-                await client.DoGoogleLogin();
-
-            await client.SetServer();
-            var profile = await client.GetProfile();
-            var settings = await client.GetSettings();
-            var mapObjects = await client.GetMapObjects();
-            var inventory = await client.GetInventory();
-            var pokemons = inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Pokemon).Where(p => p != null && p?.PokemonId > 0);
-
-
-            await ExecuteFarmingPokestopsAndPokemons(client);
-            //await ExecuteCatchAllNearbyPokemons(client);
-
-
-        }
-
-        private static async Task ExecuteFarmingPokestopsAndPokemons(Client client)
-        {
-            var mapObjects = await client.GetMapObjects();
-
-            var pokeStops = mapObjects.MapCells.SelectMany(i => i.Forts).Where(i => i.Type == FortType.Checkpoint && i.CooldownCompleteTimestampMs < DateTime.UtcNow.ToUnixTime());
-
-            foreach (var pokeStop in pokeStops)
-            {
-                var update = await client.UpdatePlayerLocation(pokeStop.Latitude, pokeStop.Longitude);
-                var fortInfo = await client.GetFort(pokeStop.Id, pokeStop.Latitude, pokeStop.Longitude);
-                var fortSearch = await client.SearchFort(pokeStop.Id, pokeStop.Latitude, pokeStop.Longitude);
-
-                System.Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] Farmed XP: {fortSearch.ExperienceAwarded}, Gems: { fortSearch.GemsAwarded}, Eggs: {fortSearch.PokemonDataEgg} Items: {GetFriendlyItemsString(fortSearch.ItemsAwarded)}");
-
-                await Task.Delay(15000);
-                await ExecuteCatchAllNearbyPokemons(client);
-            }
-        }
-
-        private static int checkForDuplicates = -1;
-
-        private static async Task ExecuteCatchAllNearbyPokemons(Client client)
-        {
-            var mapObjects = await client.GetMapObjects();
-
-            var pokemons = mapObjects.MapCells.SelectMany(i => i.CatchablePokemons);
-
-            foreach (var pokemon in pokemons)
-            {
-                var update = await client.UpdatePlayerLocation(pokemon.Latitude, pokemon.Longitude);
-                var encounterPokemonResponse = await client.EncounterPokemon(pokemon.EncounterId, pokemon.SpawnpointId);
-
-                CatchPokemonResponse caughtPokemonResponse;
-                do
-                {
-                    caughtPokemonResponse = await client.CatchPokemon(pokemon.EncounterId, pokemon.SpawnpointId, pokemon.Latitude, pokemon.Longitude, MiscEnums.Item.ITEM_POKE_BALL); //note: reverted from settings because this should not be part of settings but part of logic
-                }
-                while(caughtPokemonResponse.Status == CatchPokemonResponse.Types.CatchStatus.CatchMissed);
-
-                System.Console.WriteLine(caughtPokemonResponse.Status == CatchPokemonResponse.Types.CatchStatus.CatchSuccess ? $"[{DateTime.Now.ToString("HH:mm:ss")}] We caught a {pokemon.PokemonId} with CP {encounterPokemonResponse?.WildPokemon?.PokemonData?.Cp}" : $"[{DateTime.Now.ToString("HH:mm:ss")}] {pokemon.PokemonId} with CP {encounterPokemonResponse?.WildPokemon?.PokemonData?.Cp} got away..");
-                await Task.Delay(5000);
-
-
-                await Task.Delay(5000);
-            }
-        }
-        private static async Task TransferAllGivenPokemons(Client client, IEnumerable<PokemonData> unwantedPokemons)
-        {
-            foreach (var pokemon in unwantedPokemons)
-            {
-                var transferPokemonResponse = await client.TransferPokemon(pokemon.Id);
-
-                /*
-                ReleasePokemonOutProto.Status {
-	                UNSET = 0;
-	                SUCCESS = 1;
-	                POKEMON_DEPLOYED = 2;
-	                FAILED = 3;
-	                ERROR_POKEMON_IS_EGG = 4;
-                }*/
-
-                if (transferPokemonResponse.Status == 1)
-                {
-                    System.Console.WriteLine($"Shoved another {pokemon.PokemonId} down the meat grinder");
-                }
-                else
-                {
-                    var status = transferPokemonResponse.Status;
-
-                    System.Console.WriteLine($"Somehow failed to grind {pokemon.PokemonId}. " +
-                                             $"ReleasePokemonOutProto.Status was {status}");
-                }
-
-                await Task.Delay(3000);
-            }
-        }
-
-        private static async Task EvolveAllGivenPokemons(Client client, IEnumerable<Pokemon> pokemonToEvolve)
-        {
-            foreach (var pokemon in pokemonToEvolve)
-            {
-                /*
-                enum Holoholo.Rpc.Types.EvolvePokemonOutProto.Result {
-	                UNSET = 0;
-	                SUCCESS = 1;
-	                FAILED_POKEMON_MISSING = 2;
-	                FAILED_INSUFFICIENT_RESOURCES = 3;
-	                FAILED_POKEMON_CANNOT_EVOLVE = 4;
-	                FAILED_POKEMON_IS_DEPLOYED = 5;
-                }
-                }*/
-
-                var countOfEvolvedUnits = 0;
-                var xpCount = 0;
-
-                EvolvePokemonOut evolvePokemonOutProto;
-                do
-                {
-                    evolvePokemonOutProto = await client.EvolvePokemon((ulong)pokemon.Id); //todo: someone check whether this still works
-
-                    if (evolvePokemonOutProto.Result == 1)
-                    {
-                        System.Console.WriteLine($"Evolved {pokemon.PokemonType} successfully for {evolvePokemonOutProto.ExpAwarded}xp");
-
-                        countOfEvolvedUnits++;
-                        xpCount += evolvePokemonOutProto.ExpAwarded;
-                    }
-                    else
-                    {
-                        var result = evolvePokemonOutProto.Result;
-
-                        System.Console.WriteLine($"Failed to evolve {pokemon.PokemonType}. " +
-                                                 $"EvolvePokemonOutProto.Result was {result}");
-
-                        System.Console.WriteLine($"Due to above error, stopping evolving {pokemon.PokemonType}");
-                    }
-                }
-                while (evolvePokemonOutProto.Result == 1);
-
-                System.Console.WriteLine($"Evolved {countOfEvolvedUnits} pieces of {pokemon.PokemonType} for {xpCount}xp");
-
-                await Task.Delay(3000);
-            }
-        }
-
-        private static async Task TransferAllButStrongestUnwantedPokemon(Client client)
-        {
-            System.Console.WriteLine("[!] firing up the meat grinder");
-
-            var unwantedPokemonTypes = new[]
-            {
-                PokemonId.Pidgey,
-                PokemonId.Rattata,
-                PokemonId.Weedle,
-                PokemonId.Zubat,
-                PokemonId.Caterpie,
-                PokemonId.Pidgeotto,
-                PokemonId.NidoranFemale,
-                PokemonId.Paras,
-                PokemonId.Venonat,
-                PokemonId.Psyduck,
-                PokemonId.Poliwag,
-                PokemonId.Slowpoke,
-                PokemonId.Drowzee,
-                PokemonId.Gastly,
-                PokemonId.Goldeen,
-                PokemonId.Staryu,
-                PokemonId.Magikarp,
-                PokemonId.Eevee,
-                PokemonId.Dratini
-            };
-
-            var inventory = await client.GetInventory();
-            var pokemons = inventory.InventoryDelta.InventoryItems
-                                .Select(i => i.InventoryItemData?.Pokemon)
-                                .Where(p => p != null && p?.PokemonId > 0)
-                                .ToArray();
-
-            foreach (var unwantedPokemonType in unwantedPokemonTypes)
-            {
-                var pokemonOfDesiredType = pokemons.Where(p => p.PokemonId == unwantedPokemonType)
-                                                   .OrderByDescending(p => p.Cp)
-                                                   .ToList();
-
-                var unwantedPokemon = pokemonOfDesiredType.Skip(1) // keep the strongest one for potential battle-evolving
-                                                          .ToList();
-
-                System.Console.WriteLine($"Grinding {unwantedPokemon.Count} pokemons of type {unwantedPokemonType}");
-                await TransferAllGivenPokemons(client, unwantedPokemon);
-            }
-
-            System.Console.WriteLine("[!] finished grinding all the meat");
-        }
-
-        private static string GetFriendlyItemsString(IEnumerable<FortSearchResponse.Types.ItemAward> items)
-        {
-            var enumerable = items as IList<FortSearchResponse.Types.ItemAward> ?? items.ToList();
-
-            if (!enumerable.Any())
-                return string.Empty;
-
-            return
-                enumerable.GroupBy(i => i.ItemId)
-                          .Select(kvp => new {ItemName = kvp.Key.ToString(), Amount = kvp.Sum(x => x.ItemCount)})
-                          .Select(y => $"{y.Amount} x {y.ItemName}")
-                          .Aggregate((a, b) => $"{a}, {b}");
-        }
-
-        private static async void TransferDuplicatePokemon(Client client)
-        {
-
-            checkForDuplicates++;
-            if (checkForDuplicates % 50 == 0)
-            {
-                checkForDuplicates = 0;
-                System.Console.WriteLine($"Check for duplicates");
-                var inventory = await client.GetInventory();
-                var allpokemons = inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Pokemon).Where(p => p != null && p?.PokemonId >0);
-
-                var dupes = allpokemons.OrderBy(x => x.Cp).Select((x, i) => new { index = i, value = x })
-                  .GroupBy(x => x.value.PokemonId)
-                  .Where(x => x.Skip(1).Any());
-
-                for (int i = 0; i < dupes.Count(); i++)
-                {
-                    for (int j = 0; j < dupes.ElementAt(i).Count() - 1; j++)
-                    {
-                        var dubpokemon = dupes.ElementAt(i).ElementAt(j).value;
-                        var transfer = await client.TransferPokemon(dubpokemon.Id);
-                        System.Console.WriteLine($"Transfer {dubpokemon.PokemonId} with {dubpokemon.Cp} CP (highest has {dupes.ElementAt(i).Last().value.Cp})");
-                    }
-                }
-            }
-        }
+
     }
 }
diff --git a/PokemonGo.RocketAPI.Logic/Inventory.cs b/PokemonGo.RocketAPI.Logic/Inventory.cs
new file mode 100644
index 0000000..0835c3a
--- /dev/null
+++ b/PokemonGo.RocketAPI.Logic/Inventory.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using AllEnum;
+using PokemonGo.RocketAPI.GeneratedCode;
+
+namespace PokemonGo.RocketAPI.Logic
+{
+    public class Inventory
+    {
+        private readonly Client _client;
+
+        public Inventory(Client client)
+        {
+            _client = client;
+        }
+
+        public async Task<IEnumerable<PokemonData>> GetMyPokemon()
+        {
+            var inventory = await _client.GetInventory();
+            return inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Pokemon).Where(p => p != null && p?.PokemonId > 0);
+        }
+
+        public async Task<IEnumerable<PokemonData>> GetDuplicatePokemonToTransfer()
+        {
+            var myPokemon = await GetMyPokemon();
+
+            return myPokemon.OrderBy(x => x.Cp)
+                .GroupBy(p => p.PokemonId)
+                .Where(x => x.Count() > 1)
+                .SelectMany(p => p.ToList());
+        }
+    }
+}
diff --git a/PokemonGo.RocketAPI.Logic/Logic.cs b/PokemonGo.RocketAPI.Logic/Logic.cs
index cfc157d..7024077 100644
--- a/PokemonGo.RocketAPI.Logic/Logic.cs
+++ b/PokemonGo.RocketAPI.Logic/Logic.cs
@@ -3,10 +3,147 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using AllEnum;
+using PokemonGo.RocketAPI.Enums;
+using PokemonGo.RocketAPI.Extensions;
+using PokemonGo.RocketAPI.GeneratedCode;
+using PokemonGo.RocketAPI.Logic.Utils;

 namespace PokemonGo.RocketAPI.Logic
 {
     public class Logic
     {
+        private readonly Client _client;
+        private readonly ISettings _clientSettings;
+        private readonly Inventory _inventory;
+
+        public Logic(ISettings clientSettings)
+        {
+            _clientSettings = clientSettings;
+            _client = new Client(_clientSettings);
+            _inventory = new Inventory(_client);
+        }
+
+        public async void Execute()
+        {
+            Console.WriteLine($"Starting Execute on login server: {_clientSettings.AuthType}");
+
+            var client = new Client(_clientSettings);
+
+            while (true)
+            {
+                try
+                {
+                    if (_clientSettings.AuthType == AuthType.Ptc)
+                        await client.DoPtcLogin(_clientSettings.PtcUsername, _clientSettings.PtcPassword);
+                    else if (_clientSettings.AuthType == AuthType.Google)
+                        await client.DoGoogleLogin();
+
+                    await client.SetServer();
+
+
+                    RepeatAction(10, async () => await ExecuteFarmingPokestopsAndPokemons(client));
+                    await TransferDuplicatePokemon();
+
+                    /*
+                * Example calls below
+                *
+                var profile = await client.GetProfile();
+                var settings = await client.GetSettings();
+                var mapObjects = await client.GetMapObjects();
+                var inventory = await client.GetInventory();
+                var pokemons = inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Pokemon).Where(p => p != null && p?.PokemonId > 0);
+                */
+                }
+                catch(Exception ex)
+                {
+                    Console.WriteLine($"Exception: {ex}");
+                }
+
+                await Task.Delay(10000);
+            }
+        }
+
+        public void RepeatAction(int repeat, Action action)
+        {
+            for (int i = 0; i < repeat; i++)
+                action();
+        }
+
+        private async Task ExecuteFarmingPokestopsAndPokemons(Client client)
+        {
+            var mapObjects = await client.GetMapObjects();
+
+            var pokeStops = mapObjects.MapCells.SelectMany(i => i.Forts).Where(i => i.Type == FortType.Checkpoint && i.CooldownCompleteTimestampMs < DateTime.UtcNow.ToUnixTime());
+
+            foreach (var pokeStop in pokeStops)
+            {
+                var update = await client.UpdatePlayerLocation(pokeStop.Latitude, pokeStop.Longitude);
+                var fortInfo = await client.GetFort(pokeStop.Id, pokeStop.Latitude, pokeStop.Longitude);
+                var fortSearch = await client.SearchFort(pokeStop.Id, pokeStop.Latitude, pokeStop.Longitude);
+
+                System.Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] Farmed XP: {fortSearch.ExperienceAwarded}, Gems: { fortSearch.GemsAwarded}, Eggs: {fortSearch.PokemonDataEgg} Items: {StringUtils.GetSummedFriendlyNameOfItemAwardList(fortSearch.ItemsAwarded)}");
+
+                await Task.Delay(15000);
+                await ExecuteCatchAllNearbyPokemons(client);
+            }
+        }
+
+        private async Task ExecuteCatchAllNearbyPokemons(Client client)
+        {
+            var mapObjects = await client.GetMapObjects();
+
+            var pokemons = mapObjects.MapCells.SelectMany(i => i.CatchablePokemons);
+
+            foreach (var pokemon in pokemons)
+            {
+                var update = await client.UpdatePlayerLocation(pokemon.Latitude, pokemon.Longitude);
+                var encounterPokemonResponse = await client.EncounterPokemon(pokemon.EncounterId, pokemon.SpawnpointId);
+
+                CatchPokemonResponse caughtPokemonResponse;
+                do
+                {
+                    caughtPokemonResponse = await client.CatchPokemon(pokemon.EncounterId, pokemon.SpawnpointId, pokemon.Latitude, pokemon.Longitude, MiscEnums.Item.ITEM_POKE_BALL); //note: reverted from settings because this should not be part of settings but part of logic
+                }
+                while (caughtPokemonResponse.Status == CatchPokemonResponse.Types.CatchStatus.CatchMissed);
+
+                System.Console.WriteLine(caughtPokemonResponse.Status == CatchPokemonResponse.Types.CatchStatus.CatchSuccess ? $"[{DateTime.Now.ToString("HH:mm:ss")}] We caught a {pokemon.PokemonId} with CP {encounterPokemonResponse?.WildPokemon?.PokemonData?.Cp}" : $"[{DateTime.Now.ToString("HH:mm:ss")}] {pokemon.PokemonId} with CP {encounterPokemonResponse?.WildPokemon?.PokemonData?.Cp} got away..");
+                await Task.Delay(5000);
+            }
+        }
+
+        private async Task EvolveAllGivenPokemons(IEnumerable<Pokemon> pokemonToEvolve)
+        {
+            foreach (var pokemon in pokemonToEvolve)
+            {
+                EvolvePokemonOut evolvePokemonOutProto;
+                do
+                {
+                    evolvePokemonOutProto = await _client.EvolvePokemon((ulong)pokemon.Id);
+
+                    if (evolvePokemonOutProto.Result == EvolvePokemonOut.Types.EvolvePokemonStatus.PokemonEvolvedSuccess)
+                        System.Console.WriteLine($"Evolved {pokemon.PokemonType} successfully for {evolvePokemonOutProto.ExpAwarded}xp");
+                    else
+                        System.Console.WriteLine($"Failed to evolve {pokemon.PokemonType}. EvolvePokemonOutProto.Result was {evolvePokemonOutProto.Result}, stopping evolving {pokemon.PokemonType}");
+
+                    await Task.Delay(3000);
+                }
+                while (evolvePokemonOutProto.Result == EvolvePokemonOut.Types.EvolvePokemonStatus.PokemonEvolvedSuccess);
+
+                await Task.Delay(3000);
+            }
+        }
+
+        private async Task TransferDuplicatePokemon()
+        {
+            System.Console.WriteLine($"Transfering duplicate Pokemon");
+
+            var duplicatePokemons = await _inventory.GetDuplicatePokemonToTransfer();
+            foreach (var duplicatePokemon in duplicatePokemons)
+            {
+                var transfer = await _client.TransferPokemon(duplicatePokemon.Id);
+                System.Console.WriteLine($"Transfer {duplicatePokemon.PokemonId} with {duplicatePokemon.Cp})");
+            }
+        }
     }
 }
diff --git a/PokemonGo.RocketAPI.Logic/PokemonGo.RocketAPI.Logic.csproj b/PokemonGo.RocketAPI.Logic/PokemonGo.RocketAPI.Logic.csproj
index ca08488..9fb4da7 100644
--- a/PokemonGo.RocketAPI.Logic/PokemonGo.RocketAPI.Logic.csproj
+++ b/PokemonGo.RocketAPI.Logic/PokemonGo.RocketAPI.Logic.csproj
@@ -30,7 +30,11 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="Google.Protobuf">
+      <HintPath>..\PokemonGo.RocketAPI\bin\Debug\Google.Protobuf.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
@@ -40,9 +44,17 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Inventory.cs" />
     <Compile Include="Logic.cs" />
     <Compile Include="Navigation.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Utils\StringUtils.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\PokemonGo.RocketAPI\PokemonGo.RocketAPI.csproj">
+      <Project>{05d2da44-1b8e-4cf7-94ed-4d52451cd095}</Project>
+      <Name>PokemonGo.RocketAPI</Name>
+    </ProjectReference>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
diff --git a/PokemonGo.RocketAPI.Logic/Utils/StringUtils.cs b/PokemonGo.RocketAPI.Logic/Utils/StringUtils.cs
new file mode 100644
index 0000000..c9ab0f7
--- /dev/null
+++ b/PokemonGo.RocketAPI.Logic/Utils/StringUtils.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using PokemonGo.RocketAPI.GeneratedCode;
+
+namespace PokemonGo.RocketAPI.Logic.Utils
+{
+    public static class StringUtils
+    {
+        public static string GetSummedFriendlyNameOfItemAwardList(IEnumerable<FortSearchResponse.Types.ItemAward> items)
+        {
+            var enumerable = items as IList<FortSearchResponse.Types.ItemAward> ?? items.ToList();
+
+            if (!enumerable.Any())
+                return string.Empty;
+
+            return
+                enumerable.GroupBy(i => i.ItemId)
+                          .Select(kvp => new { ItemName = kvp.Key.ToString(), Amount = kvp.Sum(x => x.ItemCount) })
+                          .Select(y => $"{y.Amount} x {y.ItemName}")
+                          .Aggregate((a, b) => $"{a}, {b}");
+        }
+    }
+}
diff --git a/PokemonGo.RocketAPI/GeneratedCode/Payloads.cs b/PokemonGo.RocketAPI/GeneratedCode/Payloads.cs
index deecbe1..40e27f5 100644
--- a/PokemonGo.RocketAPI/GeneratedCode/Payloads.cs
+++ b/PokemonGo.RocketAPI/GeneratedCode/Payloads.cs
@@ -482,10 +482,16 @@ namespace PokemonGo.RocketAPI.GeneratedCode {
             "ZXJjZW50GAEgASgCIiQKD1RyYW5zZmVyUG9rZW1vbhIRCglQb2tlbW9uSWQY",
             "ASABKAYiOgoSVHJhbnNmZXJQb2tlbW9uT3V0Eg4KBlN0YXR1cxgBIAEoBRIU",
             "CgxDYW5keUF3YXJkZWQYAiABKAUiIgoNRXZvbHZlUG9rZW1vbhIRCglQb2tl",
-            "bW9uSWQYASABKAYikAEKEEV2b2x2ZVBva2Vtb25PdXQSDgoGUmVzdWx0GAEg",
-            "ASgFEkIKDkV2b2x2ZWRQb2tlbW9uGAIgASgLMiouUG9rZW1vbkdvLlJvY2tl",
-            "dEFQSS5HZW5lcmF0ZWRDb2RlLlBva2Vtb24SEgoKRXhwQXdhcmRlZBgDIAEo",
-            "BRIUCgxDYW5keUF3YXJkZWQYBCABKAViBnByb3RvMw=="));
+            "bW9uSWQYASABKAYiqgMKEEV2b2x2ZVBva2Vtb25PdXQSVwoGUmVzdWx0GAEg",
+            "ASgOMkcuUG9rZW1vbkdvLlJvY2tldEFQSS5HZW5lcmF0ZWRDb2RlLkV2b2x2",
+            "ZVBva2Vtb25PdXQuRXZvbHZlUG9rZW1vblN0YXR1cxJCCg5Fdm9sdmVkUG9r",
+            "ZW1vbhgCIAEoCzIqLlBva2Vtb25Hby5Sb2NrZXRBUEkuR2VuZXJhdGVkQ29k",
+            "ZS5Qb2tlbW9uEhIKCkV4cEF3YXJkZWQYAyABKAUSFAoMQ2FuZHlBd2FyZGVk",
+            "GAQgASgFIs4BChNFdm9sdmVQb2tlbW9uU3RhdHVzEhkKFVBPS0VNT05fRVZP",
+            "TFZFRF9VTlNFVBAAEhsKF1BPS0VNT05fRVZPTFZFRF9TVUNDRVNTEAESGgoW",
+            "RkFJTEVEX1BPS0VNT05fTUlTU0lORxACEiEKHUZBSUxFRF9JTlNVRkZJQ0lF",
+            "TlRfUkVTT1VSQ0VTEAMSIAocRkFJTEVEX1BPS0VNT05fQ0FOTk9UX0VWT0xW",
+            "RRAEEh4KGkZBSUxFRF9QT0tFTU9OX0lTX0RFUExPWUVEEAViBnByb3RvMw=="));
       descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
           new pbr::FileDescriptor[] { global::AllEnum.AllEnumReflection.Descriptor, },
           new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
@@ -585,7 +591,7 @@ namespace PokemonGo.RocketAPI.GeneratedCode {
             new pbr::GeneratedClrTypeInfo(typeof(global::PokemonGo.RocketAPI.GeneratedCode.TransferPokemon), global::PokemonGo.RocketAPI.GeneratedCode.TransferPokemon.Parser, new[]{ "PokemonId" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::PokemonGo.RocketAPI.GeneratedCode.TransferPokemonOut), global::PokemonGo.RocketAPI.GeneratedCode.TransferPokemonOut.Parser, new[]{ "Status", "CandyAwarded" }, null, null, null),
             new pbr::GeneratedClrTypeInfo(typeof(global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemon), global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemon.Parser, new[]{ "PokemonId" }, null, null, null),
-            new pbr::GeneratedClrTypeInfo(typeof(global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut), global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut.Parser, new[]{ "Result", "EvolvedPokemon", "ExpAwarded", "CandyAwarded" }, null, null, null)
+            new pbr::GeneratedClrTypeInfo(typeof(global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut), global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut.Parser, new[]{ "Result", "EvolvedPokemon", "ExpAwarded", "CandyAwarded" }, null, new[]{ typeof(global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut.Types.EvolvePokemonStatus) }, null)
           }));
     }
     #endregion
@@ -22083,8 +22089,8 @@ namespace PokemonGo.RocketAPI.GeneratedCode {

     /// <summary>Field number for the "Result" field.</summary>
     public const int ResultFieldNumber = 1;
-    private int result_;
-    public int Result {
+    private global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut.Types.EvolvePokemonStatus result_ = 0;
+    public global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut.Types.EvolvePokemonStatus Result {
       get { return result_; }
       set {
         result_ = value;
@@ -22155,7 +22161,7 @@ namespace PokemonGo.RocketAPI.GeneratedCode {
     public void WriteTo(pb::CodedOutputStream output) {
       if (Result != 0) {
         output.WriteRawTag(8);
-        output.WriteInt32(Result);
+        output.WriteEnum((int) Result);
       }
       if (evolvedPokemon_ != null) {
         output.WriteRawTag(18);
@@ -22174,7 +22180,7 @@ namespace PokemonGo.RocketAPI.GeneratedCode {
     public int CalculateSize() {
       int size = 0;
       if (Result != 0) {
-        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Result);
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Result);
       }
       if (evolvedPokemon_ != null) {
         size += 1 + pb::CodedOutputStream.ComputeMessageSize(EvolvedPokemon);
@@ -22217,7 +22223,7 @@ namespace PokemonGo.RocketAPI.GeneratedCode {
             input.SkipLastField();
             break;
           case 8: {
-            Result = input.ReadInt32();
+            result_ = (global::PokemonGo.RocketAPI.GeneratedCode.EvolvePokemonOut.Types.EvolvePokemonStatus) input.ReadEnum();
             break;
           }
           case 18: {
@@ -22239,6 +22245,22 @@ namespace PokemonGo.RocketAPI.GeneratedCode {
       }
     }

+    #region Nested types
+    /// <summary>Container for nested types declared in the EvolvePokemonOut message type.</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    public static partial class Types {
+      public enum EvolvePokemonStatus {
+        [pbr::OriginalName("POKEMON_EVOLVED_UNSET")] PokemonEvolvedUnset = 0,
+        [pbr::OriginalName("POKEMON_EVOLVED_SUCCESS")] PokemonEvolvedSuccess = 1,
+        [pbr::OriginalName("FAILED_POKEMON_MISSING")] FailedPokemonMissing = 2,
+        [pbr::OriginalName("FAILED_INSUFFICIENT_RESOURCES")] FailedInsufficientResources = 3,
+        [pbr::OriginalName("FAILED_POKEMON_CANNOT_EVOLVE")] FailedPokemonCannotEvolve = 4,
+        [pbr::OriginalName("FAILED_POKEMON_IS_DEPLOYED")] FailedPokemonIsDeployed = 5,
+      }
+
+    }
+    #endregion
+
   }

   #endregion
diff --git a/PokemonGo.RocketAPI/Proto/AllEnum.proto b/PokemonGo.RocketAPI/Proto/AllEnum.proto
index d580cc6..39d24bd 100644
--- a/PokemonGo.RocketAPI/Proto/AllEnum.proto
+++ b/PokemonGo.RocketAPI/Proto/AllEnum.proto
@@ -763,4 +763,4 @@ enum PokemonClass {
   NORMAL = 0;
   LEGENDARY = 1;
   MYTHIC = 2;
-}
\ No newline at end of file
+}
diff --git a/PokemonGo.RocketAPI/Proto/Payloads.proto b/PokemonGo.RocketAPI/Proto/Payloads.proto
index 3b71b5b..0d448ed 100644
--- a/PokemonGo.RocketAPI/Proto/Payloads.proto
+++ b/PokemonGo.RocketAPI/Proto/Payloads.proto
@@ -902,8 +902,18 @@ message EvolvePokemon {


 message EvolvePokemonOut {
-    int32 Result = 1;
+    EvolvePokemonStatus Result = 1;
     Pokemon EvolvedPokemon = 2;
     int32 ExpAwarded = 3;
     int32 CandyAwarded = 4;
+
+
+	enum EvolvePokemonStatus {
+		POKEMON_EVOLVED_UNSET = 0;
+		POKEMON_EVOLVED_SUCCESS = 1;
+		FAILED_POKEMON_MISSING = 2;
+		FAILED_INSUFFICIENT_RESOURCES = 3;
+		FAILED_POKEMON_CANNOT_EVOLVE = 4;
+		FAILED_POKEMON_IS_DEPLOYED = 5;
+	}
 }
\ No newline at end of file
You may download the files in Public Git.