Merge remote-tracking branch 'refs/remotes/origin/RocketBot-2.0' into Beta-Build

Brian [2016-08-18 08:01:10]
Merge remote-tracking branch 'refs/remotes/origin/RocketBot-2.0' into Beta-Build
Filename
PokemonGo.RocketBot.Logic/Event/PokemonsEncounterEvent.cs
PokemonGo.RocketBot.Logic/ILogicSettings.cs
PokemonGo.RocketBot.Logic/Navigation.cs
PokemonGo.RocketBot.Logic/PokemonGo.RocketBot.Logic.csproj
PokemonGo.RocketBot.Logic/Settings.cs
PokemonGo.RocketBot.Logic/Tasks/CatchIncensePokemonsTask.cs
PokemonGo.RocketBot.Logic/Tasks/CatchLurePokemonsTask.cs
PokemonGo.RocketBot.Logic/Tasks/CatchNearbyPokemonsTask.cs
PokemonGo.RocketBot.Logic/Tasks/FarmPokestopsTask.cs
PokemonGo.RocketBot.Logic/Utils/WebUtils.cs
PokemonGo.RocketBot.Window/Forms/MainForm.cs
PokemonGo.RocketBot.Window/Forms/MainForm.designer.cs
PokemonGo.RocketBot.Window/Helpers/ResourceHelper.cs
PokemonGo.RocketBot.Window/Images/Markers/Trainer_Front.png
PokemonGo.RocketBot.Window/Images/Markers/Trainer.png
PokemonGo.RocketBot.Window/Images/Markers/Trainer_Right.png
PokemonGo.RocketBot.Window/PokemomObject.cs
PokemonGo.RocketBot.Window/PokemonGo.RocketBot.Window.csproj
PokemonGo.RocketBot.Window/Properties/Resources.Designer.cs
PokemonGo.RocketBot.Window/Properties/Resources.resx
diff --git a/PokemonGo.RocketBot.Logic/Event/PokemonsEncounterEvent.cs b/PokemonGo.RocketBot.Logic/Event/PokemonsEncounterEvent.cs
new file mode 100644
index 0000000..6af0c6c
--- /dev/null
+++ b/PokemonGo.RocketBot.Logic/Event/PokemonsEncounterEvent.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using POGOProtos.Data;
+using POGOProtos.Map.Pokemon;
+
+namespace PokemonGo.RocketBot.Logic.Event
+{
+    public class PokemonsEncounterEvent : IEvent
+    {
+        public List<MapPokemon> EncounterPokemons;
+    }
+}
diff --git a/PokemonGo.RocketBot.Logic/ILogicSettings.cs b/PokemonGo.RocketBot.Logic/ILogicSettings.cs
index fa27d54..279aa39 100644
--- a/PokemonGo.RocketBot.Logic/ILogicSettings.cs
+++ b/PokemonGo.RocketBot.Logic/ILogicSettings.cs
@@ -46,7 +46,7 @@ namespace PokemonGo.RocketBot.Logic
         {
         }

