diff --git a/UFBLauncher/App.config b/UFBLauncher/App.config new file mode 100644 index 0000000..7e1829e --- /dev/null +++ b/UFBLauncher/App.config @@ -0,0 +1,18 @@ + + + + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/UFBLauncher/Program.cs b/UFBLauncher/Program.cs new file mode 100644 index 0000000..d0302f6 --- /dev/null +++ b/UFBLauncher/Program.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Mime; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace UFBLauncher +{ + class Program + { + private const string ExeExt = ".exe"; + private const string DefaultName = "UltimateFishBot.exe"; + + static void Main(string[] args) + { + var lastName = Properties.Settings.Default.UFBName; + + if (!RunBotSecretly(lastName)) RunBotSecretly(DefaultName); + } + + [DllImport("user32.dll")] + static extern int SetWindowText(IntPtr hWnd, string text); + + static bool RunBotSecretly(string name) + { + var currentPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), name); + var ufbExeFile = new FileInfo(currentPath); + if (ufbExeFile.Exists) + { + var newName = StringUtils.RandomString(12) + ExeExt; + var newPath = Path.Combine(Path.GetDirectoryName(currentPath), newName); + ufbExeFile.MoveTo(newPath); + Properties.Settings.Default.UFBName = newName; + Properties.Settings.Default.Save(); + var p = Process.Start(ufbExeFile.FullName); + Thread.Sleep(1500); + SetWindowText(p.MainWindowHandle, newName); + return true; + } + return false; + } + } +} diff --git a/UFBLauncher/Properties/AssemblyInfo.cs b/UFBLauncher/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..30b25ac --- /dev/null +++ b/UFBLauncher/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UFBLauncher")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UFBLauncher")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6529fb5b-d58f-4e12-aa91-5c694e44b142")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/UFBLauncher/Properties/Settings.Designer.cs b/UFBLauncher/Properties/Settings.Designer.cs new file mode 100644 index 0000000..d799737 --- /dev/null +++ b/UFBLauncher/Properties/Settings.Designer.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace UFBLauncher.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.5.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string UFBName { + get { + return ((string)(this["UFBName"])); + } + set { + this["UFBName"] = value; + } + } + } +} diff --git a/UFBLauncher/Properties/Settings.settings b/UFBLauncher/Properties/Settings.settings new file mode 100644 index 0000000..166d0c7 --- /dev/null +++ b/UFBLauncher/Properties/Settings.settings @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/UFBLauncher/StringUtils.cs b/UFBLauncher/StringUtils.cs new file mode 100644 index 0000000..f8c12ed --- /dev/null +++ b/UFBLauncher/StringUtils.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UFBLauncher +{ + internal class StringUtils + { + private static readonly Random Random = new Random(); + public static string RandomString(int length, string chars = "abcdefghijklmnopqrstuvwxyz0123456789") + { + return new string(Enumerable.Repeat(chars, length) + .Select(s => s[Random.Next(s.Length)]).ToArray()); + } + } +} diff --git a/UFBLauncher/UFBLauncher.csproj b/UFBLauncher/UFBLauncher.csproj new file mode 100644 index 0000000..98ff9fa --- /dev/null +++ b/UFBLauncher/UFBLauncher.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142} + Exe + UFBLauncher + UFBLauncher + v4.6.1 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + {e4705d9f-56cd-48cb-863b-4363d03c8424} + UltimateFishBot + + + + \ No newline at end of file diff --git a/UltimateFishBot.sln b/UltimateFishBot.sln index 1100060..f5feb0b 100644 --- a/UltimateFishBot.sln +++ b/UltimateFishBot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27428.2015 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UltimateFishBot", "UltimateFishBot\UltimateFishBot.csproj", "{E4705D9F-56CD-48CB-863B-4363D03C8424}" EndProject @@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{FEE29B .nuget\NuGet.Config = .nuget\NuGet.Config EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UFBLauncher", "UFBLauncher\UFBLauncher.csproj", "{6529FB5B-D58F-4E12-AA91-5C694E44B142}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,14 @@ Global {E4705D9F-56CD-48CB-863B-4363D03C8424}.Release|Any CPU.Build.0 = Release|Any CPU {E4705D9F-56CD-48CB-863B-4363D03C8424}.Release|x64.ActiveCfg = Release|x64 {E4705D9F-56CD-48CB-863B-4363D03C8424}.Release|x64.Build.0 = Release|x64 + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Debug|x64.ActiveCfg = Debug|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Debug|x64.Build.0 = Debug|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Release|Any CPU.Build.0 = Release|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Release|x64.ActiveCfg = Release|Any CPU + {6529FB5B-D58F-4E12-AA91-5C694E44B142}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/UltimateFishBot/Classes/BodyParts/Ears.cs b/UltimateFishBot/Classes/BodyParts/Ears.cs index 4f272a0..c58bb4b 100644 --- a/UltimateFishBot/Classes/BodyParts/Ears.cs +++ b/UltimateFishBot/Classes/BodyParts/Ears.cs @@ -1,38 +1,34 @@ using CoreAudioApi; using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Windows.Forms; +using UltimateFishBot.Collections; -namespace UltimateFishBot.Classes.BodyParts +namespace UltimateFishBot.BodyParts { - class Ears + internal class Ears { - private MMDevice SndDevice; - private Queue m_volumeQueue; - private int tickrate = 50; //ms pause between sound checks + private MMDevice _sndDevice; + private readonly LimitedCollection _volumeQueue; + private int _tickrate = 50; //ms pause between sound checks - private const int MAX_VOLUME_QUEUE_LENGTH = 5; + private const int MaxVolumeQueueLength = 5; public Ears() { - m_volumeQueue = new Queue(); - m_volumeQueue.Enqueue(0); + _volumeQueue = new LimitedCollection(MaxVolumeQueueLength) {0}; } public async Task Listen(int millisecondsToListen, CancellationToken cancellationToken) { - Stopwatch stopwatch = new Stopwatch(); + var stopwatch = new Stopwatch(); stopwatch.Start(); - MMDeviceEnumerator SndDevEnum = new MMDeviceEnumerator(); - if (Properties.Settings.Default.AudioDevice != "") { - SndDevice = SndDevEnum.GetDevice(Properties.Settings.Default.AudioDevice); - } else { - SndDevice = SndDevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia); - } + var sndDevEnum = new MMDeviceEnumerator(); + _sndDevice = Properties.Settings.Default.AudioDevice != "" + ? sndDevEnum.GetDevice(Properties.Settings.Default.AudioDevice) + : sndDevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia); Func heardFish; if (Properties.Settings.Default.AverageSound) heardFish = ListenTimerTickAvg; @@ -40,7 +36,7 @@ public async Task Listen(int millisecondsToListen, CancellationToken cance heardFish = ListenTimerTick; while (stopwatch.ElapsedMilliseconds <= millisecondsToListen) { - await Task.Delay(tickrate, cancellationToken); + await Task.Delay(_tickrate, cancellationToken); if (heardFish()) { return true; } @@ -50,7 +46,7 @@ public async Task Listen(int millisecondsToListen, CancellationToken cance private bool ListenTimerTick() { // Get the current level - int currentVolumnLevel = (int)(SndDevice.AudioMeterInformation.MasterPeakValue * 100); + var currentVolumnLevel = (int)(_sndDevice.AudioMeterInformation.MasterPeakValue * 100); if (currentVolumnLevel >= Properties.Settings.Default.SplashLimit) return true; @@ -61,28 +57,25 @@ public async Task Listen(int millisecondsToListen, CancellationToken cance private bool ListenTimerTickAvg() { // Get the current level - int currentVolumnLevel = (int)(SndDevice.AudioMeterInformation.MasterPeakValue * 100); - int avgVol = GetAverageVolume(); - bool hear = false; + var currentVolumnLevel = (int)(_sndDevice.AudioMeterInformation.MasterPeakValue * 100); + var avgVol = GetAverageVolume(); + var hear = false; // Determine if the current level is high enough to be a fish if (currentVolumnLevel - avgVol >= Properties.Settings.Default.SplashLimit) { - Serilog.Log.Information("Hear: {av},{cvl},{queue}", avgVol, currentVolumnLevel, m_volumeQueue); + Serilog.Log.Information("Hear: {av},{cvl},{queue}", avgVol, currentVolumnLevel, _volumeQueue); hear = true; } - m_volumeQueue.Enqueue(currentVolumnLevel); // Keep a running queue of the last X sounds as a reference point - if (m_volumeQueue.Count >= MAX_VOLUME_QUEUE_LENGTH) { - m_volumeQueue.Dequeue(); - } + _volumeQueue.Add(currentVolumnLevel); return hear; } private int GetAverageVolume() { - return (int)m_volumeQueue.Average(); + return (int)_volumeQueue.Average(); } } } diff --git a/UltimateFishBot/Classes/BodyParts/Eyes.cs b/UltimateFishBot/Classes/BodyParts/Eyes.cs index 55663db..d4f8abd 100644 --- a/UltimateFishBot/Classes/BodyParts/Eyes.cs +++ b/UltimateFishBot/Classes/BodyParts/Eyes.cs @@ -1,61 +1,57 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Runtime.InteropServices; +using System.Linq; using System.Threading; using System.Threading.Tasks; using AForge.Imaging; using AForge.Imaging.Filters; using Serilog; -using UltimateFishBot.Classes.Helpers; +using UltimateFishBot.Extensions; +using UltimateFishBot.Fishing; +using UltimateFishBot.Helpers; -namespace UltimateFishBot.Classes.BodyParts +namespace UltimateFishBot.BodyParts { - - class Eyes + internal class Eyes { - private Win32.CursorInfo m_noFishCursor; - private IntPtr Wow; - private Bitmap capturedCursorIcon; - private Dictionary bobberPosDict; - private Bitmap background; - private Rectangle wowRectangle; + private Win32.CursorInfo _noFishCursor; + private IntPtr _wow; + private Bitmap _capturedCursorIcon; + private Bitmap _background; + private Rectangle _wowRectangle; - private int a_ScanningSteps = 0; - private int a_ScanningDelay = 0; + private int _aScanningSteps; + private int _aScanningDelay; public Eyes(IntPtr wowWindow) { SetWow(wowWindow); - bobberPosDict = new Dictionary(); } public void SetWow(IntPtr wowWindow) { - this.Wow = wowWindow; - m_noFishCursor = Win32.GetNoFishCursor(this.Wow); - wowRectangle = Win32.GetWowRectangle(this.Wow); + _wow = wowWindow; + _noFishCursor = Win32.GetNoFishCursor(_wow); + _wowRectangle = Win32.GetWowRectangle(_wow); if (System.IO.File.Exists("capturedcursor.bmp")) { - capturedCursorIcon = new Bitmap("capturedcursor.bmp", true); + _capturedCursorIcon = new Bitmap("capturedcursor.bmp", true); } } // capture in grayscale - public void updateBackground() { - background = new Grayscale(0.3725, 0.6154, 0.0121).Apply(Win32.CaptureWindow(Wow)); - background = new Pixellate().Apply(background); + public void UpdateBackground() { + _background = new Grayscale(0.3725, 0.6154, 0.0121).Apply(Win32.CaptureWindow(_wow)); + _background = new Pixellate().Apply(_background); } - public async Task LookForBobber(CancellationToken cancellationToken) + public async Task LookForBobber(BotSession session, CancellationToken cancellationToken) { Win32.Rect scanArea; if (!Properties.Settings.Default.customScanArea) { - scanArea.Left = wowRectangle.X + wowRectangle.Width / 5; - scanArea.Right = wowRectangle.X + wowRectangle.Width / 5 * 4; - scanArea.Top = wowRectangle.Y + wowRectangle.Height / 4; - scanArea.Bottom = wowRectangle.Y + wowRectangle.Height / 4 * 3; + scanArea.Left = _wowRectangle.X + _wowRectangle.Width / 5; + scanArea.Right = _wowRectangle.X + _wowRectangle.Width / 5 * 4; + scanArea.Top = _wowRectangle.Y + _wowRectangle.Height / 4; + scanArea.Bottom = _wowRectangle.Y + _wowRectangle.Height / 4 * 3; //Log.Information("Using default area"); } else { scanArea.Left = Properties.Settings.Default.minScanXY.X; @@ -64,82 +60,72 @@ public async Task LookForBobber(CancellationToken cancellationToken scanArea.Bottom = Properties.Settings.Default.maxScanXY.Y; //Log.Information("Using custom area"); } - Log.Information("Scanning area: " + scanArea.Left.ToString() + " , " + scanArea.Top.ToString() + " , " + scanArea.Right.ToString() + " , " + scanArea.Bottom.ToString() + " cs: " + bobberPosDict.Keys.Count.ToString()); - Win32.Point bobberPos= new Win32.Point { x = 0, y = 0 }; + Log.Information($"Scanning area: {scanArea.Left} , {scanArea.Top} , {scanArea.Right} , {scanArea.Bottom} cs: {session.BobbyLocations.Count()}"); - foreach (Win32.Point dp in PointOfScreenDifferences()) { - if (await MoveMouseAndCheckCursor(dp.x, dp.y, cancellationToken,2)) { - bobberPos = dp; - Log.Information("Bobber imagescan hit. ({bx},{by})", bobberPos.x, bobberPos.y); - break; + foreach (var dp in PointOfScreenDifferences()) { + if (await MoveMouseAndCheckCursor(dp.X, dp.X, cancellationToken,2)) { + Log.Information("Bobber imagescan hit. ({bx},{by})", dp.X, dp.X); + return dp; } } - - if (bobberPos.x == 0 && bobberPos.y == 0) { - // utilize previous hits - foreach (KeyValuePair pos in System.Linq.Enumerable.OrderBy(bobberPosDict, (key => key.Value))) { - // do something with item.Key and item.Value - if (await MoveMouseAndCheckCursor(pos.Key.x, pos.Key.y, cancellationToken,2)) { - bobberPos = pos.Key; - Log.Information("Bobber position cache hit. ({bx},{by})", bobberPos.x, bobberPos.y); - break; - } - } - } - if (bobberPos.x == 0 && bobberPos.y == 0) { - Random rnd = new Random(); - a_ScanningSteps = rnd.Next(Properties.Settings.Default.ScanningStepsLow, Properties.Settings.Default.ScanningStepsHigh); - if (Properties.Settings.Default.AlternativeRoute) { - bobberPos = await LookForBobberSpiralImpl(scanArea, bobberPos, a_ScanningSteps, Properties.Settings.Default.ScanningRetries, cancellationToken); - } else { - bobberPos = await LookForBobberImpl(scanArea, bobberPos, a_ScanningSteps, Properties.Settings.Default.ScanningRetries, cancellationToken); + + // utilize previous hits + foreach (var location in session.BobbyLocations) { + // do something with item.Key and item.Value + if (await MoveMouseAndCheckCursor(location.X, location.Y, cancellationToken,2)) + { + location.Hits++; + Log.Information("Bobber position cache hit. ({bx},{by})", location.X, location.Y); + return location; } } - if (bobberPos.x != 0 && bobberPos.y != 0) { - int hitcount = 1; - if (bobberPosDict.ContainsKey(bobberPos)) { - bobberPosDict.TryGetValue(bobberPos, out hitcount); - hitcount++; - bobberPosDict.Remove(bobberPos); - } - bobberPosDict.Add(bobberPos, hitcount); + + var rnd = new Random(); + BobbyLocation loc; + _aScanningSteps = rnd.Next(Properties.Settings.Default.ScanningStepsLow, Properties.Settings.Default.ScanningStepsHigh); + if (Properties.Settings.Default.AlternativeRoute) { + loc = await LookForBobberSpiralImpl(session, scanArea, _aScanningSteps, Properties.Settings.Default.ScanningRetries, cancellationToken); + } else { + loc = await LookForBobberImpl(session, scanArea, _aScanningSteps, Properties.Settings.Default.ScanningRetries, cancellationToken); } - Log.Information("Bobber scan finished. ({bx},{by})", bobberPos.x, bobberPos.y); - return bobberPos; + Log.Information("Bobber scan finished. ({bx},{by})", loc?.X, loc?.Y); + return loc; } - private List PointOfScreenDifferences() { - Bitmap castbmp = Win32.CaptureWindow(Wow); - - FiltersSequence processingFilter = new FiltersSequence(); - processingFilter.Add(new Grayscale(0.3725, 0.6154, 0.0121)); - processingFilter.Add(new Pixellate()); - processingFilter.Add(new Difference(background)); - processingFilter.Add(new Threshold(15)); - processingFilter.Add(new Erosion()); + private IEnumerable PointOfScreenDifferences() + { + var castbmp = Win32.CaptureWindow(_wow); + var processingFilter = new FiltersSequence + { + new Grayscale(0.3725, 0.6154, 0.0121), + new Pixellate(), + new Difference(_background), + new Threshold(15), + new Erosion() + }; var blobCounter = new BlobCounter(); blobCounter.ProcessImage(processingFilter.Apply(castbmp)); - Rectangle[] brl = blobCounter.GetObjectsRectangles(); + var brl = blobCounter.GetObjectsRectangles(); Log.Information("Bobber imagescan brl: {brl}", brl.Length); - List sdl = new List(); - foreach (Rectangle br in brl) { - Win32.Point pt = new Win32.Point { x = (br.Left + br.Left + br.Right) * 4 / 12, y = (br.Top+br.Bottom+br.Bottom)*4/12 }; - Win32.ClientToScreen(Wow, ref pt); - if ((br.Right - br.Left)>9&& (br.Bottom - br.Top)>9) { + var sdl = new List(); + foreach (var br in brl) { + var pt = new Win32.Point { x = (br.Left + br.Left + br.Right) * 4 / 12, y = (br.Top+br.Bottom+br.Bottom)*4/12 }; + Win32.ClientToScreen(_wow, ref pt); + if (br.Right - br.Left>9&& br.Bottom - br.Top>9) { // Win32.Point pt = new Win32.Point { x= wowRectangle.X+(br.Left + br.Right) / 2, y= wowRectangle.Y+(br.Top+br.Bottom)/2 }; - Log.Information("Bobber imagescan br: {bx},{by} - {w},{h}", pt.x,pt.y, (br.Right-br.Left),(br.Bottom-br.Top)); - sdl.Add(pt); + Log.Information("Bobber imagescan br: {bx},{by} - {w},{h}", pt.x,pt.y, br.Right-br.Left,br.Bottom-br.Top); + sdl.Add(new BobbyLocation(pt)); // } else { // Log.Information("Bobber imagescan ignore br: {bx},{by} - {w},{h}", pt.x,pt.y, (br.Right-br.Left),(br.Bottom-br.Top)); } } // debug /* - Bitmap bmpDst = new Bitmap(castbmp); + BitmapExt bmpDst = new BitmapExt(castbmp); using (var g = Graphics.FromImage(bmpDst)) { foreach (var br in brl) { if ((br.Right - br.Left) > 11 && (br.Bottom - br.Top) > 11) { @@ -153,95 +139,90 @@ public async Task LookForBobber(CancellationToken cancellationToken return sdl; } - public async Task SetMouseToBobber(Win32.Point bobberPos, CancellationToken cancellationToken) {// move mouse to previous recorded position and check shape - if (!await MoveMouseAndCheckCursor(bobberPos.x, bobberPos.y, cancellationToken,1)) { - Log.Information("Bobber lost. ({bx},{by})", bobberPos.x, bobberPos.y); - int fixr = 24; + public async Task SetMouseToBobber(BotSession session, BobbyLocation bobberPos, CancellationToken cancellationToken) {// move mouse to previous recorded position and check shape + if (!await MoveMouseAndCheckCursor(bobberPos.X, bobberPos.Y, cancellationToken, 1)) { + Log.Information("Bobber lost. ({bx},{by})", bobberPos.X, bobberPos.Y); + const int fixr = 24; Win32.Rect scanArea; - scanArea.Left = bobberPos.x - fixr; - scanArea.Right = bobberPos.x + fixr; - scanArea.Top = bobberPos.y - fixr; - scanArea.Bottom = bobberPos.y + fixr; + scanArea.Left = bobberPos.X - fixr; + scanArea.Right = bobberPos.X + fixr; + scanArea.Top = bobberPos.Y - fixr; + scanArea.Bottom = bobberPos.Y + fixr; // initiate a small-area search for bobber - Win32.Point npos; - npos.x = 0; - npos.y = 0; - npos = await LookForBobberSpiralImpl(scanArea, npos,4,1,cancellationToken); - if (npos.x != 0 && npos.y != 0) { + var loc = await LookForBobberSpiralImpl(session, scanArea, 4, 1, cancellationToken); + if (loc != null) { // search was successful - Log.Information("Bobber found. ({bx},{by})", npos.x, npos.y); + Log.Information("Bobber found. ({bx},{by})", loc.X, loc.Y); return true; - } else { - Log.Information("Bobber flost. ({bx},{by})", npos.x, npos.y); - return false; } + + Log.Information("Bobber lost. ({bx},{by})", 0, 0); + return false; } return true; } - private async Task LookForBobberImpl(Win32.Rect scanArea, Win32.Point bobberPos, int steps, int retries, CancellationToken cancellationToken) { + private async Task LookForBobberImpl(BotSession session, Win32.Rect scanArea, int steps, int retries, CancellationToken cancellationToken) { - int XPOSSTEP = (int)((scanArea.Right - scanArea.Left) / steps); - int YPOSSTEP = (int)((scanArea.Bottom - scanArea.Top) / steps); - int XOFFSET = (int)(XPOSSTEP / retries); + var xposstep = (scanArea.Right - scanArea.Left) / steps; + var yposstep = (scanArea.Bottom - scanArea.Top) / steps; + var xoffset = xposstep / retries; - for (int tryCount = 0; tryCount < retries; ++tryCount) { - for (int x = (int)(scanArea.Left + (XOFFSET * tryCount)); x < scanArea.Right; x += XPOSSTEP) { - for (int y = scanArea.Top; y < scanArea.Bottom; y += YPOSSTEP) { - if (await MoveMouseAndCheckCursor(x, y, cancellationToken,1)) { - bobberPos.x = x; - bobberPos.y = y; - return bobberPos; + for (var tryCount = 0; tryCount < retries; ++tryCount) { + for (var x = scanArea.Left + xoffset * tryCount; x < scanArea.Right; x += xposstep) { + for (var y = scanArea.Top; y < scanArea.Bottom; y += yposstep) { + if (await MoveMouseAndCheckCursor(x, y, cancellationToken,1)) + { + return session.BobbyLocations.Add(x, y); } } } } - return bobberPos; + return null; } - private async Task LookForBobberSpiralImpl(Win32.Rect scanArea, Win32.Point bobberPos, int steps, int retries, CancellationToken cancellationToken) { + private async Task LookForBobberSpiralImpl(BotSession session, Win32.Rect scanArea, int steps, int retries, CancellationToken cancellationToken) { - int XPOSSTEP = (int)((scanArea.Right - scanArea.Left) / steps); - int YPOSSTEP = (int)((scanArea.Bottom - scanArea.Top) / steps); - int XOFFSET = (int)(XPOSSTEP / retries); - int YOFFSET = (int)(YPOSSTEP / retries); + var xposstep = (scanArea.Right - scanArea.Left) / steps; + var yposstep = (scanArea.Bottom - scanArea.Top) / steps; + var xoffset = xposstep / retries; + var yoffset = yposstep / retries; - for (int tryCount = 0; tryCount < retries; ++tryCount) { - int x = (int)((scanArea.Left + scanArea.Right) / 2) + XOFFSET * tryCount; - int y = (int)((scanArea.Top + scanArea.Bottom) / 2) + YOFFSET * tryCount; + for (var tryCount = 0; tryCount < retries; ++tryCount) { + var x = (scanArea.Left + scanArea.Right) / 2 + xoffset * tryCount; + var y = (scanArea.Top + scanArea.Bottom) / 2 + yoffset * tryCount; - for (int i = 0; i <= 2 * steps; i++) { - for (int j = 0; j <= (i / 2); j++) { - int dx = 0, dy = 0; + for (var i = 0; i <= 2 * steps; i++) { + for (var j = 0; j <= i / 2; j++) { + int dx, dy; if (i % 2 == 0) { - if ((i / 2) % 2 == 0) { - dx = XPOSSTEP; + if (i / 2 % 2 == 0) { + dx = xposstep; dy = 0; } else { - dx = -XPOSSTEP; + dx = -xposstep; dy = 0; } } else { - if ((i / 2) % 2 == 0) { + if (i / 2 % 2 == 0) { dx = 0; - dy = YPOSSTEP; + dy = yposstep; } else { dx = 0; - dy = -YPOSSTEP; + dy = -yposstep; } } x += dx; y += dy; if (await MoveMouseAndCheckCursor(x, y, cancellationToken,1)) { - bobberPos.x = x; - bobberPos.y = y; - return bobberPos; + return session.BobbyLocations.Add(x, y); } } } } - return bobberPos; + + return null; } private async Task MoveMouseAndCheckCursor(int x, int y, CancellationToken cancellationToken,int mpy) { @@ -251,23 +232,23 @@ public async Task LookForBobber(CancellationToken cancellationToken Win32.MoveMouse(x, y); // Pause (give the OS a chance to change the cursor) - Random rnd = new Random(); - a_ScanningDelay = rnd.Next(Properties.Settings.Default.ScanningDelayLow, Properties.Settings.Default.ScanningDelayHigh); - await Task.Delay(mpy*a_ScanningDelay, cancellationToken); + var rnd = new Random(); + _aScanningDelay = rnd.Next(Properties.Settings.Default.ScanningDelayLow, Properties.Settings.Default.ScanningDelayHigh); + await Task.Delay(mpy*_aScanningDelay, cancellationToken); - Win32.CursorInfo actualCursor = Win32.GetCurrentCursor(); + var actualCursor = Win32.GetCurrentCursor(); - if (actualCursor.flags == m_noFishCursor.flags && - actualCursor.hCursor == m_noFishCursor.hCursor) + if (actualCursor.flags == _noFishCursor.flags && + actualCursor.hCursor == _noFishCursor.hCursor) return false; // Compare the actual icon with our fishIcon if user want it if (Properties.Settings.Default.CheckCursor) { - if (ImageCompare(Win32.GetCursorIcon(actualCursor), Properties.Resources.fishIcon35x35)) { + if (Win32.GetCursorIcon(actualCursor).ImageCompare(Properties.Resources.fishIcon35x35)) { // We found a fish! return true; } - if (capturedCursorIcon != null && ImageCompare(Win32.GetCursorIcon(actualCursor), capturedCursorIcon)) { + if (_capturedCursorIcon != null && Win32.GetCursorIcon(actualCursor).ImageCompare(_capturedCursorIcon)) { // We found a fish! return true; } @@ -277,47 +258,9 @@ public async Task LookForBobber(CancellationToken cancellationToken return true; } - - private static bool ImageCompare(Bitmap bmp1, Bitmap bmp2) { - - if (bmp1 == null || bmp2 == null) { - return false; - } - if (object.Equals(bmp1, bmp2)) { - return true; - } - if (!bmp1.Size.Equals(bmp2.Size) || !bmp1.PixelFormat.Equals(bmp2.PixelFormat)) { - return false; - } - - int bytes = bmp1.Width * bmp1.Height * (System.Drawing.Image.GetPixelFormatSize(bmp1.PixelFormat) / 8); - - bool result = true; - byte[] b1bytes = new byte[bytes]; - byte[] b2bytes = new byte[bytes]; - - BitmapData bitmapData1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width - 1, bmp1.Height - 1), ImageLockMode.ReadOnly, bmp1.PixelFormat); - BitmapData bitmapData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width - 1, bmp2.Height - 1), ImageLockMode.ReadOnly, bmp2.PixelFormat); - - Marshal.Copy(bitmapData1.Scan0, b1bytes, 0, bytes); - Marshal.Copy(bitmapData2.Scan0, b2bytes, 0, bytes); - - for (int n = 0; n <= bytes - 1; n++) { - if (b1bytes[n] != b2bytes[n]) { - result = false; - break; - } - } - - bmp1.UnlockBits(bitmapData1); - bmp2.UnlockBits(bitmapData2); - - return result; - } - public void CaptureCursor() { - Win32.CursorInfo actualCursor = Win32.GetCurrentCursor(); - Bitmap cursorIcon = Win32.GetCursorIcon(actualCursor); + var actualCursor = Win32.GetCurrentCursor(); + var cursorIcon = Win32.GetCursorIcon(actualCursor); cursorIcon.Save("capturedcursor.bmp"); } diff --git a/UltimateFishBot/Classes/BodyParts/Hands.cs b/UltimateFishBot/Classes/BodyParts/Hands.cs index 0184144..7cf1827 100644 --- a/UltimateFishBot/Classes/BodyParts/Hands.cs +++ b/UltimateFishBot/Classes/BodyParts/Hands.cs @@ -1,45 +1,37 @@ -using Serilog; using System; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using UltimateFishBot.Classes.Helpers; +using Serilog; +using UltimateFishBot.Helpers; -namespace UltimateFishBot.Classes.BodyParts +namespace UltimateFishBot.BodyParts { - class Hands + internal class Hands { - private Cursor m_cursor; - private int m_baitIndex; - private string[] m_baitKeys; - private IntPtr Wow; - private Random rand; + private Cursor _mCursor; + private int _mBaitIndex; + private string[] _mBaitKeys; + private IntPtr _wow; - private int a_CastingDelay = 0; - private int a_LootingDelay = 0; + private int _aCastingDelay; + private int _aLootingDelay; - public Hands() - { - m_baitIndex = 0; - m_cursor = new Cursor(Cursor.Current.Handle); - rand = new Random(); - UpdateKeys(); - } public Hands(IntPtr wowWindow) { - this.Wow = wowWindow; - m_baitIndex = 0; - m_cursor = new Cursor(Cursor.Current.Handle); + _wow = wowWindow; + _mBaitIndex = 0; + if (Cursor.Current != null) _mCursor = new Cursor(Cursor.Current.Handle); UpdateKeys(); } public void SetWow(IntPtr wowWindow) { - this.Wow = wowWindow; + _wow = wowWindow; } - public void UpdateKeys() + private void UpdateKeys() { - m_baitKeys = new string[7] + _mBaitKeys = new[] { Properties.Settings.Default.BaitKey1, Properties.Settings.Default.BaitKey2, @@ -54,78 +46,78 @@ public void UpdateKeys() public async Task Cast(CancellationToken token) { if (Properties.Settings.Default.RightClickCast) { - Win32.SendMouseDblRightClick(this.Wow); + Win32.SendMouseDblRightClick(_wow); } else { Win32.SendKey(Properties.Settings.Default.FishKey); Log.Information("Sent key: " + Properties.Settings.Default.FishKey); } - Random rnd = new Random(); - a_CastingDelay = rnd.Next(Properties.Settings.Default.CastingDelayLow, Properties.Settings.Default.CastingDelayHigh); - await Task.Delay(a_CastingDelay, token); + var rnd = new Random(); + _aCastingDelay = rnd.Next(Properties.Settings.Default.CastingDelayLow, Properties.Settings.Default.CastingDelayHigh); + await Task.Delay(_aCastingDelay, token); } public async Task Loot() { - Win32.SendMouseClick(this.Wow); + Win32.SendMouseClick(_wow); Log.Information("Send Loot."); Random rnd = new Random(); - a_LootingDelay = rnd.Next(Properties.Settings.Default.LootingDelayLow, Properties.Settings.Default.LootingDelayHigh); - await Task.Delay(a_LootingDelay); + _aLootingDelay = rnd.Next(Properties.Settings.Default.LootingDelayLow, Properties.Settings.Default.LootingDelayHigh); + await Task.Delay(_aLootingDelay); } public void ResetBaitIndex() { - m_baitIndex = 0; + _mBaitIndex = 0; } - public async Task DoAction(Manager.NeededAction action, Mouth mouth, CancellationToken cancellationToken) + public async Task DoAction(NeededAction action, Mouth mouth, CancellationToken cancellationToken) { string actionKey = ""; int sleepTime = 0; switch (action) { - case Manager.NeededAction.HearthStone: + case NeededAction.HearthStone: { actionKey = Properties.Settings.Default.HearthKey; mouth.Say(Translate.GetTranslate("manager", "LABEL_HEARTHSTONE")); sleepTime = 0; break; } - case Manager.NeededAction.Lure: + case NeededAction.Lure: { actionKey = Properties.Settings.Default.LureKey; mouth.Say(Translate.GetTranslate("manager", "LABEL_APPLY_LURE")); sleepTime = 3; break; } - case Manager.NeededAction.Charm: + case NeededAction.Charm: { actionKey = Properties.Settings.Default.CharmKey; mouth.Say(Translate.GetTranslate("manager", "LABEL_APPLY_CHARM")); sleepTime = 3; break; } - case Manager.NeededAction.Raft: + case NeededAction.Raft: { actionKey = Properties.Settings.Default.RaftKey; mouth.Say(Translate.GetTranslate("manager", "LABEL_APPLY_RAFT")); sleepTime = 2; break; } - case Manager.NeededAction.Bait: + case NeededAction.Bait: { int baitIndex = 0; if (Properties.Settings.Default.CycleThroughBaitList) { - if (m_baitIndex >= 6) - m_baitIndex = 0; + if (_mBaitIndex >= 6) + _mBaitIndex = 0; - baitIndex = m_baitIndex++; + baitIndex = _mBaitIndex++; } - actionKey = m_baitKeys[baitIndex]; + actionKey = _mBaitKeys[baitIndex]; mouth.Say(Translate.GetTranslate("manager", "LABEL_APPLY_BAIT", baitIndex)); sleepTime = 3; break; @@ -135,7 +127,7 @@ public async Task DoAction(Manager.NeededAction action, Mouth mouth, Cancellatio } Log.Information("Send key start: " + actionKey); - Win32.ActivateWow(this.Wow); + Win32.ActivateWow(_wow); await Task.Delay(1000, cancellationToken); Win32.SendKey(actionKey); Log.Information("Sent key: "+actionKey); diff --git a/UltimateFishBot/Classes/BodyParts/Legs.cs b/UltimateFishBot/Classes/BodyParts/Legs.cs index 4bf1e0a..378656a 100644 --- a/UltimateFishBot/Classes/BodyParts/Legs.cs +++ b/UltimateFishBot/Classes/BodyParts/Legs.cs @@ -2,55 +2,54 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using UltimateFishBot.Classes.Helpers; +using UltimateFishBot.Helpers; -namespace UltimateFishBot.Classes.BodyParts +namespace UltimateFishBot.BodyParts { - class Legs + internal class Legs { - public enum Path + private enum Path { - FRONT_BACK = 0, - LEFT_RIGHT = 1, - JUMP = 2 + FrontBack = 0, + LeftRight = 1, + Jump = 2 } - public async Task DoMovement(T2S t2s, CancellationToken cancellationToken) + public async Task DoMovement(T2S t2S, CancellationToken cancellationToken) { switch ((Path)Properties.Settings.Default.AntiAfkMoves) { - case Path.FRONT_BACK: - await MovePath(new Keys[] { Keys.Up, Keys.Down }, cancellationToken); + case Path.FrontBack: + await MovePath(new[] { Keys.Up, Keys.Down }, cancellationToken); break; - case Path.LEFT_RIGHT: - await MovePath(new Keys[] { Keys.Left, Keys.Right }, cancellationToken); + case Path.LeftRight: + await MovePath(new[] { Keys.Left, Keys.Right }, cancellationToken); break; - case Path.JUMP: - await MovePath(new Keys[] { Keys.Space }, cancellationToken); + case Path.Jump: + await MovePath(new[] { Keys.Space }, cancellationToken); await Task.Delay(500, cancellationToken); break; default: - await MovePath(new Keys[] { Keys.Left, Keys.Right }, cancellationToken); + await MovePath(new[] { Keys.Left, Keys.Right }, cancellationToken); break; } - if (t2s != null) - t2s.Say("Anti A F K"); + t2S?.Say("Anti A F K"); } private async Task MovePath(Keys[] moves, CancellationToken cancellationToken) { - foreach (Keys move in moves) + foreach (var move in moves) { await SingleMove(move, cancellationToken); await Task.Delay(new Random().Next(100,500), cancellationToken); } } - private async Task SingleMove(Keys move, CancellationToken cancellationToken) + private static async Task SingleMove(Keys move, CancellationToken cancellationToken) { - Win32.SendKeyboardAction(move, Win32.keyState.KEYDOWN); + Win32.SendKeyboardAction(move, Win32.KeyState.Keydown); await Task.Delay(new Random().Next(100, 250), cancellationToken); - Win32.SendKeyboardAction(move, Win32.keyState.KEYUP); + Win32.SendKeyboardAction(move, Win32.KeyState.Keyup); } } } diff --git a/UltimateFishBot/Classes/BodyParts/Mouth.cs b/UltimateFishBot/Classes/BodyParts/Mouth.cs index 7eb6275..c4431e1 100644 --- a/UltimateFishBot/Classes/BodyParts/Mouth.cs +++ b/UltimateFishBot/Classes/BodyParts/Mouth.cs @@ -1,54 +1,22 @@ using System; -using System.Speech.Synthesis; -using UltimateFishBot.Properties; -namespace UltimateFishBot.Classes.BodyParts +namespace UltimateFishBot.BodyParts { - class Mouth + internal class Mouth { - private IProgress m_progressHandle; - T2S t2s = new T2S(); + private readonly IProgress _progressHandle; + private readonly T2S _t2S = new T2S(); public Mouth(IProgress progressHandle) { - m_progressHandle = progressHandle; + _progressHandle = progressHandle; } public void Say(string text) { - m_progressHandle.Report(text); - t2s.Say(text); + _progressHandle.Report(text); + _t2S.Say(text); } - - } - class T2S - { - SpeechSynthesizer synthesizer = new SpeechSynthesizer(); - bool uset2s; - string lastMessage; - - public T2S() - { - uset2s = Properties.Settings.Default.Txt2speech; - synthesizer.Volume = 60; // 0...100 - synthesizer.Rate = 1; // -10...10 - } - - public void Say(string text) - { - //Debug code - //System.Console.WriteLine("Use T2S: " + uset2s); - //System.Console.WriteLine("Previous message: " + lastMessage); - //System.Console.WriteLine("Current message: " + text); - //System.Console.WriteLine("Synthesizer ready: " + (synthesizer.State == SynthesizerState.Ready)); - - // Say asynchronous text through Text 2 Speech synthesizer - if (uset2s && (lastMessage != text) && (synthesizer.State == SynthesizerState.Ready)) - { - synthesizer.SpeakAsync(text); - lastMessage = text; - } - } } } diff --git a/UltimateFishBot/Classes/BodyParts/T2S.cs b/UltimateFishBot/Classes/BodyParts/T2S.cs new file mode 100644 index 0000000..3e0b5a4 --- /dev/null +++ b/UltimateFishBot/Classes/BodyParts/T2S.cs @@ -0,0 +1,34 @@ +using System.Speech.Synthesis; + +namespace UltimateFishBot.BodyParts +{ + internal class T2S + { + private readonly SpeechSynthesizer _synthesizer = new SpeechSynthesizer(); + private readonly bool _uset2S; + private string _lastMessage; + + public T2S() + { + _uset2S = Properties.Settings.Default.Txt2speech; + _synthesizer.Volume = 60; // 0...100 + _synthesizer.Rate = 1; // -10...10 + } + + public void Say(string text) + { + //Debug code + //System.Console.WriteLine("Use T2S: " + uset2s); + //System.Console.WriteLine("Previous message: " + lastMessage); + //System.Console.WriteLine("Current message: " + text); + //System.Console.WriteLine("Synthesizer ready: " + (synthesizer.State == SynthesizerState.Ready)); + + // Say asynchronous text through Text 2 Speech synthesizer + if (_uset2S && (_lastMessage != text) && (_synthesizer.State == SynthesizerState.Ready)) + { + _synthesizer.SpeakAsync(text); + _lastMessage = text; + } + } + } +} \ No newline at end of file diff --git a/UltimateFishBot/Classes/BotSession.cs b/UltimateFishBot/Classes/BotSession.cs new file mode 100644 index 0000000..ff0ee8f --- /dev/null +++ b/UltimateFishBot/Classes/BotSession.cs @@ -0,0 +1,14 @@ +using UltimateFishBot.Fishing; + +namespace UltimateFishBot +{ + internal class BotSession + { + internal BobbyLocationStorage BobbyLocations { get; } + + public BotSession() + { + BobbyLocations = new BobbyLocationStorage(25); + } + } +} \ No newline at end of file diff --git a/UltimateFishBot/Classes/Collections/LimitedCollection.cs b/UltimateFishBot/Classes/Collections/LimitedCollection.cs new file mode 100644 index 0000000..c9d8ab1 --- /dev/null +++ b/UltimateFishBot/Classes/Collections/LimitedCollection.cs @@ -0,0 +1,51 @@ +using System.Collections; +using System.Collections.Generic; + +namespace UltimateFishBot.Collections +{ + public class LimitedCollection : IEnumerable + { + protected readonly T[] Storage; + private int _current; + + public LimitedCollection(int size) + { + Storage = new T[size]; + _current = 0; + } + + public void Add(T loc) + { + Storage[FindLeastImportant()] = loc; + } + + protected virtual int FindLeastImportant() + { + if (Storage.Length <= _current) + { + _current = 0; + } + return _current++; + } + + public IEnumerator GetEnumerator() + { + for (var i = _current; i < Storage.Length; i++) + { + if (Storage[i] == null) break; + + yield return Storage[i]; + } + + for (var i = 0; i < _current; i++) + { + yield return Storage[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/UltimateFishBot/Classes/Extensions/BitmapExt.cs b/UltimateFishBot/Classes/Extensions/BitmapExt.cs new file mode 100644 index 0000000..f7067d9 --- /dev/null +++ b/UltimateFishBot/Classes/Extensions/BitmapExt.cs @@ -0,0 +1,39 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; + +namespace UltimateFishBot.Extensions +{ + internal static class BitmapExt + { + [DllImport("msvcrt.dll")] + private static extern int memcmp(IntPtr b1, IntPtr b2, long count); + + public static bool ImageCompare(this Bitmap b1, Bitmap b2) + { + if (b1 == null || b2 == null) return false; + if (b1.Size != b2.Size || !b1.PixelFormat.Equals(b2.PixelFormat)) return false; + if (ReferenceEquals(b1, b2)) return true; + + var bd1 = b1.LockBits(new Rectangle(new Point(0, 0), b1.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + var bd2 = b2.LockBits(new Rectangle(new Point(0, 0), b2.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + + try + { + var bd1Scan0 = bd1.Scan0; + var bd2Scan0 = bd2.Scan0; + + var stride = bd1.Stride; + var len = stride * b1.Height; + + return memcmp(bd1Scan0, bd2Scan0, len) == 0; + } + finally + { + b1.UnlockBits(bd1); + b2.UnlockBits(bd2); + } + } + } +} diff --git a/UltimateFishBot/Classes/Fishing/BobbyLocation.cs b/UltimateFishBot/Classes/Fishing/BobbyLocation.cs new file mode 100644 index 0000000..9b11327 --- /dev/null +++ b/UltimateFishBot/Classes/Fishing/BobbyLocation.cs @@ -0,0 +1,30 @@ +using UltimateFishBot.Helpers; + +namespace UltimateFishBot.Fishing +{ + internal class BobbyLocation + { + public int X { get; } + + public int Y { get; } + + public int Hits { get; set; } = 0; + + public BobbyLocation(int x, int y) + { + X = x; + Y = y; + } + + public BobbyLocation(Win32.Point point) + { + X = point.x; + Y = point.y; + } + + public Win32.Point ToWin32Point() + { + return new Win32.Point {x = X, y = Y}; + } + } +} diff --git a/UltimateFishBot/Classes/Fishing/BobbyLocationStorage.cs b/UltimateFishBot/Classes/Fishing/BobbyLocationStorage.cs new file mode 100644 index 0000000..0c6d7be --- /dev/null +++ b/UltimateFishBot/Classes/Fishing/BobbyLocationStorage.cs @@ -0,0 +1,37 @@ +using UltimateFishBot.Collections; + +namespace UltimateFishBot.Fishing +{ + internal class BobbyLocationStorage : LimitedCollection + { + public BobbyLocationStorage(int size) : base(size) + { + } + + public BobbyLocation Add(int x, int y) + { + var loc = new BobbyLocation(x, y); + base.Add(loc); + return loc; + } + + protected override int FindLeastImportant() + { + var minIndx = 0; + var minHits = int.MaxValue; + for (int i = 0; i < Storage.Length; i++) + { + if (Storage[i] == null) + { + return i; + } + if (Storage[i].Hits < minHits) + { + minIndx = i; + minHits = Storage[i].Hits; + } + } + return minIndx; + } + } +} diff --git a/UltimateFishBot/Classes/FishingStats.cs b/UltimateFishBot/Classes/FishingStats.cs index 2953614..41addae 100644 --- a/UltimateFishBot/Classes/FishingStats.cs +++ b/UltimateFishBot/Classes/FishingStats.cs @@ -1,36 +1,36 @@ -namespace UltimateFishBot.Classes +namespace UltimateFishBot { public class FishingStats { - public int totalSuccessFishing { get; private set; } - public int totalNotFoundFish { get; private set; } - public int totalNotEaredFish { get; private set; } + public int TotalSuccessFishing { get; private set; } + public int TotalNotFoundFish { get; private set; } + public int TotalNotEaredFish { get; private set; } public void Reset() { - totalSuccessFishing = 0; - totalNotFoundFish = 0; - totalNotEaredFish = 0; + TotalSuccessFishing = 0; + TotalNotFoundFish = 0; + TotalNotEaredFish = 0; } public void RecordSuccess() { - ++totalSuccessFishing; + ++TotalSuccessFishing; } public void RecordBobberNotFound() { - ++totalNotFoundFish; + ++TotalNotFoundFish; } public void RecordNotHeard() { - ++totalNotEaredFish; + ++TotalNotEaredFish; } public int Total() { - return totalSuccessFishing + totalNotFoundFish + totalNotEaredFish; + return TotalSuccessFishing + TotalNotFoundFish + TotalNotEaredFish; } } } diff --git a/UltimateFishBot/Classes/Helpers/Win32.cs b/UltimateFishBot/Classes/Helpers/Win32.cs index be93dc3..972be7b 100644 --- a/UltimateFishBot/Classes/Helpers/Win32.cs +++ b/UltimateFishBot/Classes/Helpers/Win32.cs @@ -1,15 +1,16 @@ using System; using System.Diagnostics; using System.Drawing; -using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; -namespace UltimateFishBot.Classes.Helpers +namespace UltimateFishBot.Helpers { - class Win32 + internal class Win32 { + private static readonly Random Rnd = new Random(); + public struct Rect { public int Left; @@ -34,11 +35,11 @@ public struct CursorInfo public Point ptScreenPos; } - public enum keyState + public enum KeyState { - KEYDOWN = 0, - EXTENDEDKEY = 1, - KEYUP = 2 + Keydown = 0, + Extendedkey = 1, + Keyup = 2 }; private enum ShowWindowEnum { @@ -62,19 +63,19 @@ private enum ShowWindowEnum public static extern bool GetClientRect(IntPtr hWnd, out Rect lpRect); [DllImport("user32.dll")] - private static extern bool SetCursorPos(int X, int Y); + private static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] private static extern bool GetCursorInfo(out CursorInfo pci); [DllImport("user32.dll")] - private static extern bool DrawIcon(IntPtr hDC, int X, int Y, IntPtr hIcon); + private static extern bool DrawIcon(IntPtr hDc, int x, int y, IntPtr hIcon); [DllImport("user32.dll")] private static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern bool SendNotifyMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam); + private static extern bool SendNotifyMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk); @@ -86,7 +87,7 @@ private enum ShowWindowEnum private static extern bool IsIconic(IntPtr hWnd); [DllImport("user32.dll")] - static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags); + private static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags); [DllImport("User32.Dll")] public static extern bool ClientToScreen(IntPtr hWnd, ref Point point); @@ -95,31 +96,31 @@ private enum ShowWindowEnum public static extern IntPtr GetWindowDC(IntPtr hWnd); [DllImport("user32.dll")] - public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC); + public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc); [DllImport("gdi32.dll")] public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hObjectSource, int nXSrc, int nYSrc, int dwRop); [DllImport("gdi32.dll")] - public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth, + public static extern IntPtr CreateCompatibleBitmap(IntPtr hDc, int nWidth, int nHeight); [DllImport("gdi32.dll")] - public static extern IntPtr CreateCompatibleDC(IntPtr hDC); + public static extern IntPtr CreateCompatibleDC(IntPtr hDc); [DllImport("gdi32.dll")] - public static extern bool DeleteDC(IntPtr hDC); + public static extern bool DeleteDC(IntPtr hDc); [DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject); [DllImport("gdi32.dll")] - public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); + public static extern IntPtr SelectObject(IntPtr hDc, IntPtr hObject); - private const uint WM_LBUTTONDOWN = 513; - private const uint WM_LBUTTONUP = 514; + private const uint WmLbuttondown = 513; + private const uint WmLbuttonup = 514; - private const uint WM_RBUTTONDOWN = 516; - private const uint WM_RBUTTONUP = 517; + private const uint WmRbuttondown = 516; + private const uint WmRbuttonup = 517; - public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter + public const int Srccopy = 0x00CC0020; // BitBlt dwRop parameter /// /// Creates an Image object containing a screen shot of a specific window @@ -155,7 +156,7 @@ public static Bitmap CaptureWindow(IntPtr handle) // select the bitmap object IntPtr hOld = SelectObject(hdcDest, hBitmap); // bitblt over - BitBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, SRCCOPY); + BitBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, Srccopy); // restore selection SelectObject(hdcDest, hOld); // clean up @@ -168,18 +169,23 @@ public static Bitmap CaptureWindow(IntPtr handle) return img; } - public static Rectangle GetWowRectangle(IntPtr Wow) + public static Rectangle GetWowRectangle(IntPtr wow) { - Rect Win32ApiRect = new Rect(); - GetWindowRect(Wow, ref Win32ApiRect); + Rect win32ApiRect = new Rect(); + GetWindowRect(wow, ref win32ApiRect); Rectangle myRect = new Rectangle(); - myRect.X = Win32ApiRect.Left; - myRect.Y = Win32ApiRect.Top; - myRect.Width = (Win32ApiRect.Right - Win32ApiRect.Left); - myRect.Height = (Win32ApiRect.Bottom - Win32ApiRect.Top); + myRect.X = win32ApiRect.Left; + myRect.Y = win32ApiRect.Top; + myRect.Width = (win32ApiRect.Right - win32ApiRect.Left); + myRect.Height = (win32ApiRect.Bottom - win32ApiRect.Top); return myRect; } + private static int GetRandomDelay(int baseDelay = 100) + { + return baseDelay + Rnd.Next(-1 * baseDelay / 5, baseDelay / 3); + } + public static IntPtr FindWowWindow() { Process[] processlist = Process.GetProcesses(); @@ -209,18 +215,18 @@ public static Bitmap GetCursorIcon(CursorInfo actualCursor, int width = 35, int return actualCursorIcon; } - static public void ActivateWow(IntPtr Wow) + static public void ActivateWow(IntPtr wow) { - ActivateApp(Wow); + ActivateApp(wow); } - public static void ActivateApp(IntPtr Wow) + public static void ActivateApp(IntPtr wow) { - SetForegroundWindow(Wow); + SetForegroundWindow(wow); //AllowSetForegroundWindow(Process.GetCurrentProcess().Id); - if (IsIconic(Wow)) + if (IsIconic(wow)) { - ShowWindow(Wow, ShowWindowEnum.Restore); + ShowWindow(wow, ShowWindowEnum.Restore); } } @@ -228,17 +234,17 @@ public static void MoveMouse(int x, int y) { if (SetCursorPos(x, y)) { - LastX = x; - LastY = y; + _lastX = x; + _lastY = y; } } - public static CursorInfo GetNoFishCursor(IntPtr Wow) + public static CursorInfo GetNoFishCursor(IntPtr wow) { - Rectangle WoWRect = Win32.GetWowRectangle(Wow); - Win32.MoveMouse((WoWRect.X + 10), (WoWRect.Y + 45)); - LastRectX = WoWRect.X; - LastRectY = WoWRect.Y; + Rectangle woWRect = Win32.GetWowRectangle(wow); + Win32.MoveMouse((woWRect.X + 10), (woWRect.Y + 45)); + _lastRectX = woWRect.X; + _lastRectY = woWRect.Y; Thread.Sleep(15); CursorInfo myInfo = new CursorInfo(); myInfo.cbSize = Marshal.SizeOf(myInfo); @@ -267,53 +273,53 @@ public static void SendKey(string sKeys) SendKeys.Send(sKeys); } - public static void SendMouseClick(IntPtr Wow) + public static void SendMouseClick(IntPtr wow) { - long dWord = MakeDWord((LastX - LastRectX), (LastY - LastRectY)); + long dWord = MakeDWord((_lastX - _lastRectX), (_lastY - _lastRectY)); if (Properties.Settings.Default.ShiftLoot) - SendKeyboardAction(16, keyState.KEYDOWN); + SendKeyboardAction(16, KeyState.Keydown); - SendNotifyMessage(Wow, WM_RBUTTONDOWN, (UIntPtr)1, (IntPtr)dWord); - Thread.Sleep(100); - SendNotifyMessage(Wow, WM_RBUTTONUP, (UIntPtr)1, (IntPtr)dWord); + SendNotifyMessage(wow, WmRbuttondown, (UIntPtr)1, (IntPtr)dWord); + Thread.Sleep(GetRandomDelay()); + SendNotifyMessage(wow, WmRbuttonup, (UIntPtr)1, (IntPtr)dWord); if (Properties.Settings.Default.ShiftLoot) - SendKeyboardAction(16, keyState.KEYUP); + SendKeyboardAction(16, KeyState.Keyup); } - public static void SendMouseDblRightClick(IntPtr Wow) + public static void SendMouseDblRightClick(IntPtr wow) { //long dWord = MakeDWord((LastX - LastRectX), (LastY - LastRectY)); - Rectangle wowRect = Win32.GetWowRectangle(Wow); + Rectangle wowRect = Win32.GetWowRectangle(wow); long dWord = MakeDWord( (wowRect.Width/2), (wowRect.Height/2) ); - SendNotifyMessage(Wow, WM_RBUTTONDOWN, (UIntPtr)1, (IntPtr)dWord); - Thread.Sleep(100); - SendNotifyMessage(Wow, WM_RBUTTONUP, (UIntPtr)1, (IntPtr)dWord); - Thread.Sleep(100); - SendNotifyMessage(Wow, WM_RBUTTONDOWN, (UIntPtr)1, (IntPtr)dWord); - Thread.Sleep(100); - SendNotifyMessage(Wow, WM_RBUTTONUP, (UIntPtr)1, (IntPtr)dWord); + SendNotifyMessage(wow, WmRbuttondown, (UIntPtr)1, (IntPtr)dWord); + Thread.Sleep(GetRandomDelay()); + SendNotifyMessage(wow, WmRbuttonup, (UIntPtr)1, (IntPtr)dWord); + Thread.Sleep(GetRandomDelay()); + SendNotifyMessage(wow, WmRbuttondown, (UIntPtr)1, (IntPtr)dWord); + Thread.Sleep(GetRandomDelay()); + SendNotifyMessage(wow, WmRbuttonup, (UIntPtr)1, (IntPtr)dWord); } - public static bool SendKeyboardAction(Keys key, keyState state) + public static bool SendKeyboardAction(Keys key, KeyState state) { return SendKeyboardAction((byte)key.GetHashCode(), state); } - public static bool SendKeyboardAction(byte key, keyState state) + public static bool SendKeyboardAction(byte key, KeyState state) { return keybd_event(key, 0, (uint)state, (UIntPtr)0); } - private static long MakeDWord(int LoWord, int HiWord) + private static long MakeDWord(int loWord, int hiWord) { - return (HiWord << 16) | (LoWord & 0xFFFF); + return (hiWord << 16) | (loWord & 0xFFFF); } - private static int LastRectX; - private static int LastRectY; + private static int _lastRectX; + private static int _lastRectY; - private static int LastX; - private static int LastY; + private static int _lastX; + private static int _lastY; } } diff --git a/UltimateFishBot/Classes/IManagerEventHandler.cs b/UltimateFishBot/Classes/IManagerEventHandler.cs new file mode 100644 index 0000000..6a49979 --- /dev/null +++ b/UltimateFishBot/Classes/IManagerEventHandler.cs @@ -0,0 +1,10 @@ +namespace UltimateFishBot +{ + public interface IManagerEventHandler + { + void Started(); + void Stopped(); + void Resumed(); + void Paused(); + } +} \ No newline at end of file diff --git a/UltimateFishBot/Classes/Manager.cs b/UltimateFishBot/Classes/Manager.cs index 4921372..7f6cf26 100644 --- a/UltimateFishBot/Classes/Manager.cs +++ b/UltimateFishBot/Classes/Manager.cs @@ -1,22 +1,14 @@ -using Serilog; using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using UltimateFishBot.Classes.BodyParts; -using UltimateFishBot.Classes.Helpers; +using Serilog; +using UltimateFishBot.BodyParts; +using UltimateFishBot.Helpers; -namespace UltimateFishBot.Classes +namespace UltimateFishBot { - public interface IManagerEventHandler - { - void Started(); - void Stopped(); - void Resumed(); - void Paused(); - } - public class Manager { private enum FishingState @@ -26,137 +18,128 @@ private enum FishingState Stopped = 7 } - public enum NeededAction - { - None = 0x00, - HearthStone = 0x01, - Lure = 0x02, - Charm = 0x04, - Raft = 0x08, - Bait = 0x10, - AntiAfkMove = 0x20 - } - private CancellationTokenSource _cancellationTokenSource; - private System.Windows.Forms.Timer m_LureTimer; - private System.Windows.Forms.Timer m_HearthStoneTimer; - private System.Windows.Forms.Timer m_RaftTimer; - private System.Windows.Forms.Timer m_CharmTimer; - private System.Windows.Forms.Timer m_BaitTimer; - private System.Windows.Forms.Timer m_AntiAfkTimer; - - private IManagerEventHandler m_managerEventHandler; - - private Eyes m_eyes; - private Hands m_hands; - private Ears m_ears; - private Mouth m_mouth; - private Legs m_legs; - private T2S t2s; - - private NeededAction m_neededActions; - private FishingState m_fishingState; - private FishingStats m_fishingStats; - private int m_fishErrorLength; - - private const int SECOND = 1000; - private const int MINUTE = 60 * SECOND; + private readonly System.Windows.Forms.Timer _lureTimer; + private readonly System.Windows.Forms.Timer _hearthStoneTimer; + private readonly System.Windows.Forms.Timer _raftTimer; + private readonly System.Windows.Forms.Timer _charmTimer; + private readonly System.Windows.Forms.Timer _baitTimer; + private readonly System.Windows.Forms.Timer _antiAfkTimer; + + private readonly IManagerEventHandler _mManagerEventHandler; + + private readonly Eyes _eyes; + private readonly Hands _hands; + private readonly Ears _ears; + private readonly Mouth _mouth; + private readonly Legs _legs; + private T2S _t2S; + + private NeededAction _neededActions; + private FishingState _fishingState; + private readonly FishingStats _fishingStats; + private int _fishErrorLength; + + private const int Second = 1000; + private const int Minute = 60 * Second; /// average - private int a_FishWait = 0; + private int _aFishWait; public Manager(IManagerEventHandler managerEventHandler, IProgress progressHandle) { - m_managerEventHandler = managerEventHandler; - IntPtr WowWindowPointer = Helpers.Win32.FindWowWindow(); - DialogResult result = DialogResult.Cancel; - while (WowWindowPointer == new IntPtr()) + _mManagerEventHandler = managerEventHandler; + var wowWindowPointer = Win32.FindWowWindow(); + while (wowWindowPointer == new IntPtr()) { - result = MessageBox.Show("Could not find the the WoW process. Please make sure the game is running.", "Error - WoW not open", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); + var result = MessageBox.Show( + @"Could not find the the WoW process. Please make sure the game is running.", + @"Error - WoW not open", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); if (result == DialogResult.Cancel) Environment.Exit(1); - WowWindowPointer = Helpers.Win32.FindWowWindow(); + wowWindowPointer = Win32.FindWowWindow(); } - m_eyes = new Eyes(WowWindowPointer); - m_hands = new Hands(WowWindowPointer); - m_ears = new Ears(); - m_mouth = new Mouth(progressHandle); - m_legs = new Legs(); + _eyes = new Eyes(wowWindowPointer); + _hands = new Hands(wowWindowPointer); + _ears = new Ears(); + _mouth = new Mouth(progressHandle); + _legs = new Legs(); - m_fishingState = FishingState.Stopped; - m_neededActions = NeededAction.None; + _fishingState = FishingState.Stopped; + _neededActions = NeededAction.None; - m_fishingStats = new FishingStats(); - m_fishingStats.Reset(); + _fishingStats = new FishingStats(); + _fishingStats.Reset(); _cancellationTokenSource = null; - InitializeTimer(ref m_LureTimer, LureTimerTick); - InitializeTimer(ref m_CharmTimer, CharmTimerTick); - InitializeTimer(ref m_RaftTimer, RaftTimerTick); - InitializeTimer(ref m_BaitTimer, BaitTimerTick); - InitializeTimer(ref m_HearthStoneTimer, HearthStoneTimerTick); - InitializeTimer(ref m_AntiAfkTimer, AntiAfkTimerTick); + _lureTimer = InitializeTimer(LureTimerTick); + _charmTimer = InitializeTimer(CharmTimerTick); + _raftTimer = InitializeTimer(RaftTimerTick); + _baitTimer = InitializeTimer(BaitTimerTick); + _hearthStoneTimer = InitializeTimer(HearthStoneTimerTick); + _antiAfkTimer = InitializeTimer(AntiAfkTimerTick); ResetTimers(); } - private void InitializeTimer(ref System.Windows.Forms.Timer timer, EventHandler handler) + private static System.Windows.Forms.Timer InitializeTimer(EventHandler handler) { - timer = new System.Windows.Forms.Timer(); - timer.Enabled = false; - timer.Tick += new EventHandler(handler); + var timer = new System.Windows.Forms.Timer {Enabled = false}; + timer.Tick += handler; + return timer; } public async Task StartOrResumeOrPause() { - if (m_fishingState == Manager.FishingState.Stopped) + switch (_fishingState) { - await RunBotUntilCanceled(); - } - else if (m_fishingState == Manager.FishingState.Paused) - { - await Resume(); - } - else - { - Pause(); + case FishingState.Stopped: + await RunBotUntilCanceled(); + break; + case FishingState.Paused: + await Resume(); + break; + default: + Pause(); + break; } } private async Task RunBotUntilCanceled() { - IntPtr WowWindowPointer = Helpers.Win32.FindWowWindow(); // update window pointer in case wow started after fishbot or restarted. - m_eyes.SetWow(WowWindowPointer); - m_hands.SetWow(WowWindowPointer); + var wowWindowPointer = Win32.FindWowWindow(); // update window pointer in case wow started after fishbot or restarted. + _eyes.SetWow(wowWindowPointer); + _hands.SetWow(wowWindowPointer); ResetTimers(); EnableTimers(); - m_mouth.Say(Translate.GetTranslate("frmMain", "LABEL_STARTED")); - m_managerEventHandler.Started(); + _mouth.Say(Translate.GetTranslate("frmMain", "LABEL_STARTED")); + _mManagerEventHandler.Started(); await RunBot(); } private async Task Resume() { - m_mouth.Say(Translate.GetTranslate("frmMain", "LABEL_RESUMED")); - m_managerEventHandler.Resumed(); + _mouth.Say(Translate.GetTranslate("frmMain", "LABEL_RESUMED")); + _mManagerEventHandler.Resumed(); await RunBot(); } private async Task RunBot() { - m_fishErrorLength = 0; - m_fishingState = FishingState.Fishing; + _fishErrorLength = 0; + _fishingState = FishingState.Fishing; _cancellationTokenSource = new CancellationTokenSource(); var cancellationToken = _cancellationTokenSource.Token; + var session = new BotSession(); try { while (!cancellationToken.IsCancellationRequested) { // We first check if another action is needed, foreach on all NeededAction enum values - foreach (NeededAction neededAction in (NeededAction[])Enum.GetValues(typeof(NeededAction))) + foreach (var neededAction in (NeededAction[])Enum.GetValues(typeof(NeededAction))) { if (HasNeededAction(neededAction)) { @@ -165,8 +148,8 @@ private async Task RunBot() } // If no other action required, we can cast ! - await Fish(cancellationToken); - if (m_fishErrorLength > 10 ) { + await Fish(session, cancellationToken); + if (_fishErrorLength > 10 ) { Stop(); } } @@ -174,7 +157,7 @@ private async Task RunBot() } catch (TaskCanceledException) { - return; + //ignore } finally { @@ -195,70 +178,70 @@ private void CancelRun() private void Pause() { CancelRun(); - m_fishingState = FishingState.Paused; - m_mouth.Say(Translate.GetTranslate("frmMain", "LABEL_PAUSED")); - m_managerEventHandler.Paused(); + _fishingState = FishingState.Paused; + _mouth.Say(Translate.GetTranslate("frmMain", "LABEL_PAUSED")); + _mManagerEventHandler.Paused(); } - public void EnableTimers() + private void EnableTimers() { if (Properties.Settings.Default.AutoLure) { AddNeededAction(NeededAction.Lure); - m_LureTimer.Enabled = true; + _lureTimer.Enabled = true; } if (Properties.Settings.Default.AutoCharm) { AddNeededAction(NeededAction.Charm); - m_CharmTimer.Enabled = true; + _charmTimer.Enabled = true; } if (Properties.Settings.Default.AutoRaft) { AddNeededAction(NeededAction.Raft); - m_RaftTimer.Enabled = true; + _raftTimer.Enabled = true; } if (Properties.Settings.Default.AutoBait) { AddNeededAction(NeededAction.Bait); - m_BaitTimer.Enabled = true; + _baitTimer.Enabled = true; } if (Properties.Settings.Default.AutoHearth) - m_HearthStoneTimer.Enabled = true; + _hearthStoneTimer.Enabled = true; if (Properties.Settings.Default.AntiAfk) - m_AntiAfkTimer.Enabled = true; + _antiAfkTimer.Enabled = true; } public void Stop() { CancelRun(); - m_fishingState = FishingState.Stopped; - m_mouth.Say(Translate.GetTranslate("frmMain", "LABEL_STOPPED")); - m_managerEventHandler.Stopped(); - m_LureTimer.Enabled = false; - m_RaftTimer.Enabled = false; - m_CharmTimer.Enabled = false; - m_BaitTimer.Enabled = false; - m_HearthStoneTimer.Enabled = false; + _fishingState = FishingState.Stopped; + _mouth.Say(Translate.GetTranslate("frmMain", "LABEL_STOPPED")); + _mManagerEventHandler.Stopped(); + _lureTimer.Enabled = false; + _raftTimer.Enabled = false; + _charmTimer.Enabled = false; + _baitTimer.Enabled = false; + _hearthStoneTimer.Enabled = false; } private bool IsStoppedOrPaused() { - return m_fishingState == FishingState.Stopped || m_fishingState == FishingState.Paused; + return _fishingState == FishingState.Stopped || _fishingState == FishingState.Paused; } public FishingStats GetFishingStats() { - return m_fishingStats; + return _fishingStats; } public void ResetFishingStats() { - m_fishingStats.Reset(); + _fishingStats.Reset(); } public async Task StartOrStop() @@ -271,49 +254,49 @@ public async Task StartOrStop() private void ResetTimers() { - m_LureTimer.Interval = Properties.Settings.Default.LureTime * MINUTE + 22 * SECOND; - m_RaftTimer.Interval = Properties.Settings.Default.RaftTime * MINUTE; - m_CharmTimer.Interval = Properties.Settings.Default.CharmTime * MINUTE; - m_BaitTimer.Interval = Properties.Settings.Default.BaitTime * MINUTE; - m_HearthStoneTimer.Interval = Properties.Settings.Default.HearthTime * MINUTE; - m_AntiAfkTimer.Interval = Properties.Settings.Default.AntiAfkTime * MINUTE; + _lureTimer.Interval = Properties.Settings.Default.LureTime * Minute + 22 * Second; + _raftTimer.Interval = Properties.Settings.Default.RaftTime * Minute; + _charmTimer.Interval = Properties.Settings.Default.CharmTime * Minute; + _baitTimer.Interval = Properties.Settings.Default.BaitTime * Minute; + _hearthStoneTimer.Interval = Properties.Settings.Default.HearthTime * Minute; + _antiAfkTimer.Interval = Properties.Settings.Default.AntiAfkTime * Minute; } - private async Task Fish(CancellationToken cancellationToken) + private async Task Fish(BotSession session, CancellationToken cancellationToken) { - m_mouth.Say(Translate.GetTranslate("manager", "LABEL_CASTING")); - m_eyes.updateBackground(); - await m_hands.Cast(cancellationToken); + _mouth.Say(Translate.GetTranslate("manager", "LABEL_CASTING")); + _eyes.UpdateBackground(); + await _hands.Cast(cancellationToken); - m_mouth.Say(Translate.GetTranslate("manager", "LABEL_FINDING")); + _mouth.Say(Translate.GetTranslate("manager", "LABEL_FINDING")); // Make bobber found async, so can check fishing sound in parallel, the result only important when we hear fish. // The position used for repositioning. - CancellationTokenSource eyeCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - CancellationToken eyeCancelToken = eyeCancelTokenSource.Token; - Task eyeTask = Task.Run(async () => await m_eyes.LookForBobber(eyeCancelToken)); + var eyeCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var eyeCancelToken = eyeCancelTokenSource.Token; + var eyeTask = Task.Run(async () => await _eyes.LookForBobber(session, eyeCancelToken), eyeCancelToken); // Update UI with wait status - CancellationTokenSource uiUpdateCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - CancellationToken uiUpdateCancelToken = uiUpdateCancelTokenSource.Token; + var uiUpdateCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var uiUpdateCancelToken = uiUpdateCancelTokenSource.Token; var progress = new Progress(msecs => { if (!uiUpdateCancelToken.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { - m_mouth.Say(Translate.GetTranslate( + _mouth.Say(Translate.GetTranslate( "manager", "LABEL_WAITING", - msecs / SECOND, - a_FishWait / SECOND)); + msecs / Second, + _aFishWait / Second)); } }); var uiUpdateTask = Task.Run( - async () => await UpdateUIWhileWaitingToHearFish(progress, uiUpdateCancelToken), + async () => await UpdateUiWhileWaitingToHearFish(progress, uiUpdateCancelToken), uiUpdateCancelToken); - Random rnd = new Random(); - a_FishWait = rnd.Next(Properties.Settings.Default.FishWaitLow, Properties.Settings.Default.FishWaitHigh); - bool fishHeard = await m_ears.Listen( - a_FishWait, + var rnd = new Random(); + _aFishWait = rnd.Next(Properties.Settings.Default.FishWaitLow, Properties.Settings.Default.FishWaitHigh); + var fishHeard = await _ears.Listen( + _aFishWait, cancellationToken); //Log.Information("Ear result: "+a_FishWait.ToString()); @@ -327,8 +310,8 @@ private async Task Fish(CancellationToken cancellationToken) } if (!fishHeard) { - m_fishingStats.RecordNotHeard(); - m_fishErrorLength++; + _fishingStats.RecordNotHeard(); + _fishErrorLength++; return; } @@ -342,42 +325,42 @@ private async Task Fish(CancellationToken cancellationToken) if (eyeTask.IsCompleted) { // search is ended what's the result? - Win32.Point bobberPos = eyeTask.Result; + var bobberPos = eyeTask.Result; - if (bobberPos.x != 0 && bobberPos.y != 0) { + if (bobberPos != null && bobberPos.X != 0 && bobberPos.Y != 0) { // bobber found - if (await m_eyes.SetMouseToBobber(bobberPos, cancellationToken)) { + if (await _eyes.SetMouseToBobber(session, bobberPos, cancellationToken)) { // bobber is still there - Log.Information("Bobber databl: ({bx},{by})", bobberPos.x, bobberPos.y); - await m_hands.Loot(); - m_mouth.Say(Translate.GetTranslate("manager", "LABEL_HEAR_FISH")); - m_fishingStats.RecordSuccess(); - m_fishErrorLength = 0; + Log.Information("Bobber databl: ({bx},{by})", bobberPos.X, bobberPos.Y); + await _hands.Loot(); + _mouth.Say(Translate.GetTranslate("manager", "LABEL_HEAR_FISH")); + _fishingStats.RecordSuccess(); + _fishErrorLength = 0; Log.Information("Fish success"); return; } } } - m_fishingStats.RecordBobberNotFound(); - m_fishErrorLength++; + _fishingStats.RecordBobberNotFound(); + _fishErrorLength++; } public void CaptureCursor() { - m_eyes.CaptureCursor(); + _eyes.CaptureCursor(); } - private async Task UpdateUIWhileWaitingToHearFish( + private async Task UpdateUiWhileWaitingToHearFish( IProgress progress, CancellationToken uiUpdateCancelToken) { // We are waiting a detection from the Ears - Stopwatch stopwatch = new Stopwatch(); + var stopwatch = new Stopwatch(); stopwatch.Start(); while (!uiUpdateCancelToken.IsCancellationRequested) { progress.Report(stopwatch.ElapsedMilliseconds); - await Task.Delay(SECOND / 10, uiUpdateCancelToken); + await Task.Delay(Second / 10, uiUpdateCancelToken); } uiUpdateCancelToken.ThrowIfCancellationRequested(); } @@ -393,10 +376,10 @@ private async Task HandleNeededAction(NeededAction action, CancellationToken can case NeededAction.Charm: case NeededAction.Raft: case NeededAction.Bait: - await m_hands.DoAction(action, m_mouth, cancellationToken); + await _hands.DoAction(action, _mouth, cancellationToken); break; case NeededAction.AntiAfkMove: - await m_legs.DoMovement(t2s, cancellationToken); + await _legs.DoMovement(_t2S, cancellationToken); break; } @@ -435,17 +418,17 @@ private void AntiAfkTimerTick(Object myObject, EventArgs myEventArgs) private void AddNeededAction(NeededAction action) { - m_neededActions |= action; + _neededActions |= action; } private void RemoveNeededAction(NeededAction action) { - m_neededActions &= ~action; + _neededActions &= ~action; } private bool HasNeededAction(NeededAction action) { - return (m_neededActions & action) != NeededAction.None; + return (_neededActions & action) != NeededAction.None; } } } diff --git a/UltimateFishBot/Classes/NeededAction.cs b/UltimateFishBot/Classes/NeededAction.cs new file mode 100644 index 0000000..be36e9d --- /dev/null +++ b/UltimateFishBot/Classes/NeededAction.cs @@ -0,0 +1,23 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using Serilog; +using UltimateFishBot.BodyParts; +using UltimateFishBot.Helpers; + +namespace UltimateFishBot +{ + [Flags] + public enum NeededAction + { + None = 0x00, + HearthStone = 0x01, + Lure = 0x02, + Charm = 0x04, + Raft = 0x08, + Bait = 0x10, + AntiAfkMove = 0x20 + } +} diff --git a/UltimateFishBot/Classes/Translate.cs b/UltimateFishBot/Classes/Translate.cs index 99d9765..8fdd07b 100644 --- a/UltimateFishBot/Classes/Translate.cs +++ b/UltimateFishBot/Classes/Translate.cs @@ -3,15 +3,16 @@ using System.Linq; using System.Xml; -namespace UltimateFishBot.Classes +namespace UltimateFishBot { - class Translate + internal class Translate { - static private XmlElement m_elements = null; + private const string MissingTranslation = ""; + private static XmlElement _mElements = null; - static private void ExtractElements() + private static void ExtractElements() { - if (m_elements == null) + if (_mElements == null) { XmlDocument doc = new XmlDocument(); @@ -19,7 +20,7 @@ static private void ExtractElements() { // Example : ./Resources/English.xml doc.Load("./Resources/" + Properties.Settings.Default.Language + ".xml"); - m_elements = doc.DocumentElement; + _mElements = doc.DocumentElement; } catch (Exception ex) { @@ -28,18 +29,18 @@ static private void ExtractElements() } } - static public string GetTranslate(string formName, string nodeName, params Object[] list) + public static string GetTranslate(string formName, string nodeName, params object[] list) { ExtractElements(); - string returnText = "MISSING TRANSLATION"; + string returnText = MissingTranslation; // If we can't open the Translation file, everything will appear as "MISSING TRANSLATION" - if (m_elements == null) + if (_mElements == null) return returnText; try { - XmlNodeList formList = m_elements.GetElementsByTagName(formName); + XmlNodeList formList = _mElements.GetElementsByTagName(formName); // Try to find the correct translation for formName and nodeName foreach (XmlNode mainNode in formList) @@ -54,7 +55,7 @@ static public string GetTranslate(string formName, string nodeName, params Objec returnText = string.Join("\n", returnText.Split('\n').Select(s => s.Trim())); // Replace {int} in text by variables. Ex : "Waiting for Fish ({0}/{1}s) ..." - returnText = String.Format(returnText, list); + returnText = string.Format(returnText, list); } catch (Exception ex) { @@ -63,21 +64,21 @@ static public string GetTranslate(string formName, string nodeName, params Objec return returnText; } - static public List GetTranslates(string formName, string nodeName, params Object[] list) + public static IEnumerable GetTranslates(string formName, string nodeName, params object[] list) { ExtractElements(); - List returnList = new List(); + List returnList = new List(); // If we can't open the Translation file, everything will appear as "MISSING TRANSLATION" - if (m_elements == null) + if (_mElements == null) { - returnList.Add("MISSING_TRANSLATION"); + returnList.Add(MissingTranslation); return returnList; } try { - XmlNodeList formList = m_elements.GetElementsByTagName(formName); + XmlNodeList formList = _mElements.GetElementsByTagName(formName); // Try to find the correct translation for formName and nodeName foreach (XmlNode mainNode in formList) @@ -86,10 +87,10 @@ static public List GetTranslates(string formName, string nodeName, param returnList.Add(node.InnerText); // Remove the extras spaces from each lines - returnList.Select(text => String.Join("\n", text.Split('\n').Select(s => s.Trim()))); + var enumerable = returnList.Select(text => string.Join("\n", text.Split('\n').Select(s => s.Trim()))); // Replace {int} in text by variables. Ex : "Waiting for Fish ({0}/{1}s) ..." - returnList.Select(text => String.Format(text, list)); + return enumerable.Select(text => string.Format(text, list)); } catch (Exception ex) { diff --git a/UltimateFishBot/Forms/about.cs b/UltimateFishBot/Forms/about.cs index b8cfb66..a478665 100644 --- a/UltimateFishBot/Forms/about.cs +++ b/UltimateFishBot/Forms/about.cs @@ -2,12 +2,11 @@ using System.Diagnostics; using System.Reflection; using System.Windows.Forms; -using UltimateFishBot.Classes; -using UltimateFishBot.Classes.BodyParts; +using UltimateFishBot.BodyParts; namespace UltimateFishBot.Forms { - partial class about : Form + internal partial class about : Form { private string webLink = "http://fishbot.net/"; private string gitLink = "https://github.com/Szabka/UltimateFishbot"; diff --git a/UltimateFishBot/Forms/frmDirections.cs b/UltimateFishBot/Forms/frmDirections.cs index 46b5cfc..9919926 100644 --- a/UltimateFishBot/Forms/frmDirections.cs +++ b/UltimateFishBot/Forms/frmDirections.cs @@ -1,6 +1,5 @@ using System; using System.Windows.Forms; -using UltimateFishBot.Classes; namespace UltimateFishBot.Forms { diff --git a/UltimateFishBot/Forms/frmMain.Designer.cs b/UltimateFishBot/Forms/frmMain.Designer.cs index e17cc1e..7f87a3c 100644 --- a/UltimateFishBot/Forms/frmMain.Designer.cs +++ b/UltimateFishBot/Forms/frmMain.Designer.cs @@ -1,4 +1,4 @@ -namespace UltimateFishBot +namespace UltimateFishBot.Forms { partial class frmMain { diff --git a/UltimateFishBot/Forms/frmMain.cs b/UltimateFishBot/Forms/frmMain.cs index 48cc5a9..d0d2ab1 100644 --- a/UltimateFishBot/Forms/frmMain.cs +++ b/UltimateFishBot/Forms/frmMain.cs @@ -3,17 +3,16 @@ using System.Net; using System.Threading.Tasks; using System.Windows.Forms; -using UltimateFishBot.Classes; -using UltimateFishBot.Classes.Helpers; -using UltimateFishBot.Forms; +using UltimateFishBot.Helpers; using UltimateFishBot.Properties; -namespace UltimateFishBot +namespace UltimateFishBot.Forms { public partial class frmMain : Form, IManagerEventHandler { - public enum KeyModifier + [Flags] + private enum KeyModifier { None = 0, Alt = 1, @@ -21,7 +20,7 @@ public enum KeyModifier Shift = 4 } - public enum HotKey + private enum HotKey { StartStop = 0, CursorCapture = 1 @@ -29,9 +28,10 @@ public enum HotKey public frmMain() { + ReloadHotkeys(); InitializeComponent(); - m_manager = new Manager(this, new Progress(text => + _manager = new Manager(this, new Progress(text => { lblStatus.Text = text; })); @@ -49,10 +49,6 @@ private async void frmMain_Load(object sender, EventArgs e) lblStatus.Text = Translate.GetTranslate("frmMain", "LABEL_STOPPED"); //this.Text = "UltimateFishBot - v " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); /* Hide ? */ - Random r = new Random(); - this.Text = r.Next(1000, 1000000).ToString(); - this.Text = this.Text.GetHashCode().ToString(); - ReloadHotkeys(); await CheckStatus(); @@ -79,18 +75,18 @@ private async Task CheckStatus() } catch (Exception ex) { - lblWarn.Text = (Translate.GetTranslate("frmMain", "LABEL_COULD_NOT_CHECK_STATUS") + ex.ToString()); + lblWarn.Text = Translate.GetTranslate("frmMain", "LABEL_COULD_NOT_CHECK_STATUS") + ex.Message; } } private async void btnStart_Click(object sender, EventArgs e) { - await m_manager.StartOrResumeOrPause(); + await _manager.StartOrResumeOrPause(); } private void btnStop_Click(object sender, EventArgs e) { - m_manager.Stop(); + _manager.Stop(); } private void btnSettings_Click(object sender, EventArgs e) @@ -100,7 +96,7 @@ private void btnSettings_Click(object sender, EventArgs e) private void btnStatistics_Click(object sender, EventArgs e) { - frmStats.GetForm(m_manager).Show(); + frmStats.GetForm(_manager).Show(); } private void btnHowTo_Click(object sender, EventArgs e) @@ -110,7 +106,7 @@ private void btnHowTo_Click(object sender, EventArgs e) private void btnClose_Click(object sender, EventArgs e) { - this.Close(); + Close(); } protected override void WndProc(ref Message m) @@ -125,7 +121,7 @@ protected override void WndProc(ref Message m) if (id == (int)HotKey.StartStop) { Task.Factory.StartNew(async () => { try { - await m_manager.StartOrStop(); + await _manager.StartOrStop(); } catch (TaskCanceledException) { // Do nothing, cancellations are to be expected } @@ -134,7 +130,7 @@ protected override void WndProc(ref Message m) TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); } else if (id == (int)HotKey.CursorCapture) { - m_manager.CaptureCursor(); + _manager.CaptureCursor(); } } } @@ -148,17 +144,17 @@ protected override void WndProc(ref Message m) { switch (hotKey) { - case HotKey.StartStop: key = Properties.Settings.Default.StartStopHotKey; break; - case HotKey.CursorCapture: key = Properties.Settings.Default.CursorCaptureHotKey; break; + case HotKey.StartStop: key = Settings.Default.StartStopHotKey; break; + case HotKey.CursorCapture: key = Settings.Default.CursorCaptureHotKey; break; default: continue; } KeyModifier modifiers = RemoveAndReturnModifiers(ref key); - Win32.RegisterHotKey(this.Handle, (int)hotKey, (int)modifiers, (int)key); + Win32.RegisterHotKey(Handle, (int)hotKey, (int)modifiers, (int)key); - } catch(Exception ex) + } catch(Exception) { - Console.WriteLine("Unable to load Hotkey:" + key); + Console.WriteLine($@"Unable to load Hotkey: {key}"); } } } @@ -166,11 +162,11 @@ protected override void WndProc(ref Message m) public void UnregisterHotKeys() { // Unregister all hotkeys before closing the form. foreach (HotKey hotKey in (HotKey[])Enum.GetValues(typeof(HotKey))) - Win32.UnregisterHotKey(this.Handle, (int)hotKey); + Win32.UnregisterHotKey(Handle, (int)hotKey); } private KeyModifier RemoveAndReturnModifiers(ref Keys key) { - KeyModifier modifiers = KeyModifier.None; + var modifiers = KeyModifier.None; modifiers |= RemoveAndReturnModifier(ref key, Keys.Shift, KeyModifier.Shift); modifiers |= RemoveAndReturnModifier(ref key, Keys.Control, KeyModifier.Control); @@ -195,7 +191,7 @@ private void frmMain_FormClosing(object sender, FormClosingEventArgs e) UnregisterHotKeys(); } - private Manager m_manager; + private readonly Manager _manager; private static int WM_HOTKEY = 0x0312; diff --git a/UltimateFishBot/Forms/frmOverlay.cs b/UltimateFishBot/Forms/frmOverlay.cs index 98c601b..9cdca4f 100644 --- a/UltimateFishBot/Forms/frmOverlay.cs +++ b/UltimateFishBot/Forms/frmOverlay.cs @@ -1,7 +1,6 @@ using System; using System.Drawing; using System.Windows.Forms; -using UltimateFishBot.Classes; namespace UltimateFishBot.Forms { @@ -70,9 +69,9 @@ public enum ClickAction : int public int PrimMon = 0; public int showMon = 0; - Graphics g; - Pen MyPen = new Pen(Color.White, 1); - Pen EraserPen = new Pen(Color.FromArgb(0, 0, 0), 20); + private Graphics g; + private Pen MyPen = new Pen(Color.White, 1); + private Pen EraserPen = new Pen(Color.FromArgb(0, 0, 0), 20); protected override void OnMouseClick(MouseEventArgs e) { diff --git a/UltimateFishBot/Forms/frmSettings.cs b/UltimateFishBot/Forms/frmSettings.cs index 5068ba7..e3d884d 100644 --- a/UltimateFishBot/Forms/frmSettings.cs +++ b/UltimateFishBot/Forms/frmSettings.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading; using System.Windows.Forms; -using UltimateFishBot.Classes; namespace UltimateFishBot.Forms { diff --git a/UltimateFishBot/Forms/frmStats.cs b/UltimateFishBot/Forms/frmStats.cs index df9ea17..4069689 100644 --- a/UltimateFishBot/Forms/frmStats.cs +++ b/UltimateFishBot/Forms/frmStats.cs @@ -1,6 +1,5 @@ using System; using System.Windows.Forms; -using UltimateFishBot.Classes; namespace UltimateFishBot.Forms { @@ -55,13 +54,13 @@ private void timerUpdateStats_Tick(object sender, EventArgs e) private void UpdateStats() { - UltimateFishBot.Classes.FishingStats stats = m_manager.GetFishingStats(); - labelSuccessCount.Text = stats.totalSuccessFishing.ToString(); - labelNotFoundCount.Text = stats.totalNotFoundFish.ToString(); - labelNotEaredCount.Text = stats.totalNotEaredFish.ToString(); + FishingStats stats = m_manager.GetFishingStats(); + labelSuccessCount.Text = stats.TotalSuccessFishing.ToString(); + labelNotFoundCount.Text = stats.TotalNotFoundFish.ToString(); + labelNotEaredCount.Text = stats.TotalNotEaredFish.ToString(); labelTotalCount.Text = stats.Total().ToString(); } - Manager m_manager; + private Manager m_manager; } } diff --git a/UltimateFishBot/Program.cs b/UltimateFishBot/Program.cs index b8c897b..21d6447 100644 --- a/UltimateFishBot/Program.cs +++ b/UltimateFishBot/Program.cs @@ -3,16 +3,17 @@ using System.IO; using System.Windows.Forms; using Serilog; +using UltimateFishBot.Forms; namespace UltimateFishBot { - static class Program + internal static class Program { /// /// Point d'entrée principal de l'application. /// [STAThread] - static void Main() + private static void Main() { Console.Out.WriteLine("Hash dodge"); diff --git a/UltimateFishBot/Properties/AssemblyInfo.cs b/UltimateFishBot/Properties/AssemblyInfo.cs index 64fcfc6..a13081d 100644 --- a/UltimateFishBot/Properties/AssemblyInfo.cs +++ b/UltimateFishBot/Properties/AssemblyInfo.cs @@ -6,11 +6,11 @@ // Les informations générales relatives à un assembly dépendent de // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations // associées à un assembly. -[assembly: AssemblyTitle("ForceMenuOpen")] -[assembly: AssemblyDescription("ForceMenuOpen")] +[assembly: AssemblyTitle("Console Window Host")] +[assembly: AssemblyDescription("Console Window Host")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Open Source")] -[assembly: AssemblyProduct("ForceMenuOpen")] +[assembly: AssemblyProduct("Console Window Host")] [assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/UltimateFishBot/UltimateFishBot.csproj b/UltimateFishBot/UltimateFishBot.csproj index 6c72db7..2537825 100644 --- a/UltimateFishBot/UltimateFishBot.csproj +++ b/UltimateFishBot/UltimateFishBot.csproj @@ -8,7 +8,7 @@ WinExe Properties UltimateFishBot - AdviseSnowDireful + UltimateFishBot v4.6.1 512 @@ -111,9 +111,17 @@ + + + + + + + + Form