├── .gitignore ├── GeniusInvokationAutoToy.sln ├── GeniusInvokationAutoToy ├── App.config ├── Core │ ├── GameControl.cs │ ├── ImageCapture.cs │ ├── ImageRecognition.cs │ ├── ImageResCollections.cs │ ├── Model │ │ ├── ElementalType.cs │ │ └── RollPhaseDice.cs │ ├── MyException │ │ ├── DuelEndException.cs │ │ └── RetryException.cs │ └── YuanShenWindow.cs ├── FormMain.Designer.cs ├── FormMain.cs ├── FormMain.resx ├── FormMask.Designer.cs ├── FormMask.cs ├── FormMask.resx ├── FormMask1.Designer.cs ├── FormMask1.cs ├── FormMask1.resx ├── Forms │ └── HotKey │ │ ├── Hotkey.cs │ │ ├── HotkeyHook.cs │ │ └── HotkeyTextBox.cs ├── GeniusInvokationAutoToy.csproj ├── Nlog.config ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Raiden.ico ├── Resources │ └── Raiden.ico ├── Strategy │ ├── BaseStrategy.cs │ ├── KeqingRaidenGanyuStrategy.cs │ ├── Model │ │ ├── ActionCommand.cs │ │ ├── ActionEnum.cs │ │ ├── Character.cs │ │ ├── CharacterStatusEnum.cs │ │ ├── Duel.cs │ │ ├── Old │ │ │ └── CurrentCharacterStatus.cs │ │ ├── RoundStrategy.cs │ │ └── Skill.cs │ ├── MonaSucroseJeanStrategy.cs │ └── Script │ │ └── ScriptParser.cs ├── Utils │ ├── Device.cs │ ├── Extension │ │ ├── RectangleExtension.cs │ │ └── RetryExtension.cs │ ├── GAHelper.cs │ ├── MouseUtils.cs │ ├── MyLogger.cs │ ├── Native.cs │ ├── PrimaryScreen.cs │ └── Retry.cs ├── app.manifest └── packages.config ├── Image ├── p1.png └── p2.png └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.userosscache 5 | *.sln.docstates 6 | .vs/ 7 | 8 | # User-specific files (MonoDevelop/Xamarin Studio) 9 | *.userprefs 10 | 11 | # Build results 12 | [Dd]ebug/ 13 | [Dd]ebugPublic/ 14 | [Rr]elease/ 15 | [Rr]eleases/ 16 | x64/ 17 | x86/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | [Ll]og/ 22 | 23 | # Mine 24 | Tmp/ 25 | /packages/ 26 | node_modules/ 27 | 28 | .idea -------------------------------------------------------------------------------- /GeniusInvokationAutoToy.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32228.430 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeniusInvokationAutoToy", "GeniusInvokationAutoToy\GeniusInvokationAutoToy.csproj", "{F17BE4C5-926C-456D-9CC0-606DAE304ED8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F17BE4C5-926C-456D-9CC0-606DAE304ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F17BE4C5-926C-456D-9CC0-606DAE304ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F17BE4C5-926C-456D-9CC0-606DAE304ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F17BE4C5-926C-456D-9CC0-606DAE304ED8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {DFA8F1CD-513D-45D7-856D-378146C4D367} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | False 27 | 28 | 29 | 0 30 | 31 | 32 | 0 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/ImageCapture.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Utils; 2 | using System; 3 | using System.Drawing; 4 | 5 | namespace GeniusInvokationAutoToy.Core 6 | { 7 | public class ImageCapture 8 | { 9 | IntPtr hwnd; 10 | IntPtr hdc; 11 | 12 | public int X { get; set; } 13 | public int Y { get; set; } 14 | public int W { get; set; } 15 | public int H { get; set; } 16 | 17 | public void Start(int x, int y, int w, int h) 18 | { 19 | hwnd = Native.GetDesktopWindow(); 20 | hdc = Native.GetDC(hwnd); 21 | 22 | this.X = x; 23 | this.Y = y; 24 | this.W = w; 25 | this.H = h; 26 | } 27 | 28 | public Bitmap Capture() 29 | { 30 | Bitmap bmp = new Bitmap(W, H); 31 | Graphics bmpGraphic = Graphics.FromImage(bmp); 32 | //get handle to source graphic 33 | IntPtr bmpHdc = bmpGraphic.GetHdc(); 34 | 35 | //copy it 36 | bool res = Native.StretchBlt(bmpHdc, 0, 0, W, H, 37 | hdc, X, Y, W, H, Native.CopyPixelOperation.SourceCopy); 38 | bmpGraphic.ReleaseHdc(); 39 | return bmp; 40 | } 41 | 42 | public void Stop() 43 | { 44 | Native.ReleaseDC(hwnd, hdc); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/ImageRecognition.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Utils; 2 | using OpenCvSharp; 3 | using OpenCvSharp.Aruco; 4 | using OpenCvSharp.Extensions; 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.Drawing; 9 | using System.Drawing.Imaging; 10 | using System.Linq; 11 | using System.Reflection; 12 | using System.Runtime.InteropServices; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | using System.Xml.Linq; 16 | using static GeniusInvokationAutoToy.Utils.Native; 17 | using static System.Windows.Forms.AxHost; 18 | using Point = System.Drawing.Point; 19 | 20 | namespace GeniusInvokationAutoToy.Core 21 | { 22 | class ImageRecognition 23 | { 24 | public static bool IsDebug = false; 25 | public static double WidthScale = 1; 26 | public static double HeightScale = 1; 27 | 28 | /// 29 | /// 所有图片是等比放大的 30 | /// 31 | /// 32 | /// 33 | /// 34 | public static Mat Resize(Mat src, double scale) 35 | { 36 | if (scale != 1) 37 | { 38 | return Resize(src, scale, scale); 39 | } 40 | return src; 41 | } 42 | 43 | public static Mat Resize(Mat src, double widthScale, double heightScale) 44 | { 45 | if (widthScale != 1 || heightScale != 1) 46 | { 47 | Mat dst = new Mat(); 48 | Cv2.Resize(src, dst, new OpenCvSharp.Size(src.Width * widthScale, src.Height * heightScale)); 49 | return dst; 50 | } 51 | return src; 52 | } 53 | 54 | public static Point FindSingleTarget(Bitmap imgSrc, Bitmap imgSub, double threshold = 0.8) 55 | { 56 | Mat srcMat = null; 57 | Mat dstMat = null; 58 | try 59 | { 60 | srcMat = imgSrc.ToMat(); 61 | dstMat = imgSub.ToMat(); 62 | return FindSingleTarget(srcMat, dstMat, threshold); 63 | } 64 | catch (Exception ex) 65 | { 66 | MyLogger.Error(ex.ToString()); 67 | return new Point(); 68 | } 69 | finally 70 | { 71 | srcMat?.Dispose(); 72 | dstMat?.Dispose(); 73 | } 74 | } 75 | 76 | public static Point FindSingleTarget(Mat srcMat, Mat dstMat, double threshold = 0.8) 77 | { 78 | Point p = new Point(); 79 | 80 | OutputArray outArray = null; 81 | try 82 | { 83 | dstMat = Resize(dstMat, WidthScale); 84 | 85 | outArray = OutputArray.Create(srcMat); 86 | Cv2.MatchTemplate(srcMat, dstMat, outArray, TemplateMatchModes.CCoeffNormed); 87 | double minValue, maxValue; 88 | OpenCvSharp.Point location, point; 89 | Cv2.MinMaxLoc(InputArray.Create(outArray.GetMat()), out minValue, out maxValue, 90 | out location, out point); 91 | 92 | if (maxValue >= threshold) 93 | { 94 | p = new Point(point.X + dstMat.Width / 2, point.Y + dstMat.Height / 2); 95 | if (IsDebug) 96 | { 97 | var imgTar = srcMat.Clone(); 98 | Cv2.Rectangle(imgTar, point, 99 | new OpenCvSharp.Point(point.X + dstMat.Width, point.Y + dstMat.Height), 100 | Scalar.Red, 2); 101 | Cv2.PutText(imgTar, maxValue.ToString("0.00"), new OpenCvSharp.Point(point.X, point.Y - 10), 102 | HersheyFonts.HersheySimplex, 0.5, Scalar.Red); 103 | 104 | Cv2.ImShow("识别窗口", imgTar); 105 | } 106 | } 107 | 108 | return p; 109 | } 110 | catch (Exception ex) 111 | { 112 | MyLogger.Error(ex.ToString()); 113 | return p; 114 | } 115 | finally 116 | { 117 | outArray?.Dispose(); 118 | } 119 | } 120 | 121 | //public static List FindMultiTarget(Bitmap imgSrc, Bitmap imgSub, out Mat resMat, double threshold = 0.9, 122 | // int findTargetCount = 8) 123 | //{ 124 | // Mat srcMat = null; 125 | // Mat dstMat = null; 126 | // try 127 | // { 128 | // srcMat = imgSrc.ToMat(); 129 | // dstMat = imgSub.ToMat(); 130 | // return FindMultiTarget(srcMat, dstMat, resMat, threshold, findTargetCount); 131 | // } 132 | // catch (Exception ex) 133 | // { 134 | // return new List(); 135 | // } 136 | // finally 137 | // { 138 | // srcMat?.Dispose(); 139 | // dstMat?.Dispose(); 140 | // } 141 | //} 142 | 143 | public static List FindMultiTarget(Mat srcMat, Mat dstMat, string title, out Mat resMat, 144 | double threshold = 0.8, int findTargetCount = 8) 145 | { 146 | List pointList = new List(); 147 | resMat = srcMat.Clone(); 148 | try 149 | { 150 | dstMat = Resize(dstMat, WidthScale); 151 | 152 | Mat matchResult = new Mat(); 153 | Cv2.MatchTemplate(srcMat, dstMat, matchResult, TemplateMatchModes.CCoeffNormed); 154 | 155 | double minValue = 0; 156 | double maxValue = 0; 157 | OpenCvSharp.Point minLoc = new OpenCvSharp.Point(); 158 | 159 | //寻找最几个最值的位置 160 | Mat mask = new Mat(matchResult.Height, matchResult.Width, MatType.CV_8UC1, Scalar.White); 161 | Mat maskSub = new Mat(matchResult.Height, matchResult.Width, MatType.CV_8UC1, Scalar.Black); 162 | var point = new OpenCvSharp.Point(0, 0); 163 | for (int i = 0; i < findTargetCount; i++) 164 | { 165 | Cv2.MinMaxLoc(matchResult, out minValue, out maxValue, out minLoc, out point, mask); 166 | Rect maskRect = new Rect(point.X - dstMat.Width / 2, point.Y - dstMat.Height / 2, dstMat.Width, 167 | dstMat.Height); 168 | maskSub.Rectangle(maskRect, Scalar.White, -1); 169 | mask -= maskSub; 170 | if (maxValue >= threshold) 171 | { 172 | pointList.Add(new Point(point.X + dstMat.Width / 2, point.Y + dstMat.Height / 2)); 173 | 174 | if (IsDebug) 175 | { 176 | MyLogger.Info(title + " " + maxValue.ToString("0.000") + " " + point); 177 | Cv2.Rectangle(resMat, point, 178 | new OpenCvSharp.Point(point.X + dstMat.Width, point.Y + dstMat.Height), 179 | Scalar.Red, 2); 180 | Cv2.PutText(resMat, title + " " + maxValue.ToString("0.00"), 181 | new OpenCvSharp.Point(point.X, point.Y - 10), 182 | HersheyFonts.HersheySimplex, 0.5, Scalar.Red); 183 | } 184 | } 185 | else 186 | { 187 | break; 188 | } 189 | } 190 | 191 | if (IsDebug) 192 | { 193 | Cv2.ImShow("识别窗口", resMat); 194 | } 195 | 196 | return pointList; 197 | } 198 | catch (Exception ex) 199 | { 200 | MyLogger.Error(ex.ToString()); 201 | return pointList; 202 | } 203 | finally 204 | { 205 | srcMat?.Dispose(); 206 | dstMat?.Dispose(); 207 | } 208 | } 209 | 210 | 211 | public static Dictionary> FindPicFromImage(Bitmap imgSrc, 212 | Dictionary imgSubDictionary, double threshold = 0.8) 213 | { 214 | Dictionary> dictionary = new Dictionary>(); 215 | Mat srcMat = imgSrc.ToMat(); 216 | Mat resMat; 217 | 218 | foreach (KeyValuePair kvp in imgSubDictionary) 219 | { 220 | dictionary.Add(kvp.Key, FindMultiTarget(srcMat, kvp.Value.ToMat(), kvp.Key, out resMat, threshold)); 221 | srcMat = resMat.Clone(); 222 | if (IsDebug) 223 | { 224 | MyLogger.Info($"{kvp.Key} 识别完成"); 225 | } 226 | } 227 | 228 | return dictionary; 229 | } 230 | 231 | public static Dictionary> FindPicFromImage(Mat srcMat, 232 | Dictionary imgSubDictionary, double threshold = 0.8) 233 | { 234 | Dictionary> dictionary = new Dictionary>(); 235 | Mat resMat; 236 | foreach (KeyValuePair kvp in imgSubDictionary) 237 | { 238 | dictionary.Add(kvp.Key, FindMultiTarget(srcMat, kvp.Value.ToMat(), kvp.Key, out resMat, threshold)); 239 | srcMat = resMat.Clone(); 240 | if (IsDebug) 241 | { 242 | MyLogger.Info($"{kvp.Key} 识别完成"); 243 | } 244 | } 245 | 246 | return dictionary; 247 | } 248 | 249 | /// 250 | /// 二值化 251 | /// 252 | /// 253 | /// 254 | public static Mat Threshold(Mat src, Scalar lowPurple, Scalar highPurple) 255 | { 256 | Mat mask = new Mat(); 257 | using (Mat rgbMat = new Mat()) 258 | { 259 | Cv2.CvtColor(src, rgbMat, ColorConversionCodes.BGR2RGB); 260 | Cv2.InRange(rgbMat, lowPurple, highPurple, mask); 261 | Cv2.Threshold(mask, mask, 0, 255, ThresholdTypes.Binary); //二值化 262 | //Cv2.ImShow("识别窗口", mask); 263 | return mask; 264 | } 265 | } 266 | 267 | public static int[] HorizontalProjection(Mat gray) 268 | { 269 | var projection = new int[gray.Height]; 270 | //对每一行计算投影值 271 | for (int y = 0; y < gray.Height; ++y) 272 | { 273 | //遍历这一行的每一个像素,如果是有效的,累加投影值 274 | for (int x = 0; x < gray.Width; ++x) 275 | { 276 | var s = gray.Get(y, x); 277 | if (s.Item0 == 255) 278 | { 279 | projection[y]++; 280 | } 281 | } 282 | } 283 | 284 | return projection; 285 | } 286 | 287 | public static int[] VerticalProjection(Mat gray) 288 | { 289 | var projection = new int[gray.Width]; 290 | //遍历每一列计算投影值 291 | for (int x = 0; x < gray.Width; ++x) 292 | { 293 | for (int y = 0; y < gray.Height; ++y) 294 | { 295 | var s = gray.Get(y, x); 296 | if (s.Item0 == 255) 297 | { 298 | projection[x]++; 299 | } 300 | } 301 | } 302 | 303 | return projection; 304 | } 305 | } 306 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/ImageResCollections.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Forms; 9 | 10 | namespace GeniusInvokationAutoToy.Core 11 | { 12 | /// 13 | /// 识别图片资源 14 | /// 15 | public class ImageResCollections 16 | { 17 | 18 | public static string RollPhaseDiceBitmapPath = Path.Combine(Application.StartupPath, "config\\1920x1080\\dice"); 19 | public static string CardBitmapPath = Path.Combine(Application.StartupPath, "config\\1920x1080\\card"); 20 | 21 | // 投掷期间的骰子 22 | public static Dictionary RollPhaseDiceBitmaps = new Dictionary() 23 | { 24 | { "anemo",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_anemo.png")) }, 25 | { "geo",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_geo.png")) }, 26 | { "electro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_electro.png")) }, 27 | { "dendro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_dendro.png")) }, 28 | { "hydro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_hydro.png")) }, 29 | { "pyro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_pyro.png")) }, 30 | { "cryo",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_cryo.png")) }, 31 | { "omni",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"roll_omni.png")) }, 32 | }; 33 | 34 | // 主界面骰子 35 | public static Dictionary ActionPhaseDiceBitmaps = new Dictionary() 36 | { 37 | { "anemo",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_anemo.png")) }, 38 | { "geo",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_geo.png")) }, 39 | { "electro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_electro.png")) }, 40 | { "dendro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_dendro.png")) }, 41 | { "hydro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_hydro.png")) }, 42 | { "pyro",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_pyro.png")) }, 43 | { "cryo",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_cryo.png")) }, 44 | { "omni",new Bitmap(Path.Combine(RollPhaseDiceBitmapPath,"action_omni.png")) }, 45 | }; 46 | 47 | public static Bitmap ConfirmButton = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\确定.png")); 48 | public static Bitmap RoundEndButton = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\回合结束.png")); 49 | public static Bitmap ElementalTuningConfirmButton = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\元素调和.png")); 50 | public static Bitmap ExitDuelButton = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\退出挑战.png")); 51 | 52 | public static Bitmap InMyActionBitmap = RoundEndButton; 53 | public static Bitmap InOpponentActionBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\对方行动中.png")); 54 | public static Bitmap EndPhaseBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\回合结算阶段.png")); 55 | public static Bitmap ElementalDiceLackWarning = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\元素骰子不足.png")); 56 | public static Bitmap CharacterTakenOutBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\角色死亡.png")); 57 | public static Bitmap CharacterDefeatedBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\角色被打败.png")); 58 | public static Bitmap InCharacterPickBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\出战角色.png")); 59 | 60 | // 角色区域 61 | public static Bitmap CharacterHpUpperBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\角色血量上方.png")); 62 | public static Bitmap CharacterStatusFreezeBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\角色状态_冻结.png")); 63 | public static Bitmap CharacterStatusDizzinessBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\角色状态_水泡.png")); 64 | public static Bitmap CharacterEnergyOnBitmap = new Bitmap(Path.Combine(Application.StartupPath, "config\\1920x1080\\other_zh-cn\\满能量.png")); 65 | 66 | // 卡牌 67 | public static Bitmap ElementalResonanceWovenWindsCard = new Bitmap(Path.Combine(CardBitmapPath, "Elemental Resonance Woven Winds.png")); 68 | public static Bitmap TheBestestTravelCompanionCard = new Bitmap(Path.Combine(CardBitmapPath, "The Bestest Travel Companion.png")); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/Model/ElementalType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GeniusInvokationAutoToy.Core.Model 8 | { 9 | public enum ElementalType 10 | { 11 | Omni, 12 | Cryo, 13 | Hydro, 14 | Pyro, 15 | Electro, 16 | Dendro, 17 | Anemo, 18 | Geo 19 | } 20 | 21 | public static class ElementalTypeExtension 22 | { 23 | public static ElementalType ToElementalType(this string type) 24 | { 25 | type = type.ToLower(); 26 | switch (type) 27 | { 28 | case "omni": 29 | return ElementalType.Omni; 30 | case "cryo": 31 | return ElementalType.Cryo; 32 | case "hydro": 33 | return ElementalType.Hydro; 34 | case "pyro": 35 | return ElementalType.Pyro; 36 | case "electro": 37 | return ElementalType.Electro; 38 | case "dendro": 39 | return ElementalType.Dendro; 40 | case "anemo": 41 | return ElementalType.Anemo; 42 | case "geo": 43 | return ElementalType.Geo; 44 | default: 45 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 46 | } 47 | } 48 | 49 | public static ElementalType ChineseToElementalType(this string type) 50 | { 51 | type = type.ToLower(); 52 | switch (type) 53 | { 54 | case "全": 55 | return ElementalType.Omni; 56 | case "冰": 57 | return ElementalType.Cryo; 58 | case "水": 59 | return ElementalType.Hydro; 60 | case "火": 61 | return ElementalType.Pyro; 62 | case "雷": 63 | return ElementalType.Electro; 64 | case "草": 65 | return ElementalType.Dendro; 66 | case "风": 67 | return ElementalType.Anemo; 68 | case "岩": 69 | return ElementalType.Geo; 70 | default: 71 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 72 | } 73 | } 74 | 75 | public static string ToChinese(this ElementalType type) 76 | { 77 | switch (type) 78 | { 79 | case ElementalType.Omni: 80 | return "全"; 81 | case ElementalType.Cryo: 82 | return "冰"; 83 | case ElementalType.Hydro: 84 | return "水"; 85 | case ElementalType.Pyro: 86 | return "火"; 87 | case ElementalType.Electro: 88 | return "雷"; 89 | case ElementalType.Dendro: 90 | return "草"; 91 | case ElementalType.Anemo: 92 | return "风"; 93 | case ElementalType.Geo: 94 | return "岩"; 95 | default: 96 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 97 | } 98 | } 99 | 100 | public static string ToLowerString(this ElementalType type) 101 | { 102 | return type.ToString().ToLower(); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/Model/RollPhaseDice.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Drawing; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace GeniusInvokationAutoToy.Core.Model 10 | { 11 | /// 12 | /// 投掷期间骰子 13 | /// 14 | public class RollPhaseDice 15 | { 16 | /// 17 | /// 元素类型 18 | /// 19 | public ElementalType Type { get; set; } 20 | 21 | /// 22 | /// 中心点位置 23 | /// 24 | public Point CenterPosition { get; set; } 25 | 26 | public RollPhaseDice(ElementalType type, Point centerPosition) 27 | { 28 | Type = type; 29 | CenterPosition = centerPosition; 30 | } 31 | 32 | public RollPhaseDice() 33 | { 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return $"Type:{Type},CenterPosition:{CenterPosition}"; 39 | } 40 | 41 | public void Click() 42 | { 43 | MouseUtils.Click(CenterPosition.X, CenterPosition.Y); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/MyException/DuelEndException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GeniusInvokationAutoToy.Core.MyException 8 | { 9 | public class DuelEndException: System.Exception 10 | { 11 | public DuelEndException(string message) : base(message) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/MyException/RetryException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Forms; 7 | 8 | namespace GeniusInvokationAutoToy.Core.MyException 9 | { 10 | public class RetryException : System.Exception 11 | { 12 | public RetryException() : base() 13 | { 14 | } 15 | 16 | public RetryException(string message) : base(message) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Core/YuanShenWindow.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Windows.Forms; 11 | 12 | namespace GeniusInvokationAutoToy.Core 13 | { 14 | public class YuanShenWindow 15 | { 16 | public static uint WM_LBUTTONDOWN = 0x201; //按下鼠标左键 17 | 18 | public static uint WM_LBUTTONUP = 0x202; //释放鼠标左键 19 | 20 | 21 | private IntPtr hWnd; 22 | public YuanShenWindow() 23 | { 24 | 25 | } 26 | 27 | public bool FindYSHandle() 28 | { 29 | var pros = Process.GetProcessesByName("YuanShen"); 30 | if (pros.Any()) 31 | { 32 | hWnd = pros[0].MainWindowHandle; 33 | return true; 34 | } 35 | else 36 | { 37 | pros = Process.GetProcessesByName("GenshinImpact"); 38 | if (pros.Any()) 39 | { 40 | hWnd = pros[0].MainWindowHandle; 41 | return true; 42 | } 43 | else 44 | { 45 | return false; 46 | } 47 | } 48 | } 49 | 50 | public void Focus() 51 | { 52 | Native.SendMessage(hWnd, 0x0112, (IntPtr)0xF120, (IntPtr)0); 53 | Native.SetForegroundWindow(hWnd); 54 | } 55 | 56 | public Rectangle GetSize() 57 | { 58 | Native.RECT rc = new Native.RECT(); 59 | Native.GetWindowRect(hWnd, ref rc); 60 | return new Rectangle(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top); 61 | } 62 | 63 | public void MouseLeftButtonDown() 64 | { 65 | IntPtr p = (IntPtr)((0 << 16) | 0); 66 | Native.PostMessage(hWnd, WM_LBUTTONDOWN, IntPtr.Zero, p); 67 | 68 | } 69 | 70 | public void MouseLeftButtonUp() 71 | { 72 | IntPtr p = (IntPtr)((0 << 16) | 0); 73 | Native.PostMessage(hWnd, WM_LBUTTONUP, IntPtr.Zero, p); 74 | } 75 | 76 | public void MouseClick(int x, int y) 77 | { 78 | IntPtr p = (IntPtr)((y << 16) | x); 79 | Native.PostMessage(hWnd, WM_LBUTTONDOWN, IntPtr.Zero, p); 80 | Thread.Sleep(100); 81 | Native.PostMessage(hWnd, WM_LBUTTONUP, IntPtr.Zero, p); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMain.Designer.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace GeniusInvokationAutoToy 3 | { 4 | partial class FormMain 5 | { 6 | /// 7 | /// 必需的设计器变量。 8 | /// 9 | private System.ComponentModel.IContainer components = null; 10 | 11 | /// 12 | /// 清理所有正在使用的资源。 13 | /// 14 | /// 如果应释放托管资源,为 true;否则为 false。 15 | protected override void Dispose(bool disposing) 16 | { 17 | if (disposing && (components != null)) 18 | { 19 | components.Dispose(); 20 | } 21 | base.Dispose(disposing); 22 | } 23 | 24 | #region Windows 窗体设计器生成的代码 25 | 26 | /// 27 | /// 设计器支持所需的方法 - 不要修改 28 | /// 使用代码编辑器修改此方法的内容。 29 | /// 30 | private void InitializeComponent() 31 | { 32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormMain)); 33 | this.rtbConsole = new System.Windows.Forms.RichTextBox(); 34 | this.chkTopMost = new System.Windows.Forms.CheckBox(); 35 | this.btnSwitch = new System.Windows.Forms.Button(); 36 | this.linkLabel1 = new System.Windows.Forms.LinkLabel(); 37 | this.lblYSStatus = new System.Windows.Forms.Label(); 38 | this.label1 = new System.Windows.Forms.Label(); 39 | this.label2 = new System.Windows.Forms.Label(); 40 | this.label3 = new System.Windows.Forms.Label(); 41 | this.cboGameResolution = new System.Windows.Forms.ComboBox(); 42 | this.cboStrategy = new System.Windows.Forms.ComboBox(); 43 | this.SuspendLayout(); 44 | // 45 | // rtbConsole 46 | // 47 | this.rtbConsole.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; 48 | this.rtbConsole.Location = new System.Drawing.Point(9, 93); 49 | this.rtbConsole.Margin = new System.Windows.Forms.Padding(2); 50 | this.rtbConsole.Name = "rtbConsole"; 51 | this.rtbConsole.Size = new System.Drawing.Size(387, 390); 52 | this.rtbConsole.TabIndex = 7; 53 | this.rtbConsole.Text = ""; 54 | this.rtbConsole.TextChanged += new System.EventHandler(this.rtbConsole_TextChanged); 55 | // 56 | // chkTopMost 57 | // 58 | this.chkTopMost.AutoSize = true; 59 | this.chkTopMost.Location = new System.Drawing.Point(142, 14); 60 | this.chkTopMost.Margin = new System.Windows.Forms.Padding(2); 61 | this.chkTopMost.Name = "chkTopMost"; 62 | this.chkTopMost.Size = new System.Drawing.Size(72, 16); 63 | this.chkTopMost.TabIndex = 9; 64 | this.chkTopMost.Text = "置顶界面"; 65 | this.chkTopMost.UseVisualStyleBackColor = true; 66 | this.chkTopMost.CheckedChanged += new System.EventHandler(this.chkTopMost_CheckedChanged); 67 | // 68 | // btnSwitch 69 | // 70 | this.btnSwitch.Location = new System.Drawing.Point(266, 38); 71 | this.btnSwitch.Margin = new System.Windows.Forms.Padding(2); 72 | this.btnSwitch.Name = "btnSwitch"; 73 | this.btnSwitch.Size = new System.Drawing.Size(130, 51); 74 | this.btnSwitch.TabIndex = 10; 75 | this.btnSwitch.Text = "开始自动打牌(F11)"; 76 | this.btnSwitch.UseVisualStyleBackColor = true; 77 | this.btnSwitch.Click += new System.EventHandler(this.btnSwitch_Click); 78 | // 79 | // linkLabel1 80 | // 81 | this.linkLabel1.AutoSize = true; 82 | this.linkLabel1.Location = new System.Drawing.Point(219, 15); 83 | this.linkLabel1.Name = "linkLabel1"; 84 | this.linkLabel1.Size = new System.Drawing.Size(113, 12); 85 | this.linkLabel1.TabIndex = 11; 86 | this.linkLabel1.TabStop = true; 87 | this.linkLabel1.Text = "软件主页(免费开源)"; 88 | this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); 89 | // 90 | // lblYSStatus 91 | // 92 | this.lblYSStatus.AutoSize = true; 93 | this.lblYSStatus.ForeColor = System.Drawing.Color.Red; 94 | this.lblYSStatus.Location = new System.Drawing.Point(80, 15); 95 | this.lblYSStatus.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); 96 | this.lblYSStatus.Name = "lblYSStatus"; 97 | this.lblYSStatus.Size = new System.Drawing.Size(41, 12); 98 | this.lblYSStatus.TabIndex = 19; 99 | this.lblYSStatus.Text = "未启动"; 100 | // 101 | // label1 102 | // 103 | this.label1.AutoSize = true; 104 | this.label1.Location = new System.Drawing.Point(11, 15); 105 | this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); 106 | this.label1.Name = "label1"; 107 | this.label1.Size = new System.Drawing.Size(65, 12); 108 | this.label1.TabIndex = 18; 109 | this.label1.Text = "原神状态:"; 110 | // 111 | // label2 112 | // 113 | this.label2.AutoSize = true; 114 | this.label2.Location = new System.Drawing.Point(11, 43); 115 | this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); 116 | this.label2.Name = "label2"; 117 | this.label2.Size = new System.Drawing.Size(65, 12); 118 | this.label2.TabIndex = 22; 119 | this.label2.Text = "卡牌策略:"; 120 | // 121 | // label3 122 | // 123 | this.label3.AutoSize = true; 124 | this.label3.Location = new System.Drawing.Point(11, 71); 125 | this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); 126 | this.label3.Name = "label3"; 127 | this.label3.Size = new System.Drawing.Size(77, 12); 128 | this.label3.TabIndex = 23; 129 | this.label3.Text = "游戏分辨率:"; 130 | // 131 | // cboGameResolution 132 | // 133 | this.cboGameResolution.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 134 | this.cboGameResolution.Enabled = false; 135 | this.cboGameResolution.FormattingEnabled = true; 136 | this.cboGameResolution.Items.AddRange(new object[] { 137 | "1920x1080"}); 138 | this.cboGameResolution.Location = new System.Drawing.Point(99, 69); 139 | this.cboGameResolution.Margin = new System.Windows.Forms.Padding(2); 140 | this.cboGameResolution.Name = "cboGameResolution"; 141 | this.cboGameResolution.Size = new System.Drawing.Size(149, 20); 142 | this.cboGameResolution.TabIndex = 21; 143 | // 144 | // cboStrategy 145 | // 146 | this.cboStrategy.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 147 | this.cboStrategy.FormattingEnabled = true; 148 | this.cboStrategy.Location = new System.Drawing.Point(99, 41); 149 | this.cboStrategy.Margin = new System.Windows.Forms.Padding(2); 150 | this.cboStrategy.Name = "cboStrategy"; 151 | this.cboStrategy.Size = new System.Drawing.Size(149, 20); 152 | this.cboStrategy.TabIndex = 20; 153 | // 154 | // FormMain 155 | // 156 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 157 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 158 | this.ClientSize = new System.Drawing.Size(407, 494); 159 | this.Controls.Add(this.label3); 160 | this.Controls.Add(this.label2); 161 | this.Controls.Add(this.cboGameResolution); 162 | this.Controls.Add(this.cboStrategy); 163 | this.Controls.Add(this.lblYSStatus); 164 | this.Controls.Add(this.label1); 165 | this.Controls.Add(this.linkLabel1); 166 | this.Controls.Add(this.btnSwitch); 167 | this.Controls.Add(this.chkTopMost); 168 | this.Controls.Add(this.rtbConsole); 169 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 170 | this.Margin = new System.Windows.Forms.Padding(2); 171 | this.Name = "FormMain"; 172 | this.Text = "原神自动打牌(自动七胜召唤对局)"; 173 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormMain_FormClosed); 174 | this.Load += new System.EventHandler(this.FormMain_Load); 175 | this.ResumeLayout(false); 176 | this.PerformLayout(); 177 | 178 | } 179 | 180 | #endregion 181 | private System.Windows.Forms.RichTextBox rtbConsole; 182 | private System.Windows.Forms.CheckBox chkTopMost; 183 | private System.Windows.Forms.Button btnSwitch; 184 | private System.Windows.Forms.LinkLabel linkLabel1; 185 | private System.Windows.Forms.Label lblYSStatus; 186 | private System.Windows.Forms.Label label1; 187 | private System.Windows.Forms.Label label2; 188 | private System.Windows.Forms.Label label3; 189 | private System.Windows.Forms.ComboBox cboGameResolution; 190 | private System.Windows.Forms.ComboBox cboStrategy; 191 | } 192 | } 193 | 194 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMain.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Core; 2 | using GeniusInvokationAutoToy.Forms.Hotkey; 3 | using GeniusInvokationAutoToy.Strategy; 4 | using GeniusInvokationAutoToy.Strategy.Model; 5 | using GeniusInvokationAutoToy.Strategy.Script; 6 | using GeniusInvokationAutoToy.Utils; 7 | using System; 8 | using System.Diagnostics; 9 | using System.Drawing; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using System.Windows.Forms; 16 | 17 | 18 | namespace GeniusInvokationAutoToy 19 | { 20 | public partial class FormMain : Form 21 | { 22 | private static NLog.Logger logger; 23 | 24 | private YuanShenWindow window = new YuanShenWindow(); 25 | 26 | private bool isAutoPlaying = false; 27 | 28 | private string thisVersion; 29 | 30 | CancellationTokenSource cts; 31 | 32 | public FormMain() 33 | { 34 | InitializeComponent(); 35 | } 36 | 37 | public void RtbConsoleDeleteLine() 38 | { 39 | Invoke(new Action(() => 40 | { 41 | rtbConsole.Lines = rtbConsole.Lines.Take(rtbConsole.Lines.Length - 2).ToArray(); 42 | rtbConsole.Text += Environment.NewLine; 43 | })); 44 | } 45 | 46 | private void FormMain_Load(object sender, EventArgs e) 47 | { 48 | logger = NLog.LogManager.GetCurrentClassLogger(); 49 | MyLogger.formMain = this; 50 | // 标题加上版本号 51 | string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 52 | if (currentVersion.Length > 3) 53 | { 54 | thisVersion = currentVersion.Substring(0, 3); 55 | currentVersion = " v" + thisVersion; 56 | } 57 | 58 | this.Text += currentVersion; 59 | GAHelper.Instance.RequestPageView($"七圣召唤自动打牌_{thisVersion}", $"进入七圣召唤自动打牌{thisVersion}版本主界面"); 60 | 61 | 62 | rtbConsole.Text = @"软件在Github上开源且免费 by huiyadanli 63 | 64 | 支持角色邀请、每周来客挑战、大世界NPC挑战(部分场景不支持、或者打不过/拿不满奖励)。 65 | 66 | 现在已经支持出现以下异常情况时继续进行对局:1.角色被动切换角色,比如超载、琴/砂糖技能;2.当前角色无法行动,比如冻结、被泡泡困住(水深渊法师技能) 67 | 68 | 使用方法: 69 | 1、牌组必须是和策略一致,比如【莫娜、砂糖、琴】或者【刻晴、雷电将军、甘雨】等,顺序不能变,带什么牌无所谓。策略可以自定义,放在软件所在目录的 strategy 文件夹下,格式参考自带的策略。 70 | 2、只支持1920x1080窗口或全屏游戏,游戏整个界面不能被其他窗口遮挡! 71 | 3、在游戏内进入七圣召唤对局,到初始手牌界面 72 | 4、然后直接点击开始自动打牌,双手离开键盘鼠标(快捷键F11)。 73 | "; 74 | 75 | LoadCustomScript(); 76 | YSStatus(); 77 | 78 | cboGameResolution.SelectedIndex = 0; 79 | if (cboStrategy.Items.Count - 1 >= Properties.Settings.Default.CboStrategySelectIndex) 80 | { 81 | cboStrategy.SelectedIndex = Properties.Settings.Default.CboStrategySelectIndex; 82 | } 83 | else 84 | { 85 | cboStrategy.SelectedIndex = 0; 86 | } 87 | 88 | chkTopMost.Checked = Properties.Settings.Default.TopMostChecked; 89 | 90 | try 91 | { 92 | RegisterHotKey("F11"); 93 | } 94 | catch (Exception ex) 95 | { 96 | MyLogger.Warn(ex.Message); 97 | MessageBox.Show(ex.Message, "热键注册失败", MessageBoxButtons.OK, MessageBoxIcon.Error); 98 | } 99 | 100 | //RichTextBoxTarget target = new RichTextBoxTarget(); 101 | //target.Layout = "${longdate} [${threadname:whenEmpty=${threadid}}] ${uppercase:${level}} ${message:withException=true} ${all-event-properties}"; 102 | //target.TargetRichTextBox = rtbConsole; 103 | //target.UseDefaultRowColoringRules = true; 104 | //NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, NLog.LogLevel.Trace); 105 | 106 | } 107 | 108 | private bool YSStatus() 109 | { 110 | if (window.FindYSHandle()) 111 | { 112 | lblYSStatus.ForeColor = Color.Green; 113 | lblYSStatus.Text = "已启动"; 114 | return true; 115 | } 116 | else 117 | { 118 | lblYSStatus.ForeColor = Color.Red; 119 | lblYSStatus.Text = "未启动"; 120 | return false; 121 | } 122 | } 123 | 124 | private void LoadCustomScript() 125 | { 126 | string[] files = Directory.GetFiles(Path.Combine(Application.StartupPath, "strategy"), "*.*", 127 | SearchOption.AllDirectories); 128 | 129 | foreach (string file in files) 130 | { 131 | if (file.EndsWith(".txt")) 132 | { 133 | string fileName = Path.GetFileNameWithoutExtension(file); 134 | cboStrategy.Items.Add(fileName); 135 | } 136 | } 137 | } 138 | 139 | private async void StartGame() 140 | { 141 | if (!window.FindYSHandle()) 142 | { 143 | MyLogger.Warn("未找到原神进程,请先启动原神!"); 144 | return; 145 | } 146 | 147 | Duel duel = ScriptParser.Parse(File.ReadAllText(Path.Combine(Application.StartupPath, "strategy", 148 | cboStrategy.Text + ".txt"), Encoding.UTF8)); 149 | if (duel != null) 150 | { 151 | window.Focus(); 152 | 153 | rtbConsole.Text = ""; // 清空日志 154 | 155 | cts = new CancellationTokenSource(); 156 | 157 | await duel.CustomStrategyRunAsync(cts); 158 | 159 | // 打完了切回来 160 | isAutoPlaying = false; 161 | btnSwitch.Text = "开始自动打牌(F11)"; 162 | } 163 | else 164 | { 165 | throw new Exception("加载打牌策略失败"); 166 | } 167 | } 168 | 169 | private void StopGame() 170 | { 171 | cts?.Cancel(); 172 | } 173 | 174 | private void btnSwitch_Click(object sender, EventArgs e) 175 | { 176 | if (!isAutoPlaying) 177 | { 178 | StartGame(); 179 | isAutoPlaying = true; 180 | btnSwitch.Text = "关闭自动打牌(F11)"; 181 | } 182 | else 183 | { 184 | StopGame(); 185 | isAutoPlaying = false; 186 | btnSwitch.Text = "开始自动打牌(F11)"; 187 | } 188 | } 189 | 190 | private void chkTopMost_CheckedChanged(object sender, EventArgs e) 191 | { 192 | this.TopMost = chkTopMost.Checked; 193 | } 194 | 195 | private void FormMain_FormClosed(object sender, FormClosedEventArgs e) 196 | { 197 | Properties.Settings.Default.TopMostChecked = chkTopMost.Checked; 198 | Properties.Settings.Default.CboStrategySelectIndex = cboStrategy.SelectedIndex; 199 | //Properties.Settings.Default.CboGameResolutionSelectIndex = cboGameResolution.SelectedIndex; 200 | Properties.Settings.Default.Save(); 201 | } 202 | 203 | private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 204 | { 205 | Process.Start("https://github.com/babalae/genius-invokation-auto-toy"); 206 | } 207 | 208 | #region Hotkey 209 | 210 | private Hotkey hotkey; 211 | private HotkeyHook hotkeyHook; 212 | 213 | private void rtbConsole_TextChanged(object sender, EventArgs e) 214 | { 215 | rtbConsole.SelectionStart = rtbConsole.Text.Length; 216 | rtbConsole.ScrollToCaret(); 217 | } 218 | 219 | public void RegisterHotKey(string hotkeyStr) 220 | { 221 | if (string.IsNullOrEmpty(hotkeyStr)) 222 | { 223 | UnregisterHotKey(); 224 | return; 225 | } 226 | 227 | hotkey = new Hotkey(hotkeyStr); 228 | 229 | if (hotkeyHook != null) 230 | { 231 | hotkeyHook.Dispose(); 232 | } 233 | 234 | hotkeyHook = new HotkeyHook(); 235 | // register the event that is fired after the key press. 236 | hotkeyHook.KeyPressed += new EventHandler(btnSwitch_Click); 237 | hotkeyHook.RegisterHotKey(hotkey.ModifierKey, hotkey.Key); 238 | } 239 | 240 | public void UnregisterHotKey() 241 | { 242 | if (hotkeyHook != null) 243 | { 244 | hotkeyHook.UnregisterHotKey(); 245 | hotkeyHook.Dispose(); 246 | } 247 | } 248 | 249 | #endregion 250 | } 251 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMask.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace GeniusInvokationAutoToy 2 | { 3 | partial class FormMask 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.SuspendLayout(); 32 | // 33 | // FormMask 34 | // 35 | this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); 36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; 37 | this.ClientSize = new System.Drawing.Size(545, 399); 38 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 39 | this.Name = "FormMask"; 40 | this.Text = "FormMask"; 41 | this.TopMost = true; 42 | this.Load += new System.EventHandler(this.FormMask_Load); 43 | this.ResumeLayout(false); 44 | 45 | } 46 | 47 | #endregion 48 | } 49 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | 13 | namespace GeniusInvokationAutoToy 14 | { 15 | /// 16 | /// https://stackoverflow.com/questions/21896080/how-to-make-non-interactive-graphical-overlay-on-top-of-another-program-in-c 17 | /// edit by huiyadanli 18 | /// 19 | public partial class FormMask : Form 20 | { 21 | BackgroundWorker bw = new BackgroundWorker(); 22 | Random rand = new Random(DateTime.Now.Millisecond); 23 | 24 | [DllImport("user32.dll")] 25 | [return: MarshalAs(UnmanagedType.Bool)] 26 | static extern bool BringWindowToTop(IntPtr hWnd); 27 | 28 | [DllImport("user32.dll")] 29 | [return: MarshalAs(UnmanagedType.Bool)] 30 | static extern bool SetForegroundWindow(IntPtr hWnd); 31 | 32 | [DllImport("user32.dll", EntryPoint = "SetWindowPos")] 33 | public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, 34 | int wFlags); 35 | 36 | 37 | [DllImport("user32.dll")] 38 | static extern int 39 | SendMessage(IntPtr hWnd, uint wMsg, UIntPtr wParam, IntPtr lParam); //used for maximizing the screen 40 | 41 | const int WM_SYSCOMMAND = 0x0112; //used for maximizing the screen. 42 | const int myWParam = 0xf120; //used for maximizing the screen. 43 | const int myLparam = 0x5073d; //used for maximizing the screen. 44 | 45 | 46 | int oldWindowLong; 47 | 48 | [Flags] 49 | enum WindowStyles : uint 50 | { 51 | WS_OVERLAPPED = 0x00000000, 52 | WS_POPUP = 0x80000000, 53 | WS_CHILD = 0x40000000, 54 | WS_MINIMIZE = 0x20000000, 55 | WS_VISIBLE = 0x10000000, 56 | WS_DISABLED = 0x08000000, 57 | WS_CLIPSIBLINGS = 0x04000000, 58 | WS_CLIPCHILDREN = 0x02000000, 59 | WS_MAXIMIZE = 0x01000000, 60 | WS_BORDER = 0x00800000, 61 | WS_DLGFRAME = 0x00400000, 62 | WS_VSCROLL = 0x00200000, 63 | WS_HSCROLL = 0x00100000, 64 | WS_SYSMENU = 0x00080000, 65 | WS_THICKFRAME = 0x00040000, 66 | WS_GROUP = 0x00020000, 67 | WS_TABSTOP = 0x00010000, 68 | 69 | WS_MINIMIZEBOX = 0x00020000, 70 | WS_MAXIMIZEBOX = 0x00010000, 71 | 72 | WS_CAPTION = WS_BORDER | WS_DLGFRAME, 73 | WS_TILED = WS_OVERLAPPED, 74 | WS_ICONIC = WS_MINIMIZE, 75 | WS_SIZEBOX = WS_THICKFRAME, 76 | WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW, 77 | 78 | WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | 79 | WS_MAXIMIZEBOX, 80 | WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, 81 | WS_CHILDWINDOW = WS_CHILD, 82 | 83 | //Extended Window Styles 84 | 85 | WS_EX_DLGMODALFRAME = 0x00000001, 86 | WS_EX_NOPARENTNOTIFY = 0x00000004, 87 | WS_EX_TOPMOST = 0x00000008, 88 | WS_EX_ACCEPTFILES = 0x00000010, 89 | WS_EX_TRANSPARENT = 0x00000020, 90 | 91 | //#if(WINVER >= 0x0400) 92 | 93 | WS_EX_MDICHILD = 0x00000040, 94 | WS_EX_TOOLWINDOW = 0x00000080, 95 | WS_EX_WINDOWEDGE = 0x00000100, 96 | WS_EX_CLIENTEDGE = 0x00000200, 97 | WS_EX_CONTEXTHELP = 0x00000400, 98 | 99 | WS_EX_RIGHT = 0x00001000, 100 | WS_EX_LEFT = 0x00000000, 101 | WS_EX_RTLREADING = 0x00002000, 102 | WS_EX_LTRREADING = 0x00000000, 103 | WS_EX_LEFTSCROLLBAR = 0x00004000, 104 | WS_EX_RIGHTSCROLLBAR = 0x00000000, 105 | 106 | WS_EX_CONTROLPARENT = 0x00010000, 107 | WS_EX_STATICEDGE = 0x00020000, 108 | WS_EX_APPWINDOW = 0x00040000, 109 | 110 | WS_EX_OVERLAPPEDWINDOW = (WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE), 111 | WS_EX_PALETTEWINDOW = (WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST), 112 | //#endif /* WINVER >= 0x0400 */ 113 | 114 | //#if(WIN32WINNT >= 0x0500) 115 | 116 | WS_EX_LAYERED = 0x00080000, 117 | //#endif /* WIN32WINNT >= 0x0500 */ 118 | 119 | //#if(WINVER >= 0x0500) 120 | 121 | WS_EX_NOINHERITLAYOUT = 0x00100000, // Disable inheritence of mirroring by children 122 | WS_EX_LAYOUTRTL = 0x00400000, // Right to left mirroring 123 | //#endif /* WINVER >= 0x0500 */ 124 | 125 | //#if(WIN32WINNT >= 0x0500) 126 | 127 | WS_EX_COMPOSITED = 0x02000000, 128 | 129 | WS_EX_NOACTIVATE = 0x08000000 130 | //#endif /* WIN32WINNT >= 0x0500 */ 131 | } 132 | 133 | public enum GetWindowLongConst 134 | { 135 | GWL_WNDPROC = (-4), 136 | GWL_HINSTANCE = (-6), 137 | GWL_HWNDPARENT = (-8), 138 | GWL_STYLE = (-16), 139 | GWL_EXSTYLE = (-20), 140 | GWL_USERDATA = (-21), 141 | GWL_ID = (-12) 142 | } 143 | 144 | public enum LWA 145 | { 146 | ColorKey = 0x1, 147 | Alpha = 0x2, 148 | } 149 | 150 | [DllImport("user32.dll", SetLastError = true)] 151 | static extern int GetWindowLong(IntPtr hWnd, int nIndex); 152 | 153 | [DllImport("user32.dll")] 154 | static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); 155 | 156 | [DllImport("user32.dll")] 157 | static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); 158 | 159 | /// 160 | /// Make the form (specified by its handle) a window that supports transparency. 161 | /// 162 | /// The window to make transparency supporting 163 | public void SetFormTransparent(IntPtr Handle) 164 | { 165 | oldWindowLong = GetWindowLong(Handle, (int)GetWindowLongConst.GWL_EXSTYLE); 166 | SetWindowLong(Handle, (int)GetWindowLongConst.GWL_EXSTYLE, 167 | Convert.ToInt32(oldWindowLong | (uint)WindowStyles.WS_EX_LAYERED | 168 | (uint)WindowStyles.WS_EX_TRANSPARENT)); 169 | } 170 | 171 | /// 172 | /// Make the form (specified by its handle) a normal type of window (doesn't support transparency). 173 | /// 174 | /// The Window to make normal 175 | public void SetFormNormal(IntPtr Handle) 176 | { 177 | SetWindowLong(Handle, (int)GetWindowLongConst.GWL_EXSTYLE, 178 | Convert.ToInt32(oldWindowLong | (uint)WindowStyles.WS_EX_LAYERED)); 179 | } 180 | 181 | /// 182 | /// Makes the form change White to Transparent and clickthrough-able 183 | /// Can be modified to make the form translucent (with different opacities) and change the Transparency Color. 184 | /// 185 | public void SetTheLayeredWindowAttribute() 186 | { 187 | uint transparentColor = 0xffffffff; 188 | 189 | SetLayeredWindowAttributes(this.Handle, transparentColor, 125, 0x2); 190 | 191 | this.TransparencyKey = Color.White; 192 | } 193 | 194 | /// 195 | /// Finds the Size of all computer screens combined (assumes screens are left to right, not above and below). 196 | /// 197 | /// The width and height of all screens combined 198 | public static Size getFullScreensSize() 199 | { 200 | int height = int.MinValue; 201 | int width = 0; 202 | 203 | foreach (Screen screen in System.Windows.Forms.Screen.AllScreens) 204 | { 205 | //take largest height 206 | height = Math.Max(screen.WorkingArea.Height, height); 207 | 208 | width += screen.Bounds.Width; 209 | } 210 | 211 | return new Size(width, height); 212 | } 213 | 214 | /// 215 | /// Finds the top left pixel position (with multiple screens this is often not 0,0) 216 | /// 217 | /// Position of top left pixel 218 | public static Point getTopLeft() 219 | { 220 | int minX = int.MaxValue; 221 | int minY = int.MaxValue; 222 | 223 | foreach (Screen screen in System.Windows.Forms.Screen.AllScreens) 224 | { 225 | minX = Math.Min(screen.WorkingArea.Left, minX); 226 | minY = Math.Min(screen.WorkingArea.Top, minY); 227 | } 228 | 229 | return new Point(minX, minY); 230 | } 231 | 232 | public FormMask(int x, int y, int w, int h) 233 | { 234 | InitializeComponent(); 235 | 236 | this.Opacity = .5D; //Make trasparent 237 | this.DoubleBuffered = true; 238 | 239 | MaximizeEverything(x, y, w, h); 240 | 241 | SetFormTransparent(this.Handle); 242 | 243 | SetTheLayeredWindowAttribute(); 244 | 245 | //BackgroundWorker tmpBw = new BackgroundWorker(); 246 | //tmpBw.DoWork += new DoWorkEventHandler(bw_DoWork); 247 | 248 | //this.bw = tmpBw; 249 | 250 | //this.bw.RunWorkerAsync(); 251 | } 252 | 253 | private void MaximizeEverything(int x, int y, int w, int h) 254 | { 255 | this.Location = new Point(x, y); 256 | this.Size = new Size(w, h); 257 | 258 | SendMessage(this.Handle, WM_SYSCOMMAND, (UIntPtr)myWParam, (IntPtr)myLparam); 259 | } 260 | 261 | private void bw_DoWork(object sender, DoWorkEventArgs e) 262 | { 263 | BackgroundWorker worker = sender as BackgroundWorker; 264 | 265 | int numRedLines = 500; 266 | int numWhiteLines = 1000; 267 | 268 | Size fullSize = getFullScreensSize(); 269 | Point topLeft = getTopLeft(); 270 | 271 | using (Pen redPen = new Pen(Color.Red, 10f), whitePen = new Pen(Color.White, 10f)) 272 | { 273 | using (Graphics formGraphics = this.CreateGraphics()) 274 | { 275 | while (true) 276 | { 277 | bool makeRedLines = true; 278 | 279 | for (int i = 0; i < numRedLines + numWhiteLines; i++) 280 | { 281 | if (i > numRedLines) 282 | { 283 | makeRedLines = false; 284 | } 285 | 286 | //Choose points for random lines...but don't draw over the top 100 px of the screen so you can 287 | //still find the stop run button. 288 | int pX = rand.Next(0, (-1 * topLeft.X) + fullSize.Width); 289 | int pY = rand.Next(100, (-1 * topLeft.Y) + fullSize.Height); 290 | 291 | int qX = rand.Next(0, (-1 * topLeft.X) + fullSize.Width); 292 | int qY = rand.Next(100, (-1 * topLeft.Y) + fullSize.Height); 293 | 294 | if (makeRedLines) 295 | { 296 | formGraphics.DrawLine(redPen, pX, pY, qX, qY); 297 | } 298 | else 299 | { 300 | formGraphics.DrawLine(whitePen, pX, pY, qX, qY); 301 | } 302 | 303 | Thread.Sleep(10); 304 | } 305 | } 306 | } 307 | } 308 | } 309 | 310 | private void FormMask_Load(object sender, EventArgs e) 311 | { 312 | } 313 | } 314 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMask.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMask1.Designer.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace GeniusInvokationAutoToy 3 | { 4 | partial class FormMask1 5 | { 6 | /// 7 | /// Required designer variable. 8 | /// 9 | private System.ComponentModel.IContainer components = null; 10 | 11 | /// 12 | /// Clean up any resources being used. 13 | /// 14 | /// true if managed resources should be disposed; otherwise, false. 15 | protected override void Dispose(bool disposing) 16 | { 17 | if (disposing && (components != null)) 18 | { 19 | components.Dispose(); 20 | } 21 | base.Dispose(disposing); 22 | } 23 | 24 | #region Windows Form Designer generated code 25 | 26 | /// 27 | /// Required method for Designer support - do not modify 28 | /// the contents of this method with the code editor. 29 | /// 30 | private void InitializeComponent() 31 | { 32 | this.SuspendLayout(); 33 | // 34 | // FormMotionArea 35 | // 36 | this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); 37 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 38 | this.ClientSize = new System.Drawing.Size(599, 55); 39 | this.Cursor = System.Windows.Forms.Cursors.SizeAll; 40 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 41 | this.MinimumSize = new System.Drawing.Size(30, 30); 42 | this.Name = "FormMotionArea"; 43 | this.Text = "钓鱼运动框选区"; 44 | this.TopMost = true; 45 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.FormMaskArea_FormClosed); 46 | this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.FormMotionArea_MouseDown); 47 | this.ResumeLayout(false); 48 | 49 | } 50 | 51 | #endregion 52 | } 53 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMask1.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Runtime.InteropServices; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | 13 | namespace GeniusInvokationAutoToy 14 | { 15 | /// 16 | /// https://github.com/nishanc/csharp-screenshot-winforms/blob/master/Screenshot/SelectArea.cs 17 | /// 18 | public partial class FormMask1 : Form 19 | { 20 | public FormMain FormMainInstance { get; set; } 21 | 22 | public FormMask1() 23 | { 24 | InitializeComponent(); 25 | this.Opacity = .5D; //Make trasparent 26 | this.DoubleBuffered = true; 27 | this.SetStyle(ControlStyles.ResizeRedraw, true); // this is to avoid visual artifacts 28 | } 29 | 30 | protected override void OnPaint(PaintEventArgs e) 31 | { 32 | ControlPaint.DrawBorder(e.Graphics, 33 | this.ClientRectangle, 34 | Color.Red, 35 | ButtonBorderStyle.Solid); 36 | } 37 | 38 | private void FormMaskArea_FormClosed(object sender, FormClosedEventArgs e) 39 | { 40 | 41 | } 42 | 43 | #region Moving window by click-drag on a control https://stackoverflow.com/a/13477624/5260872 44 | 45 | public bool DragEnabled { get; set; } = true; 46 | 47 | public const int WM_NCLBUTTONDOWN = 0xA1; 48 | public const int HTCAPTION = 0x2; 49 | 50 | [DllImport("User32.dll")] 51 | public static extern bool ReleaseCapture(); 52 | 53 | [DllImport("User32.dll")] 54 | public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); 55 | 56 | private void FormMotionArea_MouseDown(object sender, MouseEventArgs e) 57 | { 58 | if (DragEnabled) 59 | { 60 | ReleaseCapture(); 61 | SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0); 62 | } 63 | } 64 | #endregion 65 | 66 | #region How to resize a form without a border? https://stackoverflow.com/a/32261547/5260872 67 | private const int 68 | HTLEFT = 10, 69 | HTRIGHT = 11, 70 | HTTOP = 12, 71 | HTTOPLEFT = 13, 72 | HTTOPRIGHT = 14, 73 | HTBOTTOM = 15, 74 | HTBOTTOMLEFT = 16, 75 | HTBOTTOMRIGHT = 17; 76 | 77 | const int _ = 10; // you can rename this variable if you like 78 | 79 | Rectangle Top { get { return new Rectangle(0, 0, this.ClientSize.Width, _); } } 80 | Rectangle Left { get { return new Rectangle(0, 0, _, this.ClientSize.Height); } } 81 | 82 | Rectangle Bottom { get { return new Rectangle(0, this.ClientSize.Height - _, this.ClientSize.Width, _); } } 83 | Rectangle Right { get { return new Rectangle(this.ClientSize.Width - _, 0, _, this.ClientSize.Height); } } 84 | Rectangle TopLeft { get { return new Rectangle(0, 0, _, _); } } 85 | Rectangle TopRight { get { return new Rectangle(this.ClientSize.Width - _, 0, _, _); } } 86 | Rectangle BottomLeft { get { return new Rectangle(0, this.ClientSize.Height - _, _, _); } } 87 | Rectangle BottomRight { get { return new Rectangle(this.ClientSize.Width - _, this.ClientSize.Height - _, _, _); } } 88 | 89 | 90 | protected override void WndProc(ref Message message) 91 | { 92 | base.WndProc(ref message); 93 | 94 | if (message.Msg == 0x84) // WM_NCHITTEST 95 | { 96 | var cursor = this.PointToClient(Cursor.Position); 97 | 98 | if (TopLeft.Contains(cursor)) message.Result = (IntPtr)HTTOPLEFT; 99 | else if (TopRight.Contains(cursor)) message.Result = (IntPtr)HTTOPRIGHT; 100 | else if (BottomLeft.Contains(cursor)) message.Result = (IntPtr)HTBOTTOMLEFT; 101 | else if (BottomRight.Contains(cursor)) message.Result = (IntPtr)HTBOTTOMRIGHT; 102 | 103 | else if (Top.Contains(cursor)) message.Result = (IntPtr)HTTOP; 104 | else if (Left.Contains(cursor)) message.Result = (IntPtr)HTLEFT; 105 | else if (Right.Contains(cursor)) message.Result = (IntPtr)HTRIGHT; 106 | else if (Bottom.Contains(cursor)) message.Result = (IntPtr)HTBOTTOM; 107 | } 108 | } 109 | #endregion 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/FormMask1.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Forms/HotKey/Hotkey.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Windows.Forms; 4 | 5 | namespace GeniusInvokationAutoToy.Forms.Hotkey 6 | { 7 | public class Hotkey 8 | { 9 | public bool Alt { get; set; } 10 | 11 | public bool Control { get; set; } 12 | 13 | public bool Shift { get; set; } 14 | 15 | public bool Windows { get; set; } 16 | 17 | private Keys key; 18 | public Keys Key 19 | { 20 | get { return key; } 21 | set 22 | { 23 | if (value != Keys.ControlKey && value != Keys.Alt 24 | && value != Keys.Menu && value != Keys.ShiftKey) 25 | { 26 | key = value; 27 | } 28 | else 29 | { 30 | key = Keys.None; 31 | } 32 | } 33 | } 34 | 35 | public ModifierKeys ModifierKey 36 | { 37 | get 38 | { 39 | return 40 | (Windows ? ModifierKeys.Win : 0) | 41 | (Control ? ModifierKeys.Control : 0) | 42 | (Shift ? ModifierKeys.Shift : 0) | 43 | (Alt ? ModifierKeys.Alt : 0); 44 | } 45 | } 46 | 47 | public Hotkey() 48 | { 49 | Reset(); 50 | } 51 | 52 | public Hotkey(string hotkeyStr) 53 | { 54 | try 55 | { 56 | string[] keyStrs = hotkeyStr.Replace(" ", "").Split('+'); 57 | foreach (string keyStr in keyStrs) 58 | { 59 | string k = keyStr.ToLower(); 60 | if (k == "win") 61 | Windows = true; 62 | else if (k == "ctrl") 63 | Control = true; 64 | else if (k == "shift") 65 | Shift = true; 66 | else if (k == "alt") 67 | Alt = true; 68 | else 69 | Key = (Keys)Enum.Parse(typeof(Keys), keyStr); 70 | } 71 | } 72 | catch 73 | { 74 | throw new ArgumentException("无效的热键"); 75 | } 76 | } 77 | 78 | public override string ToString() 79 | { 80 | string str = string.Empty; 81 | if (Key != Keys.None) 82 | { 83 | str = string.Format("{0}{1}{2}{3}{4}", 84 | Windows ? "Win + " : string.Empty, 85 | Control ? "Ctrl + " : string.Empty, 86 | Shift ? "Shift + " : string.Empty, 87 | Alt ? "Alt + " : string.Empty, 88 | Key); 89 | } 90 | return str; 91 | } 92 | 93 | public void Reset() 94 | { 95 | Alt = false; 96 | Control = false; 97 | Shift = false; 98 | Windows = false; 99 | Key = Keys.None; 100 | } 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Forms/HotKey/HotkeyHook.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Utils; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Forms; 5 | 6 | namespace GeniusInvokationAutoToy.Forms.Hotkey 7 | { 8 | /// 9 | /// Code from: 10 | /// https://stackoverflow.com/questions/2450373/set-global-hotkeys-using-c-sharp 11 | /// 12 | public sealed class HotkeyHook : IDisposable 13 | { 14 | /// 15 | /// Represents the window that is used internally to get the messages. 16 | /// 17 | private class Window : NativeWindow, IDisposable 18 | { 19 | private static readonly int WM_HOTKEY = 0x0312; 20 | 21 | public Window() 22 | { 23 | // create the handle for the window. 24 | this.CreateHandle(new CreateParams()); 25 | } 26 | 27 | /// 28 | /// Overridden to get the notifications. 29 | /// 30 | /// 31 | protected override void WndProc(ref Message m) 32 | { 33 | base.WndProc(ref m); 34 | 35 | // check if we got a hot key pressed. 36 | if (m.Msg == WM_HOTKEY) 37 | { 38 | // get the keys. 39 | Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF); 40 | ModifierKeys modifier = (ModifierKeys)((int)m.LParam & 0xFFFF); 41 | 42 | // invoke the event to notify the parent. 43 | if (KeyPressed != null) 44 | KeyPressed(this, new KeyPressedEventArgs(modifier, key)); 45 | } 46 | } 47 | 48 | public event EventHandler KeyPressed; 49 | 50 | #region IDisposable Members 51 | 52 | public void Dispose() 53 | { 54 | this.DestroyHandle(); 55 | } 56 | 57 | #endregion 58 | } 59 | 60 | private Window _window = new Window(); 61 | private int _currentId; 62 | 63 | public HotkeyHook() 64 | { 65 | // register the event of the inner native window. 66 | _window.KeyPressed += delegate (object sender, KeyPressedEventArgs args) 67 | { 68 | if (KeyPressed != null) 69 | KeyPressed(this, args); 70 | }; 71 | } 72 | 73 | /// 74 | /// Registers a hot key in the system. 75 | /// 76 | /// The modifiers that are associated with the hot key. 77 | /// The key itself that is associated with the hot key. 78 | public void RegisterHotKey(ModifierKeys modifier, Keys key) 79 | { 80 | // increment the counter. 81 | _currentId = _currentId + 1; 82 | 83 | // register the hot key. 84 | if (!Native.RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key)) 85 | { 86 | if (Marshal.GetLastWin32Error() == 1409) 87 | throw new InvalidOperationException("热键已经被占用"); 88 | else 89 | throw new InvalidOperationException("热键注册失败"); 90 | } 91 | } 92 | 93 | /// 94 | /// Unregister all hot key. 95 | /// 96 | public void UnregisterHotKey() 97 | { 98 | // unregister all the registered hot keys. 99 | for (int i = _currentId; i > 0; i--) 100 | { 101 | Native.UnregisterHotKey(_window.Handle, i); 102 | } 103 | } 104 | 105 | /// 106 | /// A hot key has been pressed. 107 | /// 108 | public event EventHandler KeyPressed; 109 | 110 | #region IDisposable Members 111 | 112 | public void Dispose() 113 | { 114 | UnregisterHotKey(); 115 | // dispose the inner native window. 116 | _window.Dispose(); 117 | } 118 | 119 | #endregion 120 | } 121 | 122 | /// 123 | /// Event Args for the event that is fired after the hot key has been pressed. 124 | /// 125 | public class KeyPressedEventArgs : EventArgs 126 | { 127 | public ModifierKeys Modifier { get; } 128 | 129 | public Keys Key { get; } 130 | 131 | internal KeyPressedEventArgs(ModifierKeys modifier, Keys key) 132 | { 133 | Modifier = modifier; 134 | Key = key; 135 | } 136 | } 137 | 138 | /// 139 | /// The enumeration of possible modifiers. 140 | /// 141 | [Flags] 142 | public enum ModifierKeys : uint 143 | { 144 | Alt = 1, 145 | Control = 2, 146 | Shift = 4, 147 | Win = 8 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Forms/HotKey/HotkeyTextBox.cs: -------------------------------------------------------------------------------- 1 |  2 | using GeniusInvokationAutoToy.Utils; 3 | using System; 4 | using System.Collections; 5 | using System.Windows.Forms; 6 | 7 | namespace GeniusInvokationAutoToy.Forms.Hotkey 8 | { 9 | /// 10 | /// A simple control that allows the user to select pretty much any valid hotkey combination 11 | /// 12 | public class HotkeyTextBox : TextBox 13 | { 14 | /// 15 | /// Forces the control to be non-multiline. 16 | /// 17 | public override bool Multiline { get { return false; } } 18 | 19 | // These variables store the current hotkey and modifier(s) 20 | private Hotkey hotkey; 21 | 22 | public bool HasWinKey { get; set; } 23 | 24 | /// 25 | /// Creates a new HotkeyControl 26 | /// 27 | public HotkeyTextBox() 28 | { 29 | hotkey = new Hotkey(); 30 | 31 | ContextMenu = new ContextMenu(); // Disable right-clicking 32 | GotFocus += delegate { Native.HideCaret(Handle); }; 33 | } 34 | 35 | /// 36 | /// When the hotkey is modified externally, the hotkey string needs to be refreshed. 37 | /// 38 | public void RefreshText(string hotkeyStr = null) 39 | { 40 | if (!string.IsNullOrEmpty(hotkeyStr)) 41 | { 42 | hotkey = new Hotkey(hotkeyStr); 43 | } 44 | 45 | hotkey.Windows = HasWinKey; 46 | Text = hotkey.ToString(); 47 | } 48 | 49 | /// 50 | /// Update the text in the box to notify the user what combination is currently pressed. 51 | /// 52 | protected override void OnKeyDown(KeyEventArgs e) 53 | { 54 | // Clear the current hotkey 55 | if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) 56 | { 57 | hotkey.Reset(); 58 | return; 59 | } 60 | else 61 | { 62 | hotkey.Key = e.KeyCode; 63 | 64 | hotkey.Alt = e.Alt; 65 | hotkey.Control = e.Control; 66 | hotkey.Shift = e.Shift; 67 | hotkey.Windows = HasWinKey; 68 | } 69 | 70 | Text = hotkey.ToString(); 71 | } 72 | 73 | /// 74 | /// If the current hotkey isn't valid, reset it. 75 | /// 76 | protected override void OnKeyUp(KeyEventArgs e) 77 | { 78 | if (hotkey.Key == Keys.None) 79 | { 80 | Text = ""; 81 | } 82 | } 83 | 84 | /// 85 | /// Prevents the whatever entered to show up in the TextBox 86 | /// 87 | protected override void OnKeyPress(KeyPressEventArgs e) 88 | { 89 | e.Handled = true; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/GeniusInvokationAutoToy.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {F17BE4C5-926C-456D-9CC0-606DAE304ED8} 9 | WinExe 10 | GeniusInvokationAutoToy 11 | GeniusInvokationAutoToy 12 | v4.7.2 13 | 512 14 | true 15 | true 16 | 17 | 18 | 19 | 20 | 21 | x64 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | true 30 | 31 | 32 | x64 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | true 40 | 41 | 42 | Raiden.ico 43 | 44 | 45 | 46 | ..\packages\NLog.5.2.0\lib\net46\NLog.dll 47 | 48 | 49 | ..\packages\NLog.Windows.Forms.5.2.0\lib\net35\NLog.Windows.Forms.dll 50 | 51 | 52 | ..\packages\OpenCvSharp4.4.5.3.20210817\lib\net461\OpenCvSharp.dll 53 | 54 | 55 | ..\packages\OpenCvSharp4.4.5.3.20210817\lib\net461\OpenCvSharp.Extensions.dll 56 | 57 | 58 | 59 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll 60 | 61 | 62 | 63 | 64 | ..\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll 65 | 66 | 67 | 68 | 69 | ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll 70 | 71 | 72 | 73 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll 74 | 75 | 76 | ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll 77 | 78 | 79 | ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | Form 102 | 103 | 104 | FormMain.cs 105 | 106 | 107 | Form 108 | 109 | 110 | FormMask.cs 111 | 112 | 113 | Form 114 | 115 | 116 | FormMask1.cs 117 | 118 | 119 | 120 | 121 | Component 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | FormMain.cs 150 | 151 | 152 | FormMask.cs 153 | 154 | 155 | FormMask1.cs 156 | 157 | 158 | ResXFileCodeGenerator 159 | Resources.Designer.cs 160 | Designer 161 | 162 | 163 | True 164 | Resources.resx 165 | True 166 | 167 | 168 | 169 | Always 170 | 171 | 172 | 173 | SettingsSingleFileGenerator 174 | Settings.Designer.cs 175 | 176 | 177 | True 178 | Settings.settings 179 | True 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Nlog.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 14 | 15 | 16 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace GeniusInvokationAutoToy 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// 应用程序的主入口点。 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | #if DEBUG 18 | Application.EnableVisualStyles(); 19 | Application.SetCompatibleTextRenderingDefault(false); 20 | Application.Run(new FormMain()); 21 | #else 22 | try 23 | { 24 | Application.EnableVisualStyles(); 25 | Application.SetCompatibleTextRenderingDefault(false); 26 | //当前用户是管理员的时候,直接启动应用程序 27 | //如果不是管理员,则使用启动对象启动程序,以确保使用管理员身份运行 28 | //获得当前登录的Windows用户标示 29 | System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent(); 30 | System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(identity); 31 | //判断当前登录用户是否为管理员 32 | if (principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator)) 33 | { 34 | //如果是管理员,则直接运行 35 | Application.Run(new FormMain()); 36 | } 37 | else 38 | { 39 | //创建启动对象 40 | System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(); 41 | startInfo.UseShellExecute = true; 42 | startInfo.WorkingDirectory = Environment.CurrentDirectory; 43 | startInfo.FileName = Application.ExecutablePath; 44 | //设置启动动作,确保以管理员身份运行 45 | startInfo.Verb = "runas"; 46 | try 47 | { 48 | System.Diagnostics.Process.Start(startInfo); 49 | } 50 | catch 51 | { 52 | return; 53 | } 54 | //退出 55 | Application.Exit(); 56 | } 57 | } 58 | catch (Exception ex) 59 | { 60 | MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 61 | } 62 | #endif 63 | } 64 | 65 | static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) 66 | { 67 | MessageBox.Show(e.Exception.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 68 | } 69 | 70 | static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) 71 | { 72 | MessageBox.Show((e.ExceptionObject as Exception).Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("GeniusInvokationAutoToy")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GeniusInvokationAutoToy")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("f17be4c5-926c-456d-9cc0-606dae304ed8")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.4.0.0")] 36 | [assembly: AssemblyFileVersion("1.4.0.0")] 37 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GeniusInvokationAutoToy.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GeniusInvokationAutoToy.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GeniusInvokationAutoToy.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 29 | public bool TopMostChecked { 30 | get { 31 | return ((bool)(this["TopMostChecked"])); 32 | } 33 | set { 34 | this["TopMostChecked"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("0")] 41 | public int CboStrategySelectIndex { 42 | get { 43 | return ((int)(this["CboStrategySelectIndex"])); 44 | } 45 | set { 46 | this["CboStrategySelectIndex"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("0")] 53 | public int CboGameResolutionSelectIndex { 54 | get { 55 | return ((int)(this["CboGameResolutionSelectIndex"])); 56 | } 57 | set { 58 | this["CboGameResolutionSelectIndex"] = value; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | False 7 | 8 | 9 | 0 10 | 11 | 12 | 0 13 | 14 | 15 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Raiden.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babalae/genius-invokation-auto-toy/03de89d6f9bbf97c84b58bc27b8e774f1ddf4367/GeniusInvokationAutoToy/Raiden.ico -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Resources/Raiden.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babalae/genius-invokation-auto-toy/03de89d6f9bbf97c84b58bc27b8e774f1ddf4367/GeniusInvokationAutoToy/Resources/Raiden.ico -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/KeqingRaidenGanyuStrategy.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Core.Model; 2 | using GeniusInvokationAutoToy.Core.MyException; 3 | using GeniusInvokationAutoToy.Core; 4 | using GeniusInvokationAutoToy.Utils; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Text.RegularExpressions; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace GeniusInvokationAutoToy.Strategy 14 | { 15 | [Obsolete] 16 | public class KeqingRaidenGanyuStrategy : BaseStrategy 17 | { 18 | /// 19 | /// 刻晴充能 20 | /// 21 | protected int KeqingEnergyNum; 22 | 23 | /// 24 | /// 莫娜充能 25 | /// 26 | protected int RaidenEnergyNum; 27 | 28 | /// 29 | /// 甘雨充能 30 | /// 31 | protected int GanyuEnergyNum; 32 | 33 | public KeqingRaidenGanyuStrategy(YuanShenWindow window) : base(window) 34 | { 35 | } 36 | 37 | 38 | public override void Run(CancellationTokenSource cts1) 39 | { 40 | cts = cts1; 41 | KeqingRaidenGanyuInit(); 42 | try 43 | { 44 | MyLogger.Info("========================================"); 45 | MyLogger.Info("对局启动!"); 46 | // 对局准备(选择初始手牌、出战角色) 47 | DuelPrepare(); 48 | 49 | // 执行回合 50 | MyLogger.Info("--------------第1回合--------------"); 51 | Round1(); 52 | MyLogger.Info("--------------第2回合--------------"); 53 | Round2(); 54 | MyLogger.Info("--------------第3回合--------------"); 55 | Round3(); 56 | MyLogger.Info("--------------第4回合--------------"); 57 | Round4(); 58 | 59 | 60 | // end 61 | capture.Stop(); 62 | MyLogger.Info("没活了,结束自动打牌"); 63 | MyLogger.Info("对手还或者的话,麻烦手动终结他吧"); 64 | } 65 | catch (TaskCanceledException ex) 66 | { 67 | MyLogger.Info(ex.Message); 68 | } 69 | catch (DuelEndException ex) 70 | { 71 | MyLogger.Info(ex.Message); 72 | } 73 | catch (Exception ex) 74 | { 75 | MyLogger.Error(ex.ToString()); 76 | } 77 | finally 78 | { 79 | MyLogger.Info("========================================"); 80 | } 81 | } 82 | 83 | public void KeqingRaidenGanyuInit() 84 | { 85 | Init(); // 初始化对局变量 86 | KeqingEnergyNum = 0; 87 | RaidenEnergyNum = 0; 88 | GanyuEnergyNum = 0; 89 | } 90 | 91 | private void DuelPrepare() 92 | { 93 | // 1. 选择初始手牌 94 | Sleep(1000); 95 | MyLogger.Info("开始选择初始手牌"); 96 | while (!ClickConfirm()) 97 | { 98 | // 循环等待选择卡牌画面 99 | Sleep(1000); 100 | } 101 | 102 | MyLogger.Info("点击确认"); 103 | 104 | // 2. 选择出战角色 105 | // 此处选择第2个角色 雷神 106 | MyLogger.Info("等待3s动画..."); 107 | Sleep(3000); 108 | 109 | // 是否是再角色出战选择界面 110 | Retry.Do(IsInCharacterPickRetryThrowable, TimeSpan.FromSeconds(1), 5); 111 | MyLogger.Info("识别到已经在角色出战界面,等待1.5s"); 112 | Sleep(1500); 113 | // 识别角色所在区域 114 | Retry.Do(() => ChooseCharacterFirst(2), TimeSpan.FromSeconds(1), 5); 115 | 116 | MyLogger.Info("出战雷电将军"); 117 | 118 | // 初始化手牌 119 | CurrentCardCount = 5; 120 | } 121 | 122 | /// 123 | /// 第一回合 124 | /// 125 | public void Round1() 126 | { 127 | CurrentDiceCount = 8; 128 | // 0 投骰子 129 | ReRollDice(ElementalType.Electro, ElementalType.Omni); 130 | 131 | // 等待到我的回合 // 投骰子动画时间是不确定的 132 | WaitForMyTurn(1000); 133 | 134 | // 1 回合1 行动1 雷电将军使用1次二技能 135 | MyLogger.Info("回合1 行动1 雷电将军使用1次二技能"); 136 | bool useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Electro, CurrentDiceCount); 137 | if (!useSkillRes) 138 | { 139 | MyLogger.Warn("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 140 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 141 | } 142 | CurrentDiceCount -=3; 143 | 144 | RaidenEnergyNum++; 145 | 146 | // 等待对方行动完成 147 | WaitForMyTurn(10000); 148 | 149 | // 2 回合1 行动2 雷电将军使用1次一技能 150 | MyLogger.Info("回合1 行动2 雷电将军使用1次一技能"); 151 | useSkillRes = ActionPhaseAutoUseSkill(3, 1, ElementalType.Electro, CurrentDiceCount); 152 | if (!useSkillRes) 153 | { 154 | MyLogger.Warn("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 155 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 156 | } 157 | CurrentDiceCount -= 3; 158 | RaidenEnergyNum++; 159 | 160 | // 等待对方行动完成 161 | WaitForMyTurn(10000); 162 | 163 | // 4 回合1 结束 剩下2个骰子 164 | MyLogger.Info("我方点击回合结束"); 165 | RoundEnd(); 166 | 167 | // 5 等待对方行动+回合结算 168 | WaitOpponentAction(); 169 | } 170 | 171 | 172 | /// 173 | /// 第二回合 174 | /// 175 | public void Round2() 176 | { 177 | CurrentDiceCount = 8; 178 | CurrentCardCount += 2; 179 | // 0 投骰子 180 | ReRollDice(ElementalType.Electro, ElementalType.Cryo, ElementalType.Omni); 181 | 182 | // 等待对方行动完成 // 可能是对方先手 183 | WaitForMyTurn(1000); 184 | 185 | MyLogger.Info("回合2 行动1 雷电将军开大"); 186 | bool useSkillRes = ActionPhaseAutoUseSkill(1, 4, ElementalType.Electro, CurrentDiceCount); 187 | if (!useSkillRes) 188 | { 189 | // 运气太差直接结束 190 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 191 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 192 | } 193 | CurrentDiceCount -= 4; 194 | 195 | KeqingEnergyNum += 2; 196 | RaidenEnergyNum = 0; 197 | GanyuEnergyNum += 2; 198 | 199 | // 等待对方行动完成 200 | WaitForMyTurn(15000); 201 | 202 | MyLogger.Info("回合2 行动2 切换甘雨"); 203 | SwitchCharacterLater(3); 204 | CurrentDiceCount -= 1; 205 | 206 | // 等待对方行动完成 207 | WaitForMyTurn(10000); 208 | 209 | MyLogger.Info("回合2 行动3 甘雨使用1次二技能"); 210 | useSkillRes = ActionPhaseAutoUseSkill(3, 3, ElementalType.Cryo, CurrentDiceCount); 211 | if (!useSkillRes) 212 | { 213 | // 运气太差直接结束 214 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 215 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 216 | } 217 | CurrentDiceCount -= 3; 218 | GanyuEnergyNum++; 219 | 220 | // 等待对方行动完成 221 | WaitForMyTurn(10000); 222 | 223 | // 4 回合2 结束 剩下0个骰子 224 | MyLogger.Info("我方点击回合结束"); 225 | RoundEnd(); 226 | 227 | // 5 等待对方行动+回合结算 228 | WaitOpponentAction(); 229 | } 230 | 231 | 232 | /// 233 | /// 第三回合 234 | /// 对局可能会胜利 235 | /// 236 | public void Round3() 237 | { 238 | CurrentDiceCount = 8; 239 | CurrentCardCount += 2; 240 | // 0 投骰子 241 | ReRollDice(ElementalType.Electro, ElementalType.Cryo, ElementalType.Omni); 242 | 243 | // 等待对方行动完成 // 可能是对方先手 244 | WaitForMyTurn(1000); 245 | 246 | MyLogger.Info("回合3 行动1 甘雨开大"); 247 | bool useSkillRes = ActionPhaseAutoUseSkill(1, 3, ElementalType.Cryo, CurrentDiceCount); 248 | if (!useSkillRes) 249 | { 250 | // 运气太差直接结束 251 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 252 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 253 | } 254 | 255 | CurrentDiceCount -= 3; 256 | GanyuEnergyNum =0; 257 | 258 | // 等待对方行动完成 // 甘雨大招动画比较长 259 | WaitForMyTurn(15000); 260 | 261 | MyLogger.Info("回合3 行动2 切换刻晴"); 262 | SwitchCharacterLater(1); 263 | CurrentDiceCount -= 1; 264 | 265 | // 等待对方行动完成 266 | WaitForMyTurn(10000); 267 | 268 | MyLogger.Info("回合3 行动3 刻晴使用1次二技能"); 269 | useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Electro, CurrentDiceCount); 270 | if (!useSkillRes) 271 | { 272 | MyLogger.Info("回合3 行动3 二技能无法使用,刻晴尝试使用一技能"); 273 | useSkillRes = ActionPhaseAutoUseSkill(3, 1, ElementalType.Electro, CurrentDiceCount); 274 | if (!useSkillRes) 275 | { 276 | // 运气太差直接结束 277 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 278 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 279 | } 280 | else 281 | { 282 | CurrentDiceCount -= 3; 283 | } 284 | } 285 | else 286 | { 287 | CurrentDiceCount -= 3; 288 | } 289 | KeqingEnergyNum++; 290 | 291 | // 等待对方行动完成 292 | WaitForMyTurn(10000); 293 | 294 | // 4 回合2 结束 剩下0个骰子 295 | MyLogger.Info("我方点击回合结束"); 296 | RoundEnd(); 297 | 298 | // 5 等待对方行动+回合结算 299 | WaitOpponentAction(); 300 | } 301 | 302 | /// 303 | /// 第四回合 304 | /// 对局可能会胜利 305 | /// 306 | public void Round4() 307 | { 308 | CurrentDiceCount = 8; 309 | CurrentCardCount += 2; 310 | // 0 投骰子 311 | ReRollDice(ElementalType.Electro, ElementalType.Omni); 312 | 313 | // 等待对方行动完成 // 可能是对方先手 314 | WaitForMyTurn(1000); 315 | 316 | MyLogger.Info("回合4 行动1 刻晴开大"); 317 | bool useSkillRes = ActionPhaseAutoUseSkill(1, 4, ElementalType.Electro, CurrentDiceCount); 318 | if (!useSkillRes) 319 | { 320 | // 运气太差直接结束 321 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 322 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 323 | } 324 | CurrentDiceCount -= 4; 325 | KeqingEnergyNum = 0; 326 | 327 | // 等待对方行动完成 328 | WaitForMyTurn(10000); 329 | 330 | MyLogger.Info("回合4 行动2 刻晴使用1次二技能"); 331 | useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Electro, CurrentDiceCount); 332 | if (!useSkillRes) 333 | { 334 | MyLogger.Info("回合4 行动3 二技能无法使用,刻晴尝试使用一技能"); 335 | useSkillRes = ActionPhaseAutoUseSkill(3, 1, ElementalType.Electro, CurrentDiceCount); 336 | if (!useSkillRes) 337 | { 338 | // 运气太差直接结束 339 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 340 | throw new DuelEndException("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 341 | } 342 | else 343 | { 344 | CurrentDiceCount -= 3; 345 | } 346 | } 347 | else 348 | { 349 | CurrentDiceCount -= 3; 350 | } 351 | KeqingEnergyNum++; 352 | } 353 | } 354 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/ActionCommand.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Core.Model; 2 | using GeniusInvokationAutoToy.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace GeniusInvokationAutoToy.Strategy.Model 10 | { 11 | public class ActionCommand 12 | { 13 | /// 14 | /// 角色 15 | /// 16 | public Character Character { get; set; } 17 | 18 | public ActionEnum Action { get; set; } 19 | 20 | /// 21 | /// 目标编号(技能编号,从右往左) 22 | /// 23 | public int TargetIndex { get; set; } 24 | 25 | public override string ToString() 26 | { 27 | if (Action == ActionEnum.UseSkill) 28 | { 29 | return $"【{Character.Name}】使用【技能{TargetIndex}】"; 30 | } 31 | else if (Action == ActionEnum.SwitchLater) 32 | { 33 | return $"【{Character.Name}】切换至【角色{TargetIndex}】"; 34 | } 35 | else 36 | { 37 | return base.ToString(); 38 | } 39 | } 40 | 41 | 42 | public int GetSpecificElementDiceUseCount() 43 | { 44 | if (Action == ActionEnum.UseSkill) 45 | { 46 | return Character.Skills[TargetIndex].SpecificElementCost; 47 | } 48 | else 49 | { 50 | throw new ArgumentException("未知行动"); 51 | } 52 | } 53 | 54 | public int GetAllDiceUseCount() 55 | { 56 | if (Action == ActionEnum.UseSkill) 57 | { 58 | return Character.Skills[TargetIndex].AllCost; 59 | } 60 | else 61 | { 62 | throw new ArgumentException("未知行动"); 63 | } 64 | } 65 | 66 | public ElementalType GetDiceUseElementType() 67 | { 68 | if (Action == ActionEnum.UseSkill) 69 | { 70 | return Character.Element; 71 | } 72 | else 73 | { 74 | throw new ArgumentException("未知行动"); 75 | } 76 | } 77 | 78 | public bool SwitchLater() 79 | { 80 | return Character.SwitchLater(); 81 | } 82 | 83 | public bool UseSkill(Duel duel) 84 | { 85 | return Character.UseSkill(TargetIndex, duel); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/ActionEnum.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Core.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace GeniusInvokationAutoToy.Strategy.Model 9 | { 10 | public enum ActionEnum 11 | { 12 | ChooseFirst, SwitchLater, UseSkill 13 | } 14 | 15 | public static class ActionEnumExtension 16 | { 17 | public static ActionEnum ChineseToActionEnum(this string type) 18 | { 19 | type = type.ToLower(); 20 | switch (type) 21 | { 22 | case "出战": 23 | //return ActionEnum.ChooseFirst; 24 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 25 | case "切换": 26 | //return ActionEnum.SwitchLater; 27 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 28 | case "使用": 29 | return ActionEnum.UseSkill; 30 | default: 31 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 32 | } 33 | } 34 | 35 | public static string ToChinese(this ActionEnum type) 36 | { 37 | switch (type) 38 | { 39 | case ActionEnum.ChooseFirst: 40 | return "出战"; 41 | case ActionEnum.SwitchLater: 42 | return "切换"; 43 | case ActionEnum.UseSkill: 44 | return "使用"; 45 | default: 46 | throw new ArgumentOutOfRangeException(nameof(type), type, null); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/Character.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Core.Model; 2 | using GeniusInvokationAutoToy.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using GeniusInvokationAutoToy.Utils.Extension; 10 | using GeniusInvokationAutoToy.Core.MyException; 11 | 12 | namespace GeniusInvokationAutoToy.Strategy.Model 13 | { 14 | public class Character 15 | { 16 | /// 17 | /// 1-3 所在数组下标一致 18 | /// 19 | public int Index { get; set; } 20 | public string Name { get; set; } 21 | public ElementalType Element { get; set; } 22 | public Skill[] Skills { get; set; } 23 | 24 | 25 | /// 26 | /// 是否被打败 27 | /// 28 | public bool IsDefeated { get; set; } 29 | 30 | /// 31 | /// 充能点 32 | /// 33 | public int Energy { get; set; } 34 | 35 | 36 | /// 37 | /// 充能点来自于图像识别 38 | /// 39 | public int EnergyByRecognition { get; set; } 40 | 41 | /// 42 | /// 角色身上的负面状态 43 | /// 44 | public List StatusList { get; set; } = new List(); 45 | 46 | /// 47 | /// 角色区域 48 | /// 49 | public Rectangle Area { get; set; } 50 | /// 51 | /// 血量上方区域,用于判断是否出战 52 | /// 53 | public Rectangle HpUpperArea { get; set; } 54 | 55 | public override string ToString() 56 | { 57 | StringBuilder sb = new StringBuilder(); 58 | sb.Append($"角色{Index},"); 59 | sb.Append($"充能={EnergyByRecognition},"); 60 | if (StatusList?.Count > 0) 61 | { 62 | sb.Append($"状态:{string.Join(",", StatusList)}"); 63 | } 64 | return sb.ToString(); 65 | } 66 | 67 | public bool ChooseFirst() 68 | { 69 | return MouseUtils.DoubleClick(GameControl.GetInstance().MakeOffset(Area.GetCenterPoint())); 70 | } 71 | 72 | public bool SwitchLater() 73 | { 74 | Point p = GameControl.GetInstance().MakeOffset(Area.GetCenterPoint()); 75 | // 选择角色 76 | MouseUtils.Click(p); 77 | 78 | // 点击切人按钮 79 | GameControl.GetInstance().ActionPhasePressSwitchButton(); 80 | return true; 81 | } 82 | 83 | /// 84 | /// 角色被打败的时候双击角色牌重新出战 85 | /// 86 | /// 87 | public bool SwitchWhenTakenOut() 88 | { 89 | MyLogger.Info($"有角色被打败,当前选择{Name}出战"); 90 | Point p = GameControl.GetInstance().MakeOffset(Area.GetCenterPoint()); 91 | // 选择角色 92 | MouseUtils.Click(p); 93 | // 双击切人 94 | GameControl.GetInstance().Sleep(500); 95 | MouseUtils.Click(p); 96 | GameControl.GetInstance().Sleep(300); 97 | return true; 98 | } 99 | 100 | public bool UseSkill(int skillIndex, Duel duel) 101 | { 102 | bool res = GameControl.GetInstance() 103 | .ActionPhaseAutoUseSkill(skillIndex, Skills[skillIndex].SpecificElementCost, Skills[skillIndex].Type, duel); 104 | if (res) 105 | { 106 | return true; 107 | } 108 | else 109 | { 110 | MyLogger.Warn("没有足够的手牌或元素骰子释放技能"); 111 | return false; 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/CharacterStatusEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GeniusInvokationAutoToy.Strategy.Model 8 | { 9 | public enum CharacterStatusEnum 10 | { 11 | Frozen, Dizziness 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/Duel.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Core; 2 | using GeniusInvokationAutoToy.Core.Model; 3 | using GeniusInvokationAutoToy.Core.MyException; 4 | using GeniusInvokationAutoToy.Strategy.Model.Old; 5 | using GeniusInvokationAutoToy.Utils; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Runtime.InteropServices; 11 | using System.Text; 12 | using System.Text.RegularExpressions; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | 16 | namespace GeniusInvokationAutoToy.Strategy.Model 17 | { 18 | public class Duel 19 | { 20 | public Character CurrentCharacter { get; set; } 21 | public Character[] Characters { get; set; } = new Character[4]; 22 | 23 | /// 24 | /// 行动指令队列 25 | /// 26 | public List ActionCommandQueue { get; set; } = new List(); 27 | 28 | /// 29 | /// 当前回合数 30 | /// 31 | public int RoundNum { get; set; } = 1; 32 | 33 | /// 34 | /// 角色牌位置 35 | /// 36 | public List CharacterCardRects { get; set; } 37 | 38 | /// 39 | /// 手牌数量 40 | /// 41 | public int CurrentCardCount { get; set; } = 0; 42 | 43 | /// 44 | /// 骰子数量 45 | /// 46 | public int CurrentDiceCount { get; set; } = 0; 47 | 48 | 49 | public CancellationTokenSource Cts { get; set; } 50 | 51 | 52 | public async Task CustomStrategyRunAsync(CancellationTokenSource cts1) 53 | { 54 | await Task.Run(() => { CustomStrategyRun(cts1); }); 55 | } 56 | 57 | public void CustomStrategyRun(CancellationTokenSource cts1) 58 | { 59 | Cts = cts1; 60 | try 61 | { 62 | MyLogger.Info("========================================"); 63 | MyLogger.Info("对局启动!"); 64 | 65 | GameControl.GetInstance().Init(Cts); 66 | GameControl.GetInstance().FocusGameWindow(); 67 | Retry.Do(() => GameControl.GetInstance().IsGameFocus(), TimeSpan.FromSeconds(1), 10); 68 | // 对局准备 选择初始手牌 69 | GameControl.GetInstance().CommonDuelPrepare(); 70 | 71 | 72 | // 获取角色区域 73 | CharacterCardRects = Retry.Do(() => GameControl.GetInstance().GetCharacterRects(), 74 | TimeSpan.FromSeconds(1.5), 20); 75 | if (CharacterCardRects == null || CharacterCardRects.Count != 3) 76 | { 77 | throw new DuelEndException("未成功获取到角色区域"); 78 | } 79 | 80 | for (var i = 1; i < 4; i++) 81 | { 82 | Characters[i].Area = CharacterCardRects[i - 1]; 83 | } 84 | 85 | // 出战角色 86 | CurrentCharacter = ActionCommandQueue[0].Character; 87 | CurrentCharacter.ChooseFirst(); 88 | 89 | // 开始执行回合 90 | while (true) 91 | { 92 | MyLogger.Info($"--------------第{RoundNum}回合--------------"); 93 | ClearCharacterStatus(); // 清空单回合的异常状态 94 | if (RoundNum == 1) 95 | { 96 | CurrentCardCount = 5; 97 | } 98 | else 99 | { 100 | CurrentCardCount += 2; 101 | } 102 | 103 | CurrentDiceCount = 8; 104 | 105 | // 预计算本回合内的所有可能的元素 106 | HashSet elementSet = PredictionDiceType(); 107 | 108 | // 0 投骰子 109 | GameControl.GetInstance().ReRollDice(elementSet.ToArray()); 110 | 111 | // 等待到我的回合 // 投骰子动画时间是不确定的 // 可能是对方先手 112 | GameControl.GetInstance().WaitForMyTurn(this, 1000); 113 | 114 | // 开始执行行动 115 | while (true) 116 | { 117 | // 没骰子了就结束行动 118 | MyLogger.Info($"行动开始,当前骰子数[{CurrentDiceCount}],当前手牌数[{CurrentCardCount}]"); 119 | if (CurrentDiceCount <= 0) 120 | { 121 | MyLogger.Info("骰子已经用完"); 122 | GameControl.GetInstance().Sleep(2000); 123 | break; 124 | } 125 | 126 | // 每次行动前都要检查当前角色 127 | CurrentCharacter = GameControl.GetInstance().WhichCharacterActiveWithRetry(this); 128 | 129 | List alreadyExecutedActionIndex = new List(); 130 | List alreadyExecutedActionCommand = new List(); 131 | var i = 0; 132 | for (i = 0; i < ActionCommandQueue.Count; i++) 133 | { 134 | var actionCommand = ActionCommandQueue[i]; 135 | // 指令中的角色未被打败、角色有异常状态 跳过指令 136 | if (actionCommand.Character.IsDefeated || actionCommand.Character.StatusList?.Count > 0) 137 | { 138 | continue; 139 | } 140 | 141 | // 当前出战角色身上存在异常状态的情况下不执行本角色的指令 142 | if (CurrentCharacter.StatusList?.Count > 0 && 143 | actionCommand.Character.Index == CurrentCharacter.Index) 144 | { 145 | continue; 146 | } 147 | 148 | 149 | // 1. 判断切人 150 | if (CurrentCharacter.Index != actionCommand.Character.Index) 151 | { 152 | if (CurrentDiceCount >= 1) 153 | { 154 | actionCommand.SwitchLater(); 155 | CurrentDiceCount--; 156 | alreadyExecutedActionIndex.Add(-actionCommand.Character.Index); // 标记为已执行 157 | var switchAction = new ActionCommand 158 | { 159 | Character = CurrentCharacter, 160 | Action = ActionEnum.SwitchLater, 161 | TargetIndex = actionCommand.Character.Index 162 | }; 163 | alreadyExecutedActionCommand.Add(switchAction); 164 | MyLogger.Info("→指令执行完成:" + switchAction); 165 | break; 166 | } 167 | else 168 | { 169 | MyLogger.Info("骰子不足以进行下一步:切换角色" + actionCommand.Character.Index); 170 | break; 171 | } 172 | } 173 | 174 | // 2. 判断使用技能 175 | if (actionCommand.GetAllDiceUseCount() > CurrentDiceCount) 176 | { 177 | MyLogger.Info("骰子不足以进行下一步:" + actionCommand); 178 | break; 179 | } 180 | else 181 | { 182 | bool useSkillRes = actionCommand.UseSkill(this); 183 | if (useSkillRes) 184 | { 185 | CurrentDiceCount -= actionCommand.GetAllDiceUseCount(); 186 | alreadyExecutedActionIndex.Add(i); 187 | alreadyExecutedActionCommand.Add(actionCommand); 188 | MyLogger.Info("→指令执行完成:" + actionCommand); 189 | } 190 | else 191 | { 192 | MyLogger.Warn("→指令执行失败(可能是手牌不够):" + actionCommand); 193 | GameControl.GetInstance().Sleep(1000); 194 | GameControl.GetInstance().ClickGameWindowCenter(); 195 | } 196 | 197 | break; 198 | } 199 | } 200 | 201 | 202 | 203 | if (alreadyExecutedActionIndex.Count != 0) 204 | { 205 | foreach (var index in alreadyExecutedActionIndex) 206 | { 207 | if (index >= 0) 208 | { 209 | ActionCommandQueue.RemoveAt(index); 210 | } 211 | } 212 | 213 | alreadyExecutedActionIndex.Clear(); 214 | // 等待对方行动完成 (开大的时候等待时间久一点) 215 | int sleepTime = ComputeWaitForMyTurnTime(alreadyExecutedActionCommand); 216 | GameControl.GetInstance().WaitForMyTurn(this, sleepTime); 217 | alreadyExecutedActionCommand.Clear(); 218 | } 219 | else 220 | { 221 | // 如果没有任何指令可以执行 则跳出循环 222 | // TODO 也有可能是角色死亡/所有角色被冻结导致没有指令可以执行 223 | //if (i >= ActionCommandQueue.Count) 224 | //{ 225 | // throw new DuelEndException("策略中所有指令已经执行完毕,结束自动打牌"); 226 | //} 227 | GameControl.GetInstance().Sleep(2000); 228 | break; 229 | } 230 | 231 | if (ActionCommandQueue.Count == 0) 232 | { 233 | throw new DuelEndException("策略中所有指令已经执行完毕,结束自动打牌"); 234 | } 235 | } 236 | 237 | // 回合结束 238 | GameControl.GetInstance().Sleep(1000); 239 | MyLogger.Info("我方点击回合结束"); 240 | GameControl.GetInstance().RoundEnd(); 241 | 242 | // 等待对方行动+回合结算 243 | GameControl.GetInstance().WaitOpponentAction(this); 244 | RoundNum++; 245 | } 246 | } 247 | catch (TaskCanceledException ex) 248 | { 249 | MyLogger.Info(ex.Message); 250 | } 251 | catch (DuelEndException ex) 252 | { 253 | MyLogger.Info(ex.Message); 254 | } 255 | catch (Exception ex) 256 | { 257 | MyLogger.Error(ex.ToString()); 258 | } 259 | finally 260 | { 261 | MyLogger.Info("========================================"); 262 | } 263 | } 264 | 265 | private HashSet PredictionDiceType() 266 | { 267 | int actionUseDiceSum = 0; 268 | HashSet elementSet = new HashSet 269 | { 270 | ElementalType.Omni 271 | }; 272 | for (var i = 0; i < ActionCommandQueue.Count; i++) 273 | { 274 | var actionCommand = ActionCommandQueue[i]; 275 | 276 | // 角色未被打败的情况下才能执行 277 | if (actionCommand.Character.IsDefeated) 278 | { 279 | continue; 280 | } 281 | 282 | // 通过骰子数量判断是否可以执行 283 | 284 | // 1. 判断切人 285 | if (i > 0 && actionCommand.Character.Index != ActionCommandQueue[i - 1].Character.Index) 286 | { 287 | actionUseDiceSum++; 288 | if (actionUseDiceSum > CurrentDiceCount) 289 | { 290 | break; 291 | } 292 | else 293 | { 294 | elementSet.Add(actionCommand.GetDiceUseElementType()); 295 | //executeActionIndex.Add(-actionCommand.Character.Index); 296 | } 297 | } 298 | 299 | // 2. 判断使用技能 300 | actionUseDiceSum += actionCommand.GetAllDiceUseCount(); 301 | if (actionUseDiceSum > CurrentDiceCount) 302 | { 303 | break; 304 | } 305 | else 306 | { 307 | elementSet.Add(actionCommand.GetDiceUseElementType()); 308 | //executeActionIndex.Add(i); 309 | } 310 | } 311 | 312 | return elementSet; 313 | } 314 | 315 | public void ClearCharacterStatus() 316 | { 317 | foreach (var character in Characters) 318 | { 319 | character?.StatusList?.Clear(); 320 | } 321 | } 322 | 323 | /// 324 | /// 根据前面执行的命令计算等待时间 325 | /// 大招等待15秒 326 | /// 快速切换等待3秒 327 | /// 328 | /// 329 | /// 330 | private int ComputeWaitForMyTurnTime(List alreadyExecutedActionCommand) 331 | { 332 | foreach (var command in alreadyExecutedActionCommand) 333 | { 334 | if (command.Action == ActionEnum.UseSkill && command.TargetIndex == 1) 335 | { 336 | return 15000; 337 | } 338 | 339 | // 莫娜切换等待3秒 340 | if (command.Character.Name == "莫娜" && command.Action == ActionEnum.SwitchLater) 341 | { 342 | return 3000; 343 | } 344 | } 345 | 346 | return 10000; 347 | } 348 | 349 | /// 350 | /// 获取角色切换顺序 351 | /// 352 | /// 353 | public List GetCharacterSwitchOrder() 354 | { 355 | List orderList = new List(); 356 | for (var i = 0; i < ActionCommandQueue.Count; i++) 357 | { 358 | if (!orderList.Contains(ActionCommandQueue[i].Character.Index)) 359 | { 360 | orderList.Add(ActionCommandQueue[i].Character.Index); 361 | } 362 | } 363 | 364 | return orderList; 365 | } 366 | 367 | /// 368 | /// 获取角色存活数量 369 | /// 370 | /// 371 | public int GetCharacterAliveNum() 372 | { 373 | int num = 0; 374 | foreach (var character in Characters) 375 | { 376 | if (character != null && !character.IsDefeated) 377 | { 378 | num++; 379 | } 380 | } 381 | 382 | return num; 383 | } 384 | } 385 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/Old/CurrentCharacterStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace GeniusInvokationAutoToy.Strategy.Model.Old 8 | { 9 | public class CurrentCharacterStatus 10 | { 11 | /// 12 | /// 0-2 和所在数组下标一致 13 | /// 14 | public int ArrayIndex { get; set; } 15 | public int EnergyNum { get; set; } 16 | 17 | public List NegativeStatusList { get; set; } = new List(); 18 | 19 | public override string ToString() 20 | { 21 | StringBuilder sb = new StringBuilder(); 22 | sb.Append($"当前出战角色{ArrayIndex+1},"); 23 | sb.Append($"充能={EnergyNum},"); 24 | if (NegativeStatusList.Count > 0) 25 | { 26 | sb.Append($"负面状态:{string.Join(",", NegativeStatusList)}"); 27 | } 28 | return sb.ToString(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/RoundStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using GeniusInvokationAutoToy.Core.Model; 7 | 8 | namespace GeniusInvokationAutoToy.Strategy.Model 9 | { 10 | public class RoundStrategy 11 | { 12 | public List RawCommandList { get; set; } = new List(); 13 | 14 | public List ActionCommands { get; set; } = new List(); 15 | 16 | public List MaybeNeedElement(Duel duel) 17 | { 18 | List result = new List(); 19 | 20 | for (int i = 0; i < ActionCommands.Count; i++) 21 | { 22 | if (ActionCommands[i].Action == ActionEnum.SwitchLater 23 | && i != ActionCommands.Count-1 24 | && ActionCommands[i+1].Action == ActionEnum.UseSkill) 25 | { 26 | result.Add(duel.Characters[ActionCommands[i].TargetIndex].Element); 27 | } 28 | } 29 | return result; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Model/Skill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using GeniusInvokationAutoToy.Core.Model; 7 | 8 | namespace GeniusInvokationAutoToy.Strategy.Model 9 | { 10 | public class Skill 11 | { 12 | /// 13 | /// 1-4 和数组下标一致,游戏中是从右往左开始数的! 14 | /// 15 | public short Index { get; set; } 16 | public ElementalType Type { get; set; } 17 | /// 18 | /// 消耗指定元素骰子数量 19 | /// 20 | public int SpecificElementCost { get; set; } 21 | 22 | /// 23 | /// 消耗杂色骰子数量 24 | /// 25 | public int AnyElementCost { get; set; } = 0; 26 | /// 27 | /// 消耗指定元素骰子数量 + 消耗杂色骰子数量 = 消耗总骰子数量 28 | /// 29 | public int AllCost { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/MonaSucroseJeanStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using GeniusInvokationAutoToy.Core; 9 | using GeniusInvokationAutoToy.Core.Model; 10 | using GeniusInvokationAutoToy.Core.MyException; 11 | using GeniusInvokationAutoToy.Strategy.Model.Old; 12 | using GeniusInvokationAutoToy.Utils; 13 | using GeniusInvokationAutoToy.Utils.Extension; 14 | using NLog; 15 | 16 | namespace GeniusInvokationAutoToy.Strategy 17 | { 18 | /// 19 | ///【原神】七圣召唤-史上最无脑PVE牌组!连牌都不用出,暴打NPC!https://www.bilibili.com/video/BV1ZP41197Ws 20 | /// 虽然颠勺队伍早就有了,但是第一次知道PVE也好用 21 | /// 已经弃用 22 | /// 23 | [Obsolete] 24 | public class MonaSucroseJeanStrategy : BaseStrategy 25 | { 26 | /// 27 | /// 莫娜充能 28 | /// 29 | protected int MonaEnergyNum; 30 | /// 31 | /// 砂糖充能 32 | /// 33 | protected int SucroseEnergyNum; 34 | /// 35 | /// 琴充能 36 | /// 37 | protected int JeanEnergyNum; 38 | 39 | public MonaSucroseJeanStrategy(YuanShenWindow window) : base(window) 40 | { 41 | } 42 | 43 | 44 | public override void Run(CancellationTokenSource cts1) 45 | { 46 | cts = cts1; 47 | MonaSucroseJeanInit(); 48 | try 49 | { 50 | MyLogger.Info("========================================"); 51 | MyLogger.Info("对局启动!"); 52 | // 对局准备(选择初始手牌、出战角色) 53 | DuelPrepare(); 54 | 55 | // 执行回合 56 | MyLogger.Info("--------------第1回合--------------"); 57 | Round1(); 58 | MyLogger.Info("--------------第2回合--------------"); 59 | Round2(); 60 | 61 | 62 | for (int i = 0; i < 3; i++) 63 | { 64 | MyLogger.Info($"--------------第{3 + i}回合--------------"); 65 | if (CurrentTakenOutCharacterCount >= 2) 66 | { 67 | RoundMoreForMona(); 68 | } 69 | else 70 | { 71 | RoundMore(); 72 | } 73 | } 74 | 75 | // end 76 | capture.Stop(); 77 | MyLogger.Info("没活了,结束自动打牌"); 78 | } 79 | catch (TaskCanceledException ex) 80 | { 81 | MyLogger.Info(ex.Message); 82 | } 83 | catch (DuelEndException ex) 84 | { 85 | MyLogger.Info(ex.Message); 86 | } 87 | catch (Exception ex) 88 | { 89 | MyLogger.Error(ex.ToString()); 90 | } 91 | finally 92 | { 93 | MyLogger.Info("========================================"); 94 | } 95 | } 96 | 97 | public void MonaSucroseJeanInit() 98 | { 99 | Init(); // 初始化对局变量 100 | MonaEnergyNum = 0; 101 | } 102 | 103 | private void DuelPrepare() 104 | { 105 | // 1. 选择初始手牌 106 | Sleep(1000); 107 | MyLogger.Info("开始选择初始手牌"); 108 | while (!ClickConfirm()) 109 | { 110 | // 循环等待选择卡牌画面 111 | Sleep(1000); 112 | } 113 | 114 | MyLogger.Info("点击确认"); 115 | 116 | // 2. 选择出战角色 117 | // 此处选择第一个角色 莫娜 118 | MyLogger.Info("等待3s动画..."); 119 | Sleep(3000); 120 | 121 | // 是否是再角色出战选择界面 122 | Retry.Do(IsInCharacterPickRetryThrowable, TimeSpan.FromSeconds(1), 5); 123 | MyLogger.Info("识别到已经在角色出战界面,等待1.5s"); 124 | Sleep(1500); 125 | 126 | // 识别角色所在区域 127 | Retry.Do(() => ChooseCharacterFirst(1), TimeSpan.FromSeconds(1), 5); 128 | 129 | MyLogger.Info("出战莫娜"); 130 | 131 | // 初始化手牌 132 | CurrentCardCount = 5; 133 | } 134 | 135 | /// 136 | /// 第一回合 要尽量保证行动 137 | /// 138 | public void Round1() 139 | { 140 | CurrentDiceCount = 8; 141 | // 0 投骰子 142 | ReRollDice(ElementalType.Anemo, ElementalType.Hydro, ElementalType.Omni); 143 | 144 | // 等待到我的回合 // 投骰子动画时间是不确定的 145 | WaitForMyTurn(1000); 146 | 147 | // 1 回合1 行动1 莫娜使用1次二技能 148 | MyLogger.Info("回合1 行动1 莫娜使用1次二技能"); 149 | bool useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Hydro, CurrentDiceCount); 150 | if (!useSkillRes) 151 | { 152 | MyLogger.Warn("没有足够的手牌或元素骰子释放技能,停止自动打牌"); 153 | return; 154 | } 155 | CurrentDiceCount-=3; 156 | MonaEnergyNum++; 157 | 158 | // 等待对方行动完成 159 | WaitForMyTurn(10000); 160 | 161 | CurrentCharacterStatus = WhichCharacterActiveWithRetry(); 162 | 163 | if (CurrentCharacterStatus.ArrayIndex != 1) 164 | { 165 | // 2 回合1 行动2 切换砂糖 166 | MyLogger.Info("回合1 行动2 切换砂糖"); 167 | SwitchCharacterLater(2); 168 | CurrentDiceCount--; 169 | 170 | // 快速切换无需等待对方 但是有动画,需要延迟一会 171 | WaitForMyTurn(3000); 172 | } 173 | else 174 | { 175 | MyLogger.Warn("回合1 行动2 砂糖已经被切换,无需快速切换"); 176 | } 177 | 178 | 179 | // 3 回合1 行动2 砂糖使用1次二技能 180 | MyLogger.Info("回合1 行动3 砂糖使用1次二技能"); 181 | ActionPhaseAutoUseSkill(2, 3, ElementalType.Anemo, CurrentDiceCount); 182 | SucroseEnergyNum++; 183 | 184 | // 等待对方行动完成 185 | WaitForMyTurn(10000); 186 | 187 | // 砂糖有可能被超载切走 188 | HoldSwitchCharacter(2); 189 | 190 | // 4 回合1 结束 剩下1个骰子 191 | MyLogger.Info("我方点击回合结束"); 192 | RoundEnd(); 193 | 194 | // 5 等待对方行动+回合结算 195 | WaitOpponentAction(); 196 | } 197 | 198 | 199 | /// 200 | /// 第二回合 要尽量保证行动 201 | /// 202 | public void Round2() 203 | { 204 | CurrentCardCount += 2; 205 | CurrentDiceCount = 8; 206 | // 0 投骰子 207 | ReRollDice(ElementalType.Anemo, ElementalType.Omni); 208 | 209 | // 等待对方行动完成 // 可能是对方先手 210 | WaitForMyTurn(1000); 211 | 212 | HoldSwitchCharacter(2); 213 | // 1 回合2 行动1 砂糖使用1次二技能 214 | MyLogger.Info("回合2 砂糖使用1次二技能"); 215 | bool useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Anemo, CurrentDiceCount); 216 | if (!useSkillRes) 217 | { 218 | // 运气太差直接结束 219 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 220 | RoundEnd(); 221 | WaitOpponentAction(); 222 | return; 223 | } 224 | CurrentDiceCount -= 3; 225 | SucroseEnergyNum++; 226 | 227 | // 等待对方行动完成 228 | WaitForMyTurn(10000); 229 | 230 | HoldSwitchCharacter(2); 231 | // 2 回合2 行动2 砂糖充能已满,使用1次三技能 232 | MyLogger.Info("回合2 砂糖充能已满,使用1次三技能"); 233 | useSkillRes = ActionPhaseAutoUseSkill(1, 3, ElementalType.Anemo, CurrentDiceCount); 234 | if (!useSkillRes) 235 | { 236 | // 运气太差直接结束 237 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 238 | RoundEnd(); 239 | WaitOpponentAction(); 240 | return; 241 | } 242 | CurrentDiceCount -= 3; 243 | 244 | // 等待对方行动完成 // 大招久一点 245 | WaitForMyTurn(12000); 246 | 247 | // 4 回合2 结束 248 | MyLogger.Info("我方点击回合结束"); 249 | RoundEnd(); 250 | 251 | // 5 等待对方行动+回合结算 252 | WaitOpponentAction(); 253 | } 254 | 255 | 256 | /// 257 | /// 第三/N回合 出战为砂糖琴 258 | /// 对局可能会胜利 259 | /// 260 | public void RoundMore() 261 | { 262 | CurrentCardCount += 2; 263 | CurrentDiceCount = 8; 264 | // 0 投骰子 265 | ReRollDice(ElementalType.Anemo, ElementalType.Omni); 266 | 267 | // 等待对方行动完成 // 可能是对方先手 268 | WaitForMyTurn(1000); 269 | 270 | // 1 回合2 行动1 使用1次二技能 271 | MyLogger.Info("行动1 使用1次二技能"); 272 | bool useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Anemo, 8); 273 | if (!useSkillRes) 274 | { 275 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 276 | RoundEnd(); 277 | WaitOpponentAction(); 278 | return; 279 | } 280 | CurrentDiceCount -= 3; 281 | 282 | // 等待对方行动完成 283 | WaitForMyTurn(10000); 284 | 285 | // 2 回合2 行动2 使用1次二技能 286 | MyLogger.Info("行动2 使用1次二技能"); 287 | useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Anemo, 5); 288 | if (!useSkillRes) 289 | { 290 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 291 | RoundEnd(); 292 | WaitOpponentAction(); 293 | return; 294 | } 295 | CurrentDiceCount -= 3; 296 | 297 | // 等待对方行动完成 298 | WaitForMyTurn(10000); 299 | 300 | // 4 回合2 结束 剩下2个骰子 301 | MyLogger.Info("我方点击回合结束"); 302 | RoundEnd(); 303 | 304 | // 5 等待对方行动+回合结算 305 | WaitOpponentAction(); 306 | } 307 | 308 | /// 309 | /// 第三/N回合 出战为莫娜 310 | /// 对局可能会胜利 311 | /// 312 | public void RoundMoreForMona() 313 | { 314 | bool useSkillRes; 315 | CurrentDiceCount = 8; 316 | CurrentCardCount += 2; 317 | // 0 投骰子 318 | ReRollDice(ElementalType.Hydro, ElementalType.Omni); 319 | 320 | // 等待对方行动完成 // 可能是对方先手 321 | WaitForMyTurn(1000); 322 | 323 | if (MonaEnergyNum == 3) 324 | { 325 | CurrentDiceCount = MonaUse3Skill(CurrentDiceCount); 326 | } 327 | 328 | // 1 使用1次二技能 329 | MyLogger.Info("行动1 使用1次二技能"); 330 | useSkillRes = ActionPhaseAutoUseSkill(2, 3, ElementalType.Hydro, CurrentDiceCount); 331 | if (!useSkillRes) 332 | { 333 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 334 | RoundEnd(); 335 | WaitOpponentAction(); 336 | return; 337 | } 338 | 339 | CurrentDiceCount -= 3; 340 | MonaEnergyNum++; 341 | 342 | 343 | if (MonaEnergyNum == 3 && CurrentDiceCount >= 3) 344 | { 345 | CurrentDiceCount = MonaUse3Skill(CurrentDiceCount); 346 | } 347 | 348 | // 等待对方行动完成 349 | WaitForMyTurn(10000); 350 | 351 | // 2 使用1次一技能 352 | if (CurrentDiceCount >= 3) 353 | { 354 | MyLogger.Info("行动2 使用1次一技能"); 355 | useSkillRes = ActionPhaseAutoUseSkill(3, 1, ElementalType.Hydro, CurrentDiceCount); 356 | if (!useSkillRes) 357 | { 358 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 359 | RoundEnd(); 360 | WaitOpponentAction(); 361 | return; 362 | } 363 | 364 | CurrentDiceCount -= 3; 365 | MonaEnergyNum++; 366 | } 367 | 368 | 369 | // 等待对方行动完成 370 | WaitForMyTurn(10000); 371 | 372 | // 4 回合2 结束 剩下2个骰子 373 | MyLogger.Info("我方点击回合结束"); 374 | RoundEnd(); 375 | 376 | // 5 等待对方行动+回合结算 377 | WaitOpponentAction(); 378 | } 379 | 380 | private int MonaUse3Skill(int roundDiceCount) 381 | { 382 | bool useSkillRes; 383 | MyLogger.Info("使用1次三技能"); 384 | useSkillRes = ActionPhaseAutoUseSkill(1, 3, ElementalType.Hydro, roundDiceCount); 385 | if (!useSkillRes) 386 | { 387 | MyLogger.Info("没有足够的手牌或元素骰子释放技能,回合结束"); 388 | RoundEnd(); 389 | WaitOpponentAction(); 390 | return roundDiceCount; 391 | } 392 | 393 | roundDiceCount -= 3; 394 | MonaEnergyNum = 0; 395 | return roundDiceCount; 396 | } 397 | 398 | public void HoldSwitchCharacter(int index) 399 | { 400 | // 砂糖有可能被超载切走 401 | CurrentCharacterStatus = WhichCharacterActiveWithRetry(); 402 | if (CurrentCharacterStatus.ArrayIndex != index-1) 403 | { 404 | // 2 回合1 行动2 切换砂糖 405 | MyLogger.Info("回合1 行动4 检测到出战角色不是砂糖,可能被超载,切换回砂糖"); 406 | SwitchCharacterLater(index); // 刚好还有一个骰子可以用来切人 407 | CurrentDiceCount--; 408 | 409 | // 等待对方行动完成 410 | WaitForMyTurn(10000); 411 | } 412 | else 413 | { 414 | // 冻结、眩晕 415 | if (CurrentCharacterStatus.NegativeStatusList.Count > 0) 416 | { 417 | MyLogger.Warn("当前出战角色因为负面状态无法行动"); 418 | throw new DuelEndException("当前出战角色因为负面状态无法行动,暂时无法支持此种情况,请尝试其他卡组或使用自定策略的功能"); 419 | } 420 | } 421 | } 422 | } 423 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Strategy/Script/ScriptParser.cs: -------------------------------------------------------------------------------- 1 | using GeniusInvokationAutoToy.Strategy.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using GeniusInvokationAutoToy.Core.Model; 8 | using OpenCvSharp; 9 | using System.Text.RegularExpressions; 10 | using System.Diagnostics; 11 | using System.Windows.Forms; 12 | using GeniusInvokationAutoToy.Utils; 13 | 14 | namespace GeniusInvokationAutoToy.Strategy.Script 15 | { 16 | public class ScriptParser 17 | { 18 | public static Duel Parse(string script) 19 | { 20 | var lines = script.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); 21 | var result = new List(); 22 | foreach (var line in lines) 23 | { 24 | string l = line.Trim(); 25 | //if (l.StartsWith("//")) 26 | //{ 27 | // continue; 28 | //} 29 | result.Add(l); 30 | } 31 | 32 | return Parse(result); 33 | } 34 | 35 | public static Duel Parse(List lines) 36 | { 37 | Duel duel = new Duel(); 38 | string stage = ""; 39 | 40 | int i = 0; 41 | try 42 | { 43 | for (i = 0; i < lines.Count; i++) 44 | { 45 | var line = lines[i]; 46 | if (line.Contains(":")) 47 | { 48 | stage = line; 49 | continue; 50 | } 51 | 52 | if (line == "---" || line.StartsWith("//") || string.IsNullOrEmpty(line)) 53 | { 54 | continue; 55 | } 56 | 57 | if (stage == "角色定义:") 58 | { 59 | var character = ParseCharacter(line); 60 | duel.Characters[character.Index] = character; 61 | } 62 | else if (stage == "策略定义:") 63 | { 64 | MyAssert(duel.Characters[3] != null, "角色未定义"); 65 | 66 | string[] actionParts = line.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries); 67 | MyAssert(actionParts.Length == 3, "策略中的行动命令解析错误"); 68 | MyAssert(actionParts[1] == "使用", "策略中的行动命令解析错误"); 69 | 70 | var actionCommand = new ActionCommand(); 71 | var action = actionParts[1].ChineseToActionEnum(); 72 | actionCommand.Action = action; 73 | 74 | int j = 1; 75 | for (j = 1; j <= 3; j++) 76 | { 77 | var character = duel.Characters[j]; 78 | if (character != null && character.Name == actionParts[0]) 79 | { 80 | actionCommand.Character = character; 81 | break; 82 | } 83 | } 84 | 85 | MyAssert(j <= 3, "策略中的行动命令解析错误:角色名称无法从角色定义中匹配到"); 86 | 87 | int skillNum = int.Parse(Regex.Replace(actionParts[2], @"[^0-9]+", "")); 88 | MyAssert(skillNum < 5, "策略中的行动命令解析错误:技能编号错误"); 89 | actionCommand.TargetIndex = skillNum; 90 | duel.ActionCommandQueue.Add(actionCommand); 91 | } 92 | else 93 | { 94 | throw new Exception($"未知的定义字段:{stage}"); 95 | } 96 | } 97 | 98 | MyAssert(duel.Characters[3] != null, "角色未定义,请确认策略文本格式是否为UTF-8"); 99 | } 100 | catch (Exception ex) 101 | { 102 | MyLogger.Error($"解析脚本错误,行号:{i + 1},错误信息:{ex}"); 103 | MessageBox.Show($"解析脚本错误,行号:{i + 1},错误信息:{ex}", "策略解析失败", MessageBoxButtons.OK, MessageBoxIcon.Error); 104 | return null; 105 | } 106 | 107 | return duel; 108 | } 109 | 110 | /// 111 | /// 解析示例 112 | /// 角色1=刻晴|雷{技能3消耗=1雷骰子+2任意,技能2消耗=3雷骰子,技能1消耗=4雷骰子} 113 | /// 角色2=雷神|雷{技能3消耗=1雷骰子+2任意,技能2消耗=3雷骰子,技能1消耗=4雷骰子} 114 | /// 角色3=甘雨|冰{技能4消耗=1冰骰子+2任意,技能3消耗=1冰骰子,技能2消耗=5冰骰子,技能1消耗=3冰骰子} 115 | /// 116 | /// 117 | /// 118 | public static Character ParseCharacter(string line) 119 | { 120 | var character = new Character(); 121 | 122 | var characterAndSkill = line.Split('{'); 123 | 124 | var parts = characterAndSkill[0].Split('='); 125 | character.Index = int.Parse(Regex.Replace(parts[0], @"[^0-9]+", "")); 126 | MyAssert(character.Index >= 1 && character.Index <= 3, "角色序号必须在1-3之间"); 127 | var nameAndElement = parts[1].Split('|'); 128 | character.Name = nameAndElement[0]; 129 | character.Element = nameAndElement[1].Substring(0, 1).ChineseToElementalType(); 130 | 131 | // 技能 132 | string skillStr = characterAndSkill[1].Replace("}", ""); 133 | var skillParts = skillStr.Split(','); 134 | var skills = new Skill[skillParts.Length + 1]; 135 | for (int i = 0; i < skillParts.Length; i++) 136 | { 137 | var skill = ParseSkill(skillParts[i]); 138 | skills[skill.Index] = skill; 139 | } 140 | 141 | character.Skills = skills.ToArray(); 142 | return character; 143 | } 144 | 145 | /// 146 | /// 技能3消耗=1雷骰子+2任意 147 | /// 技能2消耗=3雷骰子 148 | /// 技能1消耗=4雷骰子 149 | /// 150 | /// 151 | /// 152 | public static Skill ParseSkill(string oneSkillStr) 153 | { 154 | var skill = new Skill(); 155 | var parts = oneSkillStr.Split('='); 156 | skill.Index = short.Parse(Regex.Replace(parts[0], @"[^0-9]+", "")); 157 | MyAssert(skill.Index >= 1 && skill.Index <= 5, "技能序号必须在1-5之间"); 158 | var costStr = parts[1]; 159 | var costParts = costStr.Split('+'); 160 | skill.SpecificElementCost = int.Parse(costParts[0].Substring(0, 1)); 161 | skill.Type = costParts[0].Substring(1, 1).ChineseToElementalType(); 162 | // 杂色骰子在+号右边 163 | if (costParts.Length > 1) 164 | { 165 | skill.AnyElementCost = int.Parse(costParts[1].Substring(0, 1)); 166 | } 167 | 168 | skill.AllCost = skill.SpecificElementCost + skill.AnyElementCost; 169 | return skill; 170 | } 171 | 172 | private static void MyAssert(bool b, string msg) 173 | { 174 | if (!b) 175 | { 176 | throw new Exception(msg); 177 | } 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/Device.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Management; 5 | using System.Net; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | 9 | namespace GeniusInvokationAutoToy.Utils 10 | { 11 | public class Device 12 | { 13 | private static string macID = null; 14 | private static string osVersion = null; 15 | 16 | private static string fingerPrint = null; 17 | 18 | #region PROP, get it only once 19 | 20 | public static string MacID 21 | { 22 | get 23 | { 24 | if (macID == null) 25 | { 26 | macID = ObtainMacID(); 27 | } 28 | return macID; 29 | } 30 | } 31 | 32 | public static string OSVersion 33 | { 34 | get 35 | { 36 | if (osVersion == null) 37 | { 38 | var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast() 39 | select x.GetPropertyValue("Caption")).FirstOrDefault(); 40 | osVersion = name != null ? name.ToString() : "Unknown"; 41 | } 42 | return osVersion; 43 | } 44 | } 45 | #endregion 46 | 47 | /// 48 | /// Calculate GUID 49 | /// 50 | /// GUID 51 | public static string Value() 52 | { 53 | try 54 | { 55 | if (fingerPrint == null) 56 | { 57 | fingerPrint = GetHash( 58 | "MAC >> " + MacID 59 | ); 60 | } 61 | return fingerPrint; 62 | } 63 | catch 64 | { 65 | return Guid.NewGuid().ToString(); 66 | } 67 | 68 | } 69 | 70 | private static string GetHash(string s) 71 | { 72 | MD5 sec = new MD5CryptoServiceProvider(); 73 | ASCIIEncoding enc = new ASCIIEncoding(); 74 | byte[] bt = enc.GetBytes(s); 75 | return GetHexString(sec.ComputeHash(bt)); 76 | } 77 | 78 | private static string GetHexString(byte[] bt) 79 | { 80 | string s = string.Empty; 81 | for (int i = 0; i < bt.Length; i++) 82 | { 83 | byte b = bt[i]; 84 | int n, n1, n2; 85 | n = (int)b; 86 | n1 = n & 15; 87 | n2 = (n >> 4) & 15; 88 | if (n2 > 9) 89 | s += ((char)(n2 - 10 + (int)'A')).ToString(); 90 | else 91 | s += n2.ToString(); 92 | if (n1 > 9) 93 | s += ((char)(n1 - 10 + (int)'A')).ToString(); 94 | else 95 | s += n1.ToString(); 96 | if ((i + 1) != bt.Length && (i + 1) % 2 == 0) s += "-"; 97 | } 98 | return s; 99 | } 100 | 101 | 102 | #region Original Device ID Getting Code 103 | 104 | public static string ObtainMacID() 105 | { 106 | return Identifier("Win32_NetworkAdapterConfiguration", "MACAddress", "IPEnabled"); 107 | } 108 | 109 | private static string Identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue) 110 | { 111 | string result = ""; 112 | try 113 | { 114 | ManagementClass mc = new ManagementClass(wmiClass); 115 | ManagementObjectCollection moc = mc.GetInstances(); 116 | foreach (ManagementObject mo in moc) 117 | { 118 | if (mo[wmiMustBeTrue].ToString() == "True") 119 | { 120 | //Only get the first one 121 | if (result == "") 122 | { 123 | result = mo[wmiProperty].ToString(); 124 | break; 125 | } 126 | } 127 | } 128 | } 129 | catch 130 | { 131 | } 132 | return result; 133 | } 134 | 135 | private static string Identifier(string wmiClass, string wmiProperty) 136 | { 137 | string result = ""; 138 | try 139 | { 140 | ManagementClass mc = new ManagementClass(wmiClass); 141 | ManagementObjectCollection moc = mc.GetInstances(); 142 | foreach (ManagementObject mo in moc) 143 | { 144 | //Only get the first one 145 | if (result == "") 146 | { 147 | result = mo[wmiProperty].ToString(); 148 | break; 149 | } 150 | } 151 | } 152 | catch 153 | { 154 | } 155 | return result; 156 | } 157 | #endregion 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/Extension/RectangleExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace GeniusInvokationAutoToy.Utils.Extension 9 | { 10 | public static class RectangleExtension 11 | { 12 | public static OpenCvSharp.Rect ToCvRect(this Rectangle rectangle) 13 | { 14 | return new OpenCvSharp.Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); 15 | } 16 | 17 | public static Rectangle ToRectangle(this OpenCvSharp.Rect rectangle) 18 | { 19 | return new Rectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); 20 | } 21 | 22 | public static Point GetCenterPoint(this Rectangle rectangle) 23 | { 24 | if (rectangle.IsEmpty) 25 | { 26 | throw new ArgumentException("rectangle is empty"); 27 | } 28 | return new Point(rectangle.X + rectangle.Width / 2, rectangle.Y + rectangle.Height / 2); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/Extension/RetryExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace GeniusInvokationAutoToy.Utils.Extension 9 | { 10 | public static class RetryExtension 11 | { 12 | public static void InvokeWithRetries(this Action @this, ushort numberOfRetries, TimeSpan sleepPeriod, string throwsMessage) 13 | { 14 | try 15 | { 16 | @this(); 17 | } 18 | catch 19 | { 20 | if (numberOfRetries == 0) 21 | { 22 | throw new Exception(throwsMessage); 23 | } 24 | 25 | Thread.Sleep(sleepPeriod); 26 | 27 | InvokeWithRetries(@this, --numberOfRetries, sleepPeriod, throwsMessage); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/GAHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Web.Script.Serialization; 8 | using System.Windows.Forms; 9 | 10 | namespace GeniusInvokationAutoToy.Utils 11 | { 12 | /// 13 | /// 用于软件的 Google Analytics 实现 By huiyadanli 14 | /// 20230409 更新 GA4 的实现 15 | /// 相关文档: 16 | /// #GA指南(过时) https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide 17 | /// #GA参数(过时) https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters 18 | /// GA4教程 https://firebase.google.com/codelabs/firebase_mp 19 | /// 测试 https://ga-dev-tools.google/ga4/event-builder/ 20 | /// 21 | public class GAHelper 22 | { 23 | private static GAHelper instance = null; 24 | private static readonly object obj = new object(); 25 | 26 | public static GAHelper Instance 27 | { 28 | get 29 | { 30 | //lock (obj) 31 | //{ 32 | if (instance == null) 33 | { 34 | instance = new GAHelper(); 35 | } 36 | return instance; 37 | //} 38 | } 39 | } 40 | 41 | // 根据实际情况修改 42 | private static readonly HttpClient client = new HttpClient(); 43 | 44 | private const string GAUrl = "https://www.google-analytics.com/mp/collect?api_secret=tMiYgGrJSIeuL5eIV1YCsQ&measurement_id=G-TQYV28WWTX"; 45 | 46 | private static readonly string cid = Device.Value(); // Anonymous Client ID. // Guid.NewGuid().ToString() 47 | 48 | 49 | public string UserAgent { get; set; } 50 | 51 | public GAHelper() 52 | { 53 | UserAgent = $"Hui Google Analytics Tracker/1.0 ({Environment.OSVersion.Platform.ToString()}; {Environment.OSVersion.Version.ToString()}; {Environment.OSVersion.VersionString})"; 54 | } 55 | 56 | public async Task RequestPageViewAsync(string page, string title = null) 57 | { 58 | try 59 | { 60 | if (page.StartsWith("/")) 61 | { 62 | page = page.Remove(0, 1); 63 | } 64 | page = page.Replace("/", "_").Replace(".", "_"); 65 | // 请求参数 66 | var values = new Dictionary 67 | { 68 | { "client_id",UserAgent}, 69 | { "user_id", cid }, 70 | { "non_personalized_ads", "false" }, 71 | { "events", new List>() 72 | { 73 | new Dictionary() 74 | { 75 | { "name",page }, 76 | { 77 | "params", 78 | new Dictionary() 79 | { 80 | { "engagement_time_msec", "1"}, 81 | } 82 | }, 83 | } 84 | } 85 | }, 86 | }; 87 | var serializer = new JavaScriptSerializer(); 88 | var json = serializer.Serialize(values); 89 | var content = new StringContent(json, Encoding.UTF8, "application/json"); 90 | var response = await client.PostAsync(GAUrl, content); 91 | Console.WriteLine(response.ToString()); 92 | } 93 | catch (Exception ex) 94 | { 95 | 96 | Console.WriteLine("GAHelper:" + ex.Message); 97 | Console.WriteLine(ex.StackTrace); 98 | } 99 | } 100 | 101 | public void RequestPageView(string page, string title = null) 102 | { 103 | Task.Run(() => RequestPageViewAsync(page, title)); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/MouseUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace GeniusInvokationAutoToy.Utils 10 | { 11 | public class MouseUtils 12 | { 13 | public static void Move(int x, int y) 14 | { 15 | Native.mouse_event(Native.MouseEventFlag.Absolute | Native.MouseEventFlag.Move, 16 | x * 65535 / PrimaryScreen.DESKTOP.Width, y * 65535 / PrimaryScreen.DESKTOP.Height, 17 | 0, 0); 18 | } 19 | 20 | public static void Move(Point point) 21 | { 22 | Move(point.X, point.Y); 23 | } 24 | 25 | public static void LeftDown() 26 | { 27 | Native.mouse_event(Native.MouseEventFlag.LeftDown, 0, 0, 0, 0); 28 | } 29 | 30 | public static void LeftUp() 31 | { 32 | Native.mouse_event(Native.MouseEventFlag.LeftUp, 0, 0, 0, 0); 33 | } 34 | 35 | public static bool Click(int x, int y) 36 | { 37 | if (x == 0 && y==0) 38 | { 39 | return false; 40 | } 41 | //MyLogger.Info($"Click {x},{y}"); 42 | Move(x, y); 43 | LeftDown(); 44 | Thread.Sleep(20); 45 | LeftUp(); 46 | return true; 47 | } 48 | 49 | public static bool Click(Point point) 50 | { 51 | return Click(point.X, point.Y); 52 | } 53 | 54 | public static bool DoubleClick(Point point) 55 | { 56 | Click(point.X, point.Y); 57 | Thread.Sleep(200); 58 | return Click(point.X, point.Y); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/MyLogger.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Forms; 8 | 9 | namespace GeniusInvokationAutoToy.Utils 10 | { 11 | 12 | public class MyLogger 13 | { 14 | static Logger _logger => LogManager.GetCurrentClassLogger(); 15 | 16 | public static FormMain formMain; 17 | 18 | public static void Info(string message, params object[] args) 19 | { 20 | _logger.Info(message, args); 21 | } 22 | public static void Trace(string message, params object[] args) 23 | { 24 | _logger.Trace(message, args); 25 | } 26 | public static void Error(string message, params object[] args) 27 | { 28 | _logger.Error(message, args); 29 | } 30 | public static void Debug(string message, params object[] args) 31 | { 32 | _logger.Debug(message, args); 33 | } 34 | public static void Fatal(string message, params object[] args) 35 | { 36 | _logger.Fatal(message, args); 37 | } 38 | public static void Warn(string message, params object[] args) 39 | { 40 | _logger.Warn(message, args); 41 | } 42 | public static void InfoSingleLine(string message, params object[] args) 43 | { 44 | formMain?.RtbConsoleDeleteLine(); 45 | _logger.Info(message, args); 46 | } 47 | 48 | public static void WarnSingleLine(string message, params object[] args) 49 | { 50 | formMain?.RtbConsoleDeleteLine(); 51 | _logger.Warn(message, args); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/Native.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace GeniusInvokationAutoToy.Utils 9 | { 10 | class Native 11 | { 12 | 13 | [DllImport("GDI32.DLL", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] 14 | public static extern bool StretchBlt(IntPtr hdcDest, int nXDest, int nYDest, int nDestWidth, int nDestHeight, 15 | IntPtr hdcSrc, int nXSrc, int nYSrc, int nSrcWidth, int nSrcHeight, CopyPixelOperation dwRop); 16 | 17 | [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] 18 | public static extern IntPtr GetDC(IntPtr hWnd); 19 | 20 | [DllImport("user32.dll", ExactSpelling = true)] 21 | public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC); 22 | 23 | [DllImport("gdi32.dll", ExactSpelling = true)] 24 | public static extern IntPtr BitBlt(IntPtr hDestDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop); 25 | 26 | [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")] 27 | public static extern IntPtr GetDesktopWindow(); 28 | 29 | /// 30 | ///Specifies a raster-operation code. These codes define how the color data for the 31 | ///source rectangle is to be combined with the color data for the destination 32 | ///rectangle to achieve the final color. 33 | /// 34 | [Flags] 35 | internal enum CopyPixelOperation 36 | { 37 | NoMirrorBitmap = -2147483648, 38 | 39 | /// dest = BLACK, 0x00000042 40 | Blackness = 66, 41 | 42 | ///dest = (NOT src) AND (NOT dest), 0x001100A6 43 | NotSourceErase = 1114278, 44 | 45 | ///dest = (NOT source), 0x00330008 46 | NotSourceCopy = 3342344, 47 | 48 | ///dest = source AND (NOT dest), 0x00440328 49 | SourceErase = 4457256, 50 | 51 | /// dest = (NOT dest), 0x00550009 52 | DestinationInvert = 5570569, 53 | 54 | /// dest = pattern XOR dest, 0x005A0049 55 | PatInvert = 5898313, 56 | 57 | ///dest = source XOR dest, 0x00660046 58 | SourceInvert = 6684742, 59 | 60 | ///dest = source AND dest, 0x008800C6 61 | SourceAnd = 8913094, 62 | 63 | /// dest = (NOT source) OR dest, 0x00BB0226 64 | MergePaint = 12255782, 65 | 66 | ///dest = (source AND pattern), 0x00C000CA 67 | MergeCopy = 12583114, 68 | 69 | ///dest = source, 0x00CC0020 70 | SourceCopy = 13369376, 71 | 72 | /// dest = source OR dest, 0x00EE0086 73 | SourcePaint = 15597702, 74 | 75 | /// dest = pattern, 0x00F00021 76 | PatCopy = 15728673, 77 | 78 | /// dest = DPSnoo, 0x00FB0A09 79 | PatPaint = 16452105, 80 | 81 | /// dest = WHITE, 0x00FF0062 82 | Whiteness = 16711778, 83 | 84 | /// 85 | /// Capture window as seen on screen. This includes layered windows 86 | /// such as WPF windows with AllowsTransparency="true", 0x40000000 87 | /// 88 | CaptureBlt = 1073741824, 89 | } 90 | 91 | 92 | [DllImport("user32")] 93 | public static extern int mouse_event(MouseEventFlag dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); 94 | [Flags] 95 | public enum MouseEventFlag : uint 96 | { 97 | Move = 0x0001, 98 | LeftDown = 0x0002, 99 | LeftUp = 0x0004, 100 | RightDown = 0x0008, 101 | RightUp = 0x0010, 102 | MiddleDown = 0x0020, 103 | MiddleUp = 0x0040, 104 | XDown = 0x0080, 105 | XUp = 0x0100, 106 | Wheel = 0x0800, 107 | VirtualDesk = 0x4000, 108 | Absolute = 0x8000 109 | } 110 | [DllImport("user32.dll")] 111 | public static extern bool SetCursorPos(int X, int Y); 112 | 113 | 114 | [DllImport("user32.dll")] 115 | public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 116 | [DllImport("User32.dll ")] 117 | public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childe, string strclass, string FrmText); 118 | [DllImport("user32.dll", EntryPoint = "PostMessage")] 119 | public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 120 | 121 | [DllImport("User32.dll", EntryPoint = "SendMessage")] 122 | public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 123 | 124 | 125 | 126 | [DllImport("user32.dll")] 127 | [return: MarshalAs(UnmanagedType.Bool)] 128 | public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); 129 | 130 | [StructLayout(LayoutKind.Sequential)] 131 | public struct RECT 132 | { 133 | public int Left; //最左坐标 134 | public int Top; //最上坐标 135 | public int Right; //最右坐标 136 | public int Bottom; //最下坐标 137 | } 138 | 139 | [DllImport("user32.dll", SetLastError = true)] 140 | public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); 141 | [DllImport("user32.dll", SetLastError = true)] 142 | public static extern bool UnregisterHotKey(IntPtr hWnd, int id); 143 | 144 | [DllImport("user32.dll")] 145 | internal static extern bool HideCaret(IntPtr controlHandle); 146 | 147 | [DllImport("user32.dll")] 148 | public static extern int SetForegroundWindow(IntPtr hwnd); 149 | 150 | [DllImport("user32.dll")] 151 | public static extern IntPtr GetForegroundWindow(); 152 | 153 | [DllImport("user32.dll")] 154 | public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); 155 | 156 | [DllImport("user32.dll")] 157 | public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId); 158 | 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/PrimaryScreen.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace GeniusInvokationAutoToy.Utils 6 | { 7 | class PrimaryScreen 8 | { 9 | #region Win32 API 10 | [DllImport("user32.dll")] 11 | static extern IntPtr GetDC(IntPtr ptr); 12 | [DllImport("gdi32.dll")] 13 | static extern int GetDeviceCaps( 14 | IntPtr hdc, // handle to DC 15 | int nIndex // index of capability 16 | ); 17 | [DllImport("user32.dll", EntryPoint = "ReleaseDC")] 18 | static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc); 19 | #endregion 20 | #region DeviceCaps常量 21 | const int HORZRES = 8; 22 | const int VERTRES = 10; 23 | const int LOGPIXELSX = 88; 24 | const int LOGPIXELSY = 90; 25 | const int DESKTOPVERTRES = 117; 26 | const int DESKTOPHORZRES = 118; 27 | #endregion 28 | 29 | #region 属性 30 | /// 31 | /// 获取屏幕分辨率当前物理大小 32 | /// 33 | public static Size WorkingArea 34 | { 35 | get 36 | { 37 | IntPtr hdc = GetDC(IntPtr.Zero); 38 | Size size = new Size(); 39 | size.Width = GetDeviceCaps(hdc, HORZRES); 40 | size.Height = GetDeviceCaps(hdc, VERTRES); 41 | ReleaseDC(IntPtr.Zero, hdc); 42 | return size; 43 | } 44 | } 45 | /// 46 | /// 当前系统DPI_X 大小 一般为96 47 | /// 48 | public static int DpiX 49 | { 50 | get 51 | { 52 | IntPtr hdc = GetDC(IntPtr.Zero); 53 | int DpiX = GetDeviceCaps(hdc, LOGPIXELSX); 54 | ReleaseDC(IntPtr.Zero, hdc); 55 | return DpiX; 56 | } 57 | } 58 | /// 59 | /// 当前系统DPI_Y 大小 一般为96 60 | /// 61 | public static int DpiY 62 | { 63 | get 64 | { 65 | IntPtr hdc = GetDC(IntPtr.Zero); 66 | int DpiX = GetDeviceCaps(hdc, LOGPIXELSY); 67 | ReleaseDC(IntPtr.Zero, hdc); 68 | return DpiX; 69 | } 70 | } 71 | /// 72 | /// 获取真实设置的桌面分辨率大小 73 | /// 74 | public static Size DESKTOP 75 | { 76 | get 77 | { 78 | IntPtr hdc = GetDC(IntPtr.Zero); 79 | Size size = new Size(); 80 | size.Width = GetDeviceCaps(hdc, DESKTOPHORZRES); 81 | size.Height = GetDeviceCaps(hdc, DESKTOPVERTRES); 82 | ReleaseDC(IntPtr.Zero, hdc); 83 | return size; 84 | } 85 | } 86 | 87 | /// 88 | /// 获取宽度缩放百分比 89 | /// 90 | public static float ScaleX 91 | { 92 | get 93 | { 94 | IntPtr hdc = GetDC(IntPtr.Zero); 95 | int t = GetDeviceCaps(hdc, DESKTOPHORZRES); 96 | int d = GetDeviceCaps(hdc, HORZRES); 97 | float ScaleX = (float)GetDeviceCaps(hdc, DESKTOPHORZRES) / (float)GetDeviceCaps(hdc, HORZRES); 98 | ReleaseDC(IntPtr.Zero, hdc); 99 | return ScaleX; 100 | } 101 | } 102 | /// 103 | /// 获取高度缩放百分比 104 | /// 105 | public static float ScaleY 106 | { 107 | get 108 | { 109 | IntPtr hdc = GetDC(IntPtr.Zero); 110 | float ScaleY = (float)(float)GetDeviceCaps(hdc, DESKTOPVERTRES) / (float)GetDeviceCaps(hdc, VERTRES); 111 | ReleaseDC(IntPtr.Zero, hdc); 112 | return ScaleY; 113 | } 114 | } 115 | #endregion 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/Utils/Retry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace GeniusInvokationAutoToy.Utils 9 | { 10 | /// 11 | /// https://stackoverflow.com/questions/1563191/cleanest-way-to-write-retry-logic 12 | /// 13 | public static class Retry 14 | { 15 | public static void Do( 16 | Action action, 17 | TimeSpan retryInterval, 18 | int maxAttemptCount = 3) 19 | { 20 | Do(() => 21 | { 22 | action(); 23 | return null; 24 | }, retryInterval, maxAttemptCount); 25 | } 26 | 27 | public static T Do( 28 | Func action, 29 | TimeSpan retryInterval, 30 | int maxAttemptCount = 3) 31 | { 32 | var exceptions = new List(); 33 | 34 | for (int attempted = 0; attempted < maxAttemptCount; attempted++) 35 | { 36 | try 37 | { 38 | if (attempted > 0) 39 | { 40 | Thread.Sleep(retryInterval); 41 | } 42 | 43 | return action(); 44 | } 45 | catch (Exception ex) 46 | { 47 | exceptions.Add(ex); 48 | } 49 | } 50 | 51 | throw new AggregateException(exceptions); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /GeniusInvokationAutoToy/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Image/p1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babalae/genius-invokation-auto-toy/03de89d6f9bbf97c84b58bc27b8e774f1ddf4367/Image/p1.png -------------------------------------------------------------------------------- /Image/p2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babalae/genius-invokation-auto-toy/03de89d6f9bbf97c84b58bc27b8e774f1ddf4367/Image/p2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 更多: **[🍨BetterGI · 更好的原神](https://github.com/babalae/better-genshin-impact)** | [🐟 自动钓鱼机](https://github.com/babalae/genshin-fishing-toy) | [🛠️账号切换](https://github.com/babalae/mihoyo-starter) 2 | 3 | # !!! 💡 迁移 💡 !!! 4 | 5 | **当前自动打牌功能已经迁移至 [🍨BetterGI · 更好的原神](https://github.com/babalae/better-genshin-impact),并进行了大幅优化!** 6 | 7 | # 🎲 七圣召唤PVE全自动打牌 8 | 9 | PC原神七圣召唤PVE全自动打牌。 10 | 11 | [📺视频演示2](https://www.bilibili.com/video/BV1Lu4y1R7kM/) (视频演示1被审核锁定了,重新做了一个) 12 | 13 | 当前只完整支持无缩放 1920x1080 的窗口化游戏,其它分辨率很有可能会没法正常识别 14 | 15 | 支持角色邀请、每周来客挑战、部分大世界NPC挑战。 16 | 17 | 部分场景不支持、或者打不过、拿不满奖励。 18 | 19 | 1.3版本开始完全支持角色被超载、冻结等异常情况。 20 | 21 | 其他分辨率、语言想要支持也简单的,只要在对应分辨率下截取一些图片替换软件目录下的文件即可。 22 | 23 | **不支持或胜率低的场景:** 24 | * 雷电将军相关卡组由于无充能判断,可能在被冻结的情况下无法进行后续步骤 25 | * 角色被击败时被超载,会无法判断角色是否被击败 26 | * 因为场地牌,挂不上元素的情况,大概率打不过 27 | * 因为场地牌,元素骰子增减的情况,暂不支持(后续会支持...) 28 | 29 | ## 截图 30 | 31 | ![image](https://github.com/babalae/genius-invokation-auto-toy/assets/15783049/b41dc84f-6a83-406c-81ef-3eaddeac28c6) 32 | 33 | ## 下载地址 34 | 35 | [📥Github下载(1.4)](https://github.com/babalae/genius-invokation-auto-toy/releases/download/1.4/GeniusInvokationAutoToy.v1.4.zip) 36 | 37 | [📥蓝奏云下载](https://wwmy.lanzouq.com/b00r9kqwf) 密码:coco 38 | 39 | 40 | ## 使用方法 41 | 42 | 由于图像识别比较吃性能,低配置电脑可能无法正常使用。 43 | 44 | 你的系统需要满足以下条件: 45 | * Windows 7 或更高版本 46 | * 系统是64位(都已经2023年了,总没有还在用32位系统的人了吧) 47 | * [.NET Framework 4.7.2](https://support.microsoft.com/zh-cn/topic/%E9%80%82%E7%94%A8%E4%BA%8E-windows-%E7%9A%84-microsoft-net-framework-4-7-2-%E7%A6%BB%E7%BA%BF%E5%AE%89%E8%A3%85%E7%A8%8B%E5%BA%8F-05a72734-2127-a15d-50cf-daf56d5faec2) 或更高版本。**低于此版本在打开程序时可能无反应,或者直接报错**。 48 | 49 | **游戏推荐图像设置(图像质量中以上、游戏亮度必须为正常亮度):** 50 | 51 | 52 | 53 | 1、首先你的牌组必须是 **【莫娜、砂糖、琴】** 或者 **【刻晴、雷电将军、甘雨】** (其他牌组可以参考下面的“自定义自动打牌策略”),顺序不能变,带什么牌无所谓。([颠勺牌组玩法来源](https://www.bilibili.com/video/BV1ZP41197Ws),雷神牌组来源NGA) 54 | 55 | 56 | 57 | 58 | 2、**只支持1920x1080分辨率的游戏,游戏整个界面不能被其他窗口遮挡!游戏不能使用任何显卡滤镜!** 59 | 60 | 3、在游戏内进入七圣召唤对局,到**初始手牌**界面,如下图: 61 | 62 | 63 | 64 | 4、然后直接点击开始自动打牌,双手离开键盘鼠标(快捷键F11)。 65 | 66 | ## 自定义自动打牌策略 67 | 68 | 在软件当前目录的 `strategy` 的文件夹下,复制一个策略示例txt文件,自行参考格式编辑即可,注意技能1~3是**从右往左数**的。软件会自动根据行动策略和当前对局情况来切换角色和使用技能。 69 | 70 | 如果你有更好的卡组策略、或者是某种情况下的针对解法,欢迎发[Issue](https://github.com/babalae/genius-invokation-auto-toy/issues)分享~ 71 | 72 | ## FAQ 73 | * 为什么需要管理员权限? 74 | * 因为游戏以管理员权限启动,软件不以管理员权限启动的话没法模拟鼠标点击。 75 | * 会不会封号? 76 | * 只能说理论上不会被封,但是mhy是自由的,用户条款上明确说明模拟操作是封号理由之一。当前使用了 mouse_event 模拟鼠标点击,还是存在被检测的可能。只能说请低调使用,请不要跳脸官方。 77 | 78 | ## 问题反馈 79 | 80 | 提 [Issue](https://github.com/babalae/genius-invokation-auto-toy/issues) 81 | 82 | ## 投喂 83 | 84 | 觉的好用的话,可以支持作者哟ヾ(・ω・`。) 👇 85 | * [⚡爱发电](https://afdian.net/@huiyadanli) 86 | * [🍚微信赞赏](https://github.com/huiyadanli/huiyadanli/blob/master/DONATE.md) 87 | --------------------------------------------------------------------------------