-        public TransferFilter(int keepMinCp, int keepMinLvl, bool useKeepMinLvl, float keepMinIvPercentage, string keepMinOperator, int keepMinDuplicatePokemon,
+        public TransferFilter(int keepMinCp, int keepMinLvl, bool useKeepMinLvl, float keepMinIvPercentage, string keepMinOperator, int keepMinDuplicatePokemon,
             List<PokemonMove> moves = null, string movesOperator = "or")
         {
             KeepMinCp = keepMinCp;
@@ -83,6 +83,7 @@ namespace PokemonGo.RocketBot.Logic
         bool UseKeepMinLvl { get; }
         string KeepMinOperator { get; }
         double WalkingSpeedInKilometerPerHour { get; }
+        double WalkingSpeedOffSetInKilometerPerHour { get; }
         bool FastSoftBanBypass { get; }
         bool EvolveAllPokemonWithEnoughCandy { get; }
         bool KeepPokemonsThatCanEvolve { get; }
diff --git a/PokemonGo.RocketBot.Logic/Navigation.cs b/PokemonGo.RocketBot.Logic/Navigation.cs
index 5841917..7c30efc 100644
--- a/PokemonGo.RocketBot.Logic/Navigation.cs
+++ b/PokemonGo.RocketBot.Logic/Navigation.cs
@@ -5,10 +5,9 @@ using System.Globalization;
 using System.Threading;
 using System.Threading.Tasks;
 using GeoCoordinatePortable;
-using PokemonGo.RocketBot.Logic.Utils;
 using PokemonGo.RocketAPI;
+using PokemonGo.RocketBot.Logic.Utils;
 using POGOProtos.Networking.Responses;
-using PokemonGo.RocketBot.Logic.Logging;

 #endregion

@@ -18,7 +17,7 @@ namespace PokemonGo.RocketBot.Logic

     public class Navigation
     {
-        private const double SpeedDownTo = 10/3.6;
+        private const double SpeedDownTo = 10 / 3.6;
         private readonly Client _client;

         public Navigation(Client client)
@@ -27,23 +26,24 @@ namespace PokemonGo.RocketBot.Logic
         }

         public async Task<PlayerUpdateResponse> Move(GeoCoordinate targetLocation,
-            double walkingSpeedInKilometersPerHour, Func<Task<bool>> functionExecutedWhileWalking,
+            double walkingSpeedInKilometersPerHour, double walkingSpeedOffSetInKilometersPerHour,
+            Func<Task<bool>> functionExecutedWhileWalking,
             CancellationToken cancellationToken, bool disableHumanLikeWalking)
         {
             cancellationToken.ThrowIfCancellationRequested();
             if (!disableHumanLikeWalking)
             {
-                var speedInMetersPerSecond = walkingSpeedInKilometersPerHour/3.6;
-
                 var sourceLocation = new GeoCoordinate(_client.CurrentLatitude, _client.CurrentLongitude);

                 var nextWaypointBearing = LocationUtils.DegreeBearing(sourceLocation, targetLocation);
-                var nextWaypointDistance = speedInMetersPerSecond;
+                var nextWaypointDistance = getspeedInMetersPerSecond(walkingSpeedInKilometersPerHour,
+                    walkingSpeedOffSetInKilometersPerHour);
+                ;
                 var waypoint = LocationUtils.CreateWaypoint(sourceLocation, nextWaypointDistance, nextWaypointBearing);

                 //Initial walking
                 var requestSendDateTime = DateTime.Now;
-                var result =
+                var result =
                     await
                         _client.Player.UpdatePlayerLocation(waypoint.Latitude, waypoint.Longitude,
                             waypoint.Altitude);
@@ -52,6 +52,8 @@ namespace PokemonGo.RocketBot.Logic

                 do
                 {
+                    var speedInMetersPerSecond = getspeedInMetersPerSecond(walkingSpeedInKilometersPerHour,
+                        walkingSpeedOffSetInKilometersPerHour);
                     cancellationToken.ThrowIfCancellationRequested();

                     var millisecondsUntilGetUpdatePlayerLocationResponse =
@@ -70,7 +72,7 @@ namespace PokemonGo.RocketBot.Logic
                     }

                     nextWaypointDistance = Math.Min(currentDistanceToTarget,
-                        millisecondsUntilGetUpdatePlayerLocationResponse/1000*speedInMetersPerSecond);
+                        millisecondsUntilGetUpdatePlayerLocationResponse / 1000 * speedInMetersPerSecond);
                     nextWaypointBearing = LocationUtils.DegreeBearing(sourceLocation, targetLocation);
                     waypoint = LocationUtils.CreateWaypoint(sourceLocation, nextWaypointDistance, nextWaypointBearing);

@@ -94,7 +96,7 @@ namespace PokemonGo.RocketBot.Logic
             var dist = LocationUtils.CalculateDistanceInMeters(curLocation, targetLocation);
             if (dist >= 100)
             {
-                var nextWaypointDistance = dist*70/100;
+                var nextWaypointDistance = dist * 70 / 100;
                 var nextWaypointBearing = LocationUtils.DegreeBearing(curLocation, targetLocation);

                 var waypoint = LocationUtils.CreateWaypoint(curLocation, nextWaypointDistance, nextWaypointBearing);
@@ -119,7 +121,7 @@ namespace PokemonGo.RocketBot.Logic
                     dist = LocationUtils.CalculateDistanceInMeters(curLocation, targetLocation);
                     if (dist >= 100)
                     {
-                        nextWaypointDistance = dist*70/100;
+                        nextWaypointDistance = dist * 70 / 100;
                     }
                     else
                     {
@@ -146,7 +148,7 @@ namespace PokemonGo.RocketBot.Logic
                 var result =
                     await
                         _client.Player.UpdatePlayerLocation(targetLocation.Latitude, targetLocation.Longitude,
-                            LocationUtils.getElevation(targetLocation.Latitude,targetLocation.Longitude));
+                            LocationUtils.getElevation(targetLocation.Latitude, targetLocation.Longitude));
                 UpdatePositionEvent?.Invoke(targetLocation.Latitude, targetLocation.Longitude);
                 if (functionExecutedWhileWalking != null)
                     await functionExecutedWhileWalking(); // look for pokemon
@@ -165,7 +167,7 @@ namespace PokemonGo.RocketBot.Logic
             var targetLocation = new GeoCoordinate(Convert.ToDouble(trk.Lat, CultureInfo.InvariantCulture),
                 Convert.ToDouble(trk.Lon, CultureInfo.InvariantCulture));

-            var speedInMetersPerSecond = walkingSpeedInKilometersPerHour/3.6;
+            var speedInMetersPerSecond = walkingSpeedInKilometersPerHour / 3.6;

             var sourceLocation = new GeoCoordinate(_client.CurrentLatitude, _client.CurrentLongitude);
             LocationUtils.CalculateDistanceInMeters(sourceLocation, targetLocation);
@@ -205,7 +207,7 @@ namespace PokemonGo.RocketBot.Logic
                 //}

                 nextWaypointDistance = Math.Min(currentDistanceToTarget,
-                    millisecondsUntilGetUpdatePlayerLocationResponse/1000*speedInMetersPerSecond);
+                    millisecondsUntilGetUpdatePlayerLocationResponse / 1000 * speedInMetersPerSecond);
                 nextWaypointBearing = LocationUtils.DegreeBearing(sourceLocation, targetLocation);
                 waypoint = LocationUtils.CreateWaypoint(sourceLocation, nextWaypointDistance, nextWaypointBearing);

@@ -224,6 +226,18 @@ namespace PokemonGo.RocketBot.Logic
             return result;
         }

+        public double getspeedInMetersPerSecond(double SpeedInKilometersPerHour, double SpeedOffSetInKilometersPerHour)
+        {
+            var random = new Random();
+            double offset;
+            if (random.Next(0, 2) == 1)
+                offset = random.NextDouble() * SpeedOffSetInKilometersPerHour;
+            else
+                offset = -random.NextDouble() * SpeedOffSetInKilometersPerHour;
+
+            return (SpeedInKilometersPerHour + offset) / 3.6;
+        }
+
         public event UpdatePositionDelegate UpdatePositionEvent;
     }
 }
\ No newline at end of file
diff --git a/PokemonGo.RocketBot.Logic/PokemonGo.RocketBot.Logic.csproj b/PokemonGo.RocketBot.Logic/PokemonGo.RocketBot.Logic.csproj
index 525714a..77c08a2 100644
--- a/PokemonGo.RocketBot.Logic/PokemonGo.RocketBot.Logic.csproj
+++ b/PokemonGo.RocketBot.Logic/PokemonGo.RocketBot.Logic.csproj
@@ -115,6 +115,7 @@
     <Compile Include="Event\LootPokestopEvent.cs" />
     <Compile Include="Event\NoPokeballEvent.cs" />
     <Compile Include="Event\OptimizeRouteEvent.cs" />
+    <Compile Include="Event\PokemonsEncounterEvent.cs" />
     <Compile Include="Event\SnipeEvent.cs" />
     <Compile Include="Event\SnipeModeEvent.cs" />
     <Compile Include="Event\PokemonListEvent.cs" />
diff --git a/PokemonGo.RocketBot.Logic/Settings.cs b/PokemonGo.RocketBot.Logic/Settings.cs
index 3f3bb33..e7fac0b 100644
--- a/PokemonGo.RocketBot.Logic/Settings.cs
+++ b/PokemonGo.RocketBot.Logic/Settings.cs
@@ -347,6 +347,8 @@ namespace PokemonGo.RocketBot.Logic
         public double DefaultLongitude;
         [DefaultValue(19.0)]
         public double WalkingSpeedInKilometerPerHour;
+        [DefaultValue(2)]
+        public double WalkingSpeedOffSetInKilometerPerHour;
         [DefaultValue(10)]
         public int MaxSpawnLocationOffset;
         //softban related
@@ -1320,6 +1322,7 @@ namespace PokemonGo.RocketBot.Logic
         public float UpgradePokemonCpMinimum => _settings.UpgradePokemonCpMinimum;
         public string UpgradePokemonMinimumStatsOperator => _settings.UpgradePokemonMinimumStatsOperator;
         public double WalkingSpeedInKilometerPerHour => _settings.WalkingSpeedInKilometerPerHour;
+        public double WalkingSpeedOffSetInKilometerPerHour => _settings.WalkingSpeedOffSetInKilometerPerHour;
         public bool FastSoftBanBypass => _settings.FastSoftBanBypass;
         public bool EvolveAllPokemonWithEnoughCandy => _settings.EvolveAllPokemonWithEnoughCandy;
         public bool KeepPokemonsThatCanEvolve => _settings.KeepPokemonsThatCanEvolve;
diff --git a/PokemonGo.RocketBot.Logic/Tasks/CatchIncensePokemonsTask.cs b/PokemonGo.RocketBot.Logic/Tasks/CatchIncensePokemonsTask.cs
index 2abbab2..a8ab015 100644
--- a/PokemonGo.RocketBot.Logic/Tasks/CatchIncensePokemonsTask.cs
+++ b/PokemonGo.RocketBot.Logic/Tasks/CatchIncensePokemonsTask.cs
@@ -9,18 +9,21 @@ using PokemonGo.RocketBot.Logic.State;
 using PokemonGo.RocketBot.Logic.Utils;
 using POGOProtos.Map.Pokemon;
 using POGOProtos.Networking.Responses;
+using System.Collections.Generic;

 #endregion

 namespace PokemonGo.RocketBot.Logic.Tasks
 {
+    public delegate void PokemonsEncounterDelegate(List<MapPokemon> pokemons);
+
     public static class CatchIncensePokemonsTask
     {
         public static async Task Execute(ISession session, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
             if (!session.LogicSettings.CatchPokemon) return;
-
+
             Logger.Write(session.Translation.GetTranslation(TranslationString.LookingForIncensePokemon), LogLevel.Debug);

             var incensePokemon = await session.Client.Map.GetIncensePokemons();
@@ -36,8 +39,9 @@ namespace PokemonGo.RocketBot.Logic.Tasks
                     SpawnPointId = incensePokemon.EncounterLocation
                 };

-                if( ( session.LogicSettings.UsePokemonSniperFilterOnly && !session.LogicSettings.PokemonToSnipe.Pokemon.Contains( pokemon.PokemonId ) ) ||
-                    ( session.LogicSettings.UsePokemonToNotCatchFilter && session.LogicSettings.PokemonsNotToCatch.Contains( pokemon.PokemonId ) ) )
+                OnPokemonEncounterEvent(new List<MapPokemon> { pokemon });
+                if ((session.LogicSettings.UsePokemonSniperFilterOnly && !session.LogicSettings.PokemonToSnipe.Pokemon.Contains(pokemon.PokemonId)) ||
+                    (session.LogicSettings.UsePokemonToNotCatchFilter && session.LogicSettings.PokemonsNotToCatch.Contains(pokemon.PokemonId)))
                 {
                     Logger.Write(session.Translation.GetTranslation(TranslationString.PokemonIgnoreFilter,
                         session.Translation.GetPokemonTranslation(pokemon.PokemonId)));
@@ -50,7 +54,7 @@ namespace PokemonGo.RocketBot.Logic.Tasks

                     var encounter =
                         await
-                            session.Client.Encounter.EncounterIncensePokemon((ulong) pokemon.EncounterId,
+                            session.Client.Encounter.EncounterIncensePokemon((ulong)pokemon.EncounterId,
                                 pokemon.SpawnPointId);

                     if (encounter.Result == IncenseEncounterResponse.Types.Result.IncenseEncounterSuccess && session.LogicSettings.CatchPokemon)
@@ -65,7 +69,7 @@ namespace PokemonGo.RocketBot.Logic.Tasks
                             {
                                 Message = session.Translation.GetTranslation(TranslationString.InvFullTransferring)
                             });
-                            await  TransferDuplicatePokemonTask.Execute(session, cancellationToken);
+                            await TransferDuplicatePokemonTask.Execute(session, cancellationToken);
                         }
                         else
                             session.EventDispatcher.Send(new WarnEvent
@@ -84,5 +88,13 @@ namespace PokemonGo.RocketBot.Logic.Tasks
                 }
             }
         }
+
+        public static event PokemonsEncounterDelegate PokemonEncounterEvent;
+
+        private static void OnPokemonEncounterEvent(List<MapPokemon> pokemons)
+        {
+            PokemonEncounterEvent?.Invoke(pokemons);
+        }
     }
+
 }
\ No newline at end of file
diff --git a/PokemonGo.RocketBot.Logic/Tasks/CatchLurePokemonsTask.cs b/PokemonGo.RocketBot.Logic/Tasks/CatchLurePokemonsTask.cs
index d87a449..a258803 100644
--- a/PokemonGo.RocketBot.Logic/Tasks/CatchLurePokemonsTask.cs
+++ b/PokemonGo.RocketBot.Logic/Tasks/CatchLurePokemonsTask.cs
@@ -8,6 +8,7 @@ using PokemonGo.RocketBot.Logic.Logging;
 using PokemonGo.RocketBot.Logic.State;
 using POGOProtos.Map.Fort;
 using POGOProtos.Networking.Responses;
+using POGOProtos.Map.Pokemon;

 #endregion

@@ -25,8 +26,8 @@ namespace PokemonGo.RocketBot.Logic.Tasks
             var fortId = currentFortData.Id;

             var pokemonId = currentFortData.LureInfo.ActivePokemonId;
-
-            if( ( session.LogicSettings.UsePokemonSniperFilterOnly && !session.LogicSettings.PokemonToSnipe.Pokemon.Contains( pokemonId ) ) ||
+
+            if ( ( session.LogicSettings.UsePokemonSniperFilterOnly && !session.LogicSettings.PokemonToSnipe.Pokemon.Contains( pokemonId ) ) ||
                     ( session.LogicSettings.UsePokemonToNotCatchFilter && session.LogicSettings.PokemonsNotToCatch.Contains( pokemonId ) ) )
             {
                 session.EventDispatcher.Send(new NoticeEvent
diff --git a/PokemonGo.RocketBot.Logic/Tasks/CatchNearbyPokemonsTask.cs b/PokemonGo.RocketBot.Logic/Tasks/CatchNearbyPokemonsTask.cs
index 16a267a..eda5c2f 100644
--- a/PokemonGo.RocketBot.Logic/Tasks/CatchNearbyPokemonsTask.cs
+++ b/PokemonGo.RocketBot.Logic/Tasks/CatchNearbyPokemonsTask.cs
@@ -11,6 +11,7 @@ using PokemonGo.RocketBot.Logic.Utils;
 using POGOProtos.Inventory.Item;
 using POGOProtos.Map.Pokemon;
 using POGOProtos.Networking.Responses;
+using System.Collections.Generic;

 #endregion

@@ -18,6 +19,8 @@ namespace PokemonGo.RocketBot.Logic.Tasks
 {
     public static class CatchNearbyPokemonsTask
     {
+        public delegate void PokemonsEncounterDelegate(List<MapPokemon> pokemons);
+
         public static async Task Execute(ISession session, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
@@ -26,6 +29,7 @@ namespace PokemonGo.RocketBot.Logic.Tasks
             Logger.Write(session.Translation.GetTranslation(TranslationString.LookingForPokemon), LogLevel.Debug);

             var pokemons = await GetNearbyPokemons(session);
+            OnPokemonEncounterEvent(pokemons.ToList());
             foreach (var pokemon in pokemons)
             {
                 cancellationToken.ThrowIfCancellationRequested();
@@ -49,8 +53,8 @@ namespace PokemonGo.RocketBot.Logic.Tasks
                     return;
                 }

-                if( ( session.LogicSettings.UsePokemonSniperFilterOnly && !session.LogicSettings.PokemonToSnipe.Pokemon.Contains( pokemon.PokemonId ) ) ||
-                    ( session.LogicSettings.UsePokemonToNotCatchFilter && session.LogicSettings.PokemonsNotToCatch.Contains( pokemon.PokemonId ) ) )
+                if ((session.LogicSettings.UsePokemonSniperFilterOnly && !session.LogicSettings.PokemonToSnipe.Pokemon.Contains(pokemon.PokemonId)) ||
+                    (session.LogicSettings.UsePokemonToNotCatchFilter && session.LogicSettings.PokemonsNotToCatch.Contains(pokemon.PokemonId)))
                 {
                     Logger.Write(session.Translation.GetTranslation(TranslationString.PokemonSkipped, session.Translation.GetPokemonTranslation(pokemon.PokemonId)));
                     continue;
@@ -113,5 +117,12 @@ namespace PokemonGo.RocketBot.Logic.Tasks

             return pokemons;
         }
+
+        public static event PokemonsEncounterDelegate PokemonEncounterEvent;
+
+        private static void OnPokemonEncounterEvent(List<MapPokemon> pokemons)
+        {
+            PokemonEncounterEvent?.Invoke(pokemons);
+        }
     }
 }
\ No newline at end of file
diff --git a/PokemonGo.RocketBot.Logic/Tasks/FarmPokestopsTask.cs b/PokemonGo.RocketBot.Logic/Tasks/FarmPokestopsTask.cs
index 8f2d85c..254fac1 100644
--- a/PokemonGo.RocketBot.Logic/Tasks/FarmPokestopsTask.cs
+++ b/PokemonGo.RocketBot.Logic/Tasks/FarmPokestopsTask.cs
@@ -44,7 +44,7 @@ namespace PokemonGo.RocketBot.Logic.Tasks

                 await session.Navigation.Move(
                     new GeoCoordinate(session.Settings.DefaultLatitude, session.Settings.DefaultLongitude, LocationUtils.getElevation(session.Settings.DefaultLatitude, session.Settings.DefaultLongitude)),
-                    session.LogicSettings.WalkingSpeedInKilometerPerHour, null, cancellationToken, session.LogicSettings.DisableHumanWalking);
+                    session.LogicSettings.WalkingSpeedInKilometerPerHour, session.LogicSettings.WalkingSpeedOffSetInKilometerPerHour, null, cancellationToken, session.LogicSettings.DisableHumanWalking);
             }

             var pokestopList = await GetPokeStops(session);
@@ -78,7 +78,7 @@ namespace PokemonGo.RocketBot.Logic.Tasks
                 session.EventDispatcher.Send(new FortTargetEvent { Name = fortInfo.Name, Distance = distance });

                 await session.Navigation.Move(new GeoCoordinate(pokeStop.Latitude, pokeStop.Longitude, LocationUtils.getElevation(pokeStop.Latitude, pokeStop.Longitude)),
-                session.LogicSettings.WalkingSpeedInKilometerPerHour,
+                session.LogicSettings.WalkingSpeedInKilometerPerHour, session.LogicSettings.WalkingSpeedOffSetInKilometerPerHour,
                 async () =>
                 {
                     // Catch normal map Pokemon
diff --git a/PokemonGo.RocketBot.Logic/Utils/WebUtils.cs b/PokemonGo.RocketBot.Logic/Utils/WebUtils.cs
index 4878814..e28a299 100644
--- a/PokemonGo.RocketBot.Logic/Utils/WebUtils.cs
+++ b/PokemonGo.RocketBot.Logic/Utils/WebUtils.cs
@@ -51,7 +51,6 @@ namespace PokemonGo.RocketBot.Logic.Utils
                     "The server returned data in an unknown encoding: " + charsetName,
                     ex);
                     */
-                var a = 1;
                 return null;
             }
         }
diff --git a/PokemonGo.RocketBot.Window/Forms/MainForm.cs b/PokemonGo.RocketBot.Window/Forms/MainForm.cs
index 29721c5..d647a3f 100644
--- a/PokemonGo.RocketBot.Window/Forms/MainForm.cs
+++ b/PokemonGo.RocketBot.Window/Forms/MainForm.cs
@@ -34,6 +34,7 @@ using POGOProtos.Data;
 using POGOProtos.Inventory;
 using POGOProtos.Inventory.Item;
 using POGOProtos.Map.Fort;
+using POGOProtos.Map.Pokemon;
 using POGOProtos.Networking.Responses;

 namespace PokemonGo.RocketBot.Window.Forms
@@ -54,6 +55,8 @@ namespace PokemonGo.RocketBot.Window.Forms
         private readonly GMapOverlay _pokemonsOverlay = new GMapOverlay("pokemons");
         private readonly GMapOverlay _pokestopsOverlay = new GMapOverlay("pokestops");
         private readonly GMapOverlay _searchAreaOverlay = new GMapOverlay("areas");
+
+        private PointLatLng _currentLatLng;
         private ConsoleLogger _logger;
         private StateMachine _machine;
         private GlobalSettings _settings;
@@ -100,7 +103,7 @@ namespace PokemonGo.RocketBot.Window.Forms
             gMapControl1.Overlays.Add(_playerOverlay);

             _playerMarker = new GMapMarkerTrainer(new PointLatLng(lat, lng),
-                (Image) Properties.Resources.ResourceManager.GetObject("Trainer"));
+                ResourceHelper.GetImage("Trainer_Front"));
             _playerOverlay.Markers.Add(_playerMarker);
             _playerMarker.Position = new PointLatLng(lat, lng);
             _searchAreaOverlay.Polygons.Clear();
@@ -191,22 +194,31 @@ namespace PokemonGo.RocketBot.Window.Forms
             Logger.SetLoggerContext(_session);

             _session.Navigation.UpdatePositionEvent +=
-                (lat, lng) => _session.EventDispatcher.Send(new UpdatePositionEvent {Latitude = lat, Longitude = lng});
+                (lat, lng) => _session.EventDispatcher.Send(new UpdatePositionEvent { Latitude = lat, Longitude = lng });
             _session.Navigation.UpdatePositionEvent += Navigation_UpdatePositionEvent;

             RouteOptimizeUtil.RouteOptimizeEvent +=
                 optimizedroute =>
-                    _session.EventDispatcher.Send(new OptimizeRouteEvent {OptimizedRoute = optimizedroute});
+                    _session.EventDispatcher.Send(new OptimizeRouteEvent { OptimizedRoute = optimizedroute });
             RouteOptimizeUtil.RouteOptimizeEvent += InitializePokestopsAndRoute;

             FarmPokestopsTask.LootPokestopEvent +=
-                pokestop => _session.EventDispatcher.Send(new LootPokestopEvent {Pokestop = pokestop});
+                pokestop => _session.EventDispatcher.Send(new LootPokestopEvent { Pokestop = pokestop });
             FarmPokestopsTask.LootPokestopEvent += UpdateMap;
+
+            CatchNearbyPokemonsTask.PokemonEncounterEvent +=
+                mappokemons =>
+                    _session.EventDispatcher.Send(new PokemonsEncounterEvent { EncounterPokemons = mappokemons });
+            CatchNearbyPokemonsTask.PokemonEncounterEvent += UpdateMap;
+
+            CatchIncensePokemonsTask.PokemonEncounterEvent +=
+                mappokemons =>
+                    _session.EventDispatcher.Send(new PokemonsEncounterEvent { EncounterPokemons = mappokemons });
+            CatchIncensePokemonsTask.PokemonEncounterEvent += UpdateMap;
         }

         private async Task StartBot()
         {
-            startStopBotToolStripMenuItem.Enabled = false;
             _machine.AsyncStart(new VersionCheckState(), _session);

             if (_settings.UseTelegramAPI)
@@ -219,66 +231,106 @@ namespace PokemonGo.RocketBot.Window.Forms
             QuitEvent.WaitOne();
         }

-        private async void InitializePokestopsAndRoute(List<FortData> pokeStops)
+        private void InitializePokestopsAndRoute(List<FortData> pokeStops)
         {
-            _pokestopsOverlay.Markers.Clear();
-            var routePoint =
-                (from pokeStop in pokeStops
-                    where pokeStop != null
-                    select new PointLatLng(pokeStop.Latitude, pokeStop.Longitude)).ToList();
-            _pokestopsOverlay.Routes.Clear();
-            var route = new GMapRoute(routePoint, "Walking Path");
-            route.Stroke = new Pen(Color.FromArgb(128, 0, 179, 253), 4);
-            _pokestopsOverlay.Routes.Add(route);
-
-            foreach (var pokeStop in pokeStops)
-            {
-                var pokeStopLoc = new PointLatLng(pokeStop.Latitude, pokeStop.Longitude);
-                var pokestopMarker = new GMapMarkerPokestops(pokeStopLoc,
-                    (Image) Properties.Resources.ResourceManager.GetObject("Pokestop"));
-                _pokestopsOverlay.Markers.Add(pokestopMarker);
-            }
+            SynchronizationContext.Post(o =>
+            {
+                _pokestopsOverlay.Markers.Clear();
+                _pokestopsOverlay.Routes.Clear();
+                _playerOverlay.Markers.Clear();
+                _playerOverlay.Routes.Clear();
+                _playerLocations.Clear();
+                var routePoint =
+                    (from pokeStop in pokeStops
+                     where pokeStop != null
+                     select new PointLatLng(pokeStop.Latitude, pokeStop.Longitude)).ToList();
+
+                var route = new GMapRoute(routePoint, "Walking Path")
+                {
+                    Stroke = new Pen(Color.FromArgb(128, 0, 179, 253), 4)
+                };
+                _pokestopsOverlay.Routes.Add(route);
+
+                foreach (var pokeStop in pokeStops)
+                {
+                    var pokeStopLoc = new PointLatLng(pokeStop.Latitude, pokeStop.Longitude);
+                    var pokestopMarker = new GMapMarkerPokestops(pokeStopLoc,
+                        ResourceHelper.GetImage("Pokestop"));
+                    _pokestopsOverlay.Markers.Add(pokestopMarker);
+                }
+            }, null);
         }

-        private void UpdateMap(FortData pokestop = null)
+        private void UpdateMap()
         {
             SynchronizationContext.Post(o =>
             {
-                if (pokestop != null)
+                var route = new GMapRoute(_playerLocations, "step")
                 {
-                    var pokeStopLoc = new PointLatLng(pokestop.Latitude, pokestop.Longitude);
+                    Stroke = new Pen(Color.FromArgb(175, 175, 175), 2) { DashStyle = DashStyle.Dot }
+                };
+                _playerOverlay.Routes.Clear();
+                _playerOverlay.Routes.Add(route);
+            }, null);
+        }
+
+        private void UpdateMap(FortData pokestop)
+        {
+            SynchronizationContext.Post(o =>
+            {
+                var pokeStopLoc = new PointLatLng(pokestop.Latitude, pokestop.Longitude);

-                    lock (_pokestopsOverlay.Markers)
+                lock (_pokestopsOverlay.Markers)
+                {
+                    for (var i = 0; i < _pokestopsOverlay.Markers.Count; i++)
                     {
-                        for (var i = 0; i < _pokestopsOverlay.Markers.Count; i++)
-                        {
-                            var marker = _pokestopsOverlay.Markers[i];
-                            if (marker.Position == pokeStopLoc)
-                                _pokestopsOverlay.Markers.Remove(marker);
-                        }
+                        var marker = _pokestopsOverlay.Markers[i];
+                        if (marker.Position == pokeStopLoc)
+                            _pokestopsOverlay.Markers.Remove(marker);
                     }
-
-                    GMapMarker pokestopMarker = new GMapMarkerPokestops(pokeStopLoc,
-                        (Image) Properties.Resources.ResourceManager.GetObject("Pokestop_looted"));
-                    //pokestopMarker.ToolTipMode = MarkerTooltipMode.OnMouseOver;
-                    //pokestopMarker.ToolTip = new GMapBaloonToolTip(pokestopMarker);
-                    _pokestopsOverlay.Markers.Add(pokestopMarker);
                 }

-                var route = new GMapRoute(_playerLocations, "step");
-                route.Stroke = new Pen(Color.FromArgb(175, 175, 175), 2);
-                route.Stroke.DashStyle = DashStyle.Dot;
-                _playerOverlay.Routes.Add(route);
-                _playerOverlay.Routes.Clear();
-                _playerOverlay.Routes.Add(route);
+                GMapMarker pokestopMarker = new GMapMarkerPokestops(pokeStopLoc,
+                    ResourceHelper.GetImage("Pokestop_looted"));
+                //pokestopMarker.ToolTipMode = MarkerTooltipMode.OnMouseOver;
+                //pokestopMarker.ToolTip = new GMapBaloonToolTip(pokestopMarker);
+                _pokestopsOverlay.Markers.Add(pokestopMarker);
+            }, null);
+        }
+
+        private void UpdateMap(List<MapPokemon> encounterPokemons)
+        {
+            SynchronizationContext.Post(o =>
+            {
+                _pokemonsOverlay.Markers.Clear();
+
+                foreach (var pokemon in encounterPokemons)
+                {
+                    var pkmImage = ResourceHelper.GetImage("Pokemon_" + pokemon.PokemonId.GetHashCode(), 50, 50);
+                    var pointLatLng = new PointLatLng(pokemon.Latitude, pokemon.Longitude);
+                    GMapMarker pkmMarker = new GMapMarkerTrainer(pointLatLng, pkmImage);
+                    _pokemonsOverlay.Markers.Add(pkmMarker);
+                }
             }, null);
         }

         private void Navigation_UpdatePositionEvent(double lat, double lng)
         {
             var latlng = new PointLatLng(lat, lng);
+
             _playerLocations.Add(latlng);
-            _playerMarker.Position = latlng;
+            var currentlatlng = _currentLatLng;
+            SynchronizationContext.Post(o =>
+            {
+                _playerOverlay.Markers.Remove(_playerMarker);
+                if (!currentlatlng.IsEmpty)
+                    _playerMarker = currentlatlng.Lng < latlng.Lng
+                        ? new GMapMarkerTrainer(latlng, ResourceHelper.GetImage("Trainer_Right"))
+                        : new GMapMarkerTrainer(latlng, ResourceHelper.GetImage("Trainer_Left"));
+                _playerOverlay.Markers.Add(_playerMarker);
+            }, null);
+
+            _currentLatLng = latlng;
             UpdateMap();
             SaveLocationToDisk(lat, lng);
         }
@@ -372,7 +424,8 @@ namespace PokemonGo.RocketBot.Window.Forms

         private void startStopBotToolStripMenuItem_Click(object sender, EventArgs e)
         {
-            Task.Run(() => StartBot());
+            startStopBotToolStripMenuItem.Enabled = false;
+            Task.Run(StartBot);
         }

         private void todoToolStripMenuItem_Click(object sender, EventArgs e)
@@ -391,20 +444,20 @@ namespace PokemonGo.RocketBot.Window.Forms
         {
             //olvPokemonList.ButtonClick += PokemonListButton_Click;

-            pkmnName.ImageGetter = delegate(object rowObject)
+            pkmnName.ImageGetter = delegate (object rowObject)
             {
                 var pokemon = rowObject as PokemonObject;

                 var key = pokemon.PokemonId.ToString();
                 if (!olvPokemonList.SmallImageList.Images.ContainsKey(key))
                 {
-                    var img = GetPokemonImage((int) pokemon.PokemonId);
+                    var img = GetPokemonImage((int)pokemon.PokemonId);
                     olvPokemonList.SmallImageList.Images.Add(key, img);
                 }
                 return key;
             };

-            olvPokemonList.FormatRow += delegate(object sender, FormatRowEventArgs e)
+            olvPokemonList.FormatRow += delegate (object sender, FormatRowEventArgs e)
             {
                 var pok = e.Model as PokemonObject;
                 if (olvPokemonList.Objects.Cast<PokemonObject>()
@@ -422,7 +475,7 @@ namespace PokemonGo.RocketBot.Window.Forms
                 }
             };

-            cmsPokemonList.Opening += delegate(object sender, CancelEventArgs e)
+            cmsPokemonList.Opening += delegate (object sender, CancelEventArgs e)
             {
                 e.Cancel = false;
                 cmsPokemonList.Items.Clear();
@@ -678,7 +731,7 @@ namespace PokemonGo.RocketBot.Window.Forms

         private Image GetPokemonImage(int pokemonId)
         {
-            return (Image) Properties.Resources.ResourceManager.GetObject("Pokemon_" + pokemonId);
+            return ResourceHelper.GetImage("Pokemon_" + pokemonId);
         }

         private async Task ReloadPokemonList()
@@ -723,7 +776,7 @@ namespace PokemonGo.RocketBot.Window.Forms
                 {
                     var pokemonObject = new PokemonObject(pokemon);
                     var family =
-                        _families.Where(i => (int) i.FamilyId <= (int) pokemon.PokemonId)
+                        _families.Where(i => (int)i.FamilyId <= (int)pokemon.PokemonId)
                             .First();
                     pokemonObject.Candy = family.Candy_;
                     pokemonObjects.Add(pokemonObject);
@@ -777,7 +830,7 @@ namespace PokemonGo.RocketBot.Window.Forms

         private async void ItemBox_ItemClick(object sender, EventArgs e)
         {
-            var item = (ItemData) sender;
+            var item = (ItemData)sender;

             using (var form = new ItemForm(item))
             {
diff --git a/PokemonGo.RocketBot.Window/Forms/MainForm.designer.cs b/PokemonGo.RocketBot.Window/Forms/MainForm.designer.cs
index 3d8f6f5..4f154ca 100644
--- a/PokemonGo.RocketBot.Window/Forms/MainForm.designer.cs
+++ b/PokemonGo.RocketBot.Window/Forms/MainForm.designer.cs
@@ -264,7 +264,7 @@ namespace PokemonGo.RocketBot.Window.Forms
             this.pkmnIV.AspectName = "GetIV";
             this.pkmnIV.AspectToStringFormat = "{0:P2}";
             this.pkmnIV.Text = "IV %";
-            this.pkmnIV.Width = 51;
+            this.pkmnIV.Width = 52;
             //
             // pkmnCandy
             //
diff --git a/PokemonGo.RocketBot.Window/Helpers/ResourceHelper.cs b/PokemonGo.RocketBot.Window/Helpers/ResourceHelper.cs
new file mode 100644
index 0000000..e976e5a
--- /dev/null
+++ b/PokemonGo.RocketBot.Window/Helpers/ResourceHelper.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PokemonGo.RocketBot.Window.Helpers
+{
+    public class ResourceHelper
+    {
+        public static Image GetImage(string name)
+        {
+            return (Image)Properties.Resources.ResourceManager.GetObject(name);
+        }
+
+        public static Image GetImage(string name, int maxHeight, int maxWidth)
+        {
+            Image image = GetImage(name);
+            var ratioX = (double)maxWidth / image.Width;
+            var ratioY = (double)maxHeight / image.Height;
+            var ratio = Math.Min(ratioX, ratioY);
+
+            var newWidth = (int)(image.Width * ratio);
+            var newHeight = (int)(image.Height * ratio);
+
+            var newImage = new Bitmap(newWidth, newHeight);
+
+            using (var graphics = Graphics.FromImage(newImage))
+                graphics.DrawImage(image, 0, 0, newWidth, newHeight);
+
+            return newImage;
+        }
+    }
+}
diff --git a/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Front.png b/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Front.png
new file mode 100644
index 0000000..20d619e
Binary files /dev/null and b/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Front.png differ
diff --git a/PokemonGo.RocketBot.Window/Images/Markers/Trainer.png b/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Left.png
similarity index 100%
rename from PokemonGo.RocketBot.Window/Images/Markers/Trainer.png
rename to PokemonGo.RocketBot.Window/Images/Markers/Trainer_Left.png
diff --git a/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Right.png b/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Right.png
new file mode 100644
index 0000000..5113c95
Binary files /dev/null and b/PokemonGo.RocketBot.Window/Images/Markers/Trainer_Right.png differ
diff --git a/PokemonGo.RocketBot.Window/PokemomObject.cs b/PokemonGo.RocketBot.Window/PokemomObject.cs
index aaf8eba..75c100d 100644
--- a/PokemonGo.RocketBot.Window/PokemomObject.cs
+++ b/PokemonGo.RocketBot.Window/PokemomObject.cs
@@ -51,7 +51,7 @@ namespace PokemonGo.RocketBot.Window

         public double GetIV
         {
-            get { return Math.Round(PokemonInfo.CalculatePokemonPerfection(PokemonData), 2); }
+            get { return Math.Round(PokemonInfo.CalculatePokemonPerfection(PokemonData) / 100, 2); }
         }

         public double GetLv
@@ -84,7 +84,7 @@ namespace PokemonGo.RocketBot.Window
             {
                 if (CandyToEvolve > 0)
                 {
-                    return Candy/CandyToEvolve;
+                    return Candy / CandyToEvolve;
                 }
                 return 0;
             }
diff --git a/PokemonGo.RocketBot.Window/PokemonGo.RocketBot.Window.csproj b/PokemonGo.RocketBot.Window/PokemonGo.RocketBot.Window.csproj
index 21fe0e8..8eb59ad 100644
--- a/PokemonGo.RocketBot.Window/PokemonGo.RocketBot.Window.csproj
+++ b/PokemonGo.RocketBot.Window/PokemonGo.RocketBot.Window.csproj
@@ -149,6 +149,7 @@
     <Compile Include="Forms\MainForm.designer.cs">
       <DependentUpon>MainForm.cs</DependentUpon>
     </Compile>
+    <Compile Include="Helpers\ResourceHelper.cs" />
     <Compile Include="Helpers\S2GMapDrawer.cs" />
     <Compile Include="Models\GMapMarkerPokestops.cs" />
     <Compile Include="Models\GMapMarkerTrainer.cs" />
@@ -328,6 +329,9 @@
     <None Include="Images\Markers\Pokestop.png" />
     <None Include="Images\Markers\Pokestop_looted.png" />
     <None Include="Images\Markers\Trainer.png" />
+    <None Include="Images\Markers\Trainer_Front.png" />
+    <None Include="Images\Markers\Trainer_Left.png" />
+    <None Include="Images\Markers\Trainer_Right.png" />
     <Content Include="Images\Pokemon\Pokemon_1.png" />
     <Content Include="Images\Pokemon\Pokemon_10.png" />
     <Content Include="Images\Pokemon\Pokemon_100.png" />
@@ -479,8 +483,6 @@
     <Content Include="Images\Pokemon\Pokemon_97.png" />
     <Content Include="Images\Pokemon\Pokemon_98.png" />
     <Content Include="Images\Pokemon\Pokemon_99.png" />
-    <None Include="Images\Pokestop_Inrange.png" />
-    <None Include="Images\Trainer.png" />
     <Content Include="Resources\encrypt.dll">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
diff --git a/PokemonGo.RocketBot.Window/Properties/Resources.Designer.cs b/PokemonGo.RocketBot.Window/Properties/Resources.Designer.cs
index c2d8172..2a9c7d7 100644
--- a/PokemonGo.RocketBot.Window/Properties/Resources.Designer.cs
+++ b/PokemonGo.RocketBot.Window/Properties/Resources.Designer.cs
@@ -1833,9 +1833,29 @@ namespace PokemonGo.RocketBot.Window.Properties {
         /// <summary>
         ///   查找 System.Drawing.Bitmap 类型的本地化资源。
         /// </summary>
-        internal static System.Drawing.Bitmap Trainer {
+        internal static System.Drawing.Bitmap Trainer_Front {
             get {
-                object obj = ResourceManager.GetObject("Trainer", resourceCulture);
+                object obj = ResourceManager.GetObject("Trainer_Front", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Trainer_Left {
+            get {
+                object obj = ResourceManager.GetObject("Trainer_Left", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Trainer_Right {
+            get {
+                object obj = ResourceManager.GetObject("Trainer_Right", resourceCulture);
                 return ((System.Drawing.Bitmap)(obj));
             }
         }
diff --git a/PokemonGo.RocketBot.Window/Properties/Resources.resx b/PokemonGo.RocketBot.Window/Properties/Resources.resx
index 3795cd2..7ebfacf 100644
--- a/PokemonGo.RocketBot.Window/Properties/Resources.resx
+++ b/PokemonGo.RocketBot.Window/Properties/Resources.resx
@@ -1990,7 +1990,13 @@
   <data name="Pokestop_looted" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Images\Markers\Pokestop_looted.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
-  <data name="Trainer" type="System.Resources.ResXFileRef, System.Windows.Forms">
-    <value>..\Images\Markers\Trainer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  <data name="Trainer_Front" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Images\Markers\Trainer_Front.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Trainer_Left" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Images\Markers\Trainer_Left.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Trainer_Right" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Images\Markers\Trainer_Right.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
 </root>
\ No newline at end of file
You may download the files in Public Git